From 3628834c21f03cbd0fffe4d4e4a17230206dc8eb Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 2 May 2021 14:43:01 +0200 Subject: [PATCH 001/114] Add boilerplate --- .gitignore | 21 +++++ .gitlab-ci.yml | 46 +++++++++ CHANGES.rst | 5 + CONTRIBUTING.rst | 78 ++++++++++++++++ LICENSE.txt | 202 ++++++++++++++++++++++++++++++++++++++++ README.rst | 85 +++++++++++++++++ docs/api/rosbags.rst | 6 ++ docs/changes.rst | 1 + docs/conf.py | 26 ++++++ docs/index.rst | 35 +++++++ pyproject.toml | 3 + setup.cfg | 157 +++++++++++++++++++++++++++++++ setup.py | 7 ++ tools/messages/.gitkeep | 0 14 files changed, 672 insertions(+) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 CHANGES.rst create mode 100644 CONTRIBUTING.rst create mode 100644 LICENSE.txt create mode 100644 README.rst create mode 100644 docs/api/rosbags.rst create mode 100644 docs/changes.rst create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 pyproject.toml create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 tools/messages/.gitkeep diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3a478baf --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +*.py[co] +*.egg-info/ +.eggs/ +__pycache__/ + +/build/ +/dist/ +/public/ +/venv/ + +/.mypy_cache/ +/.pytest_cache/ +/htmlcov/ +/.coverage +/coverage.xml +/report.xml + +/tools/messages/[^.]* + +*.sw[op] +/.vscode diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..1e9558ac --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,46 @@ +stages: + - test + - build + + +test: + stage: test + image: python:3.8 + script: + - python3.8 -m venv venv + - venv/bin/python -m pip install -r requirements-dev.txt + - venv/bin/python -m pip install -e .[dev] + - venv/bin/python -m pytest + - venv/bin/python -m coverage xml + - venv/bin/sphinx-build docs public + coverage: '/\d+\%\s*$/' + artifacts: + paths: + - public + reports: + cobertura: coverage.xml + junit: report.xml + + +build: + stage: build + image: python:3.8 + script: + - python3.8 -m venv venv + - venv/bin/python -m pip install build + - venv/bin/python -m build . + artifacts: + paths: + - dist + + +pages: + stage: build + image: python:3.8 + script: + - ls public + artifacts: + paths: + - public + only: + - master diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 00000000..a6c0955d --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,5 @@ +.. _changes: + +Changes +======= + diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 00000000..b5ee1f0b --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,78 @@ +================== +Contribution guide +================== + +Thank you for considering to contribute to rosbags. Below is information on how to report issues and submit your contributions to rosbags. + + +Rights to and license of contributions +====================================== + +Rosbags is licensed under `Apache 2.0`_. Your submission of an issue, merge request, comment, or code to us is: + +1. If your employer has rights in your contributions, your representation that your employer has authorized you to enter into this agreement on its behalf; + +2. Your agreement, or your employer's agreement, with the terms and conditions in this document; + +3. Your signature of the `Developer Certificate of Origin`_; and + +4. Your grant of a license to your contributions under `Apache 2.0`_. + + +Contributing code / merge requests +================================== + +In order to contribute code there are a few noteworthy things: + +1. Especially for non-trivial contributions, please **submit an issue first** to discuss your ideas. + +2. If your merge requests relates to an existing issue, please reference it from your merge request. + +3. When creating a merge request, please `allow collaboration`_. This enables us to make small adjustments and rebase the branch as needed. Please use dedicated branches for your merge request and don't give us access to a branch that is dear to you. + +4. Stick to *The seven rules of a great Git commit message* (see below). + +5. We require you to **sign-off your commits** (see below). Your sign-off indicates that you agreed to the terms and conditions laid out in this document, if applicable on behalf of your employer. + +.. _allow collaboration: + https://docs.gitlab.com/ee/user/project/merge_requests/allow_collaboration.html + + +The seven rules of a great Git commit message +--------------------------------------------- + +We like `The seven rules of a great Git commit message`_, summarized here for completeness, follow links for further reading. + +1. `Separate subject from body with a blank line `_ + +2. `Limit the subject line to 50 characters `_ (soft-limit 50, hard-limit 72) + +3. `Start subject line with uppercase letter `_ + +4. `Do not end the subject line with a period `_ + +5. `Use the imperative mood in the subject line `_ + +6. `Wrap the body at 72 characters `_ + +7. `Use the body to explain what and why vs. how `_ + +.. _The seven rules of a great Git commit message: https://chris.beams.io/posts/git-commit/#seven-rules + + +Signing off a commit +-------------------- + +You sign off a commit by adding a line like the following to the bottom of its commit message, separated by an empty line. + +:: + + Signed-off-by: Fullname + +Make sure it reflects your real name and email address. Git does this automatically when using ``git commit -s``. + +Except for the licenses granted herein, you reserve all right, title, and interest in and to your contributions. + + +.. _Apache 2.0: ./LICENSE.txt +.. _Developer Certificate of Origin: https://developercertificate.org/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..d373cdf9 --- /dev/null +++ b/README.rst @@ -0,0 +1,85 @@ +.. image:: https://gitlab.com/ternaris/rosbags/badges/master/pipeline.svg + :target: https://gitlab.com/ternaris/rosbags/-/commits/master + :alt: pipeline status + +.. image:: https://gitlab.com/ternaris/rosbags/badges/master/coverage.svg + :target: https://gitlab.com/ternaris/rosbags/-/commits/master + :alt: coverage report + + +======= +Rosbags +======= + +Rosbags is the **pure python** library for everything rosbag. It contains: + +- **rosbag2** reader and writer, +- **rosbag1** reader for raw messages, +- **extensible** type system with serializers and deserializers, +- **efficient converter** between rosbag1 and rosbag2, +- and more. + +Rosbags does not have any dependencies on the ROS software stacks and can be used on its own or alongside ROS1 or ROS2. + +Rosbags was developed for `MARV `_, which requires a fast, correct, and flexible library to read, manipulate, and write the various rosbag file formats. + + +Getting started +=============== + +Rosbags is published on PyPI and does not have any special dependencies. Simply install with pip:: + + pip install rosbags + + + +Documentation +============= + +Read the `documentation `_ for further information. + +.. end documentation + + +Contributing +============ + +Thank you for considering to contribute to rosbags. + +To submit issues or create merge requests please follow the instructions provided in the `contribution guide `_. + +By contributing to rosbags you accept and agree to the terms and conditions laid out in there. + + +Development +=========== + +Clone the repository and setup your local checkout:: + + git clone https://gitlab.com/ternaris/rosbags.git + + cd rosbags + python -m venv venv + . venv/bin/activate + + pip install -r requirements-dev.txt + pip install -e . + + +This creates a new virtual environment with the necessary python dependencies and installs rosbags in editable mode. The rosbags code base uses pytest as its test runner, run the test suite by simply invoking:: + + pytest + + +To build the documentation from its source run sphinx-build:: + + sphinx-build -a docs public + + +The entry point to the local documentation build should be available under ``public/index.html``. + + +Support +======= + +Professional support is available from `Ternaris `_. diff --git a/docs/api/rosbags.rst b/docs/api/rosbags.rst new file mode 100644 index 00000000..ef5b502d --- /dev/null +++ b/docs/api/rosbags.rst @@ -0,0 +1,6 @@ +Rosbags namespace +================= + +.. toctree:: + :maxdepth: 4 + diff --git a/docs/changes.rst b/docs/changes.rst new file mode 100644 index 00000000..d9e113ec --- /dev/null +++ b/docs/changes.rst @@ -0,0 +1 @@ +.. include:: ../CHANGES.rst diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..5b0537c0 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,26 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Sphinx config.""" + +import typing + +import sphinx_rtd_theme # noqa pylint: disable=unused-import + +# pylint: disable=invalid-name,redefined-builtin + +typing.TYPE_CHECKING = True + +project = 'Rosbags' +copyright = '2020-2021, Ternaris' +author = 'Ternaris' + +autoapi_python_use_implicit_namespaces = True + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', + 'sphinx_autodoc_typehints', + 'sphinx_rtd_theme', +] + +html_theme = 'sphinx_rtd_theme' diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..efe8bdfc --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,35 @@ +.. include:: ../README.rst + :end-before: Documentation + +.. include:: ../README.rst + :start-after: .. end documentation + + +.. toctree:: + :caption: Documentation + :maxdepth: 1 + :hidden: + + + +.. toctree:: + :caption: API + :glob: + :hidden: + + api/rosbags + + +.. toctree:: + :caption: Changes + :hidden: + + changes + + +.. toctree:: + :caption: Links + :hidden: + + Source Code + Issues diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..9594ad0c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=56.2.0", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..7b70f9fd --- /dev/null +++ b/setup.cfg @@ -0,0 +1,157 @@ +[metadata] +name = rosbags +version = 0.9.0 +author = Ternaris +author_email = team@ternaris.com +home_page = https://gitlab.com/ternaris/rosbags +description = Pure Python library to read, modify, convert, and write rosbag files. +long_description = file: README.rst +long_description_content_type = text/x-rst +keywords = + cdr + conversion + deserialization + idl + message + msg + reader + ros + rosbag + rosbag2 + serialization + writer +license = Apache 2.0 +license_files = LICENSE.txt +platform = any +classifiers = + Development Status :: 4 - Beta + License :: OSI Approved :: Apache Software License + Programming Language :: Python + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Topic :: Scientific/Engineering + Typing :: Typed +project_urls = + Code = https://gitlab.com/ternaris/rosbags + Documentation = https://ternaris.gitlab.io/rosbags + Issue tracker = https://gitlab.com/ternaris/rosbags/issues + +[options] +include_package_data = true +package_dir = + = src +packages = find_namespace: +zip_safe = false +python_requires = + >=3.8.2 +setup_requires = + setuptools >=40.8.0 + wheel +install_requires = + lz4 + numpy + ruamel.yaml + zstandard + +[options.extras_require] +dev = + darglint + flake8 + flake8-annotations + flake8-bugbear + flake8-commas + flake8-comprehensions + flake8-docstrings + flake8-fixme + flake8-isort + flake8-mutable + flake8-print + flake8-pytest-style + flake8-quotes + flake8-return + flake8-simplify + flake8-type-checking + flake8-use-fstring + pep8-naming + pytest + pytest-cov + pytest-flake8 + pytest-mypy + pytest-pylint + sphinx + sphinx-autodoc-typehints + sphinx-rtd-theme + +[options.packages.find] +where = src + +[sdist] +formats = gztar, zip + +[coverage:report] +exclude_lines = + pragma: no cover + if TYPE_CHECKING: + if __name__ == '__main__': + +[flake8] +avoid-escape = False +docstring_convention = google +docstring_style = google +extend-select = + # docstrings + D204, + D400, + D401, + D404, + D413, +ignore = + # do not require annotation of `self` + ANN101, + # allow line break before binary operator + W503, +max-line-length = 100 +strictness = long +suppress-none-returning = True + +[isort] +include_trailing_comma = True +line_length = 100 +multi_line_output = 3 + +[mypy] +ignore_missing_imports = True + +[pydocstyle] +convention = google +add-select = D204,D400,D401,D404,D413 + +[pylint.FORMAT] +max-line-length = 100 + +[pylint.'MESSAGES CONTROL'] +disable = + duplicate-code, + ungrouped-imports, + +[yapf] +based_on_style = google +column_limit = 100 +allow_split_before_dict_value = false +dedent_closing_brackets = true +indent_dictionary_value = false + +[tool:pytest] +addopts = + -v + --flake8 + --mypy + --pylint + --cov=src + --cov-branch + --cov-report=html + --cov-report=term + --no-cov-on-fail + --junitxml=report.xml +junit_family=xunit2 diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..10c3180c --- /dev/null +++ b/setup.py @@ -0,0 +1,7 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Minimal setuptools boilerplate.""" + +from setuptools import setup # type: ignore + +setup() diff --git a/tools/messages/.gitkeep b/tools/messages/.gitkeep new file mode 100644 index 00000000..e69de29b From a7461c8ae79947598deaafd7e4f8a4e72e80c0e4 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 2 May 2021 14:43:48 +0200 Subject: [PATCH 002/114] Add type system --- docs/api/rosbags.rst | 1 + docs/api/rosbags.typesys.rst | 6 + docs/index.rst | 1 + docs/topics/typesys.rst | 35 + src/rosbags/typesys/__init__.py | 29 + src/rosbags/typesys/__main__.py | 45 + src/rosbags/typesys/base.py | 70 + src/rosbags/typesys/idl.py | 465 ++++++ src/rosbags/typesys/msg.py | 215 +++ src/rosbags/typesys/peg.py | 247 ++++ src/rosbags/typesys/register.py | 112 ++ src/rosbags/typesys/types.py | 2420 +++++++++++++++++++++++++++++++ tests/__init__.py | 3 + tests/test_parse.py | 156 ++ 14 files changed, 3805 insertions(+) create mode 100644 docs/api/rosbags.typesys.rst create mode 100644 docs/topics/typesys.rst create mode 100644 src/rosbags/typesys/__init__.py create mode 100644 src/rosbags/typesys/__main__.py create mode 100644 src/rosbags/typesys/base.py create mode 100644 src/rosbags/typesys/idl.py create mode 100644 src/rosbags/typesys/msg.py create mode 100644 src/rosbags/typesys/peg.py create mode 100644 src/rosbags/typesys/register.py create mode 100644 src/rosbags/typesys/types.py create mode 100644 tests/__init__.py create mode 100644 tests/test_parse.py diff --git a/docs/api/rosbags.rst b/docs/api/rosbags.rst index ef5b502d..b106356c 100644 --- a/docs/api/rosbags.rst +++ b/docs/api/rosbags.rst @@ -4,3 +4,4 @@ Rosbags namespace .. toctree:: :maxdepth: 4 + rosbags.typesys diff --git a/docs/api/rosbags.typesys.rst b/docs/api/rosbags.typesys.rst new file mode 100644 index 00000000..58aad884 --- /dev/null +++ b/docs/api/rosbags.typesys.rst @@ -0,0 +1,6 @@ +rosbags.typesys +=============== + +.. automodule:: rosbags.typesys + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index efe8bdfc..83f2482d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,6 +10,7 @@ :maxdepth: 1 :hidden: + topics/typesys .. toctree:: diff --git a/docs/topics/typesys.rst b/docs/topics/typesys.rst new file mode 100644 index 00000000..acb98f8c --- /dev/null +++ b/docs/topics/typesys.rst @@ -0,0 +1,35 @@ +Type system +=========== + +Rosbags ships its own pure python typesystem :py:mod:`rosbags.typesys`. It uses parse trees to represent message definitions internally. It ships its own ``.idl`` and ``.msg`` definition parser to convert message definition files into the internal format. + +Out of the box it supports the message types defined by the standard ROS2 distribution. Message types can be parsed and added on the fly during runtime without an additional build step. + +Message instances +----------------- +The type system generates a dataclass for each message type. These dataclasses give direct read write access to all mutable fields of a message. Fields should be mutated with care as no type checking is applied during runtime. + +Extending the type system +------------------------- +Adding custom message types consists of two steps. First, message definitions are converted into parse trees using :py:func:`get_types_from_idl() ` or :py:func:`get_types_from_msg() `, and second the types are registered in the type system via :py:func:`register_types() `. The following example shows how to add messages type definitions from ``.msg`` and ``.idl`` files: + +.. code-block:: python + + from pathlib import Path + + from rosbags.typesys import get_types_from_idl, get_types_from_msg, register_types + + idl_text = Path('foo_msgs/msg/Foo.idl').read_text() + msg_text = Path('bar_msgs/msg/Bar.msg').read_text() + + # plain dictionary to hold message definitions + add_types = {} + + # add all definitions from one idl file + add_types.update(get_types_from_idl(idl_text)) + + # add definition from one msg file + add_types.update(get_types_from_msg(msg_text, 'bar_msgs/msg/Bar')) + + # make types available to rosbags serializers/deserializers + register_types(add_types) diff --git a/src/rosbags/typesys/__init__.py b/src/rosbags/typesys/__init__.py new file mode 100644 index 00000000..1dc19c22 --- /dev/null +++ b/src/rosbags/typesys/__init__.py @@ -0,0 +1,29 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbags Type System. + +The type system manages ROS message types and ships all standard ROS2 +distribution message types by default. The system supports custom message +types through parsers that dynamically parse custom message definitons +from different source formats. + +Supported formats: + - IDL files (subset of the standard necessary for parsing ROS2 IDL) `[1]`_ + - MSG files `[2]`_ + +.. _[1]: https://www.omg.org/spec/IDL/About-IDL/ +.. _[2]: http://wiki.ros.org/msg + +""" + +from .base import TypesysError +from .idl import get_types_from_idl +from .msg import get_types_from_msg +from .register import register_types + +__all__ = [ + 'TypesysError', + 'get_types_from_idl', + 'get_types_from_msg', + 'register_types', +] diff --git a/src/rosbags/typesys/__main__.py b/src/rosbags/typesys/__main__.py new file mode 100644 index 00000000..4396fcaf --- /dev/null +++ b/src/rosbags/typesys/__main__.py @@ -0,0 +1,45 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Tool to update builtin types shipped with rosbags.""" + +from __future__ import annotations + +from os import walk +from pathlib import Path +from typing import TYPE_CHECKING + +from .idl import get_types_from_idl +from .msg import get_types_from_msg +from .register import generate_python_code, register_types + +if TYPE_CHECKING: + from .base import Typesdict + + +def main() -> None: # pragma: no cover + """Update builtin types. + + Discover message definitions in filesystem and generate types.py module. + + """ + typs: Typesdict = {} + selfdir = Path(__file__).parent + for root, dirnames, files in walk(selfdir.parents[2] / 'tools' / 'messages'): + if '.rosbags_ignore' in files: + dirnames.clear() + continue + for fname in files: + path = Path(root, fname) + if path.suffix == '.idl': + typs.update(get_types_from_idl(path.read_text())) + elif path.suffix == '.msg': + name = path.relative_to(path.parents[2]).with_suffix('') + if '/msg/' not in str(name): + name = name.parent / 'msg' / name.name + typs.update(get_types_from_msg(path.read_text(), str(name))) + register_types(typs) + (selfdir / 'types.py').write_text(generate_python_code(typs)) + + +if __name__ == '__main__': + main() diff --git a/src/rosbags/typesys/base.py b/src/rosbags/typesys/base.py new file mode 100644 index 00000000..d7d34377 --- /dev/null +++ b/src/rosbags/typesys/base.py @@ -0,0 +1,70 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Types and helpers used by message definition converters.""" + +from __future__ import annotations + +from enum import IntEnum, auto +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any, Dict, List, Tuple + + from .peg import Visitor + + Fielddefs = List[Tuple[Any, Any]] + Typesdict = Dict[str, Fielddefs] + + +class TypesysError(Exception): + """Parser error.""" + + +class Nodetype(IntEnum): + """Parse tree node types. + + The first four match the Valtypes of final message definitions. + """ + + BASE = auto() + NAME = auto() + ARRAY = auto() + SEQUENCE = auto() + + LITERAL_STRING = auto() + LITERAL_NUMBER = auto() + LITERAL_BOOLEAN = auto() + LITERAL_CHAR = auto() + + MODULE = auto() + CONST = auto() + STRUCT = auto() + SDECLARATOR = auto() + ADECLARATOR = auto() + ANNOTATION = auto() + EXPRESSION_BINARY = auto() + EXPRESSION_UNARY = auto() + + +def parse_message_definition(visitor: Visitor, text: str) -> Typesdict: + """Parse message definition. + + Args: + visitor: Visitor instance to use. + text: Message definition. + + Returns: + Parsetree of message. + + Raises: + TypesysError: Message parsing failed. + + """ + try: + rule = visitor.RULES['specification'] + pos = rule.skip_ws(text, 0) + npos, trees = rule.parse(text, pos) + assert npos == len(text), f'Could not parse: {text!r}' + return visitor.visit(trees) + except Exception as err: # pylint: disable=broad-except + raise TypesysError(f'Could not parse: {text!r}') from err diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py new file mode 100644 index 00000000..acc6bafa --- /dev/null +++ b/src/rosbags/typesys/idl.py @@ -0,0 +1,465 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""IDL Parser. + +Grammar, parse tree visitor and conversion functions for message definitions in +`IDL`_ format. + +.. _IDL: https://www.omg.org/spec/IDL/About-IDL/ + +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .base import Nodetype, parse_message_definition +from .peg import Rule, Visitor, parse_grammar + +if TYPE_CHECKING: + from typing import Any + + from .base import Typesdict + +GRAMMAR_IDL = r""" +specification + = definition+ + +definition + = comment + / macro + / include + / module_dcl ';' + / const_dcl ';' + / type_dcl ';' + +comment + = r'[/][/][^\n]*' + +macro + = ifndef + / define + / endif + +ifndef + = '#ifndef' r'[a-zA-Z0-9_]+' + +define + = '#define' r'[a-zA-Z0-9_]+' + +endif + = '#endif' + +include + = '#include' include_filename + +include_filename + = '<' r'[^>]+' '>' + / '"' r'[^"]+' '"' + +module_dcl + = annotation* 'module' identifier '{' definition+ '}' + +const_dcl + = 'const' const_type identifier '=' expression + +type_dcl + = typedef_dcl + / constr_type_dcl + +typedef_dcl + = 'typedef' type_declarator + +type_declarator + = ( simple_type_spec + / template_type_spec + / constr_type_dcl + ) any_declarators + +simple_type_spec + = base_type_spec + / scoped_name + +template_type_spec + = sequence_type + / string_type + +sequence_type + = 'sequence' '<' type_spec ',' expression '>' + / 'sequence' '<' type_spec '>' + +type_spec + = template_type_spec + / simple_type_spec + +any_declarators + = any_declarator (',' any_declarator)* + +any_declarator + = array_declarator + / simple_declarator + +constr_type_dcl + = struct_dcl + +struct_dcl + = struct_def + +struct_def + = annotation* 'struct' identifier '{' member+ '}' + +member + = annotation* type_spec declarators ';' + +declarators + = declarator (',' declarator)* + +declarator + = array_declarator + / simple_declarator + +simple_declarator + = identifier + +array_declarator + = identifier fixed_array_size+ + +fixed_array_size + = '[' expression ']' + +annotation + = '@' scoped_name ('(' annotation_params ')')? + +annotation_params + = annotation_param (',' annotation_param)* + / expression + +annotation_param + = identifier '=' expression + +const_type + = base_type_spec + / string_type + / scoped_name + +base_type_spec + = integer_type + / float_type + / char_type + / boolean_type + / octet_type + +integer_type + = r'u?int(64|32|16|8)\b' + / r'(unsigned\s+)?((long\s+)?long|int|short)\b' + +float_type + = r'((long\s+)?double|float)\b' + +char_type + = r'char\b' + +boolean_type + = r'boolean\b' + +octet_type + = r'octet\b' + +string_type + = 'string' '<' expression '>' + / 'string' + +scoped_name + = identifier '::' scoped_name + / '::' scoped_name + / identifier + +identifier + = r'[a-zA-Z_][a-zA-Z_0-9]*' + +expression + = primary_expr binary_operator primary_expr + / primary_expr + / unary_operator primary_expr + +primary_expr + = literal + / scoped_name + / '(' expression ')' + +binary_operator + = '|' + / '^' + / '&' + / '<<' + / '>>' + / '+' + / '-' + / '*' + / '/' + / '%' + +unary_operator + = '+' + / '-' + / '~' + +literal + = boolean_literal + / float_literal + / integer_literal + / character_literal + / string_literals + +boolean_literal + = 'TRUE' + / 'FALSE' + +integer_literal + = hexadecimal_literal + / octal_literal + / decimal_literal + +decimal_literal + = r'[-+]?[1-9][0-9]+' + / r'[-+]?[0-9]' + +octal_literal + = r'[-+]?0[0-7]+' + +hexadecimal_literal + = r'[-+]?0[xX][a-fA-F0-9]+' + +float_literal + = r'[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?' + / r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)' + +character_literal + = '\'' r'[a-zA-Z0-9_]' '\'' + +string_literals + = string_literal+ + +string_literal + = '"' r'(\\"|[^"])*' '"' +""" + + +class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods + """IDL file visitor.""" + + # pylint: disable=no-self-use + + RULES = parse_grammar(GRAMMAR_IDL) + + def visit_specification(self, children: Any) -> Typesdict: + """Process start symbol, return only children of modules.""" + children = [x[0] for x in children if x is not None] + modules = [y for t, x in children if t == Nodetype.MODULE for y in x] + return {x[1]: x[2] for x in modules if x[0] == Nodetype.STRUCT} + + def visit_comment(self, children: Any) -> Any: + """Process comment, suppress output.""" + + def visit_macro(self, children: Any) -> Any: + """Process macro, suppress output.""" + + def visit_include(self, children: Any) -> Any: + """Process include, suppress output.""" + + def visit_type_dcl(self, children: Any) -> Any: + """Process typedef, pass structs, suppress otherwise.""" + if children[0] == Nodetype.STRUCT: + return children + return None + + def visit_module_dcl(self, children: Any) -> Any: + """Process module declaration.""" + assert len(children) == 6 + assert children[2][0] == Nodetype.NAME + name = children[2][1] + + children = children[4] + consts = [] + structs = [] + modules = [] + for item in children: + if not item or item[0] is None: + continue + item = item[0] + if item[0] == Nodetype.CONST: + consts.append(item) + elif item[0] == Nodetype.STRUCT: + structs.append(item) + else: + assert item[0] == Nodetype.MODULE + modules.append(item) + + for _, module in modules: + consts += [x for x in module if x[0] == Nodetype.CONST] + structs += [x for x in module if x[0] == Nodetype.STRUCT] + + consts = [(x[0], f'{name}/{x[1][0]}', *x[1][1:]) for x in consts] + structs = [(x[0], f'{name}/{x[1]}', *x[2:]) for x in structs] + + return (Nodetype.MODULE, consts + structs) + + def visit_const_dcl(self, children: Any) -> Any: + """Process const declaration.""" + return (Nodetype.CONST, (children[1][1], *children[2:])) + + def visit_type_declarator(self, children: Any) -> Any: + """Process type declarator, register type mapping in instance typedef dictionary.""" + assert len(children) == 2 + base, declarators = children + if base[1] in self.typedefs: + base = self.typedefs[base[1]] + declarators = [children[1][0], *[x[1:][0] for x in children[1][1]]] + for declarator in declarators: + if declarator[0] == Nodetype.ADECLARATOR: + value = (Nodetype.ARRAY, declarator[2][1], base) + else: + value = base + self.typedefs[declarator[1][1]] = value + + def visit_sequence_type(self, children: Any) -> Any: + """Process sequence type specification.""" + assert len(children) in [4, 6] + if len(children) == 6: + assert children[4][0] == Nodetype.LITERAL_NUMBER + return (Nodetype.SEQUENCE, children[2]) + return (Nodetype.SEQUENCE, children[2]) + + def create_struct_field(self, parts: Any) -> Any: + """Create struct field and expand typedefs.""" + typename, params = parts[1:3] + params = [params[0], *[x[1:][0] for x in params[1]]] + + def resolve_name(name: Any) -> Any: + while name[0] == Nodetype.NAME and name[1] in self.typedefs: + name = self.typedefs[name[1]] + return name + + yield from ((resolve_name(typename), x[1]) for x in params if x) + + def visit_struct_dcl(self, children: Any) -> Any: + """Process struct declaration.""" + assert len(children) == 6 + assert children[2][0] == Nodetype.NAME + + fields = [y for x in children[4] for y in self.create_struct_field(x)] + return (Nodetype.STRUCT, children[2][1], fields) + + def visit_simple_declarator(self, children: Any) -> Any: + """Process simple declarator.""" + assert len(children) == 2 + return (Nodetype.SDECLARATOR, children) + + def visit_array_declarator(self, children: Any) -> Any: + """Process array declarator.""" + assert len(children) == 2 + return (Nodetype.ADECLARATOR, children[0], children[1][0][1]) + + def visit_annotation(self, children: Any) -> Any: + """Process annotation.""" + assert len(children) == 3 + assert children[1][0] == Nodetype.NAME + params = children[2][0][1] + params = [ + [z for z in y if z[0] != Rule.LIT] for y in [params[0], *[x[1:][0] for x in params[1]]] + ] + return (Nodetype.ANNOTATION, children[1][1], params) + + def visit_base_type_spec(self, children: Any) -> Any: + """Process base type specifier.""" + oname = children + name = { + 'boolean': 'bool', + 'double': 'float64', + 'float': 'float32', + 'octet': 'uint8', + }.get(oname, oname) + return (Nodetype.BASE, name) + + def visit_string_type(self, children: Any) -> Any: + """Prrocess string type specifier.""" + assert len(children) in [2, 4] + if len(children) == 4: + return (Nodetype.BASE, 'string', children[2]) + return (Nodetype.BASE, 'string') + + def visit_scoped_name(self, children: Any) -> Any: + """Process scoped name.""" + if len(children) == 2: + return (Nodetype.NAME, children[1]) + assert len(children) == 3 + assert children[1][1] == '::' + return (Nodetype.NAME, f'{children[0][1]}/{children[2][1]}') + + def visit_identifier(self, children: Any) -> Any: + """Process identifier.""" + return (Nodetype.NAME, children) + + def visit_expression(self, children: Any) -> Any: + """Process expression, literals are assumed to be integers only.""" + if children[0] in [ + Nodetype.LITERAL_STRING, + Nodetype.LITERAL_NUMBER, + Nodetype.LITERAL_BOOLEAN, + Nodetype.LITERAL_CHAR, + Nodetype.NAME, + ]: + return children + + assert len(children) in [2, 3] + if len(children) == 3: + assert isinstance(children[0][1], int) + assert isinstance(children[2][1], int) + return (Nodetype.EXPRESSION_BINARY, children[1], children[0][1], children[2]) + assert len(children) == 2 + assert isinstance(children[1][1], int), children + return (Nodetype.EXPRESSION_UNARY, children[0][1], children[1]) + + def visit_boolean_literal(self, children: Any) -> Any: + """Process boolean literal.""" + return (Nodetype.LITERAL_BOOLEAN, children[1] == 'TRUE') + + def visit_float_literal(self, children: Any) -> Any: + """Process float literal.""" + return (Nodetype.LITERAL_NUMBER, float(children)) + + def visit_decimal_literal(self, children: Any) -> Any: + """Process decimal integer literal.""" + return (Nodetype.LITERAL_NUMBER, int(children)) + + def visit_octal_literal(self, children: Any) -> Any: + """Process octal integer literal.""" + return (Nodetype.LITERAL_NUMBER, int(children, 8)) + + def visit_hexadecimal_literal(self, children: Any) -> Any: + """Process hexadecimal integer literal.""" + return (Nodetype.LITERAL_NUMBER, int(children, 16)) + + def visit_character_literal(self, children: Any) -> Any: + """Process char literal.""" + return (Nodetype.LITERAL_CHAR, children[1]) + + def visit_string_literals(self, children: Any) -> Any: + """Process string literal.""" + return ( + Nodetype.LITERAL_STRING, + ''.join(y for x in children for y in x if y and y[0] != Rule.LIT), + ) + + +def get_types_from_idl(text: str) -> Typesdict: + """Get types from idl message definition. + + Args: + text: Message definition. + + Returns: + List of message message names and parsetrees. + + """ + return parse_message_definition(VisitorIDL(), text) diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py new file mode 100644 index 00000000..52943a43 --- /dev/null +++ b/src/rosbags/typesys/msg.py @@ -0,0 +1,215 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""MSG Parser. + +Grammar, parse tree visitor and conversion functions for message definitions in +`MSG`_ format. It also supports concatened message definitions as found in +Rosbag1 connection information. + +.. _MSG: http://wiki.ros.org/msg + +""" + +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING + +from .base import Nodetype, parse_message_definition +from .peg import Rule, Visitor, parse_grammar + +if TYPE_CHECKING: + from typing import Any, List + + from .base import Typesdict + +GRAMMAR_MSG = r""" +specification + = msgdef (msgsep msgdef)* + +msgdef + = r'MSG:\s' scoped_name definition+ + +msgsep + = r'================================================================================' + +definition + = comment + / const_dcl + / field_dcl + +comment + = r'#[^\n]*' + +const_dcl + = type_spec identifier '=' r'[^=][^\n]*' + +field_dcl + = type_spec identifier + +type_spec + = array_type_spec + / simple_type_spec + +array_type_spec + = simple_type_spec array_size + +simple_type_spec + = scoped_name + +array_size + = '[' integer_literal? ']' + +integer_literal + = r'[-+]?[1-9][0-9]+' + / r'[-+]?[0-9]' + +scoped_name + = identifier '/' scoped_name + / identifier + +identifier + = r'[a-zA-Z_][a-zA-Z_0-9]*' +""" + + +def normalize_msgtype(name: str) -> str: + """Normalize message typename. + + Args: + name: Message typename. + + Returns: + Normalized name. + + """ + path = Path(name) + if path.parent.name != 'msg': + return str(path.parent / 'msg' / path.name) + return name + + +def normalize_fieldtype(field: Any, names: List[str]): + """Normalize field typename. + + Args: + field: Field definition. + names: Valid message names. + + """ + dct = {Path(name).name: name for name in names} + namedef = field[0] + if namedef[0] == Nodetype.NAME: + name = namedef[1] + elif namedef[0] == Nodetype.SEQUENCE: + name = namedef[1][1] + else: + name = namedef[2][1] + + if name in VisitorMSG.BASETYPES: + inamedef = (Nodetype.BASE, name) + else: + if name in dct: + name = dct[name] + elif '/msg/' not in name: + ptype = Path(name) + name = str(ptype.parent / 'msg' / ptype.name) + inamedef = (Nodetype.NAME, name) + + if namedef[0] == Nodetype.NAME: + namedef = inamedef + elif namedef[0] == Nodetype.SEQUENCE: + namedef = (Nodetype.SEQUENCE, inamedef) + else: + namedef = (Nodetype.ARRAY, namedef[1], inamedef) + + field[0] = namedef + + +class VisitorMSG(Visitor): + """MSG file visitor.""" + + # pylint: disable=no-self-use + + RULES = parse_grammar(GRAMMAR_MSG) + + BASETYPES = { + 'bool', + 'int8', + 'int16', + 'int32', + 'int64', + 'uint8', + 'uint16', + 'uint32', + 'uint64', + 'float32', + 'float64', + 'string', + } + + def visit_comment(self, children: Any) -> Any: + """Process comment, suppress output.""" + + def visit_const_dcl(self, children: Any) -> Any: + """Process const declaration, suppress output.""" + + def visit_specification(self, children: Any) -> Typesdict: + """Process start symbol.""" + typelist = [children[0], *[x[1] for x in children[1]]] + typedict = dict(typelist) + names = list(typedict.keys()) + for _, fields in typedict.items(): + for field in fields: + normalize_fieldtype(field, names) + return typedict + + def visit_msgdef(self, children: Any) -> Any: + """Process single message definition.""" + assert len(children) == 3 + return normalize_msgtype(children[1][1]), [x for x in children[2] if x is not None] + + def visit_msgsep(self, children: Any) -> Any: + """Process message separator, suppress output.""" + + def visit_array_type_spec(self, children: Any) -> Any: + """Process array type specifier.""" + length = children[1][1] + if length: + return (Nodetype.ARRAY, int(length[0]), children[0]) + return (Nodetype.SEQUENCE, children[0]) + + def visit_simple_type_spec(self, children: Any) -> Any: + """Process simple type specifier.""" + dct = { + 'time': 'builtin_interfaces/msg/Time', + 'duration': 'builtin_interfaces/msg/Duration', + 'byte': 'uint8', + 'char': 'uint8', + } + return Nodetype.NAME, dct.get(children[1], children[1]) + + def visit_scoped_name(self, children: Any) -> Any: + """Process scoped name.""" + if len(children) == 2: + return children + assert len(children) == 3 + return (Nodetype.NAME, '/'.join(x[1] for x in children if x[0] != Rule.LIT)) + + def visit_identifier(self, children: Any) -> Any: + """Process identifier.""" + return (Nodetype.NAME, children) + + +def get_types_from_msg(text: str, name: str) -> Typesdict: + """Get type from msg message definition. + + Args: + text: Message definiton. + name: Message typename. + + Returns: + List with single message name and parsetree. + + """ + return parse_message_definition(VisitorMSG(), f'MSG: {name}\n{text}') diff --git a/src/rosbags/typesys/peg.py b/src/rosbags/typesys/peg.py new file mode 100644 index 00000000..95d1e731 --- /dev/null +++ b/src/rosbags/typesys/peg.py @@ -0,0 +1,247 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""PEG Parser. + +Parsing expression grammar inspired parser for simple EBNF-like notations. It +implements just enough features to support parsing of the different ROS message +definition formats. + +""" + +from __future__ import annotations + +import re +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any, Dict, List, Optional, Tuple + + +class Rule: + """Rule base class.""" + + LIT = 'LITERAL' + WS = re.compile(r'\s+', re.M | re.S) + + def __init__(self, value: Any, rules: Dict[str, Rule], name: Optional[str] = None): + """Initialize. + + Args: + value: Value of this rule. + rules: Grammar containing all rules. + name: Name of this rule. + + """ + self.value = value + self.rules = rules + self.name = name + + def skip_ws(self, text: str, pos: int) -> int: + """Skip whitespace.""" + match = self.WS.match(text, pos) + return match.span()[1] if match else pos + + def make_node(self, data: Any) -> Any: + """Make node for parse tree.""" + if self.name: + return { + 'node': self.name, + 'data': data, + } + return data + + def parse(self, text: str, pos: int): + """Apply rule at position.""" + raise NotImplementedError # pragma: no cover + + +class RuleLiteral(Rule): + """Rule to match string literal.""" + + def parse(self, text: str, pos: int) -> Tuple[int, Any]: + """Apply rule at position.""" + value: str = self.value[1:-1].replace('\\\'', '\'') + if text[pos:].startswith(value): + npos = pos + len(value) + npos = self.skip_ws(text, npos) + return npos, (self.LIT, value) + return -1, () + + +class RuleRegex(Rule): + """Rule to match regular expression.""" + + def parse(self, text: str, pos: int) -> Tuple[int, Any]: + """Apply rule at position.""" + pattern = re.compile(self.value[2:-1], re.M | re.S) + match = pattern.match(text, pos) + if not match: + return -1, [] + npos = self.skip_ws(text, match.span()[1]) + return npos, self.make_node(match.group()) + + +class RuleToken(Rule): + """Rule to match token.""" + + def parse(self, text: str, pos: int) -> Tuple[int, Any]: + """Apply rule at position.""" + token = self.rules[self.value] + npos, data = token.parse(text, pos) + if npos == -1: + return npos, data + return npos, self.make_node(data) + + +class RuleOneof(Rule): + """Rule to match first matching subrule.""" + + def parse(self, text: str, pos: int) -> Tuple[int, Any]: + """Apply rule at position.""" + for value in self.value: + npos, data = value.parse(text, pos) + if npos != -1: + return npos, self.make_node(data) + return -1, [] + + +class RuleSequence(Rule): + """Rule to match a sequence of subrules.""" + + def parse(self, text: str, pos: int) -> Tuple[int, Any]: + """Apply rule at position.""" + data = [] + npos = pos + for value in self.value: + npos, node = value.parse(text, npos) + if npos == -1: + return -1, [] + data.append(node) + return npos, self.make_node(data) + + +class RuleZeroPlus(Rule): + """Rule to match zero or more occurences of subrule.""" + + def parse(self, text: str, pos: int) -> Tuple[int, Any]: + """Apply rule at position.""" + data: List[Any] = [] + lpos = pos + while True: + npos, node = self.value.parse(text, lpos) + if npos == -1: + return lpos, self.make_node(data) + data.append(node) + lpos = npos + + +class RuleOnePlus(Rule): + """Rule to match one or more occurences of subrule.""" + + def parse(self, text: str, pos: int) -> Tuple[int, Any]: + """Apply rule at position.""" + npos, node = self.value.parse(text, pos) + if npos == -1: + return -1, [] + data = [node] + lpos = npos + while True: + npos, node = self.value.parse(text, lpos) + if npos == -1: + return lpos, self.make_node(data) + data.append(node) + lpos = npos + + +class RuleZeroOne(Rule): + """Rule to match zero or one occurence of subrule.""" + + def parse(self, text: str, pos: int) -> Tuple[int, Any]: + """Apply rule at position.""" + npos, node = self.value.parse(text, pos) + if npos == -1: + return pos, self.make_node([]) + return npos, self.make_node([node]) + + +class Visitor: # pylint: disable=too-few-public-methods + """Visitor transforming parse trees.""" + + RULES: Dict[str, Rule] = {} + + def __init__(self): + """Initialize.""" + self.typedefs = {} + + def visit(self, tree: Any) -> Any: + """Visit all nodes in parse tree.""" + if isinstance(tree, list): + return [self.visit(x) for x in tree] + + if not isinstance(tree, dict): + return tree + + tree['data'] = self.visit(tree['data']) + func = getattr(self, f'visit_{tree["node"]}', lambda x: x) + return func(tree['data']) + + +def split_token(tok: str) -> List[str]: + """Split repetition and grouping tokens.""" + return list(filter(None, re.split(r'(^\()|(\)(?=[*+?]?$))|([*+?]$)', tok))) + + +def collapse_tokens(toks: List[Optional[Rule]], rules: Dict[str, Rule]) -> Rule: + """Collapse linear list of tokens to oneof of sequences.""" + value: List[Rule] = [] + seq: List[Rule] = [] + for tok in toks: + if tok: + seq.append(tok) + else: + value.append(RuleSequence(seq, rules) if len(seq) > 1 else seq[0]) + seq = [] + value.append(RuleSequence(seq, rules) if len(seq) > 1 else seq[0]) + return RuleOneof(value, rules) if len(value) > 1 else value[0] + + +def parse_grammar(grammar: str) -> Dict[str, Rule]: + """Parse grammar into rule dictionary.""" + rules: Dict[str, Rule] = {} + for token in grammar.split('\n\n'): + lines = token.strip().split('\n') + name, *defs = lines + items = [z for x in defs for y in x.split(' ') if y for z in split_token(y) if z] + assert items + assert items[0] == '=' + items.pop(0) + stack: List[Optional[Rule]] = [] + parens: List[int] = [] + while items: + tok = items.pop(0) + if tok in ['*', '+', '?']: + stack[-1] = { + '*': RuleZeroPlus, + '+': RuleOnePlus, + '?': RuleZeroOne, + }[tok](stack[-1], rules) + elif tok == '/': + stack.append(None) + elif tok == '(': + parens.append(len(stack)) + elif tok == ')': + index = parens.pop() + rule = collapse_tokens(stack[index:], rules) + stack = stack[:index] + stack.append(rule) + elif len(tok) > 2 and tok[:2] == 'r\'': + stack.append(RuleRegex(tok, rules)) + elif tok[0] == '\'': + stack.append(RuleLiteral(tok, rules)) + else: + stack.append(RuleToken(tok, rules)) + + res = collapse_tokens(stack, rules) + res.name = name + rules[name] = res + return rules diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py new file mode 100644 index 00000000..4a4b07af --- /dev/null +++ b/src/rosbags/typesys/register.py @@ -0,0 +1,112 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Code generators and registration functions for the extensible type system.""" + +from __future__ import annotations + +import json +import sys +from importlib.util import module_from_spec, spec_from_loader +from typing import TYPE_CHECKING + +from . import types +from .base import TypesysError + +if TYPE_CHECKING: + from .base import Typesdict + + +def generate_python_code(typs: Typesdict) -> str: + """Generate python code from types dictionary. + + Args: + typs: Dictionary mapping message typenames to parsetrees. + + Returns: + Code for importable python module. + + """ + lines = [ + '# Copyright 2020-2021 Ternaris.', + '# SPDX-License-Identifier: Apache-2.0', + '#', + '# THIS FILE IS GENERATED, DO NOT EDIT', + '"""ROS2 message types."""', + '', + '# flake8: noqa N801', + '# pylint: disable=invalid-name,too-many-instance-attributes,too-many-lines', + '', + 'from __future__ import annotations', + '', + 'from dataclasses import dataclass', + 'from typing import TYPE_CHECKING', + '', + 'if TYPE_CHECKING:', + ' from typing import Any', + '', + '', + ] + + for name, fields in typs.items(): + pyname = name.replace('/', '__') + lines += [ + '@dataclass', + f'class {pyname}:', + f' """Class for {name}."""', + '', + *[f' {fname[1]}: Any' for _, fname in fields], + ] + + lines += [ + '', + '', + ] + + lines += ['FIELDDEFS = {'] + for name, fields in typs.items(): + pyname = name.replace('/', '__') + lines += [ + f' \'{name}\': [', + *[ + f' ({repr(fname[1])}, {json.loads(json.dumps(ftype))}),' + for ftype, fname in fields + ], + ' ],', + ] + lines += [ + '}', + '', + ] + return '\n'.join(lines) + + +def register_types(typs: Typesdict) -> None: + """Register types in type system. + + Args: + typs: Dictionary mapping message typenames to parsetrees. + + Raises: + TypesysError: Type already present with different definition. + """ + code = generate_python_code(typs) + name = 'rosbags.usertypes' + spec = spec_from_loader(name, loader=None) + module = module_from_spec(spec) + sys.modules[name] = module + exec(code, module.__dict__) # pylint: disable=exec-used + fielddefs = module.FIELDDEFS # type: ignore + + for name, fields in fielddefs.items(): + if name == 'std_msgs/msg/Header': + continue + if have := types.FIELDDEFS.get(name): + have = [(x[0].lower(), x[1]) for x in have] + fields = [(x[0].lower(), x[1]) for x in fields] + if have != fields: + raise TypesysError(f'Type {name!r} is already present with different definition.') + + for name in fielddefs.keys() - types.FIELDDEFS.keys(): + pyname = name.replace('/', '__') + setattr(types, pyname, getattr(module, pyname)) + types.FIELDDEFS[name] = fielddefs[name] diff --git a/src/rosbags/typesys/types.py b/src/rosbags/typesys/types.py new file mode 100644 index 00000000..3c484174 --- /dev/null +++ b/src/rosbags/typesys/types.py @@ -0,0 +1,2420 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +# +# THIS FILE IS GENERATED, DO NOT EDIT +"""ROS2 message types.""" + +# flake8: noqa N801 +# pylint: disable=invalid-name,too-many-instance-attributes,too-many-lines + +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + + +@dataclass +class builtin_interfaces__msg__Time: + """Class for builtin_interfaces/msg/Time.""" + + sec: Any + nanosec: Any + + +@dataclass +class builtin_interfaces__msg__Duration: + """Class for builtin_interfaces/msg/Duration.""" + + sec: Any + nanosec: Any + + +@dataclass +class diagnostic_msgs__msg__DiagnosticStatus: + """Class for diagnostic_msgs/msg/DiagnosticStatus.""" + + level: Any + name: Any + message: Any + hardware_id: Any + values: Any + + +@dataclass +class diagnostic_msgs__msg__DiagnosticArray: + """Class for diagnostic_msgs/msg/DiagnosticArray.""" + + header: Any + status: Any + + +@dataclass +class diagnostic_msgs__msg__KeyValue: + """Class for diagnostic_msgs/msg/KeyValue.""" + + key: Any + value: Any + + +@dataclass +class geometry_msgs__msg__AccelWithCovariance: + """Class for geometry_msgs/msg/AccelWithCovariance.""" + + accel: Any + covariance: Any + + +@dataclass +class geometry_msgs__msg__Point32: + """Class for geometry_msgs/msg/Point32.""" + + x: Any + y: Any + z: Any + + +@dataclass +class geometry_msgs__msg__Vector3: + """Class for geometry_msgs/msg/Vector3.""" + + x: Any + y: Any + z: Any + + +@dataclass +class geometry_msgs__msg__Inertia: + """Class for geometry_msgs/msg/Inertia.""" + + m: Any + com: Any + ixx: Any + ixy: Any + ixz: Any + iyy: Any + iyz: Any + izz: Any + + +@dataclass +class geometry_msgs__msg__PoseWithCovarianceStamped: + """Class for geometry_msgs/msg/PoseWithCovarianceStamped.""" + + header: Any + pose: Any + + +@dataclass +class geometry_msgs__msg__Twist: + """Class for geometry_msgs/msg/Twist.""" + + linear: Any + angular: Any + + +@dataclass +class geometry_msgs__msg__Pose: + """Class for geometry_msgs/msg/Pose.""" + + position: Any + orientation: Any + + +@dataclass +class geometry_msgs__msg__Point: + """Class for geometry_msgs/msg/Point.""" + + x: Any + y: Any + z: Any + + +@dataclass +class geometry_msgs__msg__Vector3Stamped: + """Class for geometry_msgs/msg/Vector3Stamped.""" + + header: Any + vector: Any + + +@dataclass +class geometry_msgs__msg__Transform: + """Class for geometry_msgs/msg/Transform.""" + + translation: Any + rotation: Any + + +@dataclass +class geometry_msgs__msg__PolygonStamped: + """Class for geometry_msgs/msg/PolygonStamped.""" + + header: Any + polygon: Any + + +@dataclass +class geometry_msgs__msg__Quaternion: + """Class for geometry_msgs/msg/Quaternion.""" + + x: Any + y: Any + z: Any + w: Any + + +@dataclass +class geometry_msgs__msg__Pose2D: + """Class for geometry_msgs/msg/Pose2D.""" + + x: Any + y: Any + theta: Any + + +@dataclass +class geometry_msgs__msg__InertiaStamped: + """Class for geometry_msgs/msg/InertiaStamped.""" + + header: Any + inertia: Any + + +@dataclass +class geometry_msgs__msg__TwistStamped: + """Class for geometry_msgs/msg/TwistStamped.""" + + header: Any + twist: Any + + +@dataclass +class geometry_msgs__msg__PoseStamped: + """Class for geometry_msgs/msg/PoseStamped.""" + + header: Any + pose: Any + + +@dataclass +class geometry_msgs__msg__PointStamped: + """Class for geometry_msgs/msg/PointStamped.""" + + header: Any + point: Any + + +@dataclass +class geometry_msgs__msg__Polygon: + """Class for geometry_msgs/msg/Polygon.""" + + points: Any + + +@dataclass +class geometry_msgs__msg__PoseArray: + """Class for geometry_msgs/msg/PoseArray.""" + + header: Any + poses: Any + + +@dataclass +class geometry_msgs__msg__AccelStamped: + """Class for geometry_msgs/msg/AccelStamped.""" + + header: Any + accel: Any + + +@dataclass +class geometry_msgs__msg__TwistWithCovarianceStamped: + """Class for geometry_msgs/msg/TwistWithCovarianceStamped.""" + + header: Any + twist: Any + + +@dataclass +class geometry_msgs__msg__QuaternionStamped: + """Class for geometry_msgs/msg/QuaternionStamped.""" + + header: Any + quaternion: Any + + +@dataclass +class geometry_msgs__msg__WrenchStamped: + """Class for geometry_msgs/msg/WrenchStamped.""" + + header: Any + wrench: Any + + +@dataclass +class geometry_msgs__msg__AccelWithCovarianceStamped: + """Class for geometry_msgs/msg/AccelWithCovarianceStamped.""" + + header: Any + accel: Any + + +@dataclass +class geometry_msgs__msg__PoseWithCovariance: + """Class for geometry_msgs/msg/PoseWithCovariance.""" + + pose: Any + covariance: Any + + +@dataclass +class geometry_msgs__msg__Wrench: + """Class for geometry_msgs/msg/Wrench.""" + + force: Any + torque: Any + + +@dataclass +class geometry_msgs__msg__TransformStamped: + """Class for geometry_msgs/msg/TransformStamped.""" + + header: Any + child_frame_id: Any + transform: Any + + +@dataclass +class geometry_msgs__msg__Accel: + """Class for geometry_msgs/msg/Accel.""" + + linear: Any + angular: Any + + +@dataclass +class geometry_msgs__msg__TwistWithCovariance: + """Class for geometry_msgs/msg/TwistWithCovariance.""" + + twist: Any + covariance: Any + + +@dataclass +class libstatistics_collector__msg__DummyMessage: + """Class for libstatistics_collector/msg/DummyMessage.""" + + header: Any + + +@dataclass +class lifecycle_msgs__msg__TransitionDescription: + """Class for lifecycle_msgs/msg/TransitionDescription.""" + + transition: Any + start_state: Any + goal_state: Any + + +@dataclass +class lifecycle_msgs__msg__State: + """Class for lifecycle_msgs/msg/State.""" + + id: Any + label: Any + + +@dataclass +class lifecycle_msgs__msg__TransitionEvent: + """Class for lifecycle_msgs/msg/TransitionEvent.""" + + timestamp: Any + transition: Any + start_state: Any + goal_state: Any + + +@dataclass +class lifecycle_msgs__msg__Transition: + """Class for lifecycle_msgs/msg/Transition.""" + + id: Any + label: Any + + +@dataclass +class nav_msgs__msg__MapMetaData: + """Class for nav_msgs/msg/MapMetaData.""" + + map_load_time: Any + resolution: Any + width: Any + height: Any + origin: Any + + +@dataclass +class nav_msgs__msg__GridCells: + """Class for nav_msgs/msg/GridCells.""" + + header: Any + cell_width: Any + cell_height: Any + cells: Any + + +@dataclass +class nav_msgs__msg__Odometry: + """Class for nav_msgs/msg/Odometry.""" + + header: Any + child_frame_id: Any + pose: Any + twist: Any + + +@dataclass +class nav_msgs__msg__Path: + """Class for nav_msgs/msg/Path.""" + + header: Any + poses: Any + + +@dataclass +class nav_msgs__msg__OccupancyGrid: + """Class for nav_msgs/msg/OccupancyGrid.""" + + header: Any + info: Any + data: Any + + +@dataclass +class rcl_interfaces__msg__ListParametersResult: + """Class for rcl_interfaces/msg/ListParametersResult.""" + + names: Any + prefixes: Any + + +@dataclass +class rcl_interfaces__msg__ParameterType: + """Class for rcl_interfaces/msg/ParameterType.""" + + structure_needs_at_least_one_member: Any + + +@dataclass +class rcl_interfaces__msg__ParameterEventDescriptors: + """Class for rcl_interfaces/msg/ParameterEventDescriptors.""" + + new_parameters: Any + changed_parameters: Any + deleted_parameters: Any + + +@dataclass +class rcl_interfaces__msg__ParameterEvent: + """Class for rcl_interfaces/msg/ParameterEvent.""" + + stamp: Any + node: Any + new_parameters: Any + changed_parameters: Any + deleted_parameters: Any + + +@dataclass +class rcl_interfaces__msg__IntegerRange: + """Class for rcl_interfaces/msg/IntegerRange.""" + + from_value: Any + to_value: Any + step: Any + + +@dataclass +class rcl_interfaces__msg__Parameter: + """Class for rcl_interfaces/msg/Parameter.""" + + name: Any + value: Any + + +@dataclass +class rcl_interfaces__msg__ParameterValue: + """Class for rcl_interfaces/msg/ParameterValue.""" + + type: Any + bool_value: Any + integer_value: Any + double_value: Any + string_value: Any + byte_array_value: Any + bool_array_value: Any + integer_array_value: Any + double_array_value: Any + string_array_value: Any + + +@dataclass +class rcl_interfaces__msg__FloatingPointRange: + """Class for rcl_interfaces/msg/FloatingPointRange.""" + + from_value: Any + to_value: Any + step: Any + + +@dataclass +class rcl_interfaces__msg__SetParametersResult: + """Class for rcl_interfaces/msg/SetParametersResult.""" + + successful: Any + reason: Any + + +@dataclass +class rcl_interfaces__msg__Log: + """Class for rcl_interfaces/msg/Log.""" + + stamp: Any + level: Any + name: Any + msg: Any + file: Any + function: Any + line: Any + + +@dataclass +class rcl_interfaces__msg__ParameterDescriptor: + """Class for rcl_interfaces/msg/ParameterDescriptor.""" + + name: Any + type: Any + description: Any + additional_constraints: Any + read_only: Any + floating_point_range: Any + integer_range: Any + + +@dataclass +class rmw_dds_common__msg__Gid: + """Class for rmw_dds_common/msg/Gid.""" + + data: Any + + +@dataclass +class rmw_dds_common__msg__NodeEntitiesInfo: + """Class for rmw_dds_common/msg/NodeEntitiesInfo.""" + + node_namespace: Any + node_name: Any + reader_gid_seq: Any + writer_gid_seq: Any + + +@dataclass +class rmw_dds_common__msg__ParticipantEntitiesInfo: + """Class for rmw_dds_common/msg/ParticipantEntitiesInfo.""" + + gid: Any + node_entities_info_seq: Any + + +@dataclass +class rosgraph_msgs__msg__Clock: + """Class for rosgraph_msgs/msg/Clock.""" + + clock: Any + + +@dataclass +class sensor_msgs__msg__Temperature: + """Class for sensor_msgs/msg/Temperature.""" + + header: Any + temperature: Any + variance: Any + + +@dataclass +class sensor_msgs__msg__Range: + """Class for sensor_msgs/msg/Range.""" + + header: Any + radiation_type: Any + field_of_view: Any + min_range: Any + max_range: Any + range: Any + + +@dataclass +class sensor_msgs__msg__RegionOfInterest: + """Class for sensor_msgs/msg/RegionOfInterest.""" + + x_offset: Any + y_offset: Any + height: Any + width: Any + do_rectify: Any + + +@dataclass +class sensor_msgs__msg__JoyFeedbackArray: + """Class for sensor_msgs/msg/JoyFeedbackArray.""" + + array: Any + + +@dataclass +class sensor_msgs__msg__TimeReference: + """Class for sensor_msgs/msg/TimeReference.""" + + header: Any + time_ref: Any + source: Any + + +@dataclass +class sensor_msgs__msg__CompressedImage: + """Class for sensor_msgs/msg/CompressedImage.""" + + header: Any + format: Any + data: Any + + +@dataclass +class sensor_msgs__msg__MultiEchoLaserScan: + """Class for sensor_msgs/msg/MultiEchoLaserScan.""" + + header: Any + angle_min: Any + angle_max: Any + angle_increment: Any + time_increment: Any + scan_time: Any + range_min: Any + range_max: Any + ranges: Any + intensities: Any + + +@dataclass +class sensor_msgs__msg__LaserEcho: + """Class for sensor_msgs/msg/LaserEcho.""" + + echoes: Any + + +@dataclass +class sensor_msgs__msg__ChannelFloat32: + """Class for sensor_msgs/msg/ChannelFloat32.""" + + name: Any + values: Any + + +@dataclass +class sensor_msgs__msg__CameraInfo: + """Class for sensor_msgs/msg/CameraInfo.""" + + header: Any + height: Any + width: Any + distortion_model: Any + d: Any + k: Any + r: Any + p: Any + binning_x: Any + binning_y: Any + roi: Any + + +@dataclass +class sensor_msgs__msg__RelativeHumidity: + """Class for sensor_msgs/msg/RelativeHumidity.""" + + header: Any + relative_humidity: Any + variance: Any + + +@dataclass +class sensor_msgs__msg__FluidPressure: + """Class for sensor_msgs/msg/FluidPressure.""" + + header: Any + fluid_pressure: Any + variance: Any + + +@dataclass +class sensor_msgs__msg__LaserScan: + """Class for sensor_msgs/msg/LaserScan.""" + + header: Any + angle_min: Any + angle_max: Any + angle_increment: Any + time_increment: Any + scan_time: Any + range_min: Any + range_max: Any + ranges: Any + intensities: Any + + +@dataclass +class sensor_msgs__msg__BatteryState: + """Class for sensor_msgs/msg/BatteryState.""" + + header: Any + voltage: Any + temperature: Any + current: Any + charge: Any + capacity: Any + design_capacity: Any + percentage: Any + power_supply_status: Any + power_supply_health: Any + power_supply_technology: Any + present: Any + cell_voltage: Any + cell_temperature: Any + location: Any + serial_number: Any + + +@dataclass +class sensor_msgs__msg__Image: + """Class for sensor_msgs/msg/Image.""" + + header: Any + height: Any + width: Any + encoding: Any + is_bigendian: Any + step: Any + data: Any + + +@dataclass +class sensor_msgs__msg__PointCloud: + """Class for sensor_msgs/msg/PointCloud.""" + + header: Any + points: Any + channels: Any + + +@dataclass +class sensor_msgs__msg__Imu: + """Class for sensor_msgs/msg/Imu.""" + + header: Any + orientation: Any + orientation_covariance: Any + angular_velocity: Any + angular_velocity_covariance: Any + linear_acceleration: Any + linear_acceleration_covariance: Any + + +@dataclass +class sensor_msgs__msg__NavSatStatus: + """Class for sensor_msgs/msg/NavSatStatus.""" + + status: Any + service: Any + + +@dataclass +class sensor_msgs__msg__Illuminance: + """Class for sensor_msgs/msg/Illuminance.""" + + header: Any + illuminance: Any + variance: Any + + +@dataclass +class sensor_msgs__msg__Joy: + """Class for sensor_msgs/msg/Joy.""" + + header: Any + axes: Any + buttons: Any + + +@dataclass +class sensor_msgs__msg__NavSatFix: + """Class for sensor_msgs/msg/NavSatFix.""" + + header: Any + status: Any + latitude: Any + longitude: Any + altitude: Any + position_covariance: Any + position_covariance_type: Any + + +@dataclass +class sensor_msgs__msg__MultiDOFJointState: + """Class for sensor_msgs/msg/MultiDOFJointState.""" + + header: Any + joint_names: Any + transforms: Any + twist: Any + wrench: Any + + +@dataclass +class sensor_msgs__msg__MagneticField: + """Class for sensor_msgs/msg/MagneticField.""" + + header: Any + magnetic_field: Any + magnetic_field_covariance: Any + + +@dataclass +class sensor_msgs__msg__JointState: + """Class for sensor_msgs/msg/JointState.""" + + header: Any + name: Any + position: Any + velocity: Any + effort: Any + + +@dataclass +class sensor_msgs__msg__PointField: + """Class for sensor_msgs/msg/PointField.""" + + name: Any + offset: Any + datatype: Any + count: Any + + +@dataclass +class sensor_msgs__msg__PointCloud2: + """Class for sensor_msgs/msg/PointCloud2.""" + + header: Any + height: Any + width: Any + fields: Any + is_bigendian: Any + point_step: Any + row_step: Any + data: Any + is_dense: Any + + +@dataclass +class sensor_msgs__msg__JoyFeedback: + """Class for sensor_msgs/msg/JoyFeedback.""" + + type: Any + id: Any + intensity: Any + + +@dataclass +class shape_msgs__msg__SolidPrimitive: + """Class for shape_msgs/msg/SolidPrimitive.""" + + type: Any + dimensions: Any + + +@dataclass +class shape_msgs__msg__Mesh: + """Class for shape_msgs/msg/Mesh.""" + + triangles: Any + vertices: Any + + +@dataclass +class shape_msgs__msg__Plane: + """Class for shape_msgs/msg/Plane.""" + + coef: Any + + +@dataclass +class shape_msgs__msg__MeshTriangle: + """Class for shape_msgs/msg/MeshTriangle.""" + + vertex_indices: Any + + +@dataclass +class statistics_msgs__msg__StatisticDataType: + """Class for statistics_msgs/msg/StatisticDataType.""" + + structure_needs_at_least_one_member: Any + + +@dataclass +class statistics_msgs__msg__StatisticDataPoint: + """Class for statistics_msgs/msg/StatisticDataPoint.""" + + data_type: Any + data: Any + + +@dataclass +class statistics_msgs__msg__MetricsMessage: + """Class for statistics_msgs/msg/MetricsMessage.""" + + measurement_source_name: Any + metrics_source: Any + unit: Any + window_start: Any + window_stop: Any + statistics: Any + + +@dataclass +class std_msgs__msg__UInt8: + """Class for std_msgs/msg/UInt8.""" + + data: Any + + +@dataclass +class std_msgs__msg__Float32MultiArray: + """Class for std_msgs/msg/Float32MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__Int8: + """Class for std_msgs/msg/Int8.""" + + data: Any + + +@dataclass +class std_msgs__msg__Empty: + """Class for std_msgs/msg/Empty.""" + + structure_needs_at_least_one_member: Any + + +@dataclass +class std_msgs__msg__String: + """Class for std_msgs/msg/String.""" + + data: Any + + +@dataclass +class std_msgs__msg__MultiArrayDimension: + """Class for std_msgs/msg/MultiArrayDimension.""" + + label: Any + size: Any + stride: Any + + +@dataclass +class std_msgs__msg__UInt64: + """Class for std_msgs/msg/UInt64.""" + + data: Any + + +@dataclass +class std_msgs__msg__UInt16: + """Class for std_msgs/msg/UInt16.""" + + data: Any + + +@dataclass +class std_msgs__msg__Float32: + """Class for std_msgs/msg/Float32.""" + + data: Any + + +@dataclass +class std_msgs__msg__Int64: + """Class for std_msgs/msg/Int64.""" + + data: Any + + +@dataclass +class std_msgs__msg__Int16MultiArray: + """Class for std_msgs/msg/Int16MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__Int16: + """Class for std_msgs/msg/Int16.""" + + data: Any + + +@dataclass +class std_msgs__msg__Float64MultiArray: + """Class for std_msgs/msg/Float64MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__MultiArrayLayout: + """Class for std_msgs/msg/MultiArrayLayout.""" + + dim: Any + data_offset: Any + + +@dataclass +class std_msgs__msg__UInt32MultiArray: + """Class for std_msgs/msg/UInt32MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__Header: + """Class for std_msgs/msg/Header.""" + + stamp: Any + frame_id: Any + + +@dataclass +class std_msgs__msg__ByteMultiArray: + """Class for std_msgs/msg/ByteMultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__Int8MultiArray: + """Class for std_msgs/msg/Int8MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__Float64: + """Class for std_msgs/msg/Float64.""" + + data: Any + + +@dataclass +class std_msgs__msg__UInt8MultiArray: + """Class for std_msgs/msg/UInt8MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__Byte: + """Class for std_msgs/msg/Byte.""" + + data: Any + + +@dataclass +class std_msgs__msg__Char: + """Class for std_msgs/msg/Char.""" + + data: Any + + +@dataclass +class std_msgs__msg__UInt64MultiArray: + """Class for std_msgs/msg/UInt64MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__Int32MultiArray: + """Class for std_msgs/msg/Int32MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__ColorRGBA: + """Class for std_msgs/msg/ColorRGBA.""" + + r: Any + g: Any + b: Any + a: Any + + +@dataclass +class std_msgs__msg__Bool: + """Class for std_msgs/msg/Bool.""" + + data: Any + + +@dataclass +class std_msgs__msg__UInt32: + """Class for std_msgs/msg/UInt32.""" + + data: Any + + +@dataclass +class std_msgs__msg__Int64MultiArray: + """Class for std_msgs/msg/Int64MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class std_msgs__msg__Int32: + """Class for std_msgs/msg/Int32.""" + + data: Any + + +@dataclass +class std_msgs__msg__UInt16MultiArray: + """Class for std_msgs/msg/UInt16MultiArray.""" + + layout: Any + data: Any + + +@dataclass +class stereo_msgs__msg__DisparityImage: + """Class for stereo_msgs/msg/DisparityImage.""" + + header: Any + image: Any + f: Any + t: Any + valid_window: Any + min_disparity: Any + max_disparity: Any + delta_d: Any + + +@dataclass +class tf2_msgs__msg__TF2Error: + """Class for tf2_msgs/msg/TF2Error.""" + + error: Any + error_string: Any + + +@dataclass +class tf2_msgs__msg__TFMessage: + """Class for tf2_msgs/msg/TFMessage.""" + + transforms: Any + + +@dataclass +class trajectory_msgs__msg__MultiDOFJointTrajectory: + """Class for trajectory_msgs/msg/MultiDOFJointTrajectory.""" + + header: Any + joint_names: Any + points: Any + + +@dataclass +class trajectory_msgs__msg__JointTrajectory: + """Class for trajectory_msgs/msg/JointTrajectory.""" + + header: Any + joint_names: Any + points: Any + + +@dataclass +class trajectory_msgs__msg__JointTrajectoryPoint: + """Class for trajectory_msgs/msg/JointTrajectoryPoint.""" + + positions: Any + velocities: Any + accelerations: Any + effort: Any + time_from_start: Any + + +@dataclass +class trajectory_msgs__msg__MultiDOFJointTrajectoryPoint: + """Class for trajectory_msgs/msg/MultiDOFJointTrajectoryPoint.""" + + transforms: Any + velocities: Any + accelerations: Any + time_from_start: Any + + +@dataclass +class unique_identifier_msgs__msg__UUID: + """Class for unique_identifier_msgs/msg/UUID.""" + + uuid: Any + + +@dataclass +class visualization_msgs__msg__Marker: + """Class for visualization_msgs/msg/Marker.""" + + header: Any + ns: Any + id: Any + type: Any + action: Any + pose: Any + scale: Any + color: Any + lifetime: Any + frame_locked: Any + points: Any + colors: Any + text: Any + mesh_resource: Any + mesh_use_embedded_materials: Any + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerInit: + """Class for visualization_msgs/msg/InteractiveMarkerInit.""" + + server_id: Any + seq_num: Any + markers: Any + + +@dataclass +class visualization_msgs__msg__MenuEntry: + """Class for visualization_msgs/msg/MenuEntry.""" + + id: Any + parent_id: Any + title: Any + command: Any + command_type: Any + + +@dataclass +class visualization_msgs__msg__MarkerArray: + """Class for visualization_msgs/msg/MarkerArray.""" + + markers: Any + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerUpdate: + """Class for visualization_msgs/msg/InteractiveMarkerUpdate.""" + + server_id: Any + seq_num: Any + type: Any + markers: Any + poses: Any + erases: Any + + +@dataclass +class visualization_msgs__msg__InteractiveMarker: + """Class for visualization_msgs/msg/InteractiveMarker.""" + + header: Any + pose: Any + name: Any + description: Any + scale: Any + menu_entries: Any + controls: Any + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerFeedback: + """Class for visualization_msgs/msg/InteractiveMarkerFeedback.""" + + header: Any + client_id: Any + marker_name: Any + control_name: Any + event_type: Any + pose: Any + menu_entry_id: Any + mouse_point: Any + mouse_point_valid: Any + + +@dataclass +class visualization_msgs__msg__ImageMarker: + """Class for visualization_msgs/msg/ImageMarker.""" + + header: Any + ns: Any + id: Any + type: Any + action: Any + position: Any + scale: Any + outline_color: Any + filled: Any + fill_color: Any + lifetime: Any + points: Any + outline_colors: Any + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerControl: + """Class for visualization_msgs/msg/InteractiveMarkerControl.""" + + name: Any + orientation: Any + orientation_mode: Any + interaction_mode: Any + always_visible: Any + markers: Any + independent_marker_orientation: Any + description: Any + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerPose: + """Class for visualization_msgs/msg/InteractiveMarkerPose.""" + + header: Any + pose: Any + name: Any + + +@dataclass +class autoware_auto_msgs__msg__BoundingBox: + """Class for autoware_auto_msgs/msg/BoundingBox.""" + + centroid: Any + size: Any + orientation: Any + velocity: Any + heading: Any + heading_rate: Any + corners: Any + variance: Any + value: Any + vehicle_label: Any + signal_label: Any + class_likelihood: Any + + +@dataclass +class autoware_auto_msgs__msg__HADMapBin: + """Class for autoware_auto_msgs/msg/HADMapBin.""" + + header: Any + map_format: Any + format_version: Any + map_version: Any + data: Any + + +@dataclass +class autoware_auto_msgs__msg__Quaternion32: + """Class for autoware_auto_msgs/msg/Quaternion32.""" + + x: Any + y: Any + z: Any + w: Any + + +@dataclass +class autoware_auto_msgs__msg__Trajectory: + """Class for autoware_auto_msgs/msg/Trajectory.""" + + header: Any + points: Any + + +@dataclass +class autoware_auto_msgs__msg__Route: + """Class for autoware_auto_msgs/msg/Route.""" + + header: Any + start_point: Any + goal_point: Any + primitives: Any + + +@dataclass +class autoware_auto_msgs__msg__MapPrimitive: + """Class for autoware_auto_msgs/msg/MapPrimitive.""" + + id: Any + primitive_type: Any + + +@dataclass +class autoware_auto_msgs__msg__TrajectoryPoint: + """Class for autoware_auto_msgs/msg/TrajectoryPoint.""" + + time_from_start: Any + x: Any + y: Any + heading: Any + longitudinal_velocity_mps: Any + lateral_velocity_mps: Any + acceleration_mps2: Any + heading_rate_rps: Any + front_wheel_angle_rad: Any + rear_wheel_angle_rad: Any + + +@dataclass +class autoware_auto_msgs__msg__Complex32: + """Class for autoware_auto_msgs/msg/Complex32.""" + + real: Any + imag: Any + + +@dataclass +class autoware_auto_msgs__msg__VehicleOdometry: + """Class for autoware_auto_msgs/msg/VehicleOdometry.""" + + stamp: Any + velocity_mps: Any + front_wheel_angle_rad: Any + rear_wheel_angle_rad: Any + + +@dataclass +class autoware_auto_msgs__msg__DiagnosticHeader: + """Class for autoware_auto_msgs/msg/DiagnosticHeader.""" + + name: Any + data_stamp: Any + computation_start: Any + runtime: Any + iterations: Any + + +@dataclass +class autoware_auto_msgs__msg__HighLevelControlCommand: + """Class for autoware_auto_msgs/msg/HighLevelControlCommand.""" + + stamp: Any + velocity_mps: Any + curvature: Any + + +@dataclass +class autoware_auto_msgs__msg__VehicleStateCommand: + """Class for autoware_auto_msgs/msg/VehicleStateCommand.""" + + stamp: Any + blinker: Any + headlight: Any + wiper: Any + gear: Any + mode: Any + hand_brake: Any + horn: Any + + +@dataclass +class autoware_auto_msgs__msg__PointClusters: + """Class for autoware_auto_msgs/msg/PointClusters.""" + + clusters: Any + + +@dataclass +class autoware_auto_msgs__msg__RawControlCommand: + """Class for autoware_auto_msgs/msg/RawControlCommand.""" + + stamp: Any + throttle: Any + brake: Any + front_steer: Any + rear_steer: Any + + +@dataclass +class autoware_auto_msgs__msg__VehicleStateReport: + """Class for autoware_auto_msgs/msg/VehicleStateReport.""" + + stamp: Any + fuel: Any + blinker: Any + headlight: Any + wiper: Any + gear: Any + mode: Any + hand_brake: Any + horn: Any + + +@dataclass +class autoware_auto_msgs__msg__ControlDiagnostic: + """Class for autoware_auto_msgs/msg/ControlDiagnostic.""" + + diag_header: Any + new_trajectory: Any + trajectory_source: Any + pose_source: Any + lateral_error_m: Any + longitudinal_error_m: Any + velocity_error_mps: Any + acceleration_error_mps2: Any + yaw_error_rad: Any + yaw_rate_error_rps: Any + + +@dataclass +class autoware_auto_msgs__msg__VehicleKinematicState: + """Class for autoware_auto_msgs/msg/VehicleKinematicState.""" + + header: Any + state: Any + delta: Any + + +@dataclass +class autoware_auto_msgs__msg__BoundingBoxArray: + """Class for autoware_auto_msgs/msg/BoundingBoxArray.""" + + header: Any + boxes: Any + + +@dataclass +class autoware_auto_msgs__msg__VehicleControlCommand: + """Class for autoware_auto_msgs/msg/VehicleControlCommand.""" + + stamp: Any + long_accel_mps2: Any + velocity_mps: Any + front_wheel_angle_rad: Any + rear_wheel_angle_rad: Any + + +FIELDDEFS = { + 'builtin_interfaces/msg/Time': [ + ('sec', [1, 'int32']), + ('nanosec', [1, 'uint32']), + ], + 'builtin_interfaces/msg/Duration': [ + ('sec', [1, 'int32']), + ('nanosec', [1, 'uint32']), + ], + 'diagnostic_msgs/msg/DiagnosticStatus': [ + ('level', [1, 'uint8']), + ('name', [1, 'string']), + ('message', [1, 'string']), + ('hardware_id', [1, 'string']), + ('values', [4, [2, 'diagnostic_msgs/msg/KeyValue']]), + ], + 'diagnostic_msgs/msg/DiagnosticArray': [ + ('header', [2, 'std_msgs/msg/Header']), + ('status', [4, [2, 'diagnostic_msgs/msg/DiagnosticStatus']]), + ], + 'diagnostic_msgs/msg/KeyValue': [ + ('key', [1, 'string']), + ('value', [1, 'string']), + ], + 'geometry_msgs/msg/AccelWithCovariance': [ + ('accel', [2, 'geometry_msgs/msg/Accel']), + ('covariance', [3, 36, [1, 'float64']]), + ], + 'geometry_msgs/msg/Point32': [ + ('x', [1, 'float32']), + ('y', [1, 'float32']), + ('z', [1, 'float32']), + ], + 'geometry_msgs/msg/Vector3': [ + ('x', [1, 'float64']), + ('y', [1, 'float64']), + ('z', [1, 'float64']), + ], + 'geometry_msgs/msg/Inertia': [ + ('m', [1, 'float64']), + ('com', [2, 'geometry_msgs/msg/Vector3']), + ('ixx', [1, 'float64']), + ('ixy', [1, 'float64']), + ('ixz', [1, 'float64']), + ('iyy', [1, 'float64']), + ('iyz', [1, 'float64']), + ('izz', [1, 'float64']), + ], + 'geometry_msgs/msg/PoseWithCovarianceStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('pose', [2, 'geometry_msgs/msg/PoseWithCovariance']), + ], + 'geometry_msgs/msg/Twist': [ + ('linear', [2, 'geometry_msgs/msg/Vector3']), + ('angular', [2, 'geometry_msgs/msg/Vector3']), + ], + 'geometry_msgs/msg/Pose': [ + ('position', [2, 'geometry_msgs/msg/Point']), + ('orientation', [2, 'geometry_msgs/msg/Quaternion']), + ], + 'geometry_msgs/msg/Point': [ + ('x', [1, 'float64']), + ('y', [1, 'float64']), + ('z', [1, 'float64']), + ], + 'geometry_msgs/msg/Vector3Stamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('vector', [2, 'geometry_msgs/msg/Vector3']), + ], + 'geometry_msgs/msg/Transform': [ + ('translation', [2, 'geometry_msgs/msg/Vector3']), + ('rotation', [2, 'geometry_msgs/msg/Quaternion']), + ], + 'geometry_msgs/msg/PolygonStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('polygon', [2, 'geometry_msgs/msg/Polygon']), + ], + 'geometry_msgs/msg/Quaternion': [ + ('x', [1, 'float64']), + ('y', [1, 'float64']), + ('z', [1, 'float64']), + ('w', [1, 'float64']), + ], + 'geometry_msgs/msg/Pose2D': [ + ('x', [1, 'float64']), + ('y', [1, 'float64']), + ('theta', [1, 'float64']), + ], + 'geometry_msgs/msg/InertiaStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('inertia', [2, 'geometry_msgs/msg/Inertia']), + ], + 'geometry_msgs/msg/TwistStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('twist', [2, 'geometry_msgs/msg/Twist']), + ], + 'geometry_msgs/msg/PoseStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('pose', [2, 'geometry_msgs/msg/Pose']), + ], + 'geometry_msgs/msg/PointStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('point', [2, 'geometry_msgs/msg/Point']), + ], + 'geometry_msgs/msg/Polygon': [ + ('points', [4, [2, 'geometry_msgs/msg/Point32']]), + ], + 'geometry_msgs/msg/PoseArray': [ + ('header', [2, 'std_msgs/msg/Header']), + ('poses', [4, [2, 'geometry_msgs/msg/Pose']]), + ], + 'geometry_msgs/msg/AccelStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('accel', [2, 'geometry_msgs/msg/Accel']), + ], + 'geometry_msgs/msg/TwistWithCovarianceStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('twist', [2, 'geometry_msgs/msg/TwistWithCovariance']), + ], + 'geometry_msgs/msg/QuaternionStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('quaternion', [2, 'geometry_msgs/msg/Quaternion']), + ], + 'geometry_msgs/msg/WrenchStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('wrench', [2, 'geometry_msgs/msg/Wrench']), + ], + 'geometry_msgs/msg/AccelWithCovarianceStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('accel', [2, 'geometry_msgs/msg/AccelWithCovariance']), + ], + 'geometry_msgs/msg/PoseWithCovariance': [ + ('pose', [2, 'geometry_msgs/msg/Pose']), + ('covariance', [3, 36, [1, 'float64']]), + ], + 'geometry_msgs/msg/Wrench': [ + ('force', [2, 'geometry_msgs/msg/Vector3']), + ('torque', [2, 'geometry_msgs/msg/Vector3']), + ], + 'geometry_msgs/msg/TransformStamped': [ + ('header', [2, 'std_msgs/msg/Header']), + ('child_frame_id', [1, 'string']), + ('transform', [2, 'geometry_msgs/msg/Transform']), + ], + 'geometry_msgs/msg/Accel': [ + ('linear', [2, 'geometry_msgs/msg/Vector3']), + ('angular', [2, 'geometry_msgs/msg/Vector3']), + ], + 'geometry_msgs/msg/TwistWithCovariance': [ + ('twist', [2, 'geometry_msgs/msg/Twist']), + ('covariance', [3, 36, [1, 'float64']]), + ], + 'libstatistics_collector/msg/DummyMessage': [ + ('header', [2, 'std_msgs/msg/Header']), + ], + 'lifecycle_msgs/msg/TransitionDescription': [ + ('transition', [2, 'lifecycle_msgs/msg/Transition']), + ('start_state', [2, 'lifecycle_msgs/msg/State']), + ('goal_state', [2, 'lifecycle_msgs/msg/State']), + ], + 'lifecycle_msgs/msg/State': [ + ('id', [1, 'uint8']), + ('label', [1, 'string']), + ], + 'lifecycle_msgs/msg/TransitionEvent': [ + ('timestamp', [1, 'uint64']), + ('transition', [2, 'lifecycle_msgs/msg/Transition']), + ('start_state', [2, 'lifecycle_msgs/msg/State']), + ('goal_state', [2, 'lifecycle_msgs/msg/State']), + ], + 'lifecycle_msgs/msg/Transition': [ + ('id', [1, 'uint8']), + ('label', [1, 'string']), + ], + 'nav_msgs/msg/MapMetaData': [ + ('map_load_time', [2, 'builtin_interfaces/msg/Time']), + ('resolution', [1, 'float32']), + ('width', [1, 'uint32']), + ('height', [1, 'uint32']), + ('origin', [2, 'geometry_msgs/msg/Pose']), + ], + 'nav_msgs/msg/GridCells': [ + ('header', [2, 'std_msgs/msg/Header']), + ('cell_width', [1, 'float32']), + ('cell_height', [1, 'float32']), + ('cells', [4, [2, 'geometry_msgs/msg/Point']]), + ], + 'nav_msgs/msg/Odometry': [ + ('header', [2, 'std_msgs/msg/Header']), + ('child_frame_id', [1, 'string']), + ('pose', [2, 'geometry_msgs/msg/PoseWithCovariance']), + ('twist', [2, 'geometry_msgs/msg/TwistWithCovariance']), + ], + 'nav_msgs/msg/Path': [ + ('header', [2, 'std_msgs/msg/Header']), + ('poses', [4, [2, 'geometry_msgs/msg/PoseStamped']]), + ], + 'nav_msgs/msg/OccupancyGrid': [ + ('header', [2, 'std_msgs/msg/Header']), + ('info', [2, 'nav_msgs/msg/MapMetaData']), + ('data', [4, [1, 'int8']]), + ], + 'rcl_interfaces/msg/ListParametersResult': [ + ('names', [4, [1, 'string']]), + ('prefixes', [4, [1, 'string']]), + ], + 'rcl_interfaces/msg/ParameterType': [ + ('structure_needs_at_least_one_member', [1, 'uint8']), + ], + 'rcl_interfaces/msg/ParameterEventDescriptors': [ + ('new_parameters', [4, [2, 'rcl_interfaces/msg/ParameterDescriptor']]), + ('changed_parameters', [4, [2, 'rcl_interfaces/msg/ParameterDescriptor']]), + ('deleted_parameters', [4, [2, 'rcl_interfaces/msg/ParameterDescriptor']]), + ], + 'rcl_interfaces/msg/ParameterEvent': [ + ('stamp', [2, 'builtin_interfaces/msg/Time']), + ('node', [1, 'string']), + ('new_parameters', [4, [2, 'rcl_interfaces/msg/Parameter']]), + ('changed_parameters', [4, [2, 'rcl_interfaces/msg/Parameter']]), + ('deleted_parameters', [4, [2, 'rcl_interfaces/msg/Parameter']]), + ], + 'rcl_interfaces/msg/IntegerRange': [ + ('from_value', [1, 'int64']), + ('to_value', [1, 'int64']), + ('step', [1, 'uint64']), + ], + 'rcl_interfaces/msg/Parameter': [ + ('name', [1, 'string']), + ('value', [2, 'rcl_interfaces/msg/ParameterValue']), + ], + 'rcl_interfaces/msg/ParameterValue': [ + ('type', [1, 'uint8']), + ('bool_value', [1, 'bool']), + ('integer_value', [1, 'int64']), + ('double_value', [1, 'float64']), + ('string_value', [1, 'string']), + ('byte_array_value', [4, [1, 'uint8']]), + ('bool_array_value', [4, [1, 'bool']]), + ('integer_array_value', [4, [1, 'int64']]), + ('double_array_value', [4, [1, 'float64']]), + ('string_array_value', [4, [1, 'string']]), + ], + 'rcl_interfaces/msg/FloatingPointRange': [ + ('from_value', [1, 'float64']), + ('to_value', [1, 'float64']), + ('step', [1, 'float64']), + ], + 'rcl_interfaces/msg/SetParametersResult': [ + ('successful', [1, 'bool']), + ('reason', [1, 'string']), + ], + 'rcl_interfaces/msg/Log': [ + ('stamp', [2, 'builtin_interfaces/msg/Time']), + ('level', [1, 'uint8']), + ('name', [1, 'string']), + ('msg', [1, 'string']), + ('file', [1, 'string']), + ('function', [1, 'string']), + ('line', [1, 'uint32']), + ], + 'rcl_interfaces/msg/ParameterDescriptor': [ + ('name', [1, 'string']), + ('type', [1, 'uint8']), + ('description', [1, 'string']), + ('additional_constraints', [1, 'string']), + ('read_only', [1, 'bool']), + ('floating_point_range', [4, [2, 'rcl_interfaces/msg/FloatingPointRange']]), + ('integer_range', [4, [2, 'rcl_interfaces/msg/IntegerRange']]), + ], + 'rmw_dds_common/msg/Gid': [ + ('data', [3, 24, [1, 'uint8']]), + ], + 'rmw_dds_common/msg/NodeEntitiesInfo': [ + ('node_namespace', [1, 'string', [6, 256]]), + ('node_name', [1, 'string', [6, 256]]), + ('reader_gid_seq', [4, [2, 'rmw_dds_common/msg/Gid']]), + ('writer_gid_seq', [4, [2, 'rmw_dds_common/msg/Gid']]), + ], + 'rmw_dds_common/msg/ParticipantEntitiesInfo': [ + ('gid', [2, 'rmw_dds_common/msg/Gid']), + ('node_entities_info_seq', [4, [2, 'rmw_dds_common/msg/NodeEntitiesInfo']]), + ], + 'rosgraph_msgs/msg/Clock': [ + ('clock', [2, 'builtin_interfaces/msg/Time']), + ], + 'sensor_msgs/msg/Temperature': [ + ('header', [2, 'std_msgs/msg/Header']), + ('temperature', [1, 'float64']), + ('variance', [1, 'float64']), + ], + 'sensor_msgs/msg/Range': [ + ('header', [2, 'std_msgs/msg/Header']), + ('radiation_type', [1, 'uint8']), + ('field_of_view', [1, 'float32']), + ('min_range', [1, 'float32']), + ('max_range', [1, 'float32']), + ('range', [1, 'float32']), + ], + 'sensor_msgs/msg/RegionOfInterest': [ + ('x_offset', [1, 'uint32']), + ('y_offset', [1, 'uint32']), + ('height', [1, 'uint32']), + ('width', [1, 'uint32']), + ('do_rectify', [1, 'bool']), + ], + 'sensor_msgs/msg/JoyFeedbackArray': [ + ('array', [4, [2, 'sensor_msgs/msg/JoyFeedback']]), + ], + 'sensor_msgs/msg/TimeReference': [ + ('header', [2, 'std_msgs/msg/Header']), + ('time_ref', [2, 'builtin_interfaces/msg/Time']), + ('source', [1, 'string']), + ], + 'sensor_msgs/msg/CompressedImage': [ + ('header', [2, 'std_msgs/msg/Header']), + ('format', [1, 'string']), + ('data', [4, [1, 'uint8']]), + ], + 'sensor_msgs/msg/MultiEchoLaserScan': [ + ('header', [2, 'std_msgs/msg/Header']), + ('angle_min', [1, 'float32']), + ('angle_max', [1, 'float32']), + ('angle_increment', [1, 'float32']), + ('time_increment', [1, 'float32']), + ('scan_time', [1, 'float32']), + ('range_min', [1, 'float32']), + ('range_max', [1, 'float32']), + ('ranges', [4, [2, 'sensor_msgs/msg/LaserEcho']]), + ('intensities', [4, [2, 'sensor_msgs/msg/LaserEcho']]), + ], + 'sensor_msgs/msg/LaserEcho': [ + ('echoes', [4, [1, 'float32']]), + ], + 'sensor_msgs/msg/ChannelFloat32': [ + ('name', [1, 'string']), + ('values', [4, [1, 'float32']]), + ], + 'sensor_msgs/msg/CameraInfo': [ + ('header', [2, 'std_msgs/msg/Header']), + ('height', [1, 'uint32']), + ('width', [1, 'uint32']), + ('distortion_model', [1, 'string']), + ('d', [4, [1, 'float64']]), + ('k', [3, 9, [1, 'float64']]), + ('r', [3, 9, [1, 'float64']]), + ('p', [3, 12, [1, 'float64']]), + ('binning_x', [1, 'uint32']), + ('binning_y', [1, 'uint32']), + ('roi', [2, 'sensor_msgs/msg/RegionOfInterest']), + ], + 'sensor_msgs/msg/RelativeHumidity': [ + ('header', [2, 'std_msgs/msg/Header']), + ('relative_humidity', [1, 'float64']), + ('variance', [1, 'float64']), + ], + 'sensor_msgs/msg/FluidPressure': [ + ('header', [2, 'std_msgs/msg/Header']), + ('fluid_pressure', [1, 'float64']), + ('variance', [1, 'float64']), + ], + 'sensor_msgs/msg/LaserScan': [ + ('header', [2, 'std_msgs/msg/Header']), + ('angle_min', [1, 'float32']), + ('angle_max', [1, 'float32']), + ('angle_increment', [1, 'float32']), + ('time_increment', [1, 'float32']), + ('scan_time', [1, 'float32']), + ('range_min', [1, 'float32']), + ('range_max', [1, 'float32']), + ('ranges', [4, [1, 'float32']]), + ('intensities', [4, [1, 'float32']]), + ], + 'sensor_msgs/msg/BatteryState': [ + ('header', [2, 'std_msgs/msg/Header']), + ('voltage', [1, 'float32']), + ('temperature', [1, 'float32']), + ('current', [1, 'float32']), + ('charge', [1, 'float32']), + ('capacity', [1, 'float32']), + ('design_capacity', [1, 'float32']), + ('percentage', [1, 'float32']), + ('power_supply_status', [1, 'uint8']), + ('power_supply_health', [1, 'uint8']), + ('power_supply_technology', [1, 'uint8']), + ('present', [1, 'bool']), + ('cell_voltage', [4, [1, 'float32']]), + ('cell_temperature', [4, [1, 'float32']]), + ('location', [1, 'string']), + ('serial_number', [1, 'string']), + ], + 'sensor_msgs/msg/Image': [ + ('header', [2, 'std_msgs/msg/Header']), + ('height', [1, 'uint32']), + ('width', [1, 'uint32']), + ('encoding', [1, 'string']), + ('is_bigendian', [1, 'uint8']), + ('step', [1, 'uint32']), + ('data', [4, [1, 'uint8']]), + ], + 'sensor_msgs/msg/PointCloud': [ + ('header', [2, 'std_msgs/msg/Header']), + ('points', [4, [2, 'geometry_msgs/msg/Point32']]), + ('channels', [4, [2, 'sensor_msgs/msg/ChannelFloat32']]), + ], + 'sensor_msgs/msg/Imu': [ + ('header', [2, 'std_msgs/msg/Header']), + ('orientation', [2, 'geometry_msgs/msg/Quaternion']), + ('orientation_covariance', [3, 9, [1, 'float64']]), + ('angular_velocity', [2, 'geometry_msgs/msg/Vector3']), + ('angular_velocity_covariance', [3, 9, [1, 'float64']]), + ('linear_acceleration', [2, 'geometry_msgs/msg/Vector3']), + ('linear_acceleration_covariance', [3, 9, [1, 'float64']]), + ], + 'sensor_msgs/msg/NavSatStatus': [ + ('status', [1, 'int8']), + ('service', [1, 'uint16']), + ], + 'sensor_msgs/msg/Illuminance': [ + ('header', [2, 'std_msgs/msg/Header']), + ('illuminance', [1, 'float64']), + ('variance', [1, 'float64']), + ], + 'sensor_msgs/msg/Joy': [ + ('header', [2, 'std_msgs/msg/Header']), + ('axes', [4, [1, 'float32']]), + ('buttons', [4, [1, 'int32']]), + ], + 'sensor_msgs/msg/NavSatFix': [ + ('header', [2, 'std_msgs/msg/Header']), + ('status', [2, 'sensor_msgs/msg/NavSatStatus']), + ('latitude', [1, 'float64']), + ('longitude', [1, 'float64']), + ('altitude', [1, 'float64']), + ('position_covariance', [3, 9, [1, 'float64']]), + ('position_covariance_type', [1, 'uint8']), + ], + 'sensor_msgs/msg/MultiDOFJointState': [ + ('header', [2, 'std_msgs/msg/Header']), + ('joint_names', [4, [1, 'string']]), + ('transforms', [4, [2, 'geometry_msgs/msg/Transform']]), + ('twist', [4, [2, 'geometry_msgs/msg/Twist']]), + ('wrench', [4, [2, 'geometry_msgs/msg/Wrench']]), + ], + 'sensor_msgs/msg/MagneticField': [ + ('header', [2, 'std_msgs/msg/Header']), + ('magnetic_field', [2, 'geometry_msgs/msg/Vector3']), + ('magnetic_field_covariance', [3, 9, [1, 'float64']]), + ], + 'sensor_msgs/msg/JointState': [ + ('header', [2, 'std_msgs/msg/Header']), + ('name', [4, [1, 'string']]), + ('position', [4, [1, 'float64']]), + ('velocity', [4, [1, 'float64']]), + ('effort', [4, [1, 'float64']]), + ], + 'sensor_msgs/msg/PointField': [ + ('name', [1, 'string']), + ('offset', [1, 'uint32']), + ('datatype', [1, 'uint8']), + ('count', [1, 'uint32']), + ], + 'sensor_msgs/msg/PointCloud2': [ + ('header', [2, 'std_msgs/msg/Header']), + ('height', [1, 'uint32']), + ('width', [1, 'uint32']), + ('fields', [4, [2, 'sensor_msgs/msg/PointField']]), + ('is_bigendian', [1, 'bool']), + ('point_step', [1, 'uint32']), + ('row_step', [1, 'uint32']), + ('data', [4, [1, 'uint8']]), + ('is_dense', [1, 'bool']), + ], + 'sensor_msgs/msg/JoyFeedback': [ + ('type', [1, 'uint8']), + ('id', [1, 'uint8']), + ('intensity', [1, 'float32']), + ], + 'shape_msgs/msg/SolidPrimitive': [ + ('type', [1, 'uint8']), + ('dimensions', [4, [1, 'float64']]), + ], + 'shape_msgs/msg/Mesh': [ + ('triangles', [4, [2, 'shape_msgs/msg/MeshTriangle']]), + ('vertices', [4, [2, 'geometry_msgs/msg/Point']]), + ], + 'shape_msgs/msg/Plane': [ + ('coef', [3, 4, [1, 'float64']]), + ], + 'shape_msgs/msg/MeshTriangle': [ + ('vertex_indices', [3, 3, [1, 'uint32']]), + ], + 'statistics_msgs/msg/StatisticDataType': [ + ('structure_needs_at_least_one_member', [1, 'uint8']), + ], + 'statistics_msgs/msg/StatisticDataPoint': [ + ('data_type', [1, 'uint8']), + ('data', [1, 'float64']), + ], + 'statistics_msgs/msg/MetricsMessage': [ + ('measurement_source_name', [1, 'string']), + ('metrics_source', [1, 'string']), + ('unit', [1, 'string']), + ('window_start', [2, 'builtin_interfaces/msg/Time']), + ('window_stop', [2, 'builtin_interfaces/msg/Time']), + ('statistics', [4, [2, 'statistics_msgs/msg/StatisticDataPoint']]), + ], + 'std_msgs/msg/UInt8': [ + ('data', [1, 'uint8']), + ], + 'std_msgs/msg/Float32MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'float32']]), + ], + 'std_msgs/msg/Int8': [ + ('data', [1, 'int8']), + ], + 'std_msgs/msg/Empty': [ + ('structure_needs_at_least_one_member', [1, 'uint8']), + ], + 'std_msgs/msg/String': [ + ('data', [1, 'string']), + ], + 'std_msgs/msg/MultiArrayDimension': [ + ('label', [1, 'string']), + ('size', [1, 'uint32']), + ('stride', [1, 'uint32']), + ], + 'std_msgs/msg/UInt64': [ + ('data', [1, 'uint64']), + ], + 'std_msgs/msg/UInt16': [ + ('data', [1, 'uint16']), + ], + 'std_msgs/msg/Float32': [ + ('data', [1, 'float32']), + ], + 'std_msgs/msg/Int64': [ + ('data', [1, 'int64']), + ], + 'std_msgs/msg/Int16MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'int16']]), + ], + 'std_msgs/msg/Int16': [ + ('data', [1, 'int16']), + ], + 'std_msgs/msg/Float64MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'float64']]), + ], + 'std_msgs/msg/MultiArrayLayout': [ + ('dim', [4, [2, 'std_msgs/msg/MultiArrayDimension']]), + ('data_offset', [1, 'uint32']), + ], + 'std_msgs/msg/UInt32MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'uint32']]), + ], + 'std_msgs/msg/Header': [ + ('stamp', [2, 'builtin_interfaces/msg/Time']), + ('frame_id', [1, 'string']), + ], + 'std_msgs/msg/ByteMultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'uint8']]), + ], + 'std_msgs/msg/Int8MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'int8']]), + ], + 'std_msgs/msg/Float64': [ + ('data', [1, 'float64']), + ], + 'std_msgs/msg/UInt8MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'uint8']]), + ], + 'std_msgs/msg/Byte': [ + ('data', [1, 'uint8']), + ], + 'std_msgs/msg/Char': [ + ('data', [1, 'uint8']), + ], + 'std_msgs/msg/UInt64MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'uint64']]), + ], + 'std_msgs/msg/Int32MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'int32']]), + ], + 'std_msgs/msg/ColorRGBA': [ + ('r', [1, 'float32']), + ('g', [1, 'float32']), + ('b', [1, 'float32']), + ('a', [1, 'float32']), + ], + 'std_msgs/msg/Bool': [ + ('data', [1, 'bool']), + ], + 'std_msgs/msg/UInt32': [ + ('data', [1, 'uint32']), + ], + 'std_msgs/msg/Int64MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'int64']]), + ], + 'std_msgs/msg/Int32': [ + ('data', [1, 'int32']), + ], + 'std_msgs/msg/UInt16MultiArray': [ + ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), + ('data', [4, [1, 'uint16']]), + ], + 'stereo_msgs/msg/DisparityImage': [ + ('header', [2, 'std_msgs/msg/Header']), + ('image', [2, 'sensor_msgs/msg/Image']), + ('f', [1, 'float32']), + ('t', [1, 'float32']), + ('valid_window', [2, 'sensor_msgs/msg/RegionOfInterest']), + ('min_disparity', [1, 'float32']), + ('max_disparity', [1, 'float32']), + ('delta_d', [1, 'float32']), + ], + 'tf2_msgs/msg/TF2Error': [ + ('error', [1, 'uint8']), + ('error_string', [1, 'string']), + ], + 'tf2_msgs/msg/TFMessage': [ + ('transforms', [4, [2, 'geometry_msgs/msg/TransformStamped']]), + ], + 'trajectory_msgs/msg/MultiDOFJointTrajectory': [ + ('header', [2, 'std_msgs/msg/Header']), + ('joint_names', [4, [1, 'string']]), + ('points', [4, [2, 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint']]), + ], + 'trajectory_msgs/msg/JointTrajectory': [ + ('header', [2, 'std_msgs/msg/Header']), + ('joint_names', [4, [1, 'string']]), + ('points', [4, [2, 'trajectory_msgs/msg/JointTrajectoryPoint']]), + ], + 'trajectory_msgs/msg/JointTrajectoryPoint': [ + ('positions', [4, [1, 'float64']]), + ('velocities', [4, [1, 'float64']]), + ('accelerations', [4, [1, 'float64']]), + ('effort', [4, [1, 'float64']]), + ('time_from_start', [2, 'builtin_interfaces/msg/Duration']), + ], + 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint': [ + ('transforms', [4, [2, 'geometry_msgs/msg/Transform']]), + ('velocities', [4, [2, 'geometry_msgs/msg/Twist']]), + ('accelerations', [4, [2, 'geometry_msgs/msg/Twist']]), + ('time_from_start', [2, 'builtin_interfaces/msg/Duration']), + ], + 'unique_identifier_msgs/msg/UUID': [ + ('uuid', [3, 16, [1, 'uint8']]), + ], + 'visualization_msgs/msg/Marker': [ + ('header', [2, 'std_msgs/msg/Header']), + ('ns', [1, 'string']), + ('id', [1, 'int32']), + ('type', [1, 'int32']), + ('action', [1, 'int32']), + ('pose', [2, 'geometry_msgs/msg/Pose']), + ('scale', [2, 'geometry_msgs/msg/Vector3']), + ('color', [2, 'std_msgs/msg/ColorRGBA']), + ('lifetime', [2, 'builtin_interfaces/msg/Duration']), + ('frame_locked', [1, 'bool']), + ('points', [4, [2, 'geometry_msgs/msg/Point']]), + ('colors', [4, [2, 'std_msgs/msg/ColorRGBA']]), + ('text', [1, 'string']), + ('mesh_resource', [1, 'string']), + ('mesh_use_embedded_materials', [1, 'bool']), + ], + 'visualization_msgs/msg/InteractiveMarkerInit': [ + ('server_id', [1, 'string']), + ('seq_num', [1, 'uint64']), + ('markers', [4, [2, 'visualization_msgs/msg/InteractiveMarker']]), + ], + 'visualization_msgs/msg/MenuEntry': [ + ('id', [1, 'uint32']), + ('parent_id', [1, 'uint32']), + ('title', [1, 'string']), + ('command', [1, 'string']), + ('command_type', [1, 'uint8']), + ], + 'visualization_msgs/msg/MarkerArray': [ + ('markers', [4, [2, 'visualization_msgs/msg/Marker']]), + ], + 'visualization_msgs/msg/InteractiveMarkerUpdate': [ + ('server_id', [1, 'string']), + ('seq_num', [1, 'uint64']), + ('type', [1, 'uint8']), + ('markers', [4, [2, 'visualization_msgs/msg/InteractiveMarker']]), + ('poses', [4, [2, 'visualization_msgs/msg/InteractiveMarkerPose']]), + ('erases', [4, [1, 'string']]), + ], + 'visualization_msgs/msg/InteractiveMarker': [ + ('header', [2, 'std_msgs/msg/Header']), + ('pose', [2, 'geometry_msgs/msg/Pose']), + ('name', [1, 'string']), + ('description', [1, 'string']), + ('scale', [1, 'float32']), + ('menu_entries', [4, [2, 'visualization_msgs/msg/MenuEntry']]), + ('controls', [4, [2, 'visualization_msgs/msg/InteractiveMarkerControl']]), + ], + 'visualization_msgs/msg/InteractiveMarkerFeedback': [ + ('header', [2, 'std_msgs/msg/Header']), + ('client_id', [1, 'string']), + ('marker_name', [1, 'string']), + ('control_name', [1, 'string']), + ('event_type', [1, 'uint8']), + ('pose', [2, 'geometry_msgs/msg/Pose']), + ('menu_entry_id', [1, 'uint32']), + ('mouse_point', [2, 'geometry_msgs/msg/Point']), + ('mouse_point_valid', [1, 'bool']), + ], + 'visualization_msgs/msg/ImageMarker': [ + ('header', [2, 'std_msgs/msg/Header']), + ('ns', [1, 'string']), + ('id', [1, 'int32']), + ('type', [1, 'int32']), + ('action', [1, 'int32']), + ('position', [2, 'geometry_msgs/msg/Point']), + ('scale', [1, 'float32']), + ('outline_color', [2, 'std_msgs/msg/ColorRGBA']), + ('filled', [1, 'uint8']), + ('fill_color', [2, 'std_msgs/msg/ColorRGBA']), + ('lifetime', [2, 'builtin_interfaces/msg/Duration']), + ('points', [4, [2, 'geometry_msgs/msg/Point']]), + ('outline_colors', [4, [2, 'std_msgs/msg/ColorRGBA']]), + ], + 'visualization_msgs/msg/InteractiveMarkerControl': [ + ('name', [1, 'string']), + ('orientation', [2, 'geometry_msgs/msg/Quaternion']), + ('orientation_mode', [1, 'uint8']), + ('interaction_mode', [1, 'uint8']), + ('always_visible', [1, 'bool']), + ('markers', [4, [2, 'visualization_msgs/msg/Marker']]), + ('independent_marker_orientation', [1, 'bool']), + ('description', [1, 'string']), + ], + 'visualization_msgs/msg/InteractiveMarkerPose': [ + ('header', [2, 'std_msgs/msg/Header']), + ('pose', [2, 'geometry_msgs/msg/Pose']), + ('name', [1, 'string']), + ], + 'autoware_auto_msgs/msg/BoundingBox': [ + ('centroid', [2, 'geometry_msgs/msg/Point32']), + ('size', [2, 'geometry_msgs/msg/Point32']), + ('orientation', [2, 'autoware_auto_msgs/msg/Quaternion32']), + ('velocity', [1, 'float32']), + ('heading', [1, 'float32']), + ('heading_rate', [1, 'float32']), + ('corners', [3, 4, [2, 'geometry_msgs/msg/Point32']]), + ('variance', [3, 8, [1, 'float32']]), + ('value', [1, 'float32']), + ('vehicle_label', [1, 'uint8']), + ('signal_label', [1, 'uint8']), + ('class_likelihood', [1, 'float32']), + ], + 'autoware_auto_msgs/msg/HADMapBin': [ + ('header', [2, 'std_msgs/msg/Header']), + ('map_format', [1, 'uint8']), + ('format_version', [1, 'string']), + ('map_version', [1, 'string']), + ('data', [4, [1, 'uint8']]), + ], + 'autoware_auto_msgs/msg/Quaternion32': [ + ('x', [1, 'float32']), + ('y', [1, 'float32']), + ('z', [1, 'float32']), + ('w', [1, 'float32']), + ], + 'autoware_auto_msgs/msg/Trajectory': [ + ('header', [2, 'std_msgs/msg/Header']), + ('points', [4, [2, 'autoware_auto_msgs/msg/TrajectoryPoint']]), + ], + 'autoware_auto_msgs/msg/Route': [ + ('header', [2, 'std_msgs/msg/Header']), + ('start_point', [2, 'autoware_auto_msgs/msg/TrajectoryPoint']), + ('goal_point', [2, 'autoware_auto_msgs/msg/TrajectoryPoint']), + ('primitives', [4, [2, 'autoware_auto_msgs/msg/MapPrimitive']]), + ], + 'autoware_auto_msgs/msg/MapPrimitive': [ + ('id', [1, 'int64']), + ('primitive_type', [1, 'string']), + ], + 'autoware_auto_msgs/msg/TrajectoryPoint': [ + ('time_from_start', [2, 'builtin_interfaces/msg/Duration']), + ('x', [1, 'float32']), + ('y', [1, 'float32']), + ('heading', [2, 'autoware_auto_msgs/msg/Complex32']), + ('longitudinal_velocity_mps', [1, 'float32']), + ('lateral_velocity_mps', [1, 'float32']), + ('acceleration_mps2', [1, 'float32']), + ('heading_rate_rps', [1, 'float32']), + ('front_wheel_angle_rad', [1, 'float32']), + ('rear_wheel_angle_rad', [1, 'float32']), + ], + 'autoware_auto_msgs/msg/Complex32': [ + ('real', [1, 'float32']), + ('imag', [1, 'float32']), + ], + 'autoware_auto_msgs/msg/VehicleOdometry': [ + ('stamp', [2, 'builtin_interfaces/msg/Time']), + ('velocity_mps', [1, 'float32']), + ('front_wheel_angle_rad', [1, 'float32']), + ('rear_wheel_angle_rad', [1, 'float32']), + ], + 'autoware_auto_msgs/msg/DiagnosticHeader': [ + ('name', [1, 'string', [6, 256]]), + ('data_stamp', [2, 'builtin_interfaces/msg/Time']), + ('computation_start', [2, 'builtin_interfaces/msg/Time']), + ('runtime', [2, 'builtin_interfaces/msg/Duration']), + ('iterations', [1, 'uint32']), + ], + 'autoware_auto_msgs/msg/HighLevelControlCommand': [ + ('stamp', [2, 'builtin_interfaces/msg/Time']), + ('velocity_mps', [1, 'float32']), + ('curvature', [1, 'float32']), + ], + 'autoware_auto_msgs/msg/VehicleStateCommand': [ + ('stamp', [2, 'builtin_interfaces/msg/Time']), + ('blinker', [1, 'uint8']), + ('headlight', [1, 'uint8']), + ('wiper', [1, 'uint8']), + ('gear', [1, 'uint8']), + ('mode', [1, 'uint8']), + ('hand_brake', [1, 'bool']), + ('horn', [1, 'bool']), + ], + 'autoware_auto_msgs/msg/PointClusters': [ + ('clusters', [4, [2, 'sensor_msgs/msg/PointCloud2']]), + ], + 'autoware_auto_msgs/msg/RawControlCommand': [ + ('stamp', [2, 'builtin_interfaces/msg/Time']), + ('throttle', [1, 'uint32']), + ('brake', [1, 'uint32']), + ('front_steer', [1, 'int32']), + ('rear_steer', [1, 'int32']), + ], + 'autoware_auto_msgs/msg/VehicleStateReport': [ + ('stamp', [2, 'builtin_interfaces/msg/Time']), + ('fuel', [1, 'uint8']), + ('blinker', [1, 'uint8']), + ('headlight', [1, 'uint8']), + ('wiper', [1, 'uint8']), + ('gear', [1, 'uint8']), + ('mode', [1, 'uint8']), + ('hand_brake', [1, 'bool']), + ('horn', [1, 'bool']), + ], + 'autoware_auto_msgs/msg/ControlDiagnostic': [ + ('diag_header', [2, 'autoware_auto_msgs/msg/DiagnosticHeader']), + ('new_trajectory', [1, 'bool']), + ('trajectory_source', [1, 'string', [6, 256]]), + ('pose_source', [1, 'string', [6, 256]]), + ('lateral_error_m', [1, 'float32']), + ('longitudinal_error_m', [1, 'float32']), + ('velocity_error_mps', [1, 'float32']), + ('acceleration_error_mps2', [1, 'float32']), + ('yaw_error_rad', [1, 'float32']), + ('yaw_rate_error_rps', [1, 'float32']), + ], + 'autoware_auto_msgs/msg/VehicleKinematicState': [ + ('header', [2, 'std_msgs/msg/Header']), + ('state', [2, 'autoware_auto_msgs/msg/TrajectoryPoint']), + ('delta', [2, 'geometry_msgs/msg/Transform']), + ], + 'autoware_auto_msgs/msg/BoundingBoxArray': [ + ('header', [2, 'std_msgs/msg/Header']), + ('boxes', [4, [2, 'autoware_auto_msgs/msg/BoundingBox']]), + ], + 'autoware_auto_msgs/msg/VehicleControlCommand': [ + ('stamp', [2, 'builtin_interfaces/msg/Time']), + ('long_accel_mps2', [1, 'float32']), + ('velocity_mps', [1, 'float32']), + ('front_wheel_angle_rad', [1, 'float32']), + ('rear_wheel_angle_rad', [1, 'float32']), + ], +} diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..5a703b86 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag tests.""" diff --git a/tests/test_parse.py b/tests/test_parse.py new file mode 100644 index 00000000..014149ce --- /dev/null +++ b/tests/test_parse.py @@ -0,0 +1,156 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Message definition parser tests.""" + +import pytest + +from rosbags.typesys import TypesysError, get_types_from_idl, get_types_from_msg, register_types +from rosbags.typesys.base import Nodetype +from rosbags.typesys.types import FIELDDEFS + +MSG = """ +# comment + +int32 global=42 + +std_msgs/Header header +std_msgs/msg/Bool bool +test_msgs/Bar sibling +float64 base +float64[] seq1 +float64[] seq2 +float64[4] array +""" + +MULTI_MSG = """ +std_msgs/Header header +byte b +char c +Other[] o + +================================================================================ +MSG: std_msgs/Header +time time + +================================================================================ +MSG: test_msgs/Other +uint64[3] Header +""" + +IDL_LANG = """ +// assign different literals and expressions + +#ifndef FOO +#define FOO + +#include +#include "local" + +const bool g_bool = TRUE; +const int8 g_int1 = 7; +const int8 g_int2 = 07; +const int8 g_int3 = 0x7; +const float64 g_float1 = 1.1; +const float64 g_float2 = 1e10; +const char g_char = 'c'; +const string g_string1 = ""; +const string<128> g_string2 = "str" "ing"; + +module Foo { + const int64 g_expr1 = ~1; + const int64 g_expr2 = 2 * 4; +}; + +#endif +""" + +IDL = """ +// comment in file +module test_msgs { + // comment in module + typedef std_msgs::msg::Bool Bool; + + module msg { + // comment in submodule + typedef Bool Balias; + typedef test_msgs::msg::Bar Bar; + typedef double d4[4]; + + @comment(type="text", text="ignore") + struct Foo { + std_msgs::msg::Header header; + Balias bool; + Bar sibling; + double x; + sequence seq1; + sequence seq2; + d4 array; + }; + }; +}; +""" + + +def test_parse_msg(): + """Test msg parser.""" + with pytest.raises(TypesysError, match='Could not parse'): + get_types_from_msg('', 'test_msgs/msg/Foo') + ret = get_types_from_msg(MSG, 'test_msgs/msg/Foo') + assert 'test_msgs/msg/Foo' in ret + fields = ret['test_msgs/msg/Foo'] + assert fields[0][0][1] == 'std_msgs/msg/Header' + assert fields[0][1][1] == 'header' + assert fields[1][0][1] == 'std_msgs/msg/Bool' + assert fields[1][1][1] == 'bool' + assert fields[2][0][1] == 'test_msgs/msg/Bar' + assert fields[2][1][1] == 'sibling' + assert fields[3][0][0] == Nodetype.BASE + assert fields[4][0][0] == Nodetype.SEQUENCE + assert fields[5][0][0] == Nodetype.SEQUENCE + assert fields[6][0][0] == Nodetype.ARRAY + + +def test_parse_multi_msg(): + """Test multi msg parser.""" + ret = get_types_from_msg(MULTI_MSG, 'test_msgs/msg/Foo') + assert len(ret) == 3 + assert 'test_msgs/msg/Foo' in ret + assert 'std_msgs/msg/Header' in ret + assert 'test_msgs/msg/Other' in ret + assert ret['test_msgs/msg/Foo'][0][0][1] == 'std_msgs/msg/Header' + assert ret['test_msgs/msg/Foo'][1][0][1] == 'uint8' + assert ret['test_msgs/msg/Foo'][2][0][1] == 'uint8' + + +def test_parse_idl(): + """Test idl parser.""" + ret = get_types_from_idl(IDL_LANG) + assert ret == {} + + ret = get_types_from_idl(IDL) + assert 'test_msgs/msg/Foo' in ret + fields = ret['test_msgs/msg/Foo'] + assert fields[0][0][1] == 'std_msgs/msg/Header' + assert fields[0][1][1] == 'header' + assert fields[1][0][1] == 'std_msgs/msg/Bool' + assert fields[1][1][1] == 'bool' + assert fields[2][0][1] == 'test_msgs/msg/Bar' + assert fields[2][1][1] == 'sibling' + assert fields[3][0][0] == Nodetype.BASE + assert fields[4][0][0] == Nodetype.SEQUENCE + assert fields[5][0][0] == Nodetype.SEQUENCE + assert fields[6][0][0] == Nodetype.ARRAY + + +def test_register_types(): + """Test type registeration.""" + assert 'foo' not in FIELDDEFS + register_types({}) + register_types({'foo': [[(1, 'bool'), (2, 'b')]]}) + assert 'foo' in FIELDDEFS + + register_types({'std_msgs/msg/Header': []}) + assert len(FIELDDEFS['std_msgs/msg/Header']) == 2 + + with pytest.raises(TypesysError, match='different definition'): + register_types({'foo': [[(1, 'bool'), (2, 'x')]]}) From abd0c1fa73dad325a8303b47b0a3fcdefdb79e97 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 2 May 2021 14:46:31 +0200 Subject: [PATCH 003/114] Add serde --- docs/api/rosbags.rst | 1 + docs/api/rosbags.serde.rst | 6 + docs/index.rst | 1 + docs/topics/serde.rst | 31 +++ src/rosbags/serde/__init__.py | 19 ++ src/rosbags/serde/cdr.py | 443 ++++++++++++++++++++++++++++++++ src/rosbags/serde/messages.py | 72 ++++++ src/rosbags/serde/primitives.py | 55 ++++ src/rosbags/serde/ros1.py | 180 +++++++++++++ src/rosbags/serde/serdes.py | 102 ++++++++ src/rosbags/serde/typing.py | 35 +++ src/rosbags/serde/utils.py | 103 ++++++++ tests/cdr.py | 441 +++++++++++++++++++++++++++++++ tests/test_serde.py | 382 +++++++++++++++++++++++++++ 14 files changed, 1871 insertions(+) create mode 100644 docs/api/rosbags.serde.rst create mode 100644 docs/topics/serde.rst create mode 100644 src/rosbags/serde/__init__.py create mode 100644 src/rosbags/serde/cdr.py create mode 100644 src/rosbags/serde/messages.py create mode 100644 src/rosbags/serde/primitives.py create mode 100644 src/rosbags/serde/ros1.py create mode 100644 src/rosbags/serde/serdes.py create mode 100644 src/rosbags/serde/typing.py create mode 100644 src/rosbags/serde/utils.py create mode 100644 tests/cdr.py create mode 100644 tests/test_serde.py diff --git a/docs/api/rosbags.rst b/docs/api/rosbags.rst index b106356c..c2d05d62 100644 --- a/docs/api/rosbags.rst +++ b/docs/api/rosbags.rst @@ -4,4 +4,5 @@ Rosbags namespace .. toctree:: :maxdepth: 4 + rosbags.serde rosbags.typesys diff --git a/docs/api/rosbags.serde.rst b/docs/api/rosbags.serde.rst new file mode 100644 index 00000000..0fe5f96e --- /dev/null +++ b/docs/api/rosbags.serde.rst @@ -0,0 +1,6 @@ +rosbags.serde +============= + +.. automodule:: rosbags.serde + :members: + :show-inheritance: \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 83f2482d..a73cf508 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,6 +11,7 @@ :hidden: topics/typesys + topics/serde .. toctree:: diff --git a/docs/topics/serde.rst b/docs/topics/serde.rst new file mode 100644 index 00000000..69b95ebd --- /dev/null +++ b/docs/topics/serde.rst @@ -0,0 +1,31 @@ +Serialization and deserialization +================================= + +The serialization and deserialization system :py:mod:`rosbags.serde` supports multiple raw message formats. For each format it provides a pair of functions, one for serialization and one for deserialization. In addition to the data to process each function usually only requires the message type name. + +Deserialization +--------------- + +Deserialize a CDR bytes object using :py:func:`deserialize_cdr() `: + +.. code-block:: python + + from rosbags.serde import deserialize_cdr + + # rawdata is of type bytes and contains serialized message + msg = deserialize_cdr(rawdata, 'geometry_msgs/msg/Quaternion') + +Serialization +--------------- + +Serialize a message with CDR using :py:func:`serialize_cdr() `: + +.. code-block:: python + + from rosbags.serde import serialize_cdr + + # serialize message with system endianess + serialized = serialize_cdr(msg, 'geometry_msgs/msg/Quaternion') + + # serialize message with explicit endianess + serialized = serialize_cdr(msg, 'geometry_msgs/msg/Quaternion', little_endian=False) diff --git a/src/rosbags/serde/__init__.py b/src/rosbags/serde/__init__.py new file mode 100644 index 00000000..55cd0961 --- /dev/null +++ b/src/rosbags/serde/__init__.py @@ -0,0 +1,19 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbags message serialization and deserialization. + +Serializers and deserializers convert between python messages objects and +the common rosbag serialization formats. Computationally cheap functions +convert directly between different serialization formats. + +""" + +from .messages import SerdeError +from .serdes import deserialize_cdr, ros1_to_cdr, serialize_cdr + +__all__ = [ + 'SerdeError', + 'deserialize_cdr', + 'ros1_to_cdr', + 'serialize_cdr', +] diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py new file mode 100644 index 00000000..ba9cc6d8 --- /dev/null +++ b/src/rosbags/serde/cdr.py @@ -0,0 +1,443 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Code generators for CDR. + +Common Data Representation `CDR`_ is the serialization format used by most ROS2 +middlewares. + +.. _CDR: https://www.omg.org/cgi-bin/doc?formal/02-06-51 + +""" + +from __future__ import annotations + +import sys +from itertools import tee +from typing import TYPE_CHECKING, Iterator, Optional, Tuple, cast + +from .typing import Field +from .utils import SIZEMAP, Valtype, align, align_after, compile_lines + +if TYPE_CHECKING: + from typing import Callable, List + + +def generate_getsize_cdr(fields: List[Field]) -> Tuple[Callable, int]: + """Generate cdr size calculation function. + + Args: + fields: Fields of message. + + Returns: + Size calculation function and static size. + + """ + # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements + size = 0 + is_stat = True + + aligned = 8 + icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + next(inext) + lines = [ + 'import sys', + 'from rosbags.serde.messages import get_msgdef', + 'def getsize_cdr(pos, message):', + ] + for fcurr, fnext in zip(icurr, inext): + fieldname, desc = fcurr + + if desc.valtype == Valtype.MESSAGE: + if desc.args.size_cdr: + lines.append(f' pos += {desc.args.size_cdr}') + size += desc.args.size_cdr + else: + lines.append(f' func = get_msgdef("{desc.args.name}").getsize_cdr') + lines.append(f' pos = func(pos, message.{fieldname})') + is_stat = False + aligned = align_after(desc) + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + lines.append(f' pos += 4 + len(message.{fieldname}.encode()) + 1') + aligned = 1 + is_stat = False + else: + lines.append(f' pos += {SIZEMAP[desc.args]}') + aligned = SIZEMAP[desc.args] + size += SIZEMAP[desc.args] + + elif desc.valtype == Valtype.ARRAY: + subdesc = desc.args[1] + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(f' val = message.{fieldname}') + for idx in range(desc.args[0]): + lines.append(' pos = (pos + 4 - 1) & -4') + lines.append(f' pos += 4 + len(val[{idx}].encode()) + 1') + aligned = 1 + is_stat = False + else: + lines.append(f' pos += {desc.args[0] * SIZEMAP[subdesc.args]}') + size += desc.args[0] * SIZEMAP[subdesc.args] + + else: + assert subdesc.valtype == Valtype.MESSAGE + anext = align(subdesc) + anext_after = align_after(subdesc) + + if subdesc.args.size_cdr: + for _ in range(desc.args[0]): + if anext > anext_after: + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + size = (size + anext - 1) & -anext + lines.append(f' pos += {subdesc.args.size_cdr}') + size += subdesc.args.size_cdr + else: + lines.append(f' func = get_msgdef("{subdesc.args.name}").getsize_cdr') + lines.append(f' val = message.{fieldname}') + for idx in range(desc.args[0]): + if anext > anext_after: + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' pos = func(pos, val[{idx}])') + is_stat = False + aligned = align_after(subdesc) + else: + assert desc.valtype == Valtype.SEQUENCE + lines.append(' pos += 4') + aligned = 4 + subdesc = desc.args + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(f' for val in message.{fieldname}:') + lines.append(' pos = (pos + 4 - 1) & -4') + lines.append(' pos += 4 + len(val.encode()) + 1') + aligned = 1 + else: + anext = align(subdesc) + if aligned < anext: + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + aligned = anext + lines.append(f' pos += len(message.{fieldname}) * {SIZEMAP[subdesc.args]}') + + else: + assert subdesc.valtype == Valtype.MESSAGE + anext = align(subdesc) + anext_after = align_after(subdesc) + lines.append(f' val = message.{fieldname}') + if subdesc.args.size_cdr: + if aligned < anext <= anext_after: + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(' for _ in val:') + if anext > anext_after: + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' pos += {subdesc.args.size_cdr}') + + else: + lines.append(f' func = get_msgdef("{subdesc.args.name}").getsize_cdr') + if aligned < anext <= anext_after: + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(' for item in val:') + if anext > anext_after: + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(' pos = func(pos, item)') + aligned = align_after(subdesc) + + aligned = min([aligned, 4]) + is_stat = False + + if fnext and aligned < (anext := align(fnext.descriptor)): + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + aligned = anext + is_stat = False + lines.append(' return pos') + return compile_lines(lines).getsize_cdr, is_stat * size # type: ignore + + +def generate_serialize_cdr(fields: List[Field], endianess: str) -> Callable: + """Generate cdr serialization function. + + Args: + fields: Fields of message. + endianess: Endianess of rawdata. + + Returns: + Serializer function. + + """ + # pylint: disable=too-many-branches,too-many-locals,too-many-statements + aligned = 8 + icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + next(inext) + lines = [ + 'import sys', + 'import numpy', + 'from rosbags.serde.messages import SerdeError, get_msgdef', + f'from rosbags.serde.primitives import pack_bool_{endianess}', + f'from rosbags.serde.primitives import pack_int8_{endianess}', + f'from rosbags.serde.primitives import pack_int16_{endianess}', + f'from rosbags.serde.primitives import pack_int32_{endianess}', + f'from rosbags.serde.primitives import pack_int64_{endianess}', + f'from rosbags.serde.primitives import pack_uint8_{endianess}', + f'from rosbags.serde.primitives import pack_uint16_{endianess}', + f'from rosbags.serde.primitives import pack_uint32_{endianess}', + f'from rosbags.serde.primitives import pack_uint64_{endianess}', + f'from rosbags.serde.primitives import pack_float32_{endianess}', + f'from rosbags.serde.primitives import pack_float64_{endianess}', + 'def serialize_cdr(rawdata, pos, message):', + ] + for fcurr, fnext in zip(icurr, inext): + fieldname, desc = fcurr + + lines.append(f' val = message.{fieldname}') + if desc.valtype == Valtype.MESSAGE: + lines.append(f' func = get_msgdef("{desc.args.name}").serialize_cdr_{endianess}') + lines.append(' pos = func(rawdata, pos, val)') + aligned = align_after(desc) + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + lines.append(' bval = memoryview(val.encode())') + lines.append(' length = len(bval) + 1') + lines.append(f' pack_int32_{endianess}(rawdata, pos, length)') + lines.append(' pos += 4') + lines.append(' rawdata[pos:pos + length - 1] = bval') + lines.append(' pos += length') + aligned = 1 + else: + lines.append(f' pack_{desc.args}_{endianess}(rawdata, pos, val)') + lines.append(f' pos += {SIZEMAP[desc.args]}') + aligned = SIZEMAP[desc.args] + + elif desc.valtype == Valtype.ARRAY: + subdesc = desc.args[1] + lines.append(f' if len(val) != {desc.args[0]}:') + lines.append(' raise SerdeError(\'Unexpected array length\')') + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + for idx in range(desc.args[0]): + lines.append(f' bval = memoryview(val[{idx}].encode())') + lines.append(' length = len(bval) + 1') + lines.append(' pos = (pos + 4 - 1) & -4') + lines.append(f' pack_int32_{endianess}(rawdata, pos, length)') + lines.append(' pos += 4') + lines.append(' rawdata[pos:pos + length - 1] = bval') + lines.append(' pos += length') + aligned = 1 + else: + if (endianess == 'le') != (sys.byteorder == 'little'): + lines.append(' val = val.byteswap()') + size = desc.args[0] * SIZEMAP[subdesc.args] + lines.append(f' rawdata[pos:pos + {size}] = val.view(numpy.uint8)') + lines.append(f' pos += {size}') + + else: + assert subdesc.valtype == Valtype.MESSAGE + anext = align(subdesc) + anext_after = align_after(subdesc) + lines.append( + f' func = get_msgdef("{subdesc.args.name}").serialize_cdr_{endianess}', + ) + for idx in range(desc.args[0]): + if anext > anext_after: + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' pos = func(rawdata, pos, val[{idx}])') + aligned = align_after(subdesc) + else: + assert desc.valtype == Valtype.SEQUENCE + lines.append(f' pack_int32_{endianess}(rawdata, pos, len(val))') + lines.append(' pos += 4') + aligned = 4 + subdesc = desc.args + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(' for item in val:') + lines.append(' bval = memoryview(item.encode())') + lines.append(' length = len(bval) + 1') + lines.append(' pos = (pos + 4 - 1) & -4') + lines.append(f' pack_int32_{endianess}(rawdata, pos, length)') + lines.append(' pos += 4') + lines.append(' rawdata[pos:pos + length - 1] = bval') + lines.append(' pos += length') + aligned = 1 + else: + lines.append(f' size = len(val) * {SIZEMAP[subdesc.args]}') + if (endianess == 'le') != (sys.byteorder == 'little'): + lines.append(' val = val.byteswap()') + if aligned < (anext := align(subdesc)): + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(' rawdata[pos:pos + size] = val.view(numpy.uint8)') + lines.append(' pos += size') + aligned = anext + + if subdesc.valtype == Valtype.MESSAGE: + anext = align(subdesc) + lines.append( + f' func = get_msgdef("{subdesc.args.name}").serialize_cdr_{endianess}', + ) + lines.append(' for item in val:') + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(' pos = func(rawdata, pos, item)') + aligned = align_after(subdesc) + + aligned = min([4, aligned]) + + if fnext and aligned < (anext := align(fnext.descriptor)): + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + aligned = anext + lines.append(' return pos') + return compile_lines(lines).serialize_cdr # type: ignore + + +def generate_deserialize_cdr(fields: List[Field], endianess: str) -> Callable: + """Generate cdr deserialization function. + + Args: + fields: Fields of message. + endianess: Endianess of rawdata. + + Returns: + Deserializer function. + + """ + # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements + aligned = 8 + icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + next(inext) + lines = [ + 'import sys', + 'import numpy', + 'from rosbags.serde.messages import SerdeError, get_msgdef', + f'from rosbags.serde.primitives import unpack_bool_{endianess}', + f'from rosbags.serde.primitives import unpack_int8_{endianess}', + f'from rosbags.serde.primitives import unpack_int16_{endianess}', + f'from rosbags.serde.primitives import unpack_int32_{endianess}', + f'from rosbags.serde.primitives import unpack_int64_{endianess}', + f'from rosbags.serde.primitives import unpack_uint8_{endianess}', + f'from rosbags.serde.primitives import unpack_uint16_{endianess}', + f'from rosbags.serde.primitives import unpack_uint32_{endianess}', + f'from rosbags.serde.primitives import unpack_uint64_{endianess}', + f'from rosbags.serde.primitives import unpack_float32_{endianess}', + f'from rosbags.serde.primitives import unpack_float64_{endianess}', + 'def deserialize_cdr(rawdata, pos, cls):', + ] + + funcname = f'deserialize_cdr_{endianess}' + lines.append(' values = []') + for fcurr, fnext in zip(icurr, inext): + desc = fcurr[1] + + if desc.valtype == Valtype.MESSAGE: + lines.append(f' msgdef = get_msgdef("{desc.args.name}")') + lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls)') + lines.append(' values.append(obj)') + aligned = align_after(desc) + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + lines.append(f' length = unpack_int32_{endianess}(rawdata, pos)[0]') + lines.append(' string = bytes(rawdata[pos + 4:pos + 4 + length - 1]).decode()') + lines.append(' values.append(string)') + lines.append(' pos += 4 + length') + aligned = 1 + else: + lines.append(f' value = unpack_{desc.args}_{endianess}(rawdata, pos)[0]') + lines.append(' values.append(value)') + lines.append(f' pos += {SIZEMAP[desc.args]}') + aligned = SIZEMAP[desc.args] + + elif desc.valtype == Valtype.ARRAY: + subdesc = desc.args[1] + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(' value = []') + for idx in range(desc.args[0]): + if idx: + lines.append(' pos = (pos + 4 - 1) & -4') + lines.append(f' length = unpack_int32_{endianess}(rawdata, pos)[0]') + lines.append( + ' value.append(bytes(rawdata[pos + 4:pos + 4 + length - 1]).decode())', + ) + lines.append(' pos += 4 + length') + lines.append(' values.append(value)') + aligned = 1 + else: + size = desc.args[0] * SIZEMAP[subdesc.args] + lines.append( + f' val = numpy.frombuffer(rawdata, ' + f'dtype=numpy.{subdesc.args}, count={desc.args[0]}, offset=pos)', + ) + if (endianess == 'le') != (sys.byteorder == 'little'): + lines.append(' val = val.byteswap()') + lines.append(' values.append(val)') + lines.append(f' pos += {size}') + else: + assert subdesc.valtype == Valtype.MESSAGE + anext = align(subdesc) + anext_after = align_after(subdesc) + lines.append(f' msgdef = get_msgdef("{subdesc.args.name}")') + lines.append(' value = []') + for _ in range(desc.args[0]): + if anext > anext_after: + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls)') + lines.append(' value.append(obj)') + lines.append(' values.append(value)') + aligned = align_after(subdesc) + + else: + assert desc.valtype == Valtype.SEQUENCE + lines.append(f' size = unpack_int32_{endianess}(rawdata, pos)[0]') + lines.append(' pos += 4') + aligned = 4 + subdesc = desc.args + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(' value = []') + lines.append(' for _ in range(size):') + lines.append(' pos = (pos + 4 - 1) & -4') + lines.append(f' length = unpack_int32_{endianess}(rawdata, pos)[0]') + lines.append( + ' value.append(bytes(rawdata[pos + 4:pos + 4 + length - 1])' + '.decode())', + ) + lines.append(' pos += 4 + length') + lines.append(' values.append(value)') + aligned = 1 + else: + lines.append(f' length = size * {SIZEMAP[subdesc.args]}') + if aligned < (anext := align(subdesc)): + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append( + f' val = numpy.frombuffer(rawdata, ' + f'dtype=numpy.{subdesc.args}, count=size, offset=pos)', + ) + if (endianess == 'le') != (sys.byteorder == 'little'): + lines.append(' val = val.byteswap()') + lines.append(' values.append(val)') + lines.append(' pos += length') + aligned = anext + + if subdesc.valtype == Valtype.MESSAGE: + anext = align(subdesc) + lines.append(f' msgdef = get_msgdef("{subdesc.args.name}")') + lines.append(' value = []') + lines.append(' for _ in range(size):') + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls)') + lines.append(' value.append(obj)') + lines.append(' values.append(value)') + aligned = align_after(subdesc) + + aligned = min([4, aligned]) + + if fnext and aligned < (anext := align(fnext.descriptor)): + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + aligned = anext + + lines.append(' return cls(*values), pos') + return compile_lines(lines).deserialize_cdr # type: ignore diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py new file mode 100644 index 00000000..3c2193d7 --- /dev/null +++ b/src/rosbags/serde/messages.py @@ -0,0 +1,72 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Runtime message loader and cache.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rosbags.typesys import types + +from .cdr import generate_deserialize_cdr, generate_getsize_cdr, generate_serialize_cdr +from .ros1 import generate_ros1_to_cdr +from .typing import Field, Msgdef +from .utils import Descriptor, Valtype + +if TYPE_CHECKING: + from typing import Any, Dict + + +MSGDEFCACHE: Dict[str, Msgdef] = {} + + +class SerdeError(Exception): + """Serialization and Deserialization Error.""" + + +def get_msgdef(typename: str) -> Msgdef: + """Retrieve message definition for typename. + + Message definitions are cached globally and generated as needed. + + Args: + typename: Msgdef type name to load. + + Returns: + Message definition. + + """ + if typename not in MSGDEFCACHE: + entries = types.FIELDDEFS[typename] + + def fixup(entry: Any) -> Descriptor: + if entry[0] == Valtype.BASE: + return Descriptor(Valtype.BASE, entry[1]) + if entry[0] == Valtype.MESSAGE: + return Descriptor(Valtype.MESSAGE, get_msgdef(entry[1])) + if entry[0] == Valtype.ARRAY: + return Descriptor(Valtype.ARRAY, (entry[1], fixup(entry[2]))) + if entry[0] == Valtype.SEQUENCE: + return Descriptor(Valtype.SEQUENCE, fixup(entry[1])) + raise SerdeError( # pragma: no cover + f'Unknown field type {entry[0]!r} encountered.', + ) + + fields = [Field(name, fixup(desc)) for name, desc in entries] + + getsize_cdr, size_cdr = generate_getsize_cdr(fields) + + MSGDEFCACHE[typename] = Msgdef( + typename, + fields, + getattr(types, typename.replace('/', '__')), + size_cdr, + getsize_cdr, + generate_serialize_cdr(fields, 'le'), + generate_serialize_cdr(fields, 'be'), + generate_deserialize_cdr(fields, 'le'), + generate_deserialize_cdr(fields, 'be'), + generate_ros1_to_cdr(fields, typename, False), + generate_ros1_to_cdr(fields, typename, True), + ) + return MSGDEFCACHE[typename] diff --git a/src/rosbags/serde/primitives.py b/src/rosbags/serde/primitives.py new file mode 100644 index 00000000..21e04338 --- /dev/null +++ b/src/rosbags/serde/primitives.py @@ -0,0 +1,55 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Serialization primitives. + +These functions are used by generated code to serialize and desesialize +primitive values. + +""" + +from struct import Struct + +pack_bool_le = Struct('?').pack_into +pack_int8_le = Struct('b').pack_into +pack_int16_le = Struct('h').pack_into +pack_int32_be = Struct('>i').pack_into +pack_int64_be = Struct('>q').pack_into +pack_uint8_be = Struct('B').pack_into +pack_uint16_be = Struct('>H').pack_into +pack_uint32_be = Struct('>I').pack_into +pack_uint64_be = Struct('>Q').pack_into +pack_float32_be = Struct('>f').pack_into +pack_float64_be = Struct('>d').pack_into +unpack_bool_be = Struct('?').unpack_from +unpack_int8_be = Struct('b').unpack_from +unpack_int16_be = Struct('>h').unpack_from +unpack_int32_be = Struct('>i').unpack_from +unpack_int64_be = Struct('>q').unpack_from +unpack_uint8_be = Struct('B').unpack_from +unpack_uint16_be = Struct('>H').unpack_from +unpack_uint32_be = Struct('>I').unpack_from +unpack_uint64_be = Struct('>Q').unpack_from +unpack_float32_be = Struct('>f').unpack_from +unpack_float64_be = Struct('>d').unpack_from diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py new file mode 100644 index 00000000..fb1c212b --- /dev/null +++ b/src/rosbags/serde/ros1.py @@ -0,0 +1,180 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Code generators for ROS1. + +`ROS1`_ uses a serialization format. This module supports fast byte-level +conversion of ROS1 to CDR. + +.. _ROS1: http://wiki.ros.org/ROS/Technical%20Overview + +""" + +from __future__ import annotations + +from itertools import tee +from typing import TYPE_CHECKING, Iterator, Optional, Tuple, cast + +from .typing import Field +from .utils import SIZEMAP, Valtype, align, align_after, compile_lines + +if TYPE_CHECKING: + from typing import Callable, List # pylint: disable=ungrouped-imports + + +def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Callable: + """Generate CDR serialization function. + + Args: + fields: Fields of message. + typename: Message type name. + copy: Generate serialization or sizing function. + + Returns: + ROS1 to CDR conversion function. + + """ + # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements + aligned = 8 + icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + next(inext) + funcname = 'ros1_to_cdr' if copy else 'getsize_ros1_to_cdr' + lines = [ + 'import sys', + 'import numpy', + 'from rosbags.serde.messages import SerdeError, get_msgdef', + 'from rosbags.serde.primitives import pack_bool_le', + 'from rosbags.serde.primitives import pack_int8_le', + 'from rosbags.serde.primitives import pack_int16_le', + 'from rosbags.serde.primitives import pack_int32_le', + 'from rosbags.serde.primitives import pack_int64_le', + 'from rosbags.serde.primitives import pack_uint8_le', + 'from rosbags.serde.primitives import pack_uint16_le', + 'from rosbags.serde.primitives import pack_uint32_le', + 'from rosbags.serde.primitives import pack_uint64_le', + 'from rosbags.serde.primitives import pack_float32_le', + 'from rosbags.serde.primitives import pack_float64_le', + 'from rosbags.serde.primitives import unpack_int32_le', + f'def {funcname}(input, ipos, output, opos):', + ] + + if typename == 'std_msgs/msg/Header': + lines.append(' ipos += 4') + + for fcurr, fnext in zip(icurr, inext): + _, desc = fcurr + + if desc.valtype == Valtype.MESSAGE: + lines.append(f' func = get_msgdef("{desc.args.name}").{funcname}') + lines.append(' ipos, opos = func(input, ipos, output, opos)') + aligned = align_after(desc) + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + lines.append(' length = unpack_int32_le(input, ipos)[0] + 1') + if copy: + lines.append(' pack_int32_le(output, opos, length)') + lines.append(' ipos += 4') + lines.append(' opos += 4') + if copy: + lines.append(' output[opos:opos + length - 1] = input[ipos:ipos + length - 1]') + lines.append(' ipos += length - 1') + lines.append(' opos += length') + aligned = 1 + else: + size = SIZEMAP[desc.args] + if copy: + lines.append(f' output[opos:opos + {size}] = input[ipos:ipos + {size}]') + lines.append(f' ipos += {size}') + lines.append(f' opos += {size}') + aligned = size + + elif desc.valtype == Valtype.ARRAY: + subdesc = desc.args[1] + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + for _ in range(desc.args[0]): + lines.append(' opos = (opos + 4 - 1) & -4') + lines.append(' length = unpack_int32_le(input, ipos)[0] + 1') + if copy: + lines.append(' pack_int32_le(output, opos, length)') + lines.append(' ipos += 4') + lines.append(' opos += 4') + if copy: + lines.append( + ' output[opos:opos + length - 1] = input[ipos:ipos + length - 1]', + ) + lines.append(' ipos += length - 1') + lines.append(' opos += length') + aligned = 1 + else: + size = desc.args[0] * SIZEMAP[subdesc.args] + if copy: + lines.append(f' output[opos:opos + {size}] = input[ipos:ipos + {size}]') + lines.append(f' ipos += {size}') + lines.append(f' opos += {size}') + aligned = SIZEMAP[subdesc.args] + + if subdesc.valtype == Valtype.MESSAGE: + anext = align(subdesc) + anext_after = align_after(subdesc) + + lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') + for _ in range(desc.args[0]): + if anext > anext_after: + lines.append(f' opos = (opos + {anext} - 1) & -{anext}') + lines.append(' ipos, opos = func(input, ipos, output, opos)') + aligned = anext_after + else: + assert desc.valtype == Valtype.SEQUENCE + lines.append(' size = unpack_int32_le(input, ipos)[0]') + if copy: + lines.append(' pack_int32_le(output, opos, size)') + lines.append(' ipos += 4') + lines.append(' opos += 4') + subdesc = desc.args + aligned = 4 + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(' for _ in range(size):') + lines.append(' length = unpack_int32_le(input, ipos)[0] + 1') + lines.append(' opos = (opos + 4 - 1) & -4') + if copy: + lines.append(' pack_int32_le(output, opos, length)') + lines.append(' ipos += 4') + lines.append(' opos += 4') + if copy: + lines.append( + ' output[opos:opos + length - 1] = input[ipos:ipos + length - 1]', + ) + lines.append(' ipos += length - 1') + lines.append(' opos += length') + aligned = 1 + else: + if aligned < (anext := align(subdesc)): + lines.append(f' opos = (opos + {anext} - 1) & -{anext}') + lines.append(f' length = size * {SIZEMAP[subdesc.args]}') + if copy: + lines.append(' output[opos:opos + length] = input[ipos:ipos + length]') + lines.append(' ipos += length') + lines.append(' opos += length') + aligned = anext + + else: + assert subdesc.valtype == Valtype.MESSAGE + anext = align(subdesc) + lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') + lines.append(' for _ in range(size):') + lines.append(f' opos = (opos + {anext} - 1) & -{anext}') + lines.append(' ipos, opos = func(input, ipos, output, opos)') + aligned = align_after(subdesc) + + aligned = min([aligned, 4]) + + if fnext and aligned < (anext := align(fnext.descriptor)): + lines.append(f' opos = (opos + {anext} - 1) & -{anext}') + aligned = anext + + lines.append(' return ipos, opos') + return getattr(compile_lines(lines), funcname) diff --git a/src/rosbags/serde/serdes.py b/src/rosbags/serde/serdes.py new file mode 100644 index 00000000..260edc08 --- /dev/null +++ b/src/rosbags/serde/serdes.py @@ -0,0 +1,102 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Serialization, deserializion and conversion functions.""" + +from __future__ import annotations + +import sys +from struct import pack_into +from typing import TYPE_CHECKING + +from .messages import get_msgdef + +if TYPE_CHECKING: + from typing import Any + + +def deserialize_cdr(rawdata: bytes, typename: str) -> Any: + """Deserialize raw data into a message object. + + Args: + rawdata: Serialized data. + typename: Message type name. + + Returns: + Deserialized message object. + + """ + little_endian = bool(rawdata[1]) + + msgdef = get_msgdef(typename) + func = msgdef.deserialize_cdr_le if little_endian else msgdef.deserialize_cdr_be + message, pos = func(rawdata[4:], 0, msgdef.cls) + assert pos + 4 + 3 >= len(rawdata) + return message + + +def serialize_cdr( + message: Any, + typename: str, + little_endian: bool = sys.byteorder == 'little', +) -> memoryview: + """Serialize message object to bytes. + + Args: + message: Message object. + typename: Message type name. + little_endian: Should use little endianess. + + Returns: + Serialized bytes. + + """ + msgdef = get_msgdef(typename) + size = 4 + msgdef.getsize_cdr(0, message) + rawdata = memoryview(bytearray(size)) + pack_into('BB', rawdata, 0, 0, little_endian) + + func = msgdef.serialize_cdr_le if little_endian else msgdef.serialize_cdr_be + + pos = func(rawdata[4:], 0, message) + assert pos + 4 == size + return rawdata.toreadonly() + + +def ros1_to_cdr(raw: bytes, typename: str) -> memoryview: + """Convert serialized ROS1 message directly to CDR. + + This should be reasonably fast as conversions happen on a byte-level + without going through deserialization and serialization. + + Args: + raw: ROS1 serialized message. + typename: Message type name. + + Returns: + CDR serialized message. + + """ + msgdef = get_msgdef(typename) + + ipos, opos = msgdef.getsize_ros1_to_cdr( + raw, + 0, + None, + 0, + ) + assert ipos == len(raw) + + raw = memoryview(raw) + size = 4 + opos + rawdata = memoryview(bytearray(size)) + pack_into('BB', rawdata, 0, 0, True) + + ipos, opos = msgdef.ros1_to_cdr( + raw, + 0, + rawdata[4:], + 0, + ) + assert ipos == len(raw) + assert opos + 4 == size + return rawdata.toreadonly() diff --git a/src/rosbags/serde/typing.py b/src/rosbags/serde/typing.py new file mode 100644 index 00000000..a3b0d70c --- /dev/null +++ b/src/rosbags/serde/typing.py @@ -0,0 +1,35 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Python types used in this package.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, NamedTuple + +if TYPE_CHECKING: + from typing import Any, Callable, List # pylint: disable=ungrouped-imports + + from .utils import Descriptor + + +class Field(NamedTuple): + """Metadata of a field.""" + + name: str + descriptor: Descriptor + + +class Msgdef(NamedTuple): + """Metadata of a message.""" + + name: str + fields: List[Field] + cls: Any + size_cdr: int + getsize_cdr: Callable + serialize_cdr_le: Callable + serialize_cdr_be: Callable + deserialize_cdr_le: Callable + deserialize_cdr_be: Callable + getsize_ros1_to_cdr: Callable + ros1_to_cdr: Callable diff --git a/src/rosbags/serde/utils.py b/src/rosbags/serde/utils.py new file mode 100644 index 00000000..15362b23 --- /dev/null +++ b/src/rosbags/serde/utils.py @@ -0,0 +1,103 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Helpers used by code generators.""" + +from __future__ import annotations + +from enum import IntEnum +from importlib.util import module_from_spec, spec_from_loader +from typing import TYPE_CHECKING, NamedTuple + +if TYPE_CHECKING: + from types import ModuleType + from typing import Any, Dict, List + + +class Valtype(IntEnum): + """Msg field value types.""" + + BASE = 1 + MESSAGE = 2 + ARRAY = 3 + SEQUENCE = 4 + + +class Descriptor(NamedTuple): + """Value type descriptor.""" + + valtype: Valtype + args: Any # Union[Descriptor, Msgdef, Tuple[int, Descriptor], str] + + +SIZEMAP: Dict[str, int] = { + 'bool': 1, + 'int8': 1, + 'int16': 2, + 'int32': 4, + 'int64': 8, + 'uint8': 1, + 'uint16': 2, + 'uint32': 4, + 'uint64': 8, + 'float32': 4, + 'float64': 8, +} + + +def align(entry: Descriptor) -> int: + """Get alignment requirement for entry. + + Args: + entry: Field. + + Returns: + Required alignment in bytes. + + """ + if entry.valtype == Valtype.BASE: + if entry.args == 'string': + return 4 + return SIZEMAP[entry.args] + if entry.valtype == Valtype.MESSAGE: + return align(entry.args.fields[0].descriptor) + if entry.valtype == Valtype.ARRAY: + return align(entry.args[1]) + assert entry.valtype == Valtype.SEQUENCE + return 4 + + +def align_after(entry: Descriptor) -> int: + """Get alignment after entry. + + Args: + entry: Field. + + Returns: + Memory alignment after entry. + + """ + if entry.valtype == Valtype.BASE: + if entry.args == 'string': + return 1 + return SIZEMAP[entry.args] + if entry.valtype == Valtype.MESSAGE: + return align_after(entry.args.fields[-1].descriptor) + if entry.valtype == Valtype.ARRAY: + return align_after(entry.args[1]) + assert entry.valtype == Valtype.SEQUENCE + return min([4, align_after(entry.args)]) + + +def compile_lines(lines: List[str]) -> ModuleType: + """Compile lines of code to module. + + Args: + lines: Lines of python code. + + Returns: + Compiled and loaded module. + + """ + module = module_from_spec(spec_from_loader('tmpmod', loader=None)) + exec('\n'.join(lines), module.__dict__) # pylint: disable=exec-used + return module diff --git a/tests/cdr.py b/tests/cdr.py new file mode 100644 index 00000000..f1361891 --- /dev/null +++ b/tests/cdr.py @@ -0,0 +1,441 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Reference CDR message serializer and deserializer.""" + +from __future__ import annotations + +import sys +from struct import Struct, pack_into, unpack_from +from typing import TYPE_CHECKING, Dict, List, Union, cast + +import numpy + +from rosbags.serde.messages import SerdeError, get_msgdef +from rosbags.serde.typing import Msgdef +from rosbags.serde.utils import SIZEMAP, Valtype + +if TYPE_CHECKING: + from typing import Any, Tuple + + from rosbags.serde.typing import Descriptor + +Array = Union[List[Msgdef], List[str], numpy.ndarray] +BasetypeMap = Dict[str, Struct] +BASETYPEMAP_LE: BasetypeMap = { + 'bool': Struct('?'), + 'int8': Struct('b'), + 'int16': Struct('h'), + 'int32': Struct('>i'), + 'int64': Struct('>q'), + 'uint8': Struct('B'), + 'uint16': Struct('>H'), + 'uint32': Struct('>I'), + 'uint64': Struct('>Q'), + 'float32': Struct('>f'), + 'float64': Struct('>d'), +} + + +def deserialize_number(rawdata: bytes, bmap: BasetypeMap, pos: int, basetype: str) \ + -> Tuple[Union[bool, float, int], int]: + """Deserialize a single boolean, float, or int. + + Args: + rawdata: Serialized data. + bmap: Basetype metadata. + pos: Read position. + basetype: Number type string. + + Returns: + Deserialized number and new read position. + + """ + dtype, size = bmap[basetype], SIZEMAP[basetype] + pos = (pos + size - 1) & -size + return dtype.unpack_from(rawdata, pos)[0], pos + size + + +def deserialize_string(rawdata: bytes, bmap: BasetypeMap, pos: int) \ + -> Tuple[str, int]: + """Deserialize a string value. + + Args: + rawdata: Serialized data. + bmap: Basetype metadata. + pos: Read position. + + Returns: + Deserialized string and new read position. + + """ + pos = (pos + 4 - 1) & -4 + length = bmap['int32'].unpack_from(rawdata, pos)[0] + val = bytes(rawdata[pos + 4:pos + 4 + length - 1]) + return val.decode(), pos + 4 + length + + +def deserialize_array(rawdata: bytes, bmap: BasetypeMap, pos: int, num: int, desc: Descriptor) \ + -> Tuple[Array, int]: + """Deserialize an array of items of same type. + + Args: + rawdata: Serialized data. + bmap: Basetype metadata. + pos: Read position. + num: Number of elements. + desc: Element type descriptor. + + Returns: + Deserialized array and new read position. + + Raises: + SerdeError: Unexpected element type. + + """ + if desc.valtype == Valtype.BASE: + if desc.args == 'string': + strs = [] + while (num := num - 1) >= 0: + val, pos = deserialize_string(rawdata, bmap, pos) + strs.append(val) + return strs, pos + + size = SIZEMAP[desc.args] + pos = (pos + size - 1) & -size + ndarr = numpy.frombuffer(rawdata, dtype=desc.args, count=num, offset=pos) + if (bmap is BASETYPEMAP_LE) != (sys.byteorder == 'little'): + ndarr = ndarr.byteswap() # no inplace on readonly array + return ndarr, pos + num * SIZEMAP[desc.args] + + if desc.valtype == Valtype.MESSAGE: + msgs = [] + while (num := num - 1) >= 0: + msg, pos = deserialize_message(rawdata, bmap, pos, desc.args) + msgs.append(msg) + return msgs, pos + + raise SerdeError(f'Nested arrays {desc!r} are not supported.') + + +def deserialize_message(rawdata: bytes, bmap: BasetypeMap, pos: int, msgdef: Msgdef) \ + -> Tuple[Msgdef, int]: + """Deserialize a message. + + Args: + rawdata: Serialized data. + bmap: Basetype metadata. + pos: Read position. + msgdef: Message definition. + + Returns: + Deserialized message and new read position. + + """ + values: List[Any] = [] + + for _, desc in msgdef.fields: + if desc.valtype == Valtype.MESSAGE: + obj, pos = deserialize_message(rawdata, bmap, pos, desc.args) + values.append(obj) + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + val, pos = deserialize_string(rawdata, bmap, pos) + values.append(val) + else: + num, pos = deserialize_number(rawdata, bmap, pos, desc.args) + values.append(num) + + elif desc.valtype == Valtype.ARRAY: + arr, pos = deserialize_array(rawdata, bmap, pos, *desc.args) + values.append(arr) + + elif desc.valtype == Valtype.SEQUENCE: + size, pos = deserialize_number(rawdata, bmap, pos, 'int32') + arr, pos = deserialize_array(rawdata, bmap, pos, int(size), desc.args) + values.append(arr) + + return msgdef.cls(*values), pos + + +def deserialize(rawdata: bytes, typename: str) -> Msgdef: + """Deserialize raw data into a message object. + + Args: + rawdata: Serialized data. + typename: Type to deserialize. + + Returns: + Deserialized message object. + + """ + _, little_endian = unpack_from('BB', rawdata, 0) + + msgdef = get_msgdef(typename) + obj, _ = deserialize_message( + rawdata[4:], + BASETYPEMAP_LE if little_endian else BASETYPEMAP_BE, + 0, + msgdef, + ) + + return obj + + +def serialize_number( + rawdata: memoryview, + bmap: BasetypeMap, + pos: int, + basetype: str, + val: Union[bool, float, int], +) -> int: + """Serialize a single boolean, float, or int. + + Args: + rawdata: Serialized data. + bmap: Basetype metadata. + pos: Write position. + basetype: Number type string. + val: Value to serialize. + + Returns: + Next write position. + + """ + dtype, size = bmap[basetype], SIZEMAP[basetype] + pos = (pos + size - 1) & -size + dtype.pack_into(rawdata, pos, val) + return pos + size + + +def serialize_string(rawdata: memoryview, bmap: BasetypeMap, pos: int, val: str) \ + -> int: + """Deserialize a string value. + + Args: + rawdata: Serialized data. + bmap: Basetype metadata. + pos: Write position. + val: Value to serialize. + + Returns: + Next write position. + + """ + bval = memoryview(val.encode()) + length = len(bval) + 1 + + pos = (pos + 4 - 1) & -4 + bmap['int32'].pack_into(rawdata, pos, length) + rawdata[pos + 4:pos + 4 + length - 1] = bval + return pos + 4 + length + + +def serialize_array( + rawdata: memoryview, + bmap: BasetypeMap, + pos: int, + desc: Descriptor, + val: Array, +) -> int: + """Serialize an array of items of same type. + + Args: + rawdata: Serialized data. + bmap: Basetype metadata. + pos: Write position. + desc: Element type descriptor. + val: Value to serialize. + + Returns: + Next write position. + + Raises: + SerdeError: Unexpected element type. + + """ + if desc.valtype == Valtype.BASE: + if desc.args == 'string': + for item in val: + pos = serialize_string(rawdata, bmap, pos, cast(str, item)) + return pos + + size = SIZEMAP[desc.args] + pos = (pos + size - 1) & -size + size *= len(val) + val = cast(numpy.ndarray, val) + if (bmap is BASETYPEMAP_LE) != (sys.byteorder == 'little'): + val = val.byteswap() # no inplace on readonly array + rawdata[pos:pos + size] = memoryview(val.tobytes()) + return pos + size + + if desc.valtype == Valtype.MESSAGE: + for item in val: + pos = serialize_message(rawdata, bmap, pos, item, desc.args) + return pos + + raise SerdeError(f'Nested arrays {desc!r} are not supported.') # pragma: no cover + + +def serialize_message( + rawdata: memoryview, + bmap: BasetypeMap, + pos: int, + message: Any, + msgdef: Msgdef, +) -> int: + """Serialize a message. + + Args: + rawdata: Serialized data. + bmap: Basetype metadata. + pos: Write position. + message: Message object. + msgdef: Message definition. + + Returns: + Next write position. + + """ + for fieldname, desc in msgdef.fields: + val = getattr(message, fieldname) + if desc.valtype == Valtype.MESSAGE: + pos = serialize_message(rawdata, bmap, pos, val, desc.args) + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + pos = serialize_string(rawdata, bmap, pos, val) + else: + pos = serialize_number(rawdata, bmap, pos, desc.args, val) + + elif desc.valtype == Valtype.ARRAY: + pos = serialize_array(rawdata, bmap, pos, desc.args[1], val) + + elif desc.valtype == Valtype.SEQUENCE: + size = len(val) + pos = serialize_number(rawdata, bmap, pos, 'int32', size) + pos = serialize_array(rawdata, bmap, pos, desc.args, val) + + return pos + + +def get_array_size(desc: Descriptor, val: Array, size: int) -> int: + """Calculate size of an array. + + Args: + desc: Element type descriptor. + val: Array to calculate size of. + size: Current size of message. + + Returns: + Size of val in bytes. + + Raises: + SerdeError: Unexpected element type. + + """ + if desc.valtype == Valtype.BASE: + if desc.args == 'string': + for item in val: + size = (size + 4 - 1) & -4 + size += 4 + len(item) + 1 + return size + + isize = SIZEMAP[desc.args] + size = (size + isize - 1) & -isize + return size + isize * len(val) + + if desc.valtype == Valtype.MESSAGE: + for item in val: + size = get_size(item, desc.args, size) + return size + + raise SerdeError(f'Nested arrays {desc!r} are not supported.') # pragma: no cover + + +def get_size(message: Any, msgdef: Msgdef, size: int = 0) -> int: + """Calculate size of serialzied message. + + Args: + message: Message object. + msgdef: Message definition. + size: Current size of message. + + Returns: + Size of message in bytes. + + Raises: + SerdeError: Unexpected array length in message. + + """ + for fieldname, desc in msgdef.fields: + val = getattr(message, fieldname) + if desc.valtype == Valtype.MESSAGE: + size = get_size(val, desc.args, size) + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + size = (size + 4 - 1) & -4 + size += 4 + len(val.encode()) + 1 + else: + isize = SIZEMAP[desc.args] + size = (size + isize - 1) & -isize + size += isize + + elif desc.valtype == Valtype.ARRAY: + if len(val) != desc.args[0]: + raise SerdeError(f'Unexpected array length: {len(val)} != {desc.args[0]}.') + size = get_array_size(desc.args[1], val, size) + + elif desc.valtype == Valtype.SEQUENCE: + size = (size + 4 - 1) & -4 + size += 4 + size = get_array_size(desc.args, val, size) + + return size + + +def serialize( + message: Any, + typename: str, + little_endian: bool = sys.byteorder == 'little', +) -> memoryview: + """Serialize message object to bytes. + + Args: + message: Message object. + typename: Type to serialize. + little_endian: Should use little endianess. + + Returns: + Serialized bytes. + + """ + msgdef = get_msgdef(typename) + size = 4 + get_size(message, msgdef) + rawdata = memoryview(bytearray(size)) + + pack_into('BB', rawdata, 0, 0, little_endian) + pos = serialize_message( + rawdata[4:], + BASETYPEMAP_LE if little_endian else BASETYPEMAP_BE, + 0, + message, + msgdef, + ) + assert pos + 4 == size + return rawdata.toreadonly() diff --git a/tests/test_serde.py b/tests/test_serde.py new file mode 100644 index 00000000..6353b277 --- /dev/null +++ b/tests/test_serde.py @@ -0,0 +1,382 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Serializer and deserializer tests.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import MagicMock, patch + +import numpy +import pytest + +from rosbags.serde import SerdeError, deserialize_cdr, ros1_to_cdr, serialize_cdr +from rosbags.serde.messages import get_msgdef +from rosbags.typesys import get_types_from_msg, register_types + +from .cdr import deserialize, serialize + +if TYPE_CHECKING: + from typing import Any, Tuple, Union + +MSG_POLY = ( + ( + b'\x00\x01\x00\x00' # header + b'\x02\x00\x00\x00' # number of points = 2 + b'\x00\x00\x80\x3f' # x = 1 + b'\x00\x00\x00\x40' # y = 2 + b'\x00\x00\x40\x40' # z = 3 + b'\x00\x00\xa0\x3f' # x = 1.25 + b'\x00\x00\x10\x40' # y = 2.25 + b'\x00\x00\x50\x40' # z = 3.25 + ), + 'geometry_msgs/msg/Polygon', + True, +) + +MSG_MAGN = ( + ( + b'\x00\x01\x00\x00' # header + b'\xc4\x02\x00\x00\x00\x01\x00\x00' # timestamp = 708s 256ns + b'\x06\x00\x00\x00foo42\x00' # frameid 'foo42' + b'\x00\x00\x00\x00\x00\x00' # padding + b'\x00\x00\x00\x00\x00\x00\x60\x40' # x = 128 + b'\x00\x00\x00\x00\x00\x00\x60\x40' # y = 128 + b'\x00\x00\x00\x00\x00\x00\x60\x40' # z = 128 + b'\x00\x00\x00\x00\x00\x00\xF0\x3F' # covariance matrix = 3x3 diag + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\xF0\x3F' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\xF0\x3F' + ), + 'sensor_msgs/msg/MagneticField', + True, +) + +MSG_MAGN_BIG = ( + ( + b'\x00\x00\x00\x00' # header + b'\x00\x00\x02\xc4\x00\x00\x01\x00' # timestamp = 708s 256ns + b'\x00\x00\x00\x06foo42\x00' # frameid 'foo42' + b'\x00\x00\x00\x00\x00\x00' # padding + b'\x40\x60\x00\x00\x00\x00\x00\x00' # x = 128 + b'\x40\x60\x00\x00\x00\x00\x00\x00' # y = 128 + b'\x40\x60\x00\x00\x00\x00\x00\x00' # z = 128 + b'\x3F\xF0\x00\x00\x00\x00\x00\x00' # covariance matrix = 3x3 diag + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x3F\xF0\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x3F\xF0\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00' # garbage + ), + 'sensor_msgs/msg/MagneticField', + False, +) + +MSG_JOINT = ( + ( + b'\x00\x01\x00\x00' # header + b'\xc4\x02\x00\x00\x00\x01\x00\x00' # timestamp = 708s 256ns + b'\x04\x00\x00\x00bar\x00' # frameid 'bar' + b'\x02\x00\x00\x00' # number of strings + b'\x02\x00\x00\x00a\x00' # string 'a' + b'\x00\x00' # padding + b'\x02\x00\x00\x00b\x00' # string 'b' + b'\x00\x00' # padding + b'\x00\x00\x00\x00' # number of points + b'\x00\x00\x00' # garbage + ), + 'trajectory_msgs/msg/JointTrajectory', + True, +) + +MESSAGES = [MSG_POLY, MSG_MAGN, MSG_MAGN_BIG, MSG_JOINT] + +STATIC_64_64 = """ +uint64[2] u64 +""" + +STATIC_64_16 = """ +uint64 u64 +uint16 u16 +""" + +STATIC_16_64 = """ +uint16 u16 +uint64 u64 +""" + +DYNAMIC_64_64 = """ +uint64[] u64 +""" + +DYNAMIC_64_B_64 = """ +uint64 u64 +bool b +float64 f64 +""" + +DYNAMIC_64_S = """ +uint64 u64 +string s +""" + +DYNAMIC_S_64 = """ +string s +uint64 u64 +""" + +CUSTOM = """ +string base_str +float32 base_f32 +test_msgs/msg/static_64_64 msg_s66 +test_msgs/msg/static_64_16 msg_s61 +test_msgs/msg/static_16_64 msg_s16 +test_msgs/msg/dynamic_64_64 msg_d66 +test_msgs/msg/dynamic_64_b_64 msg_d6b6 +test_msgs/msg/dynamic_64_s msg_d6s +test_msgs/msg/dynamic_s_64 msg_ds6 + +string[2] arr_base_str +float32[2] arr_base_f32 +test_msgs/msg/static_64_64[2] arr_msg_s66 +test_msgs/msg/static_64_16[2] arr_msg_s61 +test_msgs/msg/static_16_64[2] arr_msg_s16 +test_msgs/msg/dynamic_64_64[2] arr_msg_d66 +test_msgs/msg/dynamic_64_b_64[2] arr_msg_d6b6 +test_msgs/msg/dynamic_64_s[2] arr_msg_d6s +test_msgs/msg/dynamic_s_64[2] arr_msg_ds6 + +string[] seq_base_str +float32[] seq_base_f32 +test_msgs/msg/static_64_64[] seq_msg_s66 +test_msgs/msg/static_64_16[] seq_msg_s61 +test_msgs/msg/static_16_64[] seq_msg_s16 +test_msgs/msg/dynamic_64_64[] seq_msg_d66 +test_msgs/msg/dynamic_64_b_64[] seq_msg_d6b6 +test_msgs/msg/dynamic_64_s[] seq_msg_d6s +test_msgs/msg/dynamic_s_64[] seq_msg_ds6 +""" + + +@pytest.fixture() +def _comparable(): + """Make messages containing numpy arrays comparable. + + Notes: + This solution is necessary as numpy.ndarray is not directly patchable. + """ + frombuffer = numpy.frombuffer + + def arreq(self: MagicMock, other: Union[MagicMock, Any]) -> bool: + return (getattr(self, '_mock_wraps') == getattr(other, '_mock_wraps', other)).all() + + class CNDArray(MagicMock): + """Mock ndarray.""" + + def __init__(self, *args: Any, **kwargs: Any): + super().__init__(*args, **kwargs) + self.__eq__ = arreq # type: ignore + + def byteswap(self, *args: Any) -> 'CNDArray': + """Wrap return value also in mock.""" + return CNDArray(wraps=self._mock_wraps.byteswap(*args)) + + def wrap_frombuffer(*args: Any, **kwargs: Any) -> CNDArray: + return CNDArray(wraps=frombuffer(*args, **kwargs)) + + with patch.object(numpy, 'frombuffer', side_effect=wrap_frombuffer): + yield + + +@pytest.mark.parametrize('message', MESSAGES) +def test_serde(message: Tuple[bytes, str, bool]): + """Test serialization deserialization roundtrip.""" + rawdata, typ, is_little = message + + serdeser = serialize_cdr(deserialize_cdr(rawdata, typ), typ, is_little) + assert serdeser == serialize(deserialize(rawdata, typ), typ, is_little) + assert serdeser == rawdata[0:len(serdeser)] + assert len(rawdata) - len(serdeser) < 4 + assert all(x == 0 for x in rawdata[len(serdeser):]) + + +@pytest.mark.usefixtures('_comparable') +def test_deserializer(): + """Test deserializer.""" + msg = deserialize_cdr(*MSG_POLY[:2]) + assert msg == deserialize(*MSG_POLY[:2]) + assert len(msg.points) == 2 + assert msg.points[0].x == 1 + assert msg.points[0].y == 2 + assert msg.points[0].z == 3 + assert msg.points[1].x == 1.25 + assert msg.points[1].y == 2.25 + assert msg.points[1].z == 3.25 + + msg = deserialize_cdr(*MSG_MAGN[:2]) + assert msg == deserialize(*MSG_MAGN[:2]) + assert 'MagneticField' in repr(msg) + assert msg.header.stamp.sec == 708 + assert msg.header.stamp.nanosec == 256 + assert msg.header.frame_id == 'foo42' + field = msg.magnetic_field + assert (field.x, field.y, field.z) == (128., 128., 128.) + assert (numpy.diag(msg.magnetic_field_covariance.reshape(3, 3)) == [1., 1., 1.]).all() + + msg_big = deserialize_cdr(*MSG_MAGN_BIG[:2]) + assert msg_big == deserialize(*MSG_MAGN_BIG[:2]) + assert msg.magnetic_field == msg_big.magnetic_field + + +@pytest.mark.usefixtures('_comparable') +def test_serializer(): + """Test serializer.""" + + class Foo: # pylint: disable=too-few-public-methods + """Dummy class.""" + + data = 7 + + msg = Foo() + ret = serialize_cdr(msg, 'std_msgs/msg/Int8', True) + assert ret == serialize(msg, 'std_msgs/msg/Int8', True) + assert ret == b'\x00\x01\x00\x00\x07' + + ret = serialize_cdr(msg, 'std_msgs/msg/Int8', False) + assert ret == serialize(msg, 'std_msgs/msg/Int8', False) + assert ret == b'\x00\x00\x00\x00\x07' + + ret = serialize_cdr(msg, 'std_msgs/msg/Int16', True) + assert ret == serialize(msg, 'std_msgs/msg/Int16', True) + assert ret == b'\x00\x01\x00\x00\x07\x00' + + ret = serialize_cdr(msg, 'std_msgs/msg/Int16', False) + assert ret == serialize(msg, 'std_msgs/msg/Int16', False) + assert ret == b'\x00\x00\x00\x00\x00\x07' + + +@pytest.mark.usefixtures('_comparable') +def test_serializer_errors(): + """Test seralizer with broken messages.""" + + class Foo: # pylint: disable=too-few-public-methods + """Dummy class.""" + + coef = numpy.array([1, 2, 3, 4]) + + msg = Foo() + ret = serialize_cdr(msg, 'shape_msgs/msg/Plane', True) + assert ret == serialize(msg, 'shape_msgs/msg/Plane', True) + + msg.coef = numpy.array([1, 2, 3, 4, 4]) + with pytest.raises(SerdeError, match='array length'): + serialize_cdr(msg, 'shape_msgs/msg/Plane', True) + + +@pytest.mark.usefixtures('_comparable') +def test_custom_type(): + """Test custom type.""" + cname = 'test_msgs/msg/custom' + register_types(dict(get_types_from_msg(STATIC_64_64, 'test_msgs/msg/static_64_64'))) + register_types(dict(get_types_from_msg(STATIC_64_16, 'test_msgs/msg/static_64_16'))) + register_types(dict(get_types_from_msg(STATIC_16_64, 'test_msgs/msg/static_16_64'))) + register_types(dict(get_types_from_msg(DYNAMIC_64_64, 'test_msgs/msg/dynamic_64_64'))) + register_types(dict(get_types_from_msg(DYNAMIC_64_B_64, 'test_msgs/msg/dynamic_64_b_64'))) + register_types(dict(get_types_from_msg(DYNAMIC_64_S, 'test_msgs/msg/dynamic_64_s'))) + register_types(dict(get_types_from_msg(DYNAMIC_S_64, 'test_msgs/msg/dynamic_s_64'))) + register_types(dict(get_types_from_msg(CUSTOM, cname))) + + static_64_64 = get_msgdef('test_msgs/msg/static_64_64').cls + static_64_16 = get_msgdef('test_msgs/msg/static_64_16').cls + static_16_64 = get_msgdef('test_msgs/msg/static_16_64').cls + dynamic_64_64 = get_msgdef('test_msgs/msg/dynamic_64_64').cls + dynamic_64_b_64 = get_msgdef('test_msgs/msg/dynamic_64_b_64').cls + dynamic_64_s = get_msgdef('test_msgs/msg/dynamic_64_s').cls + dynamic_s_64 = get_msgdef('test_msgs/msg/dynamic_s_64').cls + custom = get_msgdef('test_msgs/msg/custom').cls + + msg = custom( + 'str', + 1.5, + static_64_64(numpy.array([64, 64], dtype=numpy.uint64)), + static_64_16(64, 16), + static_16_64(16, 64), + dynamic_64_64(numpy.array([33, 33], dtype=numpy.uint64)), + dynamic_64_b_64(64, True, 1.25), + dynamic_64_s(64, 's'), + dynamic_s_64('s', 64), + # arrays + ['str_1', ''], + numpy.array([1.5, 0.75], dtype=numpy.float32), + [ + static_64_64(numpy.array([64, 64], dtype=numpy.uint64)), + static_64_64(numpy.array([64, 64], dtype=numpy.uint64)), + ], + [static_64_16(64, 16), static_64_16(64, 16)], + [static_16_64(16, 64), static_16_64(16, 64)], + [ + dynamic_64_64(numpy.array([33, 33], dtype=numpy.uint64)), + dynamic_64_64(numpy.array([33, 33], dtype=numpy.uint64)), + ], + [ + dynamic_64_b_64(64, True, 1.25), + dynamic_64_b_64(64, True, 1.25), + ], + [dynamic_64_s(64, 's'), dynamic_64_s(64, 's')], + [dynamic_s_64('s', 64), dynamic_s_64('s', 64)], + # sequences + ['str_1', ''], + numpy.array([1.5, 0.75], dtype=numpy.float32), + [ + static_64_64(numpy.array([64, 64], dtype=numpy.uint64)), + static_64_64(numpy.array([64, 64], dtype=numpy.uint64)), + ], + [static_64_16(64, 16), static_64_16(64, 16)], + [static_16_64(16, 64), static_16_64(16, 64)], + [ + dynamic_64_64(numpy.array([33, 33], dtype=numpy.uint64)), + dynamic_64_64(numpy.array([33, 33], dtype=numpy.uint64)), + ], + [ + dynamic_64_b_64(64, True, 1.25), + dynamic_64_b_64(64, True, 1.25), + ], + [dynamic_64_s(64, 's'), dynamic_64_s(64, 's')], + [dynamic_s_64('s', 64), dynamic_s_64('s', 64)], + ) + + res = deserialize_cdr(serialize_cdr(msg, cname), cname) + assert res == deserialize(serialize(msg, cname), cname) + assert res == msg + + +def test_ros1_to_cdr(): + """Test ROS1 to CDR conversion.""" + register_types(dict(get_types_from_msg(STATIC_16_64, 'test_msgs/msg/static_16_64'))) + msg_ros = (b'\x01\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02') + msg_cdr = ( + b'\x00\x01\x00\x00' + b'\x01\x00' + b'\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + ) + assert ros1_to_cdr(msg_ros, 'test_msgs/msg/static_16_64') == msg_cdr + + register_types(dict(get_types_from_msg(DYNAMIC_S_64, 'test_msgs/msg/dynamic_s_64'))) + msg_ros = (b'\x01\x00\x00\x00X' b'\x00\x00\x00\x00\x00\x00\x00\x02') + msg_cdr = ( + b'\x00\x01\x00\x00' + b'\x02\x00\x00\x00X\x00' + b'\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + ) + assert ros1_to_cdr(msg_ros, 'test_msgs/msg/dynamic_s_64') == msg_cdr From ffacb7602c4929947badbeab333b2a540539b690 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 2 May 2021 14:49:33 +0200 Subject: [PATCH 004/114] Add rosbag2 support --- README.rst | 14 ++ docs/api/rosbags.rosbag2.rst | 6 + docs/api/rosbags.rst | 1 + docs/index.rst | 1 + docs/topics/rosbag2.rst | 65 ++++++++ src/rosbags/rosbag2/__init__.py | 18 +++ src/rosbags/rosbag2/reader.py | 231 ++++++++++++++++++++++++++ src/rosbags/rosbag2/writer.py | 247 ++++++++++++++++++++++++++++ tests/test_reader.py | 278 ++++++++++++++++++++++++++++++++ tests/test_roundtrip.py | 42 +++++ tests/test_writer.py | 94 +++++++++++ 11 files changed, 997 insertions(+) create mode 100644 docs/api/rosbags.rosbag2.rst create mode 100644 docs/topics/rosbag2.rst create mode 100644 src/rosbags/rosbag2/__init__.py create mode 100644 src/rosbags/rosbag2/reader.py create mode 100644 src/rosbags/rosbag2/writer.py create mode 100644 tests/test_reader.py create mode 100644 tests/test_roundtrip.py create mode 100644 tests/test_writer.py diff --git a/README.rst b/README.rst index d373cdf9..7d297dea 100644 --- a/README.rst +++ b/README.rst @@ -32,6 +32,20 @@ Rosbags is published on PyPI and does not have any special dependencies. Simply pip install rosbags +Read and deserialize rosbag2 messages: + +.. code-block:: python + + from rosbags.rosbag2 import Reader + from rosbags.serde import deserialize_cdr + + # create reader instance and open for reading + with Reader('/home/ros/rosbag_2020_03_24') as reader: + for topic, msgtype, timestamp, rawdata in reader.messages(['/imu_raw/Imu']): + msg = deserialize_cdr(rawdata, msgtype) + print(msg.header.frame_id) + + Documentation ============= diff --git a/docs/api/rosbags.rosbag2.rst b/docs/api/rosbags.rosbag2.rst new file mode 100644 index 00000000..391beafb --- /dev/null +++ b/docs/api/rosbags.rosbag2.rst @@ -0,0 +1,6 @@ +rosbags.rosbag2 +=============== + +.. automodule:: rosbags.rosbag2 + :members: + :show-inheritance: diff --git a/docs/api/rosbags.rst b/docs/api/rosbags.rst index c2d05d62..fff448cb 100644 --- a/docs/api/rosbags.rst +++ b/docs/api/rosbags.rst @@ -4,5 +4,6 @@ Rosbags namespace .. toctree:: :maxdepth: 4 + rosbags.rosbag2 rosbags.serde rosbags.typesys diff --git a/docs/index.rst b/docs/index.rst index a73cf508..7ac2b0a1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,6 +12,7 @@ topics/typesys topics/serde + topics/rosbag2 .. toctree:: diff --git a/docs/topics/rosbag2.rst b/docs/topics/rosbag2.rst new file mode 100644 index 00000000..17bf3129 --- /dev/null +++ b/docs/topics/rosbag2.rst @@ -0,0 +1,65 @@ +Rosbag2 +======= +The :py:mod:`rosbags.rosbag2` package provides a conformant implementation of rosbag2. It provides read-write access to raw message data saved inside rosbag2 containers, and supports all features present in the C++ reference implementation. + +Supported Versions +------------------ +All versions up to the current (ROS2 Foxy) version 4 are supported. + +Supported Features +------------------ +Rosbag2 is a flexible format that supports plugging different serialization methods, compression formats, and storage containers together. The rosbag2 C++ reference implementation is build around plugins that provide serialization, compression, and storage. This project implements all rosbag2 core plugins that are distributed with the C++ reference implementation. + +:Serializers: + - cdr (without wstring) + +:Compressors: + - zstd + +:Storages: + - sqlite3 + +Writing rosbag2 +--------------- +Instances of the :py:class:`Writer ` class can create and write to new rosbag2 files. It is usually used as a context manager. Before the first message of a topic can be written, its topic must first be added to the bag. The following example shows the typical usage pattern: + +.. code-block:: python + + from rosbags.rosbag2 import Writer + from rosbags.serde import serialize_cdr + + # create writer instance and open for writing + with Writer('/home/ros/rosbag_2020_03_24') as writer: + # add new topic + topic = '/imu_raw/Imu' + msgtype = 'sensor_msgs/msg/Imu' + writer.add_topic(topic, msgtype, 'cdr') + + # serialize and write message + writer.write(topic, timestamp, serialize_cdr(message, msgtype)) + +Reading rosbag2 +--------------- +Instances of the :py:class:`Reader ` class are used to read rosbag2 metadata and its contents. Most of the metadata is available on Reader instances right away, messages can only be accessed after the bag has been opened. To this end it is recommended to use the Reader as a context manager. The following example shows the typical usage pattern: + +.. code-block:: python + + from rosbags.rosbag2 import Reader + from rosbags.serde import deserialize_cdr + + # create reader instance and open for reading + with Reader('/home/ros/rosbag_2020_03_24') as reader: + # topic and msgtype information is available on .topics dict + for topic, msgtype in reader.topics.items(): + print(topic, msgtype) + + # iterate over messages + for topic, msgtype, timestamp, rawdata in reader.messages(): + if topic == '/imu_raw/Imu': + msg = deserialize_cdr(rawdata, msgtype) + print(msg.header.frame_id) + + # messages() accepts topic filters + for topic, msgtype, rawdata, timestamp in reader.messages(['/imu_raw/Imu']): + msg = deserialize_cdr(rawdata, msgtype) + print(msg.header.frame_id) diff --git a/src/rosbags/rosbag2/__init__.py b/src/rosbags/rosbag2/__init__.py new file mode 100644 index 00000000..ca3cccbf --- /dev/null +++ b/src/rosbags/rosbag2/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbags support for rosbag2 files. + +Readers and writers provide access to metadata and raw message content saved +in the rosbag2 format. + +""" + +from .reader import Reader, ReaderError +from .writer import Writer, WriterError + +__all__ = [ + 'Reader', + 'ReaderError', + 'Writer', + 'WriterError', +] diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py new file mode 100644 index 00000000..3fac5ff2 --- /dev/null +++ b/src/rosbags/rosbag2/reader.py @@ -0,0 +1,231 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag2 reader.""" + +from __future__ import annotations + +import sqlite3 +from contextlib import contextmanager +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import TYPE_CHECKING + +import zstandard +from ruamel.yaml import YAML, YAMLError + +if TYPE_CHECKING: + from types import TracebackType + from typing import Any, Generator, Iterable, List, Literal, Optional, Tuple, Type, Union + + +class ReaderError(Exception): + """Reader Error.""" + + +@contextmanager +def decompress(path: Path, do_decompress: bool): + """Transparent rosbag2 database decompression context. + + This context manager will yield a path to the decompressed file contents. + + Args: + path: Potentially compressed file. + do_decompress: Flag indicating if decompression shall occur. + + Yields: + Path of transparently decompressed file. + + """ + if do_decompress: + decomp = zstandard.ZstdDecompressor() + with TemporaryDirectory() as tempdir: + dbfile = Path(tempdir, path.stem) + with path.open('rb') as infile, dbfile.open('wb') as outfile: + decomp.copy_stream(infile, outfile) + yield dbfile + else: + yield path + + +class Reader: + """Reader for rosbag2 files. + + It implements all necessary features to access metadata and message + streams. + + Version history: + + - Version 1: Initial format. + - Version 2: Changed field sizes in C++ implementation. + - Version 3: Added compression. + - Version 4: Added QoS metadata to topics, changed relative file paths + """ + + def __init__(self, path: Union[Path, str]): + """Open rosbag and check metadata. + + Args: + path: Filesystem path to bag. + + Raises: + ReaderError: Bag not readable or bag metadata. + """ + path = Path(path) + self.path = Path + self.bio = False + try: + yaml = YAML(typ='safe') + yamlpath = path / 'metadata.yaml' + dct = yaml.load(yamlpath.read_text()) + except OSError as err: + raise ReaderError(f'Could not read metadata at {yamlpath}: {err}.') from None + except YAMLError as exc: + raise ReaderError(f'Could not load YAML from {yamlpath}: {exc}') from None + + try: + self.metadata = dct['rosbag2_bagfile_information'] + if (ver := self.metadata['version']) > 4: + raise ReaderError(f'Rosbag2 version {ver} not supported; please report issue.') + if storageid := self.metadata['storage_identifier'] != 'sqlite3': + raise ReaderError( + f'Storage plugin {storageid!r} not supported; please report issue.', + ) + + self.paths = [path / Path(x).name for x in self.metadata['relative_file_paths']] + missing = [x for x in self.paths if not x.exists()] + if missing: + raise ReaderError(f'Some database files are missing: {[str(x) for x in missing]!r}') + + topics = [x['topic_metadata'] for x in self.metadata['topics_with_message_count']] + noncdr = {y for x in topics if (y := x['serialization_format']) != 'cdr'} + if noncdr: + raise ReaderError(f'Serialization format {noncdr!r} is not supported.') + self.topics = {x['name']: x['type'] for x in topics} + + if self.compression_mode and (cfmt := self.compression_format) != 'zstd': + raise ReaderError(f'Compression format {cfmt!r} is not supported.') + except KeyError as exc: + raise ReaderError(f'A metadata key is missing {exc!r}.') from None + + def open(self): + """Open rosbag2.""" + # Future storage formats will require file handles. + self.bio = True + + def close(self): + """Close rosbag2.""" + # Future storage formats will require file handles. + assert self.bio + self.bio = False + + @property + def duration(self) -> int: + """Duration in nanoseconds between earliest and latest messages.""" + return self.metadata['duration']['nanoseconds'] + + @property + def start_time(self) -> int: + """Timestamp in nanoseconds of the earliest message.""" + return self.metadata['starting_time']['nanoseconds_since_epoch'] + + @property + def end_time(self) -> int: + """Timestamp in nanoseconds of the latest message.""" + return self.start_time + self.duration + + @property + def message_count(self) -> int: + """Total message count.""" + return self.metadata['message_count'] + + @property + def compression_format(self) -> Optional[str]: + """Compression format.""" + return self.metadata.get('compression_format', None) or None + + @property + def compression_mode(self) -> Optional[str]: + """Compression mode.""" + mode = self.metadata.get('compression_mode', '').lower() + return mode if mode != 'none' else None + + def messages( # pylint: disable=too-many-locals + self, + topics: Iterable[str] = (), + start: Optional[int] = None, + stop: Optional[int] = None, + ) -> Generator[Tuple[str, str, int, bytes], None, None]: + """Read messages from bag. + + Args: + topics: Iterable with topic names to filter for. An empty iterable + yields all messages. + start: Yield only messages at or after this timestamp (ns). + stop: Yield only messages before this timestamp (ns). + + Yields: + Tuples of topic name, type, timestamp (ns), and rawdata. + + Raises: + ReaderError: Bag not open. + + """ + if not self.bio: + raise ReaderError('Rosbag is not open.') + + topics = tuple(topics) + for filepath in self.paths: + with decompress(filepath, self.compression_mode == 'file') as path: + conn = sqlite3.connect(f'file:{path}?immutable=1', uri=True) + conn.row_factory = lambda _, x: x + cur = conn.cursor() + cur.execute( + 'SELECT count(*) FROM sqlite_master ' + 'WHERE type="table" AND name IN ("messages", "topics")', + ) + if cur.fetchone()[0] != 2: + raise ReaderError(f'Cannot open database {path} or database missing tables.') + + query = [ + 'SELECT topics.name,topics.type,messages.timestamp,messages.data', + 'FROM messages JOIN topics ON messages.topic_id=topics.id', + ] + args: List[Any] = [] + + if topics: + query.append(f'WHERE topics.name IN ({",".join("?" for _ in topics)})') + args += topics + + if start is not None: + query.append(f'{"AND" if args else "WHERE"} messages.timestamp >= ?') + args.append(start) + + if stop is not None: + query.append(f'{"AND" if args else "WHERE"} messages.timestamp < ?') + args.append(stop) + + query.append('ORDER BY timestamp') + cur.execute(' '.join(query), args) + + if self.compression_mode == 'message': + decomp = zstandard.ZstdDecompressor().decompress + for row in cur: + topic, msgtype, timestamp, data = row + yield topic, msgtype, timestamp, decomp(data) + else: + yield from cur + + def __enter__(self) -> Reader: + """Open rosbag2 when entering contextmanager.""" + self.open() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Literal[False]: + """Close rosbag2 when exiting contextmanager.""" + self.close() + return False diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py new file mode 100644 index 00000000..042b096e --- /dev/null +++ b/src/rosbags/rosbag2/writer.py @@ -0,0 +1,247 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag2 reader.""" + +from __future__ import annotations + +import sqlite3 +from enum import IntEnum, auto +from pathlib import Path +from typing import TYPE_CHECKING + +import zstandard +from ruamel.yaml import YAML + +if TYPE_CHECKING: + from types import TracebackType + from typing import Any, Dict, Literal, Optional, Type, Union + + +class WriterError(Exception): + """Writer Error.""" + + +class Writer: # pylint: disable=too-many-instance-attributes + """Rosbag2 writer. + + This class implements writing of rosbag2 files in version 4. It should be + used as a contextmanager. + + """ + + SQLITE_SCHEMA = """ + CREATE TABLE topics( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + type TEXT NOT NULL, + serialization_format TEXT NOT NULL, + offered_qos_profiles TEXT NOT NULL + ); + CREATE TABLE messages( + id INTEGER PRIMARY KEY, + topic_id INTEGER NOT NULL, + timestamp INTEGER NOT NULL, + data BLOB NOT NULL + ); + CREATE INDEX timestamp_idx ON messages (timestamp ASC); + """ + + class CompressionMode(IntEnum): + """Compession modes.""" + + NONE = auto() + FILE = auto() + MESSAGE = auto() + + class CompressionFormat(IntEnum): + """Compession formats.""" + + ZSTD = auto() + + def __init__(self, path: Union[Path, str]): + """Initialize writer. + + Args: + path: Filesystem path to bag. + + Raises: + WriterError: Target path exisits already, Writer can only create new rosbags. + + """ + path = Path(path) + self.path = path + if path.exists(): + raise WriterError(f'{path} exists already, not overwriting.') + self.metapath = path / 'metadata.yaml' + self.dbpath = path / f'{path.name}.db3' + self.compression_mode = '' + self.compression_format = '' + self.compressor: Optional[zstandard.ZstdCompressor] = None + self.topics: Dict[str, Any] = {} + self.conn = None + self.cursor: Optional[sqlite3.Cursor] = None + self.topics = {} + + def set_compression(self, mode: CompressionMode, fmt: CompressionFormat): + """Enable compression on bag. + + This function has to be called before opening. + + Args: + mode: Compression mode to use, either 'file' or 'message' + fmt: Compressor to use, currently only 'zstd' + + Raises: + WriterError: Bag already open. + + """ + if self.conn: + raise WriterError(f'Cannot set compression, bag {self.path} already open.') + if mode == self.CompressionMode.NONE: + return + self.compression_mode = mode.name.lower() + self.compression_format = fmt.name.lower() + self.compressor = zstandard.ZstdCompressor() + + def open(self): + """Open rosbag2 for writing. + + Create base directory and open database connection. + + """ + try: + self.path.mkdir(mode=0o755, parents=True) + except FileExistsError: + raise WriterError(f'{self.path} exists already, not overwriting.') from None + + self.conn = sqlite3.connect(f'file:{self.dbpath}', uri=True) + self.conn.executescript(self.SQLITE_SCHEMA) + self.cursor = self.conn.cursor() + + def add_topic( + self, + name: str, + typ: str, + serialization_format: str = 'cdr', + offered_qos_profiles: str = '', + ): + """Add a topic. + + This function can only be called after opening a bag. + + Args: + name: Topic name. + typ: Message type. + serialization_format: Serialization format. + offered_qos_profiles: QOS Profile. + + Raises: + WriterError: Bag not open or topic previously registered. + + """ + if not self.cursor: + raise WriterError('Bag was not opened.') + if name in self.topics: + raise WriterError(f'Topics can only be added once: {name!r}.') + meta = (len(self.topics) + 1, name, typ, serialization_format, offered_qos_profiles) + self.cursor.execute('INSERT INTO topics VALUES(?, ?, ?, ?, ?)', meta) + self.topics[name] = [*meta, 0] + + def write(self, topic: str, timestamp: int, data: bytes): + """Write message to rosbag2. + + Args: + topic: Topic message belongs to. + timestamp: Message timestamp (ns). + data: Serialized message data. + + Raises: + WriterError: Bag not open or topic not registered. + + """ + if not self.cursor: + raise WriterError('Bag was not opened.') + if topic not in self.topics: + raise WriterError(f'Tried to write to unknown topic {topic!r}.') + + if self.compression_mode == 'message': + assert self.compressor + data = self.compressor.compress(data) + + tmeta = self.topics[topic] + self.cursor.execute( + 'INSERT INTO messages (topic_id, timestamp, data) VALUES(?, ?, ?)', + (tmeta[0], timestamp, data), + ) + tmeta[-1] += 1 + + def close(self): + """Close rosbag2 after writing. + + Closes open database transactions and writes metadata.yaml. + + """ + self.cursor.close() + self.cursor = None + + duration, start, count = self.conn.execute( + 'SELECT max(timestamp) - min(timestamp) + 1, min(timestamp), count(*) FROM messages', + ).fetchone() + + self.conn.commit() + self.conn.execute('PRAGMA optimize') + self.conn.close() + + if self.compression_mode == 'file': + src = self.dbpath + self.dbpath = src.with_suffix(f'.db3.{self.compression_format}') + with src.open('rb') as infile, self.dbpath.open('wb') as outfile: + self.compressor.copy_stream(infile, outfile) + src.unlink() + + metadata = { + 'rosbag2_bagfile_information': { + 'version': 4, + 'storage_identifier': 'sqlite3', + 'relative_file_paths': [self.dbpath.name], + 'duration': { + 'nanoseconds': duration, + }, + 'starting_time': { + 'nanoseconds_since_epoch': start, + }, + 'message_count': count, + 'topics_with_message_count': [ + { + 'topic_metadata': { + 'name': x[1], + 'type': x[2], + 'serialization_format': x[3], + 'offered_qos_profiles': x[4], + }, + 'message_count': x[5], + } for x in self.topics.values() + ], + 'compression_format': self.compression_format, + 'compression_mode': self.compression_mode, + }, + } + with self.metapath.open('w') as metafile: + yaml = YAML(typ='safe') + yaml.default_flow_style = False + yaml.dump(metadata, metafile) + + def __enter__(self) -> Writer: + """Open rosbag2 when entering contextmanager.""" + self.open() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Literal[False]: + """Close rosbag2 when exiting contextmanager.""" + self.close() + return False diff --git a/tests/test_reader.py b/tests/test_reader.py new file mode 100644 index 00000000..d603e587 --- /dev/null +++ b/tests/test_reader.py @@ -0,0 +1,278 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Reader tests.""" + +# pylint: disable=redefined-outer-name + +from __future__ import annotations + +import sqlite3 +from pathlib import Path +from typing import TYPE_CHECKING +from unittest import mock + +import pytest +import zstandard + +from rosbags.rosbag2 import Reader, ReaderError, Writer + +from .test_serde import MSG_JOINT, MSG_MAGN, MSG_MAGN_BIG, MSG_POLY + +if TYPE_CHECKING: + from _pytest.fixtures import SubRequest + +METADATA = """ +rosbag2_bagfile_information: + version: 4 + storage_identifier: sqlite3 + relative_file_paths: + - db.db3{extension} + duration: + nanoseconds: 42 + starting_time: + nanoseconds_since_epoch: 666 + message_count: 4 + topics_with_message_count: + - topic_metadata: + name: /poly + type: geometry_msgs/msg/Polygon + serialization_format: cdr + offered_qos_profiles: "" + message_count: 1 + - topic_metadata: + name: /magn + type: sensor_msgs/msg/MagneticField + serialization_format: cdr + offered_qos_profiles: "" + message_count: 2 + - topic_metadata: + name: /joint + type: trajectory_msgs/msg/JointTrajectory + serialization_format: cdr + offered_qos_profiles: "" + message_count: 1 + compression_format: {compression_format} + compression_mode: {compression_mode} +""" + + +@pytest.fixture(params=['none', 'file', 'message']) +def bag(request: SubRequest, tmp_path: Path) -> Path: + """Manually contruct bag.""" + (tmp_path / 'metadata.yaml').write_text( + METADATA.format( + extension='' if request.param != 'file' else '.zstd', + compression_format='""' if request.param == 'none' else 'zstd', + compression_mode='""' if request.param == 'none' else request.param.upper(), + ), + ) + + comp = zstandard.ZstdCompressor() + + dbpath = tmp_path / 'db.db3' + dbh = sqlite3.connect(dbpath) + dbh.executescript(Writer.SQLITE_SCHEMA) + + cur = dbh.cursor() + cur.execute( + 'INSERT INTO topics VALUES(?, ?, ?, ?, ?)', + (1, '/poly', 'geometry_msgs/msg/Polygon', 'cdr', ''), + ) + cur.execute( + 'INSERT INTO topics VALUES(?, ?, ?, ?, ?)', + (2, '/magn', 'sensor_msgs/msg/MagneticField', 'cdr', ''), + ) + cur.execute( + 'INSERT INTO topics VALUES(?, ?, ?, ?, ?)', + (3, '/joint', 'trajectory_msgs/msg/JointTrajectory', 'cdr', ''), + ) + cur.execute( + 'INSERT INTO messages VALUES(?, ?, ?, ?)', + (1, 1, 666, MSG_POLY[0] if request.param != 'message' else comp.compress(MSG_POLY[0])), + ) + cur.execute( + 'INSERT INTO messages VALUES(?, ?, ?, ?)', + (2, 2, 708, MSG_MAGN[0] if request.param != 'message' else comp.compress(MSG_MAGN[0])), + ) + cur.execute( + 'INSERT INTO messages VALUES(?, ?, ?, ?)', + ( + 3, + 2, + 708, + MSG_MAGN_BIG[0] if request.param != 'message' else comp.compress(MSG_MAGN_BIG[0]), + ), + ) + cur.execute( + 'INSERT INTO messages VALUES(?, ?, ?, ?)', + (4, 3, 708, MSG_JOINT[0] if request.param != 'message' else comp.compress(MSG_JOINT[0])), + ) + dbh.commit() + + if request.param == 'file': + with dbpath.open('rb') as ifh, (tmp_path / 'db.db3.zstd').open('wb') as ofh: + comp.copy_stream(ifh, ofh) + dbpath.unlink() + + return tmp_path + + +def test_reader(bag: Path): + """Test reader and deserializer on simple bag.""" + with Reader(bag) as reader: + assert reader.duration == 42 + assert reader.start_time == 666 + assert reader.end_time == 708 + assert reader.message_count == 4 + if reader.compression_mode: + assert reader.compression_format == 'zstd' + + gen = reader.messages() + + topic, msgtype, timestamp, rawdata = next(gen) + assert topic == '/poly' + assert msgtype == 'geometry_msgs/msg/Polygon' + assert timestamp == 666 + assert rawdata == MSG_POLY[0] + + for idx in range(2): + topic, msgtype, timestamp, rawdata = next(gen) + assert topic == '/magn' + assert msgtype == 'sensor_msgs/msg/MagneticField' + assert timestamp == 708 + assert rawdata == [MSG_MAGN, MSG_MAGN_BIG][idx][0] + + topic, msgtype, timestamp, rawdata = next(gen) + assert topic == '/joint' + assert msgtype == 'trajectory_msgs/msg/JointTrajectory' + + with pytest.raises(StopIteration): + next(gen) + + +def test_message_filters(bag: Path): + """Test reader filters messages.""" + with Reader(bag) as reader: + + gen = reader.messages(['/magn']) + topic, _, _, _ = next(gen) + assert topic == '/magn' + topic, _, _, _ = next(gen) + assert topic == '/magn' + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(start=667) + topic, _, _, _ = next(gen) + assert topic == '/magn' + topic, _, _, _ = next(gen) + assert topic == '/magn' + topic, _, _, _ = next(gen) + assert topic == '/joint' + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(stop=667) + topic, _, _, _ = next(gen) + assert topic == '/poly' + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(['/magn'], stop=667) + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(start=666, stop=666) + with pytest.raises(StopIteration): + next(gen) + + +def test_user_errors(bag: Path): + """Test user errors.""" + reader = Reader(bag) + with pytest.raises(ReaderError, match='Rosbag is not open'): + next(reader.messages()) + + +def test_failure_cases(tmp_path: Path): + """Test bags with broken fs layout.""" + with pytest.raises(ReaderError, match='not read metadata'): + Reader(tmp_path) + + metadata = tmp_path / 'metadata.yaml' + + metadata.write_text('') + with pytest.raises(ReaderError, match='not read'), \ + mock.patch.object(Path, 'read_text', side_effect=PermissionError): + Reader(tmp_path) + + metadata.write_text(' invalid:\nthis is not yaml') + with pytest.raises(ReaderError, match='not load YAML from'): + Reader(tmp_path) + + metadata.write_text('foo:') + with pytest.raises(ReaderError, match='key is missing'): + Reader(tmp_path) + + metadata.write_text( + METADATA.format( + extension='', + compression_format='""', + compression_mode='""', + ).replace('version: 4', 'version: 999'), + ) + with pytest.raises(ReaderError, match='version 999'): + Reader(tmp_path) + + metadata.write_text( + METADATA.format( + extension='', + compression_format='""', + compression_mode='""', + ).replace('sqlite3', 'hdf5'), + ) + with pytest.raises(ReaderError, match='Storage plugin'): + Reader(tmp_path) + + metadata.write_text( + METADATA.format( + extension='', + compression_format='""', + compression_mode='""', + ), + ) + with pytest.raises(ReaderError, match='files are missing'): + Reader(tmp_path) + + (tmp_path / 'db.db3').write_text('') + + metadata.write_text( + METADATA.format( + extension='', + compression_format='""', + compression_mode='""', + ).replace('cdr', 'bson'), + ) + with pytest.raises(ReaderError, match='Serialization format'): + Reader(tmp_path) + + metadata.write_text( + METADATA.format( + extension='', + compression_format='"gz"', + compression_mode='"file"', + ), + ) + with pytest.raises(ReaderError, match='Compression format'): + Reader(tmp_path) + + metadata.write_text( + METADATA.format( + extension='', + compression_format='""', + compression_mode='""', + ), + ) + with pytest.raises(ReaderError, match='not open database'), \ + Reader(tmp_path) as reader: + next(reader.messages()) diff --git a/tests/test_roundtrip.py b/tests/test_roundtrip.py new file mode 100644 index 00000000..6e4694b1 --- /dev/null +++ b/tests/test_roundtrip.py @@ -0,0 +1,42 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Test full data roundtrip.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from rosbags.rosbag2 import Reader, Writer +from rosbags.serde import deserialize_cdr, serialize_cdr + +if TYPE_CHECKING: + from pathlib import Path + + +@pytest.mark.parametrize('mode', [*Writer.CompressionMode]) +def test_roundtrip(mode: Writer.CompressionMode, tmp_path: Path): + """Test full data roundtrip.""" + + class Foo: # pylint: disable=too-few-public-methods + """Dummy class.""" + + data = 1.25 + + path = tmp_path / 'rosbag2' + wbag = Writer(path) + wbag.set_compression(mode, wbag.CompressionFormat.ZSTD) + with wbag: + msgtype = 'std_msgs/msg/Float64' + wbag.add_topic('/test', msgtype) + wbag.write('/test', 42, serialize_cdr(Foo, msgtype)) + + rbag = Reader(path) + with rbag: + gen = rbag.messages() + _, msgtype, _, raw = next(gen) + msg = deserialize_cdr(raw, msgtype) + assert msg.data == Foo.data + with pytest.raises(StopIteration): + next(gen) diff --git a/tests/test_writer.py b/tests/test_writer.py new file mode 100644 index 00000000..d67c7e83 --- /dev/null +++ b/tests/test_writer.py @@ -0,0 +1,94 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Writer tests.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from rosbags.rosbag2 import Writer, WriterError + +if TYPE_CHECKING: + from pathlib import Path + + +def test_writer(tmp_path: Path): + """Test Writer.""" + path = (tmp_path / 'rosbag2') + with Writer(path) as bag: + bag.add_topic('/test', 'std_msgs/msg/Int8') + bag.write('/test', 42, b'\x00') + bag.write('/test', 666, b'\x01' * 4096) + assert (path / 'metadata.yaml').exists() + assert (path / 'rosbag2.db3').exists() + size = (path / 'rosbag2.db3').stat().st_size + + path = (tmp_path / 'compress_none') + bag = Writer(path) + bag.set_compression(bag.CompressionMode.NONE, bag.CompressionFormat.ZSTD) + with bag: + bag.add_topic('/test', 'std_msgs/msg/Int8') + bag.write('/test', 42, b'\x00') + bag.write('/test', 666, b'\x01' * 4096) + assert (path / 'metadata.yaml').exists() + assert (path / 'compress_none.db3').exists() + assert size == (path / 'compress_none.db3').stat().st_size + + path = (tmp_path / 'compress_file') + bag = Writer(path) + bag.set_compression(bag.CompressionMode.FILE, bag.CompressionFormat.ZSTD) + with bag: + bag.add_topic('/test', 'std_msgs/msg/Int8') + bag.write('/test', 42, b'\x00') + bag.write('/test', 666, b'\x01' * 4096) + assert (path / 'metadata.yaml').exists() + assert not (path / 'compress_file.db3').exists() + assert (path / 'compress_file.db3.zstd').exists() + + path = (tmp_path / 'compress_message') + bag = Writer(path) + bag.set_compression(bag.CompressionMode.MESSAGE, bag.CompressionFormat.ZSTD) + with bag: + bag.add_topic('/test', 'std_msgs/msg/Int8') + bag.write('/test', 42, b'\x00') + bag.write('/test', 666, b'\x01' * 4096) + assert (path / 'metadata.yaml').exists() + assert (path / 'compress_message.db3').exists() + assert size > (path / 'compress_message.db3').stat().st_size + + +def test_failure_cases(tmp_path: Path): + """Test writer failure cases.""" + with pytest.raises(WriterError, match='exists'): + Writer(tmp_path) + + bag = Writer(tmp_path / 'race') + (tmp_path / 'race').mkdir() + with pytest.raises(WriterError, match='exists'): + bag.open() + + bag = Writer(tmp_path / 'compress_after_open') + bag.open() + with pytest.raises(WriterError, match='already open'): + bag.set_compression(bag.CompressionMode.FILE, bag.CompressionFormat.ZSTD) + + bag = Writer(tmp_path / 'topic') + with pytest.raises(WriterError, match='was not opened'): + bag.add_topic('/tf', 'tf_msgs/msg/tf2') + + bag = Writer(tmp_path / 'write') + with pytest.raises(WriterError, match='was not opened'): + bag.write('/tf', 0, b'') + + bag = Writer(tmp_path / 'topic') + bag.open() + bag.add_topic('/tf', 'tf_msgs/msg/tf2') + with pytest.raises(WriterError, match='only be added once'): + bag.add_topic('/tf', 'tf_msgs/msg/tf2') + + bag = Writer(tmp_path / 'notopic') + bag.open() + with pytest.raises(WriterError, match='unknown topic'): + bag.write('/test', 42, b'\x00') From 4de0c99274e898b3bca1b9abb50df608f6432228 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 2 May 2021 14:50:17 +0200 Subject: [PATCH 005/114] Add rosbag1 support --- docs/api/rosbags.rosbag1.rst | 6 + docs/api/rosbags.rst | 1 + docs/index.rst | 1 + docs/topics/rosbag1.rst | 27 ++ src/rosbags/rosbag1/__init__.py | 18 + src/rosbags/rosbag1/reader.py | 645 ++++++++++++++++++++++++++++++++ tests/test_reader1.py | 394 +++++++++++++++++++ 7 files changed, 1092 insertions(+) create mode 100644 docs/api/rosbags.rosbag1.rst create mode 100644 docs/topics/rosbag1.rst create mode 100644 src/rosbags/rosbag1/__init__.py create mode 100644 src/rosbags/rosbag1/reader.py create mode 100644 tests/test_reader1.py diff --git a/docs/api/rosbags.rosbag1.rst b/docs/api/rosbags.rosbag1.rst new file mode 100644 index 00000000..887312fb --- /dev/null +++ b/docs/api/rosbags.rosbag1.rst @@ -0,0 +1,6 @@ +rosbags.rosbag1 +=============== + +.. automodule:: rosbags.rosbag1 + :members: + :show-inheritance: diff --git a/docs/api/rosbags.rst b/docs/api/rosbags.rst index fff448cb..2dca8d05 100644 --- a/docs/api/rosbags.rst +++ b/docs/api/rosbags.rst @@ -4,6 +4,7 @@ Rosbags namespace .. toctree:: :maxdepth: 4 + rosbags.rosbag1 rosbags.rosbag2 rosbags.serde rosbags.typesys diff --git a/docs/index.rst b/docs/index.rst index 7ac2b0a1..583a285e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ topics/typesys topics/serde topics/rosbag2 + topics/rosbag1 .. toctree:: diff --git a/docs/topics/rosbag1.rst b/docs/topics/rosbag1.rst new file mode 100644 index 00000000..e718188e --- /dev/null +++ b/docs/topics/rosbag1.rst @@ -0,0 +1,27 @@ +Rosbag1 +======= + +The :py:mod:`rosbags.rosbag1` package provides fast read-only access to raw messages stored in the legacy bag format. The rosbag1 support is built for a ROS2 world and some APIs and values perform normalizations to mimic ROS2 behavior and make messages originating from rosbag1 and rosbag2 behave identically. Most notably message types are internally renamed to match their ROS2 counterparts. + +Reading rosbag1 +--------------- +Instances of the :py:class:`Reader ` class are typically used as context managers and provide access to bag metadata and contents after the bag has been opened. The following example shows the typical usage pattern: + +.. code-block:: python + + from rosbags.rosbag1 import Reader + + # create reader instance + with Reader('/home/ros/rosbag_2020_03_24.bag') as reader: + # topic and msgtype information is available on .topics dictionary + for topic, info in reader.topics.items(): + print(topic, info) + + # iterate over messages + for topic, msgtype, rawdata, timestamp in reader.messages(): + if topic == '/imu_raw/Imu': + print(timestamp) + + # messages() accepts topic filters + for topic, msgtype, rawdata, timestamp in reader.messages(['/imu_raw/Imu']): + print(timestamp) diff --git a/src/rosbags/rosbag1/__init__.py b/src/rosbags/rosbag1/__init__.py new file mode 100644 index 00000000..c9a93618 --- /dev/null +++ b/src/rosbags/rosbag1/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbags support for rosbag1 files. + +Reader provides access to metadata and raw message content saved in the +rosbag1 format. + +Supported versions: + - Rosbag1 v2.0 + +""" + +from .reader import Reader, ReaderError + +__all__ = [ + 'Reader', + 'ReaderError', +] diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py new file mode 100644 index 00000000..56661ad9 --- /dev/null +++ b/src/rosbags/rosbag1/reader.py @@ -0,0 +1,645 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag1 v2.0 reader.""" + +from __future__ import annotations + +import bisect +import heapq +import os +import re +import struct +from bz2 import decompress as bz2_decompress +from enum import Enum, IntEnum +from functools import reduce +from io import BytesIO +from itertools import groupby +from pathlib import Path +from typing import TYPE_CHECKING, NamedTuple + +from lz4.frame import decompress as lz4_decompress # type: ignore + +from rosbags.typesys.msg import normalize_msgtype + +if TYPE_CHECKING: + from types import TracebackType + from typing import ( + BinaryIO, + Callable, + Dict, + Generator, + Iterable, + List, + Literal, + Optional, + Tuple, + Type, + Union, + ) + + +class ReaderError(Exception): + """Reader Error.""" + + +class Compression(Enum): + """Compression mode.""" + + NONE = 'none' + BZ2 = 'bz2' + LZ4 = 'lz4' + + +class RecordType(IntEnum): + """Record type.""" + + MSGDATA = 2 + BAGHEADER = 3 + IDXDATA = 4 + CHUNK = 5 + CHUNK_INFO = 6 + CONNECTION = 7 + + +class Connection(NamedTuple): + """Connection information.""" + + cid: int + topic: str + msgtype: str + md5sum: str + msgdef: str + indexes: List + + +class ChunkInfo(NamedTuple): + """Chunk information.""" + + pos: int + start_time: int + end_time: int + connection_counts: Dict[int, int] + + +class Chunk(NamedTuple): + """Chunk metadata.""" + + datasize: int + datapos: int + decompressor: Callable + + +class TopicInfo(NamedTuple): + """Topic information.""" + + conn_count: int + msgcount: int + msgdef: str + msgtype: str + + +class IndexData(NamedTuple): + """Index data.""" + + time: int + chunk_pos: int + offset: int + + def __lt__(self, other: Tuple[int, ...]) -> bool: + """Compare by time only.""" + return self.time < other[0] + + def __le__(self, other: Tuple[int, ...]) -> bool: + """Compare by time only.""" + return self.time <= other[0] + + def __eq__(self, other: object) -> bool: + """Compare by time only.""" + if not isinstance(other, IndexData): # pragma: no cover + return NotImplemented + return self.time == other[0] + + def __ge__(self, other: Tuple[int, ...]) -> bool: + """Compare by time only.""" + return self.time >= other[0] + + def __gt__(self, other: Tuple[int, ...]) -> bool: + """Compare by time only.""" + return self.time > other[0] + + def __ne__(self, other: object) -> bool: + """Compare by time only.""" + if not isinstance(other, IndexData): # pragma: no cover + return NotImplemented + return self.time != other[0] + + +deserialize_uint8 = struct.Struct(' int: + """Deserialize time value. + + Args: + val: Serialized bytes. + + Returns: + Deserialized value. + + """ + sec, nsec = struct.unpack(' int: + """Get uint8 value from field. + + Args: + name: Name of field. + + Returns: + Deserialized value. + + Raises: + ReaderError: Field not present or not deserializable. + + """ + try: + return deserialize_uint8(self[name])[0] + except (KeyError, struct.error) as err: + raise ReaderError(f'Could not read uint8 field {name!r}.') from err + + def get_uint32(self, name: str) -> int: + """Get uint32 value from field. + + Args: + name: Name of field. + + Returns: + Deserialized value. + + Raises: + ReaderError: Field not present or not deserializable. + + """ + try: + return deserialize_uint32(self[name])[0] + except (KeyError, struct.error) as err: + raise ReaderError(f'Could not read uint32 field {name!r}.') from err + + def get_uint64(self, name: str) -> int: + """Get uint64 value from field. + + Args: + name: Name of field. + + Returns: + Deserialized value. + + Raises: + ReaderError: Field not present or not deserializable. + + """ + try: + return deserialize_uint64(self[name])[0] + except (KeyError, struct.error) as err: + raise ReaderError(f'Could not read uint64 field {name!r}.') from err + + def get_string(self, name: str) -> str: + """Get string value from field. + + Args: + name: Name of field. + + Returns: + Deserialized value. + + Raises: + ReaderError: Field not present or not deserializable. + + """ + try: + return self[name].decode() + except (KeyError, ValueError) as err: + raise ReaderError(f'Could not read string field {name!r}.') from err + + def get_time(self, name: str) -> int: + """Get time value from field. + + Args: + name: Name of field. + + Returns: + Deserialized value. + + Raises: + ReaderError: Field not present or not deserializable. + + """ + try: + return deserialize_time(self[name]) + except (KeyError, struct.error) as err: + raise ReaderError(f'Could not read time field {name!r}.') from err + + @classmethod + def read(cls: type, src: BinaryIO, expect: Optional[RecordType] = None) -> 'Header': + """Read header from file handle. + + Args: + src: File handle. + expect: Expected record op. + + Returns: + Header object. + + Raises: + ReaderError: Header could not parsed. + + """ + try: + binary = read_bytes(src, read_uint32(src)) + except ReaderError as err: + raise ReaderError('Header could not be read from file.') from err + + header = cls() + pos = 0 + length = len(binary) + while pos < length: + try: + size = deserialize_uint32(binary[pos:pos + 4])[0] + except struct.error as err: + raise ReaderError('Header field size could not be read.') from err + pos += 4 + + if pos + size > length: + raise ReaderError('Declared field size is too large for header.') + + name, sep, value = binary[pos:pos + size].partition(b'=') + if not sep: + raise ReaderError('Header field could not be parsed.') + pos += size + + header[name.decode()] = value + + if expect: + have = header.get_uint8('op') + if expect != have: + raise ReaderError(f'Record of type {RecordType(have).name!r} is unexpected.') + + return header + + +def read_uint32(src: BinaryIO) -> int: + """Read uint32 from source. + + Args: + src: File handle. + + Returns: + Uint32 value. + + Raises: + ReaderError: Value unreadable or not deserializable. + + """ + try: + return deserialize_uint32(src.read(4))[0] + except struct.error as err: + raise ReaderError('Could not read uint32.') from err + + +def read_bytes(src: BinaryIO, size: int) -> bytes: + """Read bytes from source. + + Args: + src: File handle. + size: Number of bytes to read. + + Returns: + Read bytes. + + Raises: + ReaderError: Not enough bytes available. + + """ + data = src.read(size) + if len(data) != size: + raise ReaderError(f'Got only {len(data)} of requested {size} bytes.') + return data + + +def normalize(name: str) -> str: + """Normalize topic name. + + Args: + name: Topic name. + + Returns: + Normalized name. + + """ + return f'{"/" * (name[0] == "/")}{"/".join(x for x in name.split("/") if x)}' + + +class Reader: + """Rosbag 1 version 2.0 reader. + + This class is designed for a ROS2 world, it will automatically normalize + message type names to be in line with their ROS2 counterparts. + + """ + + def __init__(self, path: Union[str, Path]): + """Initialize. + + Args: + path: Filesystem path to bag. + + Raises: + ReaderError: Path does not exist. + + """ + self.path = Path(path) + if not self.path.exists(): + raise ReaderError(f'File {str(self.path)!r} does not exist.') + + self.bio: Optional[BinaryIO] = None + self.connections: Dict[int, Connection] = {} + self.chunk_infos: List[ChunkInfo] = [] + self.chunks: Dict[int, Chunk] = {} + self.current_chunk = (-1, BytesIO()) + self.topics: Dict[str, TopicInfo] = {} + + def open(self): # pylint: disable=too-many-branches,too-many-locals + """Open rosbag and read metadata.""" + try: + self.bio = self.path.open('rb') + except OSError as err: + raise ReaderError(f'Could not open file {str(self.path)!r}: {err.strerror}.') from err + + try: + magic = self.bio.readline().decode() + if not magic: + raise ReaderError(f'File {str(self.path)!r} seems to be empty.') + + matches = re.match(r'#ROSBAG V(\d+).(\d+)\n', magic) + if not matches: + raise ReaderError('File magic is invalid.') + major, minor = matches.groups() + version = int(major) * 100 + int(minor) + if version != 200: + raise ReaderError(f'Bag version {version!r} is not supported.') + + header = Header.read(self.bio, RecordType.BAGHEADER) + index_pos = header.get_uint64('index_pos') + conn_count = header.get_uint32('conn_count') + chunk_count = header.get_uint32('chunk_count') + try: + encryptor = header.get_string('encryptor') + if encryptor: + raise ValueError + except ValueError: + raise ReaderError(f'Bag encryption {encryptor!r} is not supported.') from None + except ReaderError: + pass + + if index_pos == 0: + raise ReaderError('Bag is not indexed, reindex before reading.') + + self.bio.seek(index_pos) + self.connections = dict(self.read_connection() for _ in range(conn_count)) + self.chunk_infos = [self.read_chunk_info() for _ in range(chunk_count)] + self.chunks = {} + for chunk_info in self.chunk_infos: + self.bio.seek(chunk_info.pos) + self.chunks[chunk_info.pos] = self.read_chunk() + + for _ in range(len(chunk_info.connection_counts)): + cid, index = self.read_index_data(chunk_info.pos) + self.connections[cid].indexes.append(index) + + for connection in self.connections.values(): + connection.indexes[:] = list(heapq.merge(*connection.indexes, key=lambda x: x.time)) + assert connection.indexes + + self.topics = {} + for topic, connections in groupby( + sorted(self.connections.values(), key=lambda x: x.topic), + key=lambda x: x.topic, + ): + connections = list(connections) + count = reduce( + lambda x, y: x + y, + ( + y.connection_counts.get(x.cid, 0) + for x in connections + for y in self.chunk_infos + ), + ) + + self.topics[topic] = TopicInfo( + len(connections), + count, + connections[0].msgdef, + connections[0].msgtype, + ) + except ReaderError: + self.close() + raise + + def close(self): + """Close rosbag.""" + assert self.bio + self.bio.close() + self.bio = None + + @property + def duration(self) -> int: + """Duration in nanoseconds between earliest and latest messages.""" + return self.end_time - self.start_time + + @property + def start_time(self) -> int: + """Timestamp in nanoseconds of the earliest message.""" + return min(x.start_time for x in self.chunk_infos) + + @property + def end_time(self) -> int: + """Timestamp in nanoseconds of the latest message.""" + return max(x.end_time for x in self.chunk_infos) + + @property + def message_count(self) -> int: + """Total message count.""" + return reduce(lambda x, y: x + y, (x.msgcount for x in self.topics.values()), 0) + + def read_connection(self) -> Tuple[int, Connection]: + """Read connection record from current position.""" + assert self.bio + header = Header.read(self.bio, RecordType.CONNECTION) + conn = header.get_uint32('conn') + topic = normalize(header.get_string('topic')) + + header = Header.read(self.bio) + typ = header.get_string('type') + md5sum = header.get_string('md5sum') + msgdef = header.get_string('message_definition') + + return conn, Connection(conn, topic, normalize_msgtype(typ), md5sum, msgdef, []) + + def read_chunk_info(self) -> ChunkInfo: + """Read chunk info record from current position.""" + assert self.bio + header = Header.read(self.bio, RecordType.CHUNK_INFO) + + ver = header.get_uint32('ver') + if ver != 1: + raise ReaderError(f'CHUNK_INFO version {ver} is not supported.') + + chunk_pos = header.get_uint64('chunk_pos') + start_time = header.get_time('start_time') + end_time = header.get_time('end_time') + count = header.get_uint32('count') + + self.bio.seek(4, os.SEEK_CUR) + + return ChunkInfo( + chunk_pos, + start_time, + end_time, + {read_uint32(self.bio): read_uint32(self.bio) for _ in range(count)}, + ) + + def read_chunk(self) -> Chunk: + """Read chunk record header from current position.""" + assert self.bio + header = Header.read(self.bio, RecordType.CHUNK) + compression = header.get_string('compression') + datasize = read_uint32(self.bio) + datapos = self.bio.tell() + self.bio.seek(datasize, os.SEEK_CUR) + try: + decompressor = { + Compression.NONE.value: lambda x: x, + Compression.BZ2.value: bz2_decompress, + Compression.LZ4.value: lz4_decompress, + }[compression] + except KeyError: + raise ReaderError(f'Compression {compression!r} is not supported.') from None + + return Chunk( + datasize, + datapos, + decompressor, + ) + + def read_index_data(self, pos: int) -> Tuple[int, List[IndexData]]: + """Read index data from position. + + Args: + pos: Seek position. + + Returns: + Connection id and list of index data. + + Raises: + ReaderError: Record unreadable. + + """ + assert self.bio + header = Header.read(self.bio, RecordType.IDXDATA) + + ver = header.get_uint32('ver') + if ver != 1: + raise ReaderError(f'IDXDATA version {ver} is not supported.') + conn = header.get_uint32('conn') + count = header.get_uint32('count') + + self.bio.seek(4, os.SEEK_CUR) + + index: List[IndexData] = [] + for _ in range(count): + time = deserialize_time(self.bio.read(8)) + offset = read_uint32(self.bio) + bisect.insort(index, IndexData(time, pos, offset)) + return conn, index + + def messages( + self, + topics: Optional[Iterable[str]] = None, + start: Optional[int] = None, + stop: Optional[int] = None, + ) -> Generator[Tuple[str, str, int, bytes], None, None]: + """Read messages from bag. + + Args: + topics: Iterable with topic names to filter for. An empty iterable + yields all messages. + start: Yield only messages at or after this timestamp (ns). + stop: Yield only messages before this timestamp (ns). + + Yields: + Tuples of topic name, type, timestamp (ns), and rawdata. + + Raises: + ReaderError: Bag not open or data corrupt. + + """ + if not self.bio: + raise ReaderError('Rosbag is not open.') + + indexes = [x.indexes for x in self.connections.values() if not topics or x.topic in topics] + for entry in heapq.merge(*indexes): + if start and entry.time < start: + continue + if stop and entry.time >= stop: + return + + if self.current_chunk[0] != entry.chunk_pos: + self.current_chunk[1].close() + + chunk_header = self.chunks[entry.chunk_pos] + self.bio.seek(chunk_header.datapos) + chunk = chunk_header.decompressor(read_bytes(self.bio, chunk_header.datasize)) + self.current_chunk = (entry.chunk_pos, BytesIO(chunk)) + + chunk = self.current_chunk[1] + chunk.seek(entry.offset) + + while True: + header = Header.read(chunk) + have = header.get_uint8('op') + if have != RecordType.CONNECTION: + break + chunk.seek(read_uint32(chunk), os.SEEK_CUR) + + if have != RecordType.MSGDATA: + raise ReaderError('Expected to find message data.') + + connection = self.connections[header.get_uint32('conn')] + time = header.get_time('time') + + data = read_bytes(chunk, read_uint32(chunk)) + + assert entry.time == time + yield connection.topic, connection.msgtype, time, data + + def __enter__(self) -> Reader: + """Open rosbag1 when entering contextmanager.""" + self.open() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Literal[False]: + """Close rosbag1 when exiting contextmanager.""" + self.close() + return False diff --git a/tests/test_reader1.py b/tests/test_reader1.py new file mode 100644 index 00000000..9004f6f3 --- /dev/null +++ b/tests/test_reader1.py @@ -0,0 +1,394 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Reader tests.""" + +from collections import defaultdict +from struct import pack +from unittest.mock import patch + +import pytest + +from rosbags.rosbag1 import Reader, ReaderError +from rosbags.rosbag1.reader import IndexData + + +def ser(data): + """Serialize record header.""" + if isinstance(data, dict): + fields = [] + for key, value in data.items(): + field = b'='.join([key.encode(), value]) + fields.append(pack('= x42_2_0 + assert not x42_1_0 > x42_2_0 + + assert x42_1_0 < x43_3_0 + assert x42_1_0 <= x43_3_0 + assert not x42_1_0 == x43_3_0 + assert x42_1_0 != x43_3_0 + assert not x42_1_0 >= x43_3_0 + assert not x42_1_0 > x43_3_0 + + +def test_reader(tmp_path): # pylint: disable=too-many-statements + """Test reader and deserializer on simple bag.""" + # empty bag + bag = tmp_path / 'test.bag' + write_bag(bag, create_default_header()) + with Reader(bag) as reader: + assert reader.message_count == 0 + + # empty bag, explicit encryptor + bag = tmp_path / 'test.bag' + write_bag(bag, {**create_default_header(), 'encryptor': b''}) + with Reader(bag) as reader: + assert reader.message_count == 0 + + # single message + write_bag( + bag, create_default_header(), chunks=[[ + create_connection(), + create_message(time=42), + ]] + ) + with Reader(bag) as reader: + assert reader.message_count == 1 + assert reader.duration == 0 + assert reader.start_time == 42 * 10**9 + assert reader.end_time == 42 * 10**9 + assert len(reader.topics.keys()) == 1 + assert reader.topics['/topic0'].msgcount == 1 + msgs = list(reader.messages()) + assert len(msgs) == 1 + + # sorts by time on same topic + write_bag( + bag, + create_default_header(), + chunks=[ + [ + create_connection(), + create_message(time=10, msg=10), + create_message(time=5, msg=5), + ] + ] + ) + with Reader(bag) as reader: + assert reader.message_count == 2 + assert reader.duration == 5 * 10**9 + assert reader.start_time == 5 * 10**9 + assert reader.end_time == 10 * 10**9 + assert len(reader.topics.keys()) == 1 + assert reader.topics['/topic0'].msgcount == 2 + msgs = list(reader.messages()) + assert len(msgs) == 2 + assert msgs[0][3] == b'MSGCONTENT5' + assert msgs[1][3] == b'MSGCONTENT10' + + # sorts by time on different topic + write_bag( + bag, + create_default_header(), + chunks=[ + [ + create_connection(), + create_message(time=10, msg=10), + create_connection(cid=2, topic=2), + create_message(cid=2, time=5, msg=5), + ] + ] + ) + with Reader(bag) as reader: + assert len(reader.topics.keys()) == 2 + assert reader.topics['/topic0'].msgcount == 1 + assert reader.topics['/topic2'].msgcount == 1 + msgs = list(reader.messages()) + assert len(msgs) == 2 + assert msgs[0][3] == b'MSGCONTENT5' + assert msgs[1][3] == b'MSGCONTENT10' + + msgs = list(reader.messages(['/topic0'])) + assert len(msgs) == 1 + assert msgs[0][3] == b'MSGCONTENT10' + + msgs = list(reader.messages(start=7 * 10**9)) + assert len(msgs) == 1 + assert msgs[0][3] == b'MSGCONTENT10' + + msgs = list(reader.messages(stop=7 * 10**9)) + assert len(msgs) == 1 + assert msgs[0][3] == b'MSGCONTENT5' + + +def test_user_errors(tmp_path): + """Test user errors.""" + bag = tmp_path / 'test.bag' + write_bag(bag, create_default_header(), chunks=[[ + create_connection(), + create_message(), + ]]) + + reader = Reader(bag) + with pytest.raises(ReaderError, match='is not open'): + next(reader.messages()) + + +def test_failure_cases(tmp_path): # pylint: disable=too-many-statements + """Test failure cases.""" + bag = tmp_path / 'test.bag' + with pytest.raises(ReaderError, match='does not exist'): + Reader(bag).open() + + bag.write_text('') + with patch('pathlib.Path.open', side_effect=IOError), \ + pytest.raises(ReaderError, match='not open'): + Reader(bag).open() + + with pytest.raises(ReaderError, match='empty'): + Reader(bag).open() + + bag.write_text('#BADMAGIC') + with pytest.raises(ReaderError, match='magic is invalid'): + Reader(bag).open() + + bag.write_text('#ROSBAG V3.0\n') + with pytest.raises(ReaderError, match='Bag version 300 is not supported.'): + Reader(bag).open() + + bag.write_bytes(b'#ROSBAG V2.0\x0a\x00') + with pytest.raises(ReaderError, match='Header could not be read from file.'): + Reader(bag).open() + + bag.write_bytes(b'#ROSBAG V2.0\x0a\x01\x00\x00\x00') + with pytest.raises(ReaderError, match='Header could not be read from file.'): + Reader(bag).open() + + bag.write_bytes(b'#ROSBAG V2.0\x0a\x01\x00\x00\x00\x01') + with pytest.raises(ReaderError, match='Header field size could not be read.'): + Reader(bag).open() + + bag.write_bytes(b'#ROSBAG V2.0\x0a\x04\x00\x00\x00\x01\x00\x00\x00') + with pytest.raises(ReaderError, match='Declared field size is too large for header.'): + Reader(bag).open() + + bag.write_bytes(b'#ROSBAG V2.0\x0a\x05\x00\x00\x00\x01\x00\x00\x00x') + with pytest.raises(ReaderError, match='Header field could not be parsed.'): + Reader(bag).open() + + write_bag(bag, {'encryptor': b'enc', **create_default_header()}) + with pytest.raises(ReaderError, match='is not supported'): + Reader(bag).open() + + write_bag(bag, {**create_default_header(), 'index_pos': pack(' Date: Sun, 2 May 2021 14:51:08 +0200 Subject: [PATCH 006/114] Add rosbag conversion tools --- README.rst | 8 +++ docs/api/rosbags.convert.rst | 6 ++ docs/api/rosbags.rst | 1 + docs/index.rst | 1 + docs/topics/convert.rst | 29 ++++++++ setup.cfg | 4 ++ src/rosbags/convert/__init__.py | 16 +++++ src/rosbags/convert/__main__.py | 62 ++++++++++++++++++ src/rosbags/convert/converter.py | 55 ++++++++++++++++ tests/test_convert.py | 109 +++++++++++++++++++++++++++++++ 10 files changed, 291 insertions(+) create mode 100644 docs/api/rosbags.convert.rst create mode 100644 docs/topics/convert.rst create mode 100644 src/rosbags/convert/__init__.py create mode 100644 src/rosbags/convert/__main__.py create mode 100644 src/rosbags/convert/converter.py create mode 100644 tests/test_convert.py diff --git a/README.rst b/README.rst index 7d297dea..7a8e2a1b 100644 --- a/README.rst +++ b/README.rst @@ -46,6 +46,14 @@ Read and deserialize rosbag2 messages: print(msg.header.frame_id) +Convert rosbag1 to rosbag2:: + + # Convert "foo.bag", result will be "foo/" + rosbags-convert foo.bag + + # Convert "foo.bag", save the result as "bar" + rosbags-convert foo.bag --dst /path/to/bar + Documentation ============= diff --git a/docs/api/rosbags.convert.rst b/docs/api/rosbags.convert.rst new file mode 100644 index 00000000..ae336cf1 --- /dev/null +++ b/docs/api/rosbags.convert.rst @@ -0,0 +1,6 @@ +rosbags.convert +=============== + +.. automodule:: rosbags.convert + :members: + :show-inheritance: diff --git a/docs/api/rosbags.rst b/docs/api/rosbags.rst index 2dca8d05..19c8a4f3 100644 --- a/docs/api/rosbags.rst +++ b/docs/api/rosbags.rst @@ -4,6 +4,7 @@ Rosbags namespace .. toctree:: :maxdepth: 4 + rosbags.convert rosbags.rosbag1 rosbags.rosbag2 rosbags.serde diff --git a/docs/index.rst b/docs/index.rst index 583a285e..904b22ff 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,6 +14,7 @@ topics/serde topics/rosbag2 topics/rosbag1 + topics/convert .. toctree:: diff --git a/docs/topics/convert.rst b/docs/topics/convert.rst new file mode 100644 index 00000000..c4d2014d --- /dev/null +++ b/docs/topics/convert.rst @@ -0,0 +1,29 @@ +Convert Rosbag1 to Rosbag2 +========================== + +The :py:mod:`rosbags.convert` package includes a CLI tool to convert legacy rosbag1 files to rosbag2. + +Features +-------- + +- Reasonably fast, as it converts raw ROS1 messages to raw CDR messages without going though deserialization and serialization +- Tries to match ROS1 message type names to registered ROS2 types +- Automatically registers unknown message types present in the legacy rosbag file for the conversion +- Handles differences of ``std_msgs/msg/Header`` between both ROS versions + +Limitations +----------- + +- Refuses to convert unindexed rosbag files, please reindex files before conversion +- Currently does not handle split bags + +Usage +----- + +.. code-block:: console + + # Convert "foo.bag", result will be "foo/" + $ rosbags-convert foo.bag + + # Convert "foo.bag", save the result as "bar" + $ rosbags-convert foo.bag --dst /path/to/bar diff --git a/setup.cfg b/setup.cfg index 7b70f9fd..21c5151c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -54,6 +54,10 @@ install_requires = ruamel.yaml zstandard +[options.entry_points] +console_scripts = + rosbags-convert = rosbags.convert.__main__:main + [options.extras_require] dev = darglint diff --git a/src/rosbags/convert/__init__.py b/src/rosbags/convert/__init__.py new file mode 100644 index 00000000..e4eddce0 --- /dev/null +++ b/src/rosbags/convert/__init__.py @@ -0,0 +1,16 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbags file format conversion. + +Conversion function transforms files from rosbag1 format to the latest rosbag2 +format. It automatically matches ROS1 message types to their ROS2 counterparts +and adds custom types not present in the type system. + +""" + +from .converter import ConverterError, convert + +__all__ = [ + 'ConverterError', + 'convert', +] diff --git a/src/rosbags/convert/__main__.py b/src/rosbags/convert/__main__.py new file mode 100644 index 00000000..2ab89904 --- /dev/null +++ b/src/rosbags/convert/__main__.py @@ -0,0 +1,62 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""CLI tool for rosbag conversion.""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path +from typing import TYPE_CHECKING + +from .converter import ConverterError, convert + +if TYPE_CHECKING: + from typing import Callable + + +def pathtype(exists: bool = True) -> Callable: + """Path argument for argparse. + + Args: + exists: Path should exists in filesystem. + + Returns: + Argparse type function. + + """ + + def topath(pathname: str) -> Path: + path = Path(pathname) + if exists != path.exists(): + raise argparse.ArgumentTypeError( + f'{path} should {"exist" if exists else "not exist"}.', + ) + return path + + return topath + + +def main() -> None: + """Parse cli arguments and run conversion.""" + parser = argparse.ArgumentParser(description='Convert rosbag1 to rosbag2.') + parser.add_argument( + 'src', + type=pathtype(), + help='source path to read rosbag1 from', + ) + parser.add_argument( + '--dst', + type=pathtype(exists=False), + help='destination path for rosbag2', + ) + args = parser.parse_args() + try: + convert(args.src, args.dst) + except ConverterError as err: + print(f'ERROR: {err}') # noqa: T001 + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py new file mode 100644 index 00000000..f1e4e9f3 --- /dev/null +++ b/src/rosbags/convert/converter.py @@ -0,0 +1,55 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag1 to Rosbag2 Converter.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rosbags.rosbag1 import Reader, ReaderError +from rosbags.rosbag2 import Writer, WriterError +from rosbags.serde import ros1_to_cdr +from rosbags.typesys import get_types_from_msg, register_types + +if TYPE_CHECKING: + from pathlib import Path + from typing import Any, Dict, Optional + + +class ConverterError(Exception): + """Converter Error.""" + + +def convert(src: Path, dst: Optional[Path]) -> None: + """Convert Rosbag1 to Rosbag2. + + Args: + src: Rosbag1 path. + dst: Rosbag2 path. + + Raises: + ConverterError: An error occured during reading, writing, or + converting. + + """ + dst = dst if dst else src.with_suffix('') + if dst.exists(): + raise ConverterError(f'Output path {str(dst)!r} exists already.') + + try: + with Reader(src) as reader, Writer(dst) as writer: + typs: Dict[str, Any] = {} + for name, topic in reader.topics.items(): + writer.add_topic(name, topic.msgtype) + typs.update(get_types_from_msg(topic.msgdef, topic.msgtype)) + register_types(typs) + + for topic, msgtype, timestamp, data in reader.messages(): + data = ros1_to_cdr(data, msgtype) + writer.write(topic, timestamp, data) + except ReaderError as err: + raise ConverterError(f'Reading source bag: {err}') from err + except WriterError as err: + raise ConverterError(f'Writing destination bag: {err}') from err + except Exception as err: # pylint: disable=broad-except + raise ConverterError(f'Converting rosbag: {err!r}') from err diff --git a/tests/test_convert.py b/tests/test_convert.py new file mode 100644 index 00000000..1d015f35 --- /dev/null +++ b/tests/test_convert.py @@ -0,0 +1,109 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag1to2 converter tests.""" + +import sys +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest + +from rosbags.convert import ConverterError, convert +from rosbags.convert.__main__ import main +from rosbags.rosbag1 import ReaderError +from rosbags.rosbag2 import WriterError + + +def test_cliwrapper(tmp_path: Path): + """Test cli wrapper.""" + (tmp_path / 'subdir').mkdir() + (tmp_path / 'ros1.bag').write_text('') + + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt']), \ + pytest.raises(SystemExit): + main() + assert not cvrt.called + + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt', str(tmp_path / 'no.bag')]), \ + pytest.raises(SystemExit): + main() + assert not cvrt.called + + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt', str(tmp_path / 'ros1.bag')]): + main() + cvrt.assert_called_with(tmp_path / 'ros1.bag', None) + + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt', + str(tmp_path / 'ros1.bag'), + '--dst', + str(tmp_path / 'subdir')]), \ + pytest.raises(SystemExit): + main() + assert not cvrt.called + + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt', + str(tmp_path / 'ros1.bag'), + '--dst', + str(tmp_path / 'target')]): + main() + cvrt.assert_called_with(tmp_path / 'ros1.bag', tmp_path / 'target') + + with patch.object(sys, 'argv', ['cvt', str(tmp_path / 'ros1.bag')]), \ + patch('builtins.print') as mock_print, \ + patch('rosbags.convert.__main__.convert', side_effect=ConverterError('exc')), \ + pytest.raises(SystemExit): + main() + mock_print.assert_called_with('ERROR: exc') + + +def test_convert(tmp_path: Path): + """Test conversion function.""" + (tmp_path / 'subdir').mkdir() + (tmp_path / 'foo.bag').write_text('') + + with pytest.raises(ConverterError, match='exists already'): + convert(Path('foo.bag'), tmp_path / 'subdir') + + with patch('rosbags.convert.converter.Reader') as reader, \ + patch('rosbags.convert.converter.Writer') as writer, \ + patch('rosbags.convert.converter.get_types_from_msg', return_value={'typ': 'def'}), \ + patch('rosbags.convert.converter.register_types') as register_types, \ + patch('rosbags.convert.converter.ros1_to_cdr') as ros1_to_cdr: + + reader.return_value.__enter__.return_value.topics = { + '/topic': Mock(msgtype='typ', msgdef='def'), + } + reader.return_value.__enter__.return_value.messages.return_value = [ + ('/topic', 'typ', 42, b'\x42'), + ] + + ros1_to_cdr.return_value = b'666' + + convert(Path('foo.bag'), None) + + reader.assert_called_with(Path('foo.bag')) + reader.return_value.__enter__.return_value.messages.assert_called_with() + + writer.assert_called_with(Path('foo')) + writer.return_value.__enter__.return_value.add_topic.assert_called_with('/topic', 'typ') + writer.return_value.__enter__.return_value.write.assert_called_with('/topic', 42, b'666') + + register_types.assert_called_with({'typ': 'def'}) + ros1_to_cdr.assert_called_with(b'\x42', 'typ') + + ros1_to_cdr.side_effect = KeyError('exc') + with pytest.raises(ConverterError, match='Converting rosbag: '): + convert(Path('foo.bag'), None) + + writer.side_effect = WriterError('exc') + with pytest.raises(ConverterError, match='Writing destination bag: '): + convert(Path('foo.bag'), None) + + reader.side_effect = ReaderError('exc') + with pytest.raises(ConverterError, match='Reading source bag: '): + convert(Path('foo.bag'), None) From bbedf76b98f421addaa5c30f86681ce14d89f6a6 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 2 May 2021 14:52:59 +0200 Subject: [PATCH 007/114] Add bench tool --- tools/bench/Dockerfile | 13 ++++ tools/bench/README.rst | 11 +++ tools/bench/bench.py | 147 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 tools/bench/Dockerfile create mode 100644 tools/bench/README.rst create mode 100644 tools/bench/bench.py diff --git a/tools/bench/Dockerfile b/tools/bench/Dockerfile new file mode 100644 index 00000000..aa1460b9 --- /dev/null +++ b/tools/bench/Dockerfile @@ -0,0 +1,13 @@ +FROM ros:rolling + +RUN apt-get update \ + && apt-get upgrade -y \ + && apt-get install -y \ + python3-pip + +RUN python3 -m pip install ruamel.yaml zstandard + +COPY src/rosbags /opt/ros/rolling/lib/python3.8/site-packages/rosbags +COPY tools/bench/bench.py / + +CMD ["/usr/bin/python3", "/bench.py", "/rosbag2"] diff --git a/tools/bench/README.rst b/tools/bench/README.rst new file mode 100644 index 00000000..a8b11992 --- /dev/null +++ b/tools/bench/README.rst @@ -0,0 +1,11 @@ +===== +Bench +===== + +Check and benchmark ``rosbags.rosbag2`` agains ``rosbag2_py``. The provided Dockerfile creates an execution environment for the script. Run from the root of this repository:: + + $ docker build -t rosbags/bench -f tools/bench/Dockerfile . + +The docker image expects that the rosbag2 file to benchmark is mounted under ``/rosbag2``:: + + $ docker run --rm -v /path/to/bag:/rosbag2 rosbags/bench diff --git a/tools/bench/bench.py b/tools/bench/bench.py new file mode 100644 index 00000000..d759a805 --- /dev/null +++ b/tools/bench/bench.py @@ -0,0 +1,147 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Check and benchmark rosbag2 read implementations.""" + +# pylint: disable=import-error + +from __future__ import annotations + +import sys +from math import isnan +from pathlib import Path +from timeit import timeit +from typing import TYPE_CHECKING + +import numpy +from rclpy.serialization import deserialize_message # type: ignore +from rosbag2_py import ConverterOptions, SequentialReader, StorageOptions # type: ignore +from rosidl_runtime_py.utilities import get_message # type: ignore + +from rosbags.rosbag2 import Reader +from rosbags.serde import deserialize_cdr + +if TYPE_CHECKING: + from typing import Any + + +class ReaderPy: # pylint: disable=too-few-public-methods + """Mimimal shim using rosbag2_py to emulate rosbag2 API.""" + + def __init__(self, path: Path): + """Initialize reader shim.""" + soptions = StorageOptions(str(path), 'sqlite3') + coptions = ConverterOptions('', '') + self.reader = SequentialReader() + self.reader.open(soptions, coptions) + self.typemap = {x.name: x.type for x in self.reader.get_all_topics_and_types()} + + def messages(self): + """Expose rosbag2 like generator behavior.""" + while self.reader.has_next(): + topic, data, timestamp = self.reader.read_next() + yield topic, self.typemap[topic], timestamp, data + + +def deserialize_py(data: bytes, msgtype: str) -> Any: + """Deserialization helper for rosidl_runtime_py + rclpy.""" + pytype = get_message(msgtype) + return deserialize_message(data, pytype) + + +def compare_msg(lite: Any, native: Any): + """Compare rosbag2 (lite) vs rosbag2_py (native) message content. + + Args: + lite: Message from rosbag2. + native: Message from rosbag2_py. + + Raises: + AssertionError: If messages are not identical. + + """ + for fieldname in native.get_fields_and_field_types().keys(): + native_val = getattr(native, fieldname) + lite_val = getattr(lite, fieldname) + + if hasattr(lite_val, '__dataclass_fields__'): + compare_msg(lite_val, native_val) + + elif isinstance(lite_val, numpy.ndarray): + assert not (native_val != lite_val).any(), f'{fieldname}: {native_val} != {lite_val}' + + elif isinstance(lite_val, list): + assert len(native_val) == len(lite_val), f'{fieldname} length mismatch' + for sub1, sub2 in zip(native_val, lite_val): + compare_msg(sub2, sub1) + elif isinstance(lite_val, float) and isnan(lite_val): + assert isnan(native_val) + else: + assert native_val == lite_val, f'{fieldname}: {native_val} != {lite_val}' + + +def compare(path: Path): + """Compare raw and deserialized messages.""" + with Reader(path) as reader: + gens = (reader.messages(), ReaderPy(path).messages()) + for item, item_py in zip(*gens): + topic, msgtype, timestamp, data = item + topic_py, msgtype_py, timestamp_py, data_py = item_py + + assert topic == topic_py + assert msgtype == msgtype_py + assert timestamp == timestamp_py + assert data == data_py + + msg_py = deserialize_py(data_py, msgtype_py) + msg = deserialize_cdr(data, msgtype) + + compare_msg(msg, msg_py) + assert len(list(gens[0])) == 0 + assert len(list(gens[1])) == 0 + + +def read_deser_rosbag2_py(path: Path): + """Read testbag with rosbag2_py.""" + soptions = StorageOptions(str(path), 'sqlite3') + coptions = ConverterOptions('', '') + reader = SequentialReader() + reader.open(soptions, coptions) + typemap = {x.name: x.type for x in reader.get_all_topics_and_types()} + + while reader.has_next(): + topic, rawdata, _ = reader.read_next() + msgtype = typemap[topic] + pytype = get_message(msgtype) + deserialize_message(rawdata, pytype) + + +def read_deser_rosbag2(path: Path): + """Read testbag with rosbag2lite.""" + with Reader(path) as reader: + for _, msgtype, _, data in reader.messages(): + deserialize_cdr(data, msgtype) + + +def main(): + """Benchmark rosbag2 against rosbag2_py.""" + path = Path(sys.argv[1]) + try: + print('Comparing messages from rosbag2 and rosbag2_py.') # noqa: T001 + compare(path) + except AssertionError as err: + print(f'Comparison failed {err!r}') # noqa: T001 + sys.exit(1) + + print('Measuring execution times of rosbag2 and rosbag2_py.') # noqa: T001 + time_py = timeit(lambda: read_deser_rosbag2_py(path), number=1) + time = timeit(lambda: read_deser_rosbag2(path), number=1) + print( # noqa: T001 + f'Processing times:\n' + f'rosbag2_py {time_py:.3f}\n' + f'rosbag2 {time:.3f}\n' + f'speedup {time_py / time:.2f}\n', + ) + + +if __name__ == '__main__': + main() From ea67671f53eecfba2fe4ae1227ecd33e7794d5df Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 2 May 2021 14:53:20 +0200 Subject: [PATCH 008/114] Add compare tool --- tools/compare/Dockerfile | 11 ++++ tools/compare/README.rst | 11 ++++ tools/compare/compare.py | 138 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 tools/compare/Dockerfile create mode 100644 tools/compare/README.rst create mode 100644 tools/compare/compare.py diff --git a/tools/compare/Dockerfile b/tools/compare/Dockerfile new file mode 100644 index 00000000..9e5fb59e --- /dev/null +++ b/tools/compare/Dockerfile @@ -0,0 +1,11 @@ +FROM ros:rolling + +RUN apt-get update \ + && apt-get upgrade -y \ + && apt-get install -y \ + python3-pip \ + python3-rosbag + +COPY tools/compare/compare.py / + +CMD ["/usr/bin/python3", "/compare.py", "/rosbag1", "/rosbag2"] diff --git a/tools/compare/README.rst b/tools/compare/README.rst new file mode 100644 index 00000000..8b581e53 --- /dev/null +++ b/tools/compare/README.rst @@ -0,0 +1,11 @@ +======= +Compare +======= + +Check if the contents of a ``rosbag1`` and ``rosbag2`` file are identical. The provided Dockerfile creates an execution environment for the script. Run from the root of this repository:: + + $ docker build -t rosbags/compare -f tools/compare/Dockerfile . + +The docker image expects that the rosbag1 and rosbag2 files to be mounted ``/rosbag1`` and ``/rosbag2`` respectively:: + + $ docker run --rm -v /path/to/rosbag1.bag:/rosbag1 -v /path/to/rosbag2:/rosbag2 rosbags/compare diff --git a/tools/compare/compare.py b/tools/compare/compare.py new file mode 100644 index 00000000..d79ba820 --- /dev/null +++ b/tools/compare/compare.py @@ -0,0 +1,138 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Tool checking if Rosbag1 and Rosbag2 contents are equal.""" + +# pylint: disable=import-error + +from __future__ import annotations + +import array +import math +import sys +from typing import TYPE_CHECKING +from unittest.mock import Mock + +import genpy # type: ignore +import rosgraph_msgs.msg # type: ignore +from rclpy.serialization import deserialize_message # type: ignore +from rosbag2_py import ConverterOptions, SequentialReader, StorageOptions # type: ignore +from rosidl_runtime_py.utilities import get_message # type: ignore + +rosgraph_msgs.msg.Log = Mock() +rosgraph_msgs.msg.TopicStatistics = Mock() + +import rosbag.bag # type:ignore # noqa: E402 pylint: disable=wrong-import-position + +if TYPE_CHECKING: + from typing import Any, List + + from rosbag.bag import _Connection_Info + + +class Reader: # pylint: disable=too-few-public-methods + """Mimimal shim using rosbag2_py to emulate rosbag2 API.""" + + def __init__(self, path: str): + """Initialize reader shim.""" + self.reader = SequentialReader() + self.reader.open(StorageOptions(path, 'sqlite3'), ConverterOptions('', '')) + self.typemap = {x.name: x.type for x in self.reader.get_all_topics_and_types()} + + def messages(self): + """Expose rosbag2 like generator behavior.""" + while self.reader.has_next(): + topic, data, timestamp = self.reader.read_next() + pytype = get_message(self.typemap[topic]) + yield topic, timestamp, deserialize_message(data, pytype) + + +def fixup_ros1(conns: List[_Connection_Info]): + """Monkeypatch ROS2 fieldnames onto ROS1 objects. + + Args: + conns: Rosbag1 connections. + + """ + genpy.Time.sec = property(lambda x: x.secs) + genpy.Time.nanosec = property(lambda x: x.nsecs) + genpy.Duration.sec = property(lambda x: x.secs) + genpy.Duration.nanosec = property(lambda x: x.nsecs) + + if conn := next((x for x in conns if x.datatype == 'sensor_msgs/CameraInfo'), None): + print('Patching CameraInfo') # noqa: T001 + # pylint: disable=assignment-from-no-return,too-many-function-args + cls = rosbag.bag._get_message_type(conn) # pylint: disable=protected-access + cls.d = property(lambda x: x.D, lambda x, y: setattr(x, 'D', y)) # noqa: B010 + cls.k = property(lambda x: x.K, lambda x, y: setattr(x, 'K', y)) # noqa: B010 + cls.r = property(lambda x: x.R, lambda x, y: setattr(x, 'R', y)) # noqa: B010 + cls.p = property(lambda x: x.P, lambda x, y: setattr(x, 'P', y)) # noqa: B010 + + +def compare(ref: Any, msg: Any): + """Compare message to its reference. + + Args: + ref: Reference ROS1 message. + msg: Converted ROS2 message. + + Return: + True if messages are identical. + + """ + if hasattr(msg, 'get_fields_and_field_types'): + for name in msg.get_fields_and_field_types(): + refval = getattr(ref, name) + msgval = getattr(msg, name) + compare(refval, msgval) + + elif isinstance(msg, array.array): + if isinstance(ref, bytes): + assert msg.tobytes() == ref + else: + assert (msg == ref).all() + + elif isinstance(msg, list): + assert len(msg) == len(ref) + for refitem, msgitem in zip(ref, msg): + compare(refitem, msgitem) + + elif isinstance(msg, str): + assert msg == ref + + elif isinstance(msg, float) and math.isnan(ref): + assert math.isnan(msg) + + else: + assert ref == msg + + +def main(path1: str, path2: str): + """Compare rosbag1 to rosbag2 message by message. + + Args: + path1: Rosbag1 filename. + path2: Rosbag2 filename. + + """ + reader1 = rosbag.bag.Bag(path1) + src1 = reader1.read_messages() + src2 = Reader(path2).messages() + + fixup_ros1(reader1._connections.values()) # pylint: disable=protected-access + + for msg1, msg2 in zip(src1, src2): + assert msg1.topic == msg2[0] + assert msg1.timestamp.to_nsec() == msg2[1] + compare(msg1.message, msg2[2]) + + assert next(src1, None) is None + assert next(src2, None) is None + + print('Bags are identical.') # noqa: T001 + + +if __name__ == '__main__': + if len(sys.argv) != 3: + print(f'Usage: {sys.argv} [rosbag1] [rosbag2]') # noqa: T001 + sys.exit(1) + main(sys.argv[1], sys.argv[2]) From 96c26bca48b15a1a4780e3e63d3a1c45c4d99b92 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 2 May 2021 14:53:51 +0200 Subject: [PATCH 009/114] Update requirements --- requirements-dev.txt | 657 +++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 152 ++++++++++ 2 files changed, 809 insertions(+) create mode 100644 requirements-dev.txt create mode 100644 requirements.txt diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 00000000..90af0108 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,657 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile --generate-hashes +# +alabaster==0.7.12 \ + --hash=sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359 \ + --hash=sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02 + # via sphinx +astor==0.8.1 \ + --hash=sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5 \ + --hash=sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e + # via flake8-simplify +astroid==2.5.6 \ + --hash=sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e \ + --hash=sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975 + # via pylint +attrs==20.3.0 \ + --hash=sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6 \ + --hash=sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700 + # via + # flake8-bugbear + # pytest + # pytest-mypy +babel==2.9.1 \ + --hash=sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9 \ + --hash=sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0 + # via sphinx +certifi==2020.12.5 \ + --hash=sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c \ + --hash=sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830 + # via requests +chardet==4.0.0 \ + --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \ + --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5 + # via requests +coverage==5.5 \ + --hash=sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c \ + --hash=sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6 \ + --hash=sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45 \ + --hash=sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a \ + --hash=sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03 \ + --hash=sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529 \ + --hash=sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a \ + --hash=sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a \ + --hash=sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2 \ + --hash=sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6 \ + --hash=sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759 \ + --hash=sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53 \ + --hash=sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a \ + --hash=sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4 \ + --hash=sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff \ + --hash=sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502 \ + --hash=sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793 \ + --hash=sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb \ + --hash=sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905 \ + --hash=sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821 \ + --hash=sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b \ + --hash=sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81 \ + --hash=sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0 \ + --hash=sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b \ + --hash=sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3 \ + --hash=sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184 \ + --hash=sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701 \ + --hash=sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a \ + --hash=sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82 \ + --hash=sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638 \ + --hash=sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5 \ + --hash=sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083 \ + --hash=sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6 \ + --hash=sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90 \ + --hash=sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465 \ + --hash=sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a \ + --hash=sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3 \ + --hash=sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e \ + --hash=sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066 \ + --hash=sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf \ + --hash=sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b \ + --hash=sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae \ + --hash=sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669 \ + --hash=sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873 \ + --hash=sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b \ + --hash=sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6 \ + --hash=sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb \ + --hash=sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160 \ + --hash=sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c \ + --hash=sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079 \ + --hash=sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d \ + --hash=sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6 + # via pytest-cov +darglint==1.8.0 \ + --hash=sha256:aa605ef47817a6d14797d32b390466edab621768ea4ca5cc0f3c54f6d8dcaec8 \ + --hash=sha256:ac6797bcc918cd8d8f14c168a4a364f54e1aeb4ced59db58e7e4c6dfec2fe15c + # via rosbags +docutils==0.16 \ + --hash=sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af \ + --hash=sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc + # via + # sphinx + # sphinx-rtd-theme +filelock==3.0.12 \ + --hash=sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59 \ + --hash=sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836 + # via pytest-mypy +flake8-annotations==2.6.2 \ + --hash=sha256:0d6cd2e770b5095f09689c9d84cc054c51b929c41a68969ea1beb4b825cac515 \ + --hash=sha256:d10c4638231f8a50c0a597c4efce42bd7b7d85df4f620a0ddaca526138936a4f + # via rosbags +flake8-bugbear==21.4.3 \ + --hash=sha256:2346c81f889955b39e4a368eb7d508de723d9de05716c287dc860a4073dc57e7 \ + --hash=sha256:4f305dca96be62bf732a218fe6f1825472a621d3452c5b994d8f89dae21dbafa + # via rosbags +flake8-commas==2.0.0 \ + --hash=sha256:d3005899466f51380387df7151fb59afec666a0f4f4a2c6a8995b975de0f44b7 \ + --hash=sha256:ee2141a3495ef9789a3894ed8802d03eff1eaaf98ce6d8653a7c573ef101935e + # via rosbags +flake8-comprehensions==3.4.0 \ + --hash=sha256:7258a28e229fb9a8d16370b9c47a7d66396ba0201abb06c9d11df41b18ed64c4 \ + --hash=sha256:c00039be9f3959a26a98da3024f0fe809859bf1753ccb90e228cc40f3ac31ca7 + # via rosbags +flake8-docstrings==1.6.0 \ + --hash=sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde \ + --hash=sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b + # via rosbags +flake8-fixme==1.1.1 \ + --hash=sha256:226a6f2ef916730899f29ac140bed5d4a17e5aba79f00a0e3ae1eff1997cb1ac \ + --hash=sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a + # via rosbags +flake8-isort==4.0.0 \ + --hash=sha256:2b91300f4f1926b396c2c90185844eb1a3d5ec39ea6138832d119da0a208f4d9 \ + --hash=sha256:729cd6ef9ba3659512dee337687c05d79c78e1215fdf921ed67e5fe46cce2f3c + # via rosbags +flake8-mutable==1.2.0 \ + --hash=sha256:38fd9dadcbcda6550a916197bc40ed76908119dabb37fbcca30873666c31d2d5 \ + --hash=sha256:ee9b77111b867d845177bbc289d87d541445ffcc6029a0c5c65865b42b18c6a6 + # via rosbags +flake8-plugin-utils==1.3.1 \ + --hash=sha256:6e996bc24ebe327558f24efd106f1be5f0c033c8cbb6eed815631f73d487f1c9 \ + --hash=sha256:efdbf9d15b18f72b7c348dd360f30e7cf3e73aa67ff832d5343eb5aa1115f250 + # via + # flake8-pytest-style + # flake8-return +flake8-polyfill==1.0.2 \ + --hash=sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9 \ + --hash=sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda + # via pep8-naming +flake8-print==4.0.0 \ + --hash=sha256:5afac374b7dc49aac2c36d04b5eb1d746d72e6f5df75a6ecaecd99e9f79c6516 \ + --hash=sha256:6c0efce658513169f96d7a24cf136c434dc711eb00ebd0a985eb1120103fe584 + # via rosbags +flake8-pytest-style==1.4.1 \ + --hash=sha256:1bd3b7bb95608d6b70daebb0b0ee636be92d8175527f2e9919cbeb76f7515118 \ + --hash=sha256:bc4c10e5fcc2a20937bb6ce523579e9f91adfcb5403dce3adda5ac7c9bce364f + # via rosbags +flake8-quotes==3.2.0 \ + --hash=sha256:3f1116e985ef437c130431ac92f9b3155f8f652fda7405ac22ffdfd7a9d1055e + # via rosbags +flake8-return==1.1.2 \ + --hash=sha256:183d0ad2f8553cb2c63c0cf288eb799d967577a74639599525adcd3860f6bb12 \ + --hash=sha256:d646d3b010a9736ddc23c24f98ad3282999f575da45d6eb9cefe4adddb44062d + # via rosbags +flake8-simplify==0.14.0 \ + --hash=sha256:365d0b812fc708af2286a8364ed7b23715bb873431c7f3188bc0b4cd6dcb3c0a \ + --hash=sha256:5793b3c7bd826d7489580f8146bbd40d5bface15d9be5020ddbf07980b844709 + # via rosbags +flake8-type-checking==1.0.0 \ + --hash=sha256:a39d5f46bc93a90e8bae85de644f0c24b8304ea675b30ec918213c3c69774604 \ + --hash=sha256:baacff5a7aacd11fb4c262c52a1c6dd030cd648934e4394b18a77cf3f2fe6458 + # via rosbags +flake8-use-fstring==1.1 \ + --hash=sha256:a0eea849ffe33fb6903c210c243c0f418da86a530e46cb13e64f1bdb045f22dc + # via rosbags +flake8==3.9.1 \ + --hash=sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378 \ + --hash=sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a + # via + # flake8-annotations + # flake8-bugbear + # flake8-commas + # flake8-comprehensions + # flake8-docstrings + # flake8-isort + # flake8-mutable + # flake8-polyfill + # flake8-print + # flake8-quotes + # flake8-simplify + # flake8-type-checking + # flake8-use-fstring + # pytest-flake8 + # rosbags +idna==2.10 \ + --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \ + --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 + # via requests +imagesize==1.2.0 \ + --hash=sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1 \ + --hash=sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1 + # via sphinx +iniconfig==1.1.1 \ + --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ + --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 + # via pytest +isort==5.8.0 \ + --hash=sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6 \ + --hash=sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d + # via + # flake8-isort + # pylint +jinja2==2.11.3 \ + --hash=sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419 \ + --hash=sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6 + # via sphinx +lazy-object-proxy==1.6.0 \ + --hash=sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653 \ + --hash=sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61 \ + --hash=sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2 \ + --hash=sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837 \ + --hash=sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3 \ + --hash=sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43 \ + --hash=sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726 \ + --hash=sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3 \ + --hash=sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587 \ + --hash=sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8 \ + --hash=sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a \ + --hash=sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd \ + --hash=sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f \ + --hash=sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad \ + --hash=sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4 \ + --hash=sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b \ + --hash=sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf \ + --hash=sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981 \ + --hash=sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741 \ + --hash=sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e \ + --hash=sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93 \ + --hash=sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b + # via astroid +lz4==3.1.3 \ + --hash=sha256:081ef0a3b5941cb03127f314229a1c78bd70c9c220bb3f4dd80033e707feaa18 \ + --hash=sha256:0a3b7eeb879577f2c7c0872156c70f4ddfbb023c1198e33d54422e24fa5494ae \ + --hash=sha256:0acbc2b797fe3c51917011c8d7f6c99398ae33cc4a1ca23c3a246d60bbf56fc8 \ + --hash=sha256:0f889e854114f87b5f99e8c82c9bf85417468b291b99a2cb27bcdcc864841a33 \ + --hash=sha256:19d3b8dca0c18991ee243acf86932eb917f14e2e61dd34c7852a1088659d5499 \ + --hash=sha256:1f8320b7b047ec4ba9a7de3509a067ccaac84dab2cadf629d0518760594c3b6a \ + --hash=sha256:37c23ca41040751649e0266f9f267c0148db12968a0a031272ee2a99cef7c753 \ + --hash=sha256:38266a6fa124e3ec2ce3ed6fd34f8e86b13c588f12b005873421afb295caee2d \ + --hash=sha256:3c00b56fd9aef8d3f776653c92cec262d42b6ea144e9a41b58b8c22a85f90045 \ + --hash=sha256:408b2c1b65697d9bc6468c987977314acefc71573b996bd86190053ae7ffe8d1 \ + --hash=sha256:41a388a34eab3cca6180cdb179bd8fdfcf7fd1a569f0e9e6084ad0540a0d53a9 \ + --hash=sha256:4c3558f4b98adb7acee6f4b45edd848684daae6a92a5ff31f8071eb910779568 \ + --hash=sha256:502d6dc17aca64e4dc95d6e7920dca906b5eabc1e657213bd07066c97fbc8cd3 \ + --hash=sha256:511c755d89048a2583ab88088fe451f7e3f15cde30560c058d80c9ac097dab21 \ + --hash=sha256:57dbd50d6abeb85f8d273b9f24f0063c4b97aae07d267302101884611a2413da \ + --hash=sha256:57e5b0a818addacae254b9160a183122b6bc4737bc77c988b72e1c57bd22ed9e \ + --hash=sha256:5aa4dd12debc5cb90980e6bb26be8b1586e57b87aaf6c773b9b799bca16edd99 \ + --hash=sha256:71f6f4dc48669ba3807a5cb5876048dc9b6467c3db312acf2040a61ea9487161 \ + --hash=sha256:7cf0e6e1020dbf8ea72ff57ece3f321f603cfa54f14337b96f7b68a7c1a742b4 \ + --hash=sha256:81b54fc66555fc7653467bf5b789d0e480ab88d17c858405e326d9c898baff2e \ + --hash=sha256:869734b6f0e8a19af10a75c769db179dcd3d867e29c29b3808ef884e76799071 \ + --hash=sha256:af1bc2952214c5a3ec6706cb86bd3e321570c62136539d32e4a57da777b002f0 \ + --hash=sha256:b4b56ae630a41980b6cf17a043b57691ff1f1677425b67556453fd96257b2a9b \ + --hash=sha256:b91fbc9571d3f3fea587ce541f38a2e71ef192075b59c2846182cb98f99862a0 \ + --hash=sha256:c0a5f9b6962aaa4632e4385143a12f5b49ee8605a42589073e54c8f23ce111b2 \ + --hash=sha256:c25dffdb8ab9eb449aacf94ba45b3e6f573b38a1041be9370716cc68dea445a6 \ + --hash=sha256:c41759a97ccac751f69e48f12671c2c3e5e1ae3000d3ee5dfe750b31511d1576 \ + --hash=sha256:d50c9584fb355d5d51414b802f7012578240bcb259550b48de628e19cd5bff6c \ + --hash=sha256:d6afa929d2c8afafd8b89e898498484b145a94cf3c140bb68094a94590ab2c2a \ + --hash=sha256:de2aac0cfda79c5a3eda9dbed21d78dc05c4a9ac00061748c3b57ea0e4a0b6a8 \ + --hash=sha256:e3029738e64a0af1b04a32a39b32b0bba0e2088f61805e074c9a7e4bc212568f + # via rosbags +markupsafe==1.1.1 \ + --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \ + --hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \ + --hash=sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235 \ + --hash=sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5 \ + --hash=sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42 \ + --hash=sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f \ + --hash=sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39 \ + --hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \ + --hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b \ + --hash=sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014 \ + --hash=sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f \ + --hash=sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1 \ + --hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \ + --hash=sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183 \ + --hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \ + --hash=sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b \ + --hash=sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1 \ + --hash=sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15 \ + --hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \ + --hash=sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85 \ + --hash=sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1 \ + --hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \ + --hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \ + --hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \ + --hash=sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850 \ + --hash=sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0 \ + --hash=sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735 \ + --hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \ + --hash=sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb \ + --hash=sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e \ + --hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \ + --hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \ + --hash=sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1 \ + --hash=sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2 \ + --hash=sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21 \ + --hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \ + --hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \ + --hash=sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7 \ + --hash=sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b \ + --hash=sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8 \ + --hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \ + --hash=sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193 \ + --hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \ + --hash=sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b \ + --hash=sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f \ + --hash=sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2 \ + --hash=sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5 \ + --hash=sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c \ + --hash=sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032 \ + --hash=sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7 \ + --hash=sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be \ + --hash=sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621 + # via jinja2 +mccabe==0.6.1 \ + --hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \ + --hash=sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f + # via + # flake8 + # pylint +mypy-extensions==0.4.3 \ + --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ + --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 + # via mypy +mypy==0.812 \ + --hash=sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e \ + --hash=sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064 \ + --hash=sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c \ + --hash=sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4 \ + --hash=sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97 \ + --hash=sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df \ + --hash=sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8 \ + --hash=sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a \ + --hash=sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56 \ + --hash=sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7 \ + --hash=sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6 \ + --hash=sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5 \ + --hash=sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a \ + --hash=sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521 \ + --hash=sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564 \ + --hash=sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49 \ + --hash=sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66 \ + --hash=sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a \ + --hash=sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119 \ + --hash=sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506 \ + --hash=sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c \ + --hash=sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb + # via pytest-mypy +numpy==1.20.2 \ + --hash=sha256:2428b109306075d89d21135bdd6b785f132a1f5a3260c371cee1fae427e12727 \ + --hash=sha256:377751954da04d4a6950191b20539066b4e19e3b559d4695399c5e8e3e683bf6 \ + --hash=sha256:4703b9e937df83f5b6b7447ca5912b5f5f297aba45f91dbbbc63ff9278c7aa98 \ + --hash=sha256:471c0571d0895c68da309dacee4e95a0811d0a9f9f532a48dc1bea5f3b7ad2b7 \ + --hash=sha256:61d5b4cf73622e4d0c6b83408a16631b670fc045afd6540679aa35591a17fe6d \ + --hash=sha256:6c915ee7dba1071554e70a3664a839fbc033e1d6528199d4621eeaaa5487ccd2 \ + --hash=sha256:6e51e417d9ae2e7848314994e6fc3832c9d426abce9328cf7571eefceb43e6c9 \ + --hash=sha256:719656636c48be22c23641859ff2419b27b6bdf844b36a2447cb39caceb00935 \ + --hash=sha256:780ae5284cb770ade51d4b4a7dce4faa554eb1d88a56d0e8b9f35fca9b0270ff \ + --hash=sha256:878922bf5ad7550aa044aa9301d417e2d3ae50f0f577de92051d739ac6096cee \ + --hash=sha256:924dc3f83de20437de95a73516f36e09918e9c9c18d5eac520062c49191025fb \ + --hash=sha256:97ce8b8ace7d3b9288d88177e66ee75480fb79b9cf745e91ecfe65d91a856042 \ + --hash=sha256:9c0fab855ae790ca74b27e55240fe4f2a36a364a3f1ebcfd1fb5ac4088f1cec3 \ + --hash=sha256:9cab23439eb1ebfed1aaec9cd42b7dc50fc96d5cd3147da348d9161f0501ada5 \ + --hash=sha256:a8e6859913ec8eeef3dbe9aed3bf475347642d1cdd6217c30f28dee8903528e6 \ + --hash=sha256:aa046527c04688af680217fffac61eec2350ef3f3d7320c07fd33f5c6e7b4d5f \ + --hash=sha256:abc81829c4039e7e4c30f7897938fa5d4916a09c2c7eb9b244b7a35ddc9656f4 \ + --hash=sha256:bad70051de2c50b1a6259a6df1daaafe8c480ca98132da98976d8591c412e737 \ + --hash=sha256:c73a7975d77f15f7f68dacfb2bca3d3f479f158313642e8ea9058eea06637931 \ + --hash=sha256:d15007f857d6995db15195217afdbddfcd203dfaa0ba6878a2f580eaf810ecd6 \ + --hash=sha256:d76061ae5cab49b83a8cf3feacefc2053fac672728802ac137dd8c4123397677 \ + --hash=sha256:e8e4fbbb7e7634f263c5b0150a629342cc19b47c5eba8d1cd4363ab3455ab576 \ + --hash=sha256:e9459f40244bb02b2f14f6af0cd0732791d72232bbb0dc4bab57ef88e75f6935 \ + --hash=sha256:edb1f041a9146dcf02cd7df7187db46ab524b9af2515f392f337c7cbbf5b52cd + # via rosbags +packaging==20.9 \ + --hash=sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5 \ + --hash=sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a + # via + # pytest + # sphinx +pep8-naming==0.11.1 \ + --hash=sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724 \ + --hash=sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738 + # via rosbags +pluggy==0.13.1 \ + --hash=sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0 \ + --hash=sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d + # via pytest +py==1.10.0 \ + --hash=sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3 \ + --hash=sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a + # via pytest +pycodestyle==2.7.0 \ + --hash=sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068 \ + --hash=sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef + # via + # flake8 + # flake8-print +pydocstyle==6.0.0 \ + --hash=sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f \ + --hash=sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d + # via flake8-docstrings +pyflakes==2.3.1 \ + --hash=sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3 \ + --hash=sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db + # via flake8 +pygments==2.8.1 \ + --hash=sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94 \ + --hash=sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8 + # via sphinx +pylint==2.8.2 \ + --hash=sha256:586d8fa9b1891f4b725f587ef267abe2a1bad89d6b184520c7f07a253dd6e217 \ + --hash=sha256:f7e2072654a6b6afdf5e2fb38147d3e2d2d43c89f648637baab63e026481279b + # via pytest-pylint +pyparsing==2.4.7 \ + --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ + --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b + # via packaging +pytest-cov==2.11.1 \ + --hash=sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7 \ + --hash=sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da + # via rosbags +pytest-flake8==1.0.7 \ + --hash=sha256:c28cf23e7d359753c896745fd4ba859495d02e16c84bac36caa8b1eec58f5bc1 \ + --hash=sha256:f0259761a903563f33d6f099914afef339c085085e643bee8343eb323b32dd6b + # via rosbags +pytest-mypy==0.8.1 \ + --hash=sha256:1fa55723a4bf1d054fcba1c3bd694215a2a65cc95ab10164f5808afd893f3b11 \ + --hash=sha256:6e68e8eb7ceeb7d1c83a1590912f784879f037b51adfb9c17b95c6b2fc57466b + # via rosbags +pytest-pylint==0.18.0 \ + --hash=sha256:790c7a8019fab08e59bd3812db1657a01995a975af8b1c6ce95b9aa39d61da27 \ + --hash=sha256:b63aaf8b80ff33c8ceaa7f68323ed04102c7790093ccf6bdb261a4c2dc6fd564 + # via rosbags +pytest==6.2.3 \ + --hash=sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634 \ + --hash=sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc + # via + # pytest-cov + # pytest-flake8 + # pytest-mypy + # pytest-pylint + # rosbags +pytz==2021.1 \ + --hash=sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da \ + --hash=sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798 + # via babel +requests==2.25.1 \ + --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \ + --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e + # via sphinx +ruamel.yaml.clib==0.2.2 \ + --hash=sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b \ + --hash=sha256:1236df55e0f73cd138c0eca074ee086136c3f16a97c2ac719032c050f7e0622f \ + --hash=sha256:1f8c0a4577c0e6c99d208de5c4d3fd8aceed9574bb154d7a2b21c16bb924154c \ + --hash=sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91 \ + --hash=sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc \ + --hash=sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7 \ + --hash=sha256:2fd336a5c6415c82e2deb40d08c222087febe0aebe520f4d21910629018ab0f3 \ + --hash=sha256:30dca9bbcbb1cc858717438218d11eafb78666759e5094dd767468c0d577a7e7 \ + --hash=sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6 \ + --hash=sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6 \ + --hash=sha256:46d6d20815064e8bb023ea8628cfb7402c0f0e83de2c2227a88097e239a7dffd \ + --hash=sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0 \ + --hash=sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62 \ + --hash=sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99 \ + --hash=sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5 \ + --hash=sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026 \ + --hash=sha256:6c0a5dc52fc74eb87c67374a4e554d4761fd42a4d01390b7e868b30d21f4b8bb \ + --hash=sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2 \ + --hash=sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1 \ + --hash=sha256:75f0ee6839532e52a3a53f80ce64925ed4aed697dd3fa890c4c918f3304bd4f4 \ + --hash=sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b \ + --hash=sha256:8be05be57dc5c7b4a0b24edcaa2f7275866d9c907725226cdde46da09367d923 \ + --hash=sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e \ + --hash=sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c \ + --hash=sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988 \ + --hash=sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f \ + --hash=sha256:b4b0d31f2052b3f9f9b5327024dc629a253a83d8649d4734ca7f35b60ec3e9e5 \ + --hash=sha256:c6ac7e45367b1317e56f1461719c853fd6825226f45b835df7436bb04031fd8a \ + --hash=sha256:daf21aa33ee9b351f66deed30a3d450ab55c14242cfdfcd377798e2c0d25c9f1 \ + --hash=sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2 \ + --hash=sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f + # via ruamel.yaml +ruamel.yaml==0.17.4 \ + --hash=sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28 \ + --hash=sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22 + # via rosbags +six==1.15.0 \ + --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \ + --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced + # via flake8-print +snowballstemmer==2.1.0 \ + --hash=sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2 \ + --hash=sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914 + # via + # pydocstyle + # sphinx +sphinx-autodoc-typehints==1.12.0 \ + --hash=sha256:193617d9dbe0847281b1399d369e74e34cd959c82e02c7efde077fca908a9f52 \ + --hash=sha256:5e81776ec422dd168d688ab60f034fccfafbcd94329e9537712c93003bddc04a + # via rosbags +sphinx-rtd-theme==0.5.2 \ + --hash=sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a \ + --hash=sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f + # via rosbags +sphinx==3.5.4 \ + --hash=sha256:19010b7b9fa0dc7756a6e105b2aacd3a80f798af3c25c273be64d7beeb482cb1 \ + --hash=sha256:2320d4e994a191f4b4be27da514e46b3d6b420f2ff895d064f52415d342461e8 + # via + # rosbags + # sphinx-autodoc-typehints + # sphinx-rtd-theme +sphinxcontrib-applehelp==1.0.2 \ + --hash=sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a \ + --hash=sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58 + # via sphinx +sphinxcontrib-devhelp==1.0.2 \ + --hash=sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e \ + --hash=sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4 + # via sphinx +sphinxcontrib-htmlhelp==1.0.3 \ + --hash=sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f \ + --hash=sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b + # via sphinx +sphinxcontrib-jsmath==1.0.1 \ + --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \ + --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8 + # via sphinx +sphinxcontrib-qthelp==1.0.3 \ + --hash=sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72 \ + --hash=sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6 + # via sphinx +sphinxcontrib-serializinghtml==1.1.4 \ + --hash=sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc \ + --hash=sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a + # via sphinx +testfixtures==6.17.1 \ + --hash=sha256:5ec3a0dd6f71cc4c304fbc024a10cc293d3e0b852c868014b9f233203e149bda \ + --hash=sha256:9ed31e83f59619e2fa17df053b241e16e0608f4580f7b5a9333a0c9bdcc99137 + # via flake8-isort +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via + # pylint + # pytest + # pytest-pylint +typed-ast==1.4.3 \ + --hash=sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace \ + --hash=sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff \ + --hash=sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266 \ + --hash=sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528 \ + --hash=sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6 \ + --hash=sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808 \ + --hash=sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4 \ + --hash=sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363 \ + --hash=sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341 \ + --hash=sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04 \ + --hash=sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41 \ + --hash=sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e \ + --hash=sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3 \ + --hash=sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899 \ + --hash=sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805 \ + --hash=sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c \ + --hash=sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c \ + --hash=sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39 \ + --hash=sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a \ + --hash=sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3 \ + --hash=sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7 \ + --hash=sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f \ + --hash=sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075 \ + --hash=sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0 \ + --hash=sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40 \ + --hash=sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428 \ + --hash=sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927 \ + --hash=sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3 \ + --hash=sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f \ + --hash=sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65 + # via mypy +typing-extensions==3.10.0.0 \ + --hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \ + --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \ + --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84 + # via mypy +urllib3==1.26.4 \ + --hash=sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df \ + --hash=sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937 + # via requests +wrapt==1.12.1 \ + --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7 + # via astroid +zstandard==0.15.2 \ + --hash=sha256:1c5ef399f81204fbd9f0df3debf80389fd8aa9660fe1746d37c80b0d45f809e9 \ + --hash=sha256:1faefe33e3d6870a4dce637bcb41f7abb46a1872a595ecc7b034016081c37543 \ + --hash=sha256:1fb23b1754ce834a3a1a1e148cc2faad76eeadf9d889efe5e8199d3fb839d3c6 \ + --hash=sha256:22f127ff5da052ffba73af146d7d61db874f5edb468b36c9cb0b857316a21b3d \ + --hash=sha256:2353b61f249a5fc243aae3caa1207c80c7e6919a58b1f9992758fa496f61f839 \ + --hash=sha256:24cdcc6f297f7c978a40fb7706877ad33d8e28acc1786992a52199502d6da2a4 \ + --hash=sha256:31e35790434da54c106f05fa93ab4d0fab2798a6350e8a73928ec602e8505836 \ + --hash=sha256:3547ff4eee7175d944a865bbdf5529b0969c253e8a148c287f0668fe4eb9c935 \ + --hash=sha256:378ac053c0cfc74d115cbb6ee181540f3e793c7cca8ed8cd3893e338af9e942c \ + --hash=sha256:3e1cd2db25117c5b7c7e86a17cde6104a93719a9df7cb099d7498e4c1d13ee5c \ + --hash=sha256:3fe469a887f6142cc108e44c7f42c036e43620ebaf500747be2317c9f4615d4f \ + --hash=sha256:4800ab8ec94cbf1ed09c2b4686288750cab0642cb4d6fba2a56db66b923aeb92 \ + --hash=sha256:52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f \ + --hash=sha256:5752f44795b943c99be367fee5edf3122a1690b0d1ecd1bd5ec94c7fd2c39c94 \ + --hash=sha256:5d53f02aeb8fdd48b88bc80bece82542d084fb1a7ba03bf241fd53b63aee4f22 \ + --hash=sha256:69b7a5720b8dfab9005a43c7ddb2e3ccacbb9a2442908ae4ed49dd51ab19698a \ + --hash=sha256:6cc162b5b6e3c40b223163a9ea86cd332bd352ddadb5fd142fc0706e5e4eaaff \ + --hash=sha256:6f5d0330bc992b1e267a1b69fbdbb5ebe8c3a6af107d67e14c7a5b1ede2c5945 \ + --hash=sha256:6ffadd48e6fe85f27ca3ca10cfd3ef3d0f933bef7316870285ffeb58d791ca9c \ + --hash=sha256:72a011678c654df8323aa7b687e3147749034fdbe994d346f139ab9702b59cea \ + --hash=sha256:77d26452676f471223571efd73131fd4a626622c7960458aab2763e025836fc5 \ + --hash=sha256:7a88cc773ffe55992ff7259a8df5fb3570168d7138c69aadba40142d0e5ce39a \ + --hash=sha256:7b16bd74ae7bfbaca407a127e11058b287a4267caad13bd41305a5e630472549 \ + --hash=sha256:855d95ec78b6f0ff66e076d5461bf12d09d8e8f7e2b3fc9de7236d1464fd730e \ + --hash=sha256:8baf7991547441458325ca8fafeae79ef1501cb4354022724f3edd62279c5b2b \ + --hash=sha256:8fb77dd152054c6685639d855693579a92f276b38b8003be5942de31d241ebfb \ + --hash=sha256:92d49cc3b49372cfea2d42f43a2c16a98a32a6bc2f42abcde121132dbfc2f023 \ + --hash=sha256:94d0de65e37f5677165725f1fc7fb1616b9542d42a9832a9a0bdcba0ed68b63b \ + --hash=sha256:9867206093d7283d7de01bd2bf60389eb4d19b67306a0a763d1a8a4dbe2fb7c3 \ + --hash=sha256:9ee3c992b93e26c2ae827404a626138588e30bdabaaf7aa3aa25082a4e718790 \ + --hash=sha256:a4f8af277bb527fa3d56b216bda4da931b36b2d3fe416b6fc1744072b2c1dbd9 \ + --hash=sha256:ab9f19460dfa4c5dd25431b75bee28b5f018bf43476858d64b1aa1046196a2a0 \ + --hash=sha256:ac43c1821ba81e9344d818c5feed574a17f51fca27976ff7d022645c378fbbf5 \ + --hash=sha256:af5a011609206e390b44847da32463437505bf55fd8985e7a91c52d9da338d4b \ + --hash=sha256:b0975748bb6ec55b6d0f6665313c2cf7af6f536221dccd5879b967d76f6e7899 \ + --hash=sha256:b4963dad6cf28bfe0b61c3265d1c74a26a7605df3445bfcd3ba25de012330b2d \ + --hash=sha256:b7d3a484ace91ed827aa2ef3b44895e2ec106031012f14d28bd11a55f24fa734 \ + --hash=sha256:bd3c478a4a574f412efc58ba7e09ab4cd83484c545746a01601636e87e3dbf23 \ + --hash=sha256:c9e2dcb7f851f020232b991c226c5678dc07090256e929e45a89538d82f71d2e \ + --hash=sha256:d25c8eeb4720da41e7afbc404891e3a945b8bb6d5230e4c53d23ac4f4f9fc52c \ + --hash=sha256:dc8c03d0c5c10c200441ffb4cce46d869d9e5c4ef007f55856751dc288a2dffd \ + --hash=sha256:ec58e84d625553d191a23d5988a19c3ebfed519fff2a8b844223e3f074152163 \ + --hash=sha256:eda0719b29792f0fea04a853377cfff934660cb6cd72a0a0eeba7a1f0df4a16e \ + --hash=sha256:edde82ce3007a64e8434ccaf1b53271da4f255224d77b880b59e7d6d73df90c8 \ + --hash=sha256:f36722144bc0a5068934e51dca5a38a5b4daac1be84f4423244277e4baf24e7a \ + --hash=sha256:f8bb00ced04a8feff05989996db47906673ed45b11d86ad5ce892b5741e5f9dd \ + --hash=sha256:f98fc5750aac2d63d482909184aac72a979bfd123b112ec53fd365104ea15b1c \ + --hash=sha256:ff5b75f94101beaa373f1511319580a010f6e03458ee51b1a386d7de5331440a + # via rosbags + +# WARNING: The following packages were not pinned, but pip requires them to be +# pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag. +# setuptools diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..18846300 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,152 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile --generate-hashes +# +lz4==3.1.3 \ + --hash=sha256:081ef0a3b5941cb03127f314229a1c78bd70c9c220bb3f4dd80033e707feaa18 \ + --hash=sha256:0a3b7eeb879577f2c7c0872156c70f4ddfbb023c1198e33d54422e24fa5494ae \ + --hash=sha256:0acbc2b797fe3c51917011c8d7f6c99398ae33cc4a1ca23c3a246d60bbf56fc8 \ + --hash=sha256:0f889e854114f87b5f99e8c82c9bf85417468b291b99a2cb27bcdcc864841a33 \ + --hash=sha256:19d3b8dca0c18991ee243acf86932eb917f14e2e61dd34c7852a1088659d5499 \ + --hash=sha256:1f8320b7b047ec4ba9a7de3509a067ccaac84dab2cadf629d0518760594c3b6a \ + --hash=sha256:37c23ca41040751649e0266f9f267c0148db12968a0a031272ee2a99cef7c753 \ + --hash=sha256:38266a6fa124e3ec2ce3ed6fd34f8e86b13c588f12b005873421afb295caee2d \ + --hash=sha256:3c00b56fd9aef8d3f776653c92cec262d42b6ea144e9a41b58b8c22a85f90045 \ + --hash=sha256:408b2c1b65697d9bc6468c987977314acefc71573b996bd86190053ae7ffe8d1 \ + --hash=sha256:41a388a34eab3cca6180cdb179bd8fdfcf7fd1a569f0e9e6084ad0540a0d53a9 \ + --hash=sha256:4c3558f4b98adb7acee6f4b45edd848684daae6a92a5ff31f8071eb910779568 \ + --hash=sha256:502d6dc17aca64e4dc95d6e7920dca906b5eabc1e657213bd07066c97fbc8cd3 \ + --hash=sha256:511c755d89048a2583ab88088fe451f7e3f15cde30560c058d80c9ac097dab21 \ + --hash=sha256:57dbd50d6abeb85f8d273b9f24f0063c4b97aae07d267302101884611a2413da \ + --hash=sha256:57e5b0a818addacae254b9160a183122b6bc4737bc77c988b72e1c57bd22ed9e \ + --hash=sha256:5aa4dd12debc5cb90980e6bb26be8b1586e57b87aaf6c773b9b799bca16edd99 \ + --hash=sha256:71f6f4dc48669ba3807a5cb5876048dc9b6467c3db312acf2040a61ea9487161 \ + --hash=sha256:7cf0e6e1020dbf8ea72ff57ece3f321f603cfa54f14337b96f7b68a7c1a742b4 \ + --hash=sha256:81b54fc66555fc7653467bf5b789d0e480ab88d17c858405e326d9c898baff2e \ + --hash=sha256:869734b6f0e8a19af10a75c769db179dcd3d867e29c29b3808ef884e76799071 \ + --hash=sha256:af1bc2952214c5a3ec6706cb86bd3e321570c62136539d32e4a57da777b002f0 \ + --hash=sha256:b4b56ae630a41980b6cf17a043b57691ff1f1677425b67556453fd96257b2a9b \ + --hash=sha256:b91fbc9571d3f3fea587ce541f38a2e71ef192075b59c2846182cb98f99862a0 \ + --hash=sha256:c0a5f9b6962aaa4632e4385143a12f5b49ee8605a42589073e54c8f23ce111b2 \ + --hash=sha256:c25dffdb8ab9eb449aacf94ba45b3e6f573b38a1041be9370716cc68dea445a6 \ + --hash=sha256:c41759a97ccac751f69e48f12671c2c3e5e1ae3000d3ee5dfe750b31511d1576 \ + --hash=sha256:d50c9584fb355d5d51414b802f7012578240bcb259550b48de628e19cd5bff6c \ + --hash=sha256:d6afa929d2c8afafd8b89e898498484b145a94cf3c140bb68094a94590ab2c2a \ + --hash=sha256:de2aac0cfda79c5a3eda9dbed21d78dc05c4a9ac00061748c3b57ea0e4a0b6a8 \ + --hash=sha256:e3029738e64a0af1b04a32a39b32b0bba0e2088f61805e074c9a7e4bc212568f + # via rosbags (setup.py) +numpy==1.20.2 \ + --hash=sha256:2428b109306075d89d21135bdd6b785f132a1f5a3260c371cee1fae427e12727 \ + --hash=sha256:377751954da04d4a6950191b20539066b4e19e3b559d4695399c5e8e3e683bf6 \ + --hash=sha256:4703b9e937df83f5b6b7447ca5912b5f5f297aba45f91dbbbc63ff9278c7aa98 \ + --hash=sha256:471c0571d0895c68da309dacee4e95a0811d0a9f9f532a48dc1bea5f3b7ad2b7 \ + --hash=sha256:61d5b4cf73622e4d0c6b83408a16631b670fc045afd6540679aa35591a17fe6d \ + --hash=sha256:6c915ee7dba1071554e70a3664a839fbc033e1d6528199d4621eeaaa5487ccd2 \ + --hash=sha256:6e51e417d9ae2e7848314994e6fc3832c9d426abce9328cf7571eefceb43e6c9 \ + --hash=sha256:719656636c48be22c23641859ff2419b27b6bdf844b36a2447cb39caceb00935 \ + --hash=sha256:780ae5284cb770ade51d4b4a7dce4faa554eb1d88a56d0e8b9f35fca9b0270ff \ + --hash=sha256:878922bf5ad7550aa044aa9301d417e2d3ae50f0f577de92051d739ac6096cee \ + --hash=sha256:924dc3f83de20437de95a73516f36e09918e9c9c18d5eac520062c49191025fb \ + --hash=sha256:97ce8b8ace7d3b9288d88177e66ee75480fb79b9cf745e91ecfe65d91a856042 \ + --hash=sha256:9c0fab855ae790ca74b27e55240fe4f2a36a364a3f1ebcfd1fb5ac4088f1cec3 \ + --hash=sha256:9cab23439eb1ebfed1aaec9cd42b7dc50fc96d5cd3147da348d9161f0501ada5 \ + --hash=sha256:a8e6859913ec8eeef3dbe9aed3bf475347642d1cdd6217c30f28dee8903528e6 \ + --hash=sha256:aa046527c04688af680217fffac61eec2350ef3f3d7320c07fd33f5c6e7b4d5f \ + --hash=sha256:abc81829c4039e7e4c30f7897938fa5d4916a09c2c7eb9b244b7a35ddc9656f4 \ + --hash=sha256:bad70051de2c50b1a6259a6df1daaafe8c480ca98132da98976d8591c412e737 \ + --hash=sha256:c73a7975d77f15f7f68dacfb2bca3d3f479f158313642e8ea9058eea06637931 \ + --hash=sha256:d15007f857d6995db15195217afdbddfcd203dfaa0ba6878a2f580eaf810ecd6 \ + --hash=sha256:d76061ae5cab49b83a8cf3feacefc2053fac672728802ac137dd8c4123397677 \ + --hash=sha256:e8e4fbbb7e7634f263c5b0150a629342cc19b47c5eba8d1cd4363ab3455ab576 \ + --hash=sha256:e9459f40244bb02b2f14f6af0cd0732791d72232bbb0dc4bab57ef88e75f6935 \ + --hash=sha256:edb1f041a9146dcf02cd7df7187db46ab524b9af2515f392f337c7cbbf5b52cd + # via rosbags (setup.py) +ruamel.yaml.clib==0.2.2 \ + --hash=sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b \ + --hash=sha256:1236df55e0f73cd138c0eca074ee086136c3f16a97c2ac719032c050f7e0622f \ + --hash=sha256:1f8c0a4577c0e6c99d208de5c4d3fd8aceed9574bb154d7a2b21c16bb924154c \ + --hash=sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91 \ + --hash=sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc \ + --hash=sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7 \ + --hash=sha256:2fd336a5c6415c82e2deb40d08c222087febe0aebe520f4d21910629018ab0f3 \ + --hash=sha256:30dca9bbcbb1cc858717438218d11eafb78666759e5094dd767468c0d577a7e7 \ + --hash=sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6 \ + --hash=sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6 \ + --hash=sha256:46d6d20815064e8bb023ea8628cfb7402c0f0e83de2c2227a88097e239a7dffd \ + --hash=sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0 \ + --hash=sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62 \ + --hash=sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99 \ + --hash=sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5 \ + --hash=sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026 \ + --hash=sha256:6c0a5dc52fc74eb87c67374a4e554d4761fd42a4d01390b7e868b30d21f4b8bb \ + --hash=sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2 \ + --hash=sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1 \ + --hash=sha256:75f0ee6839532e52a3a53f80ce64925ed4aed697dd3fa890c4c918f3304bd4f4 \ + --hash=sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b \ + --hash=sha256:8be05be57dc5c7b4a0b24edcaa2f7275866d9c907725226cdde46da09367d923 \ + --hash=sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e \ + --hash=sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c \ + --hash=sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988 \ + --hash=sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f \ + --hash=sha256:b4b0d31f2052b3f9f9b5327024dc629a253a83d8649d4734ca7f35b60ec3e9e5 \ + --hash=sha256:c6ac7e45367b1317e56f1461719c853fd6825226f45b835df7436bb04031fd8a \ + --hash=sha256:daf21aa33ee9b351f66deed30a3d450ab55c14242cfdfcd377798e2c0d25c9f1 \ + --hash=sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2 \ + --hash=sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f + # via ruamel.yaml +ruamel.yaml==0.17.4 \ + --hash=sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28 \ + --hash=sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22 + # via rosbags (setup.py) +zstandard==0.15.2 \ + --hash=sha256:1c5ef399f81204fbd9f0df3debf80389fd8aa9660fe1746d37c80b0d45f809e9 \ + --hash=sha256:1faefe33e3d6870a4dce637bcb41f7abb46a1872a595ecc7b034016081c37543 \ + --hash=sha256:1fb23b1754ce834a3a1a1e148cc2faad76eeadf9d889efe5e8199d3fb839d3c6 \ + --hash=sha256:22f127ff5da052ffba73af146d7d61db874f5edb468b36c9cb0b857316a21b3d \ + --hash=sha256:2353b61f249a5fc243aae3caa1207c80c7e6919a58b1f9992758fa496f61f839 \ + --hash=sha256:24cdcc6f297f7c978a40fb7706877ad33d8e28acc1786992a52199502d6da2a4 \ + --hash=sha256:31e35790434da54c106f05fa93ab4d0fab2798a6350e8a73928ec602e8505836 \ + --hash=sha256:3547ff4eee7175d944a865bbdf5529b0969c253e8a148c287f0668fe4eb9c935 \ + --hash=sha256:378ac053c0cfc74d115cbb6ee181540f3e793c7cca8ed8cd3893e338af9e942c \ + --hash=sha256:3e1cd2db25117c5b7c7e86a17cde6104a93719a9df7cb099d7498e4c1d13ee5c \ + --hash=sha256:3fe469a887f6142cc108e44c7f42c036e43620ebaf500747be2317c9f4615d4f \ + --hash=sha256:4800ab8ec94cbf1ed09c2b4686288750cab0642cb4d6fba2a56db66b923aeb92 \ + --hash=sha256:52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f \ + --hash=sha256:5752f44795b943c99be367fee5edf3122a1690b0d1ecd1bd5ec94c7fd2c39c94 \ + --hash=sha256:5d53f02aeb8fdd48b88bc80bece82542d084fb1a7ba03bf241fd53b63aee4f22 \ + --hash=sha256:69b7a5720b8dfab9005a43c7ddb2e3ccacbb9a2442908ae4ed49dd51ab19698a \ + --hash=sha256:6cc162b5b6e3c40b223163a9ea86cd332bd352ddadb5fd142fc0706e5e4eaaff \ + --hash=sha256:6f5d0330bc992b1e267a1b69fbdbb5ebe8c3a6af107d67e14c7a5b1ede2c5945 \ + --hash=sha256:6ffadd48e6fe85f27ca3ca10cfd3ef3d0f933bef7316870285ffeb58d791ca9c \ + --hash=sha256:72a011678c654df8323aa7b687e3147749034fdbe994d346f139ab9702b59cea \ + --hash=sha256:77d26452676f471223571efd73131fd4a626622c7960458aab2763e025836fc5 \ + --hash=sha256:7a88cc773ffe55992ff7259a8df5fb3570168d7138c69aadba40142d0e5ce39a \ + --hash=sha256:7b16bd74ae7bfbaca407a127e11058b287a4267caad13bd41305a5e630472549 \ + --hash=sha256:855d95ec78b6f0ff66e076d5461bf12d09d8e8f7e2b3fc9de7236d1464fd730e \ + --hash=sha256:8baf7991547441458325ca8fafeae79ef1501cb4354022724f3edd62279c5b2b \ + --hash=sha256:8fb77dd152054c6685639d855693579a92f276b38b8003be5942de31d241ebfb \ + --hash=sha256:92d49cc3b49372cfea2d42f43a2c16a98a32a6bc2f42abcde121132dbfc2f023 \ + --hash=sha256:94d0de65e37f5677165725f1fc7fb1616b9542d42a9832a9a0bdcba0ed68b63b \ + --hash=sha256:9867206093d7283d7de01bd2bf60389eb4d19b67306a0a763d1a8a4dbe2fb7c3 \ + --hash=sha256:9ee3c992b93e26c2ae827404a626138588e30bdabaaf7aa3aa25082a4e718790 \ + --hash=sha256:a4f8af277bb527fa3d56b216bda4da931b36b2d3fe416b6fc1744072b2c1dbd9 \ + --hash=sha256:ab9f19460dfa4c5dd25431b75bee28b5f018bf43476858d64b1aa1046196a2a0 \ + --hash=sha256:ac43c1821ba81e9344d818c5feed574a17f51fca27976ff7d022645c378fbbf5 \ + --hash=sha256:af5a011609206e390b44847da32463437505bf55fd8985e7a91c52d9da338d4b \ + --hash=sha256:b0975748bb6ec55b6d0f6665313c2cf7af6f536221dccd5879b967d76f6e7899 \ + --hash=sha256:b4963dad6cf28bfe0b61c3265d1c74a26a7605df3445bfcd3ba25de012330b2d \ + --hash=sha256:b7d3a484ace91ed827aa2ef3b44895e2ec106031012f14d28bd11a55f24fa734 \ + --hash=sha256:bd3c478a4a574f412efc58ba7e09ab4cd83484c545746a01601636e87e3dbf23 \ + --hash=sha256:c9e2dcb7f851f020232b991c226c5678dc07090256e929e45a89538d82f71d2e \ + --hash=sha256:d25c8eeb4720da41e7afbc404891e3a945b8bb6d5230e4c53d23ac4f4f9fc52c \ + --hash=sha256:dc8c03d0c5c10c200441ffb4cce46d869d9e5c4ef007f55856751dc288a2dffd \ + --hash=sha256:ec58e84d625553d191a23d5988a19c3ebfed519fff2a8b844223e3f074152163 \ + --hash=sha256:eda0719b29792f0fea04a853377cfff934660cb6cd72a0a0eeba7a1f0df4a16e \ + --hash=sha256:edde82ce3007a64e8434ccaf1b53271da4f255224d77b880b59e7d6d73df90c8 \ + --hash=sha256:f36722144bc0a5068934e51dca5a38a5b4daac1be84f4423244277e4baf24e7a \ + --hash=sha256:f8bb00ced04a8feff05989996db47906673ed45b11d86ad5ce892b5741e5f9dd \ + --hash=sha256:f98fc5750aac2d63d482909184aac72a979bfd123b112ec53fd365104ea15b1c \ + --hash=sha256:ff5b75f94101beaa373f1511319580a010f6e03458ee51b1a386d7de5331440a + # via rosbags (setup.py) From a8baffcbd76aaba0fd820cdaee20e3562fc5c789 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 16 May 2021 16:27:43 +0200 Subject: [PATCH 010/114] Update changes --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a6c0955d..078a4749 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,3 +3,7 @@ Changes ======= +0.9.0 - 2021-05-16 +------------------ + +- Initial Release From ea40c7413389c694da14deb3d0967e2e471fecf4 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 5 Jul 2021 10:28:59 +0200 Subject: [PATCH 011/114] Assert importlib specs --- src/rosbags/serde/utils.py | 4 +++- src/rosbags/typesys/register.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rosbags/serde/utils.py b/src/rosbags/serde/utils.py index 15362b23..7ee18e49 100644 --- a/src/rosbags/serde/utils.py +++ b/src/rosbags/serde/utils.py @@ -98,6 +98,8 @@ def compile_lines(lines: List[str]) -> ModuleType: Compiled and loaded module. """ - module = module_from_spec(spec_from_loader('tmpmod', loader=None)) + spec = spec_from_loader('tmpmod', loader=None) + assert spec + module = module_from_spec(spec) exec('\n'.join(lines), module.__dict__) # pylint: disable=exec-used return module diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index 4a4b07af..875f930e 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -92,6 +92,7 @@ def register_types(typs: Typesdict) -> None: code = generate_python_code(typs) name = 'rosbags.usertypes' spec = spec_from_loader(name, loader=None) + assert spec module = module_from_spec(spec) sys.modules[name] = module exec(code, module.__dict__) # pylint: disable=exec-used From 1d00fa317dde3dad6f802924d6cacdf35f25e611 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 5 Jul 2021 11:16:38 +0200 Subject: [PATCH 012/114] Fix msg parsing on non-POSIX platforms --- src/rosbags/typesys/msg.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 52943a43..7242d12a 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -12,7 +12,7 @@ Rosbag1 connection information. from __future__ import annotations -from pathlib import Path +from pathlib import PurePosixPath as Path from typing import TYPE_CHECKING from .base import Nodetype, parse_message_definition @@ -85,8 +85,8 @@ def normalize_msgtype(name: str) -> str: """ path = Path(name) if path.parent.name != 'msg': - return str(path.parent / 'msg' / path.name) - return name + path = path.parent / 'msg' / path.name + return str(path) def normalize_fieldtype(field: Any, names: List[str]): From d83e9aaaae1bfbb5b40c6c0d7539988d083b837e Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sat, 3 Jul 2021 13:03:58 +0200 Subject: [PATCH 013/114] Support multi-line comments in idl files --- src/rosbags/typesys/idl.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index acc6bafa..b9542272 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -34,7 +34,8 @@ definition / type_dcl ';' comment - = r'[/][/][^\n]*' + = r'/\*.*?\*/' + / r'[/][/][^\n]*' macro = ifndef From aba557c0153ea401deceedc2f0488e191b91a7bf Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sat, 3 Jul 2021 13:46:04 +0200 Subject: [PATCH 014/114] Remove non-default ROS2 message types --- src/rosbags/typesys/types.py | 344 ----------------------------------- 1 file changed, 344 deletions(-) diff --git a/src/rosbags/typesys/types.py b/src/rosbags/typesys/types.py index 3c484174..861d911c 100644 --- a/src/rosbags/typesys/types.py +++ b/src/rosbags/typesys/types.py @@ -1326,216 +1326,6 @@ class visualization_msgs__msg__InteractiveMarkerPose: name: Any -@dataclass -class autoware_auto_msgs__msg__BoundingBox: - """Class for autoware_auto_msgs/msg/BoundingBox.""" - - centroid: Any - size: Any - orientation: Any - velocity: Any - heading: Any - heading_rate: Any - corners: Any - variance: Any - value: Any - vehicle_label: Any - signal_label: Any - class_likelihood: Any - - -@dataclass -class autoware_auto_msgs__msg__HADMapBin: - """Class for autoware_auto_msgs/msg/HADMapBin.""" - - header: Any - map_format: Any - format_version: Any - map_version: Any - data: Any - - -@dataclass -class autoware_auto_msgs__msg__Quaternion32: - """Class for autoware_auto_msgs/msg/Quaternion32.""" - - x: Any - y: Any - z: Any - w: Any - - -@dataclass -class autoware_auto_msgs__msg__Trajectory: - """Class for autoware_auto_msgs/msg/Trajectory.""" - - header: Any - points: Any - - -@dataclass -class autoware_auto_msgs__msg__Route: - """Class for autoware_auto_msgs/msg/Route.""" - - header: Any - start_point: Any - goal_point: Any - primitives: Any - - -@dataclass -class autoware_auto_msgs__msg__MapPrimitive: - """Class for autoware_auto_msgs/msg/MapPrimitive.""" - - id: Any - primitive_type: Any - - -@dataclass -class autoware_auto_msgs__msg__TrajectoryPoint: - """Class for autoware_auto_msgs/msg/TrajectoryPoint.""" - - time_from_start: Any - x: Any - y: Any - heading: Any - longitudinal_velocity_mps: Any - lateral_velocity_mps: Any - acceleration_mps2: Any - heading_rate_rps: Any - front_wheel_angle_rad: Any - rear_wheel_angle_rad: Any - - -@dataclass -class autoware_auto_msgs__msg__Complex32: - """Class for autoware_auto_msgs/msg/Complex32.""" - - real: Any - imag: Any - - -@dataclass -class autoware_auto_msgs__msg__VehicleOdometry: - """Class for autoware_auto_msgs/msg/VehicleOdometry.""" - - stamp: Any - velocity_mps: Any - front_wheel_angle_rad: Any - rear_wheel_angle_rad: Any - - -@dataclass -class autoware_auto_msgs__msg__DiagnosticHeader: - """Class for autoware_auto_msgs/msg/DiagnosticHeader.""" - - name: Any - data_stamp: Any - computation_start: Any - runtime: Any - iterations: Any - - -@dataclass -class autoware_auto_msgs__msg__HighLevelControlCommand: - """Class for autoware_auto_msgs/msg/HighLevelControlCommand.""" - - stamp: Any - velocity_mps: Any - curvature: Any - - -@dataclass -class autoware_auto_msgs__msg__VehicleStateCommand: - """Class for autoware_auto_msgs/msg/VehicleStateCommand.""" - - stamp: Any - blinker: Any - headlight: Any - wiper: Any - gear: Any - mode: Any - hand_brake: Any - horn: Any - - -@dataclass -class autoware_auto_msgs__msg__PointClusters: - """Class for autoware_auto_msgs/msg/PointClusters.""" - - clusters: Any - - -@dataclass -class autoware_auto_msgs__msg__RawControlCommand: - """Class for autoware_auto_msgs/msg/RawControlCommand.""" - - stamp: Any - throttle: Any - brake: Any - front_steer: Any - rear_steer: Any - - -@dataclass -class autoware_auto_msgs__msg__VehicleStateReport: - """Class for autoware_auto_msgs/msg/VehicleStateReport.""" - - stamp: Any - fuel: Any - blinker: Any - headlight: Any - wiper: Any - gear: Any - mode: Any - hand_brake: Any - horn: Any - - -@dataclass -class autoware_auto_msgs__msg__ControlDiagnostic: - """Class for autoware_auto_msgs/msg/ControlDiagnostic.""" - - diag_header: Any - new_trajectory: Any - trajectory_source: Any - pose_source: Any - lateral_error_m: Any - longitudinal_error_m: Any - velocity_error_mps: Any - acceleration_error_mps2: Any - yaw_error_rad: Any - yaw_rate_error_rps: Any - - -@dataclass -class autoware_auto_msgs__msg__VehicleKinematicState: - """Class for autoware_auto_msgs/msg/VehicleKinematicState.""" - - header: Any - state: Any - delta: Any - - -@dataclass -class autoware_auto_msgs__msg__BoundingBoxArray: - """Class for autoware_auto_msgs/msg/BoundingBoxArray.""" - - header: Any - boxes: Any - - -@dataclass -class autoware_auto_msgs__msg__VehicleControlCommand: - """Class for autoware_auto_msgs/msg/VehicleControlCommand.""" - - stamp: Any - long_accel_mps2: Any - velocity_mps: Any - front_wheel_angle_rad: Any - rear_wheel_angle_rad: Any - - FIELDDEFS = { 'builtin_interfaces/msg/Time': [ ('sec', [1, 'int32']), @@ -2283,138 +2073,4 @@ FIELDDEFS = { ('pose', [2, 'geometry_msgs/msg/Pose']), ('name', [1, 'string']), ], - 'autoware_auto_msgs/msg/BoundingBox': [ - ('centroid', [2, 'geometry_msgs/msg/Point32']), - ('size', [2, 'geometry_msgs/msg/Point32']), - ('orientation', [2, 'autoware_auto_msgs/msg/Quaternion32']), - ('velocity', [1, 'float32']), - ('heading', [1, 'float32']), - ('heading_rate', [1, 'float32']), - ('corners', [3, 4, [2, 'geometry_msgs/msg/Point32']]), - ('variance', [3, 8, [1, 'float32']]), - ('value', [1, 'float32']), - ('vehicle_label', [1, 'uint8']), - ('signal_label', [1, 'uint8']), - ('class_likelihood', [1, 'float32']), - ], - 'autoware_auto_msgs/msg/HADMapBin': [ - ('header', [2, 'std_msgs/msg/Header']), - ('map_format', [1, 'uint8']), - ('format_version', [1, 'string']), - ('map_version', [1, 'string']), - ('data', [4, [1, 'uint8']]), - ], - 'autoware_auto_msgs/msg/Quaternion32': [ - ('x', [1, 'float32']), - ('y', [1, 'float32']), - ('z', [1, 'float32']), - ('w', [1, 'float32']), - ], - 'autoware_auto_msgs/msg/Trajectory': [ - ('header', [2, 'std_msgs/msg/Header']), - ('points', [4, [2, 'autoware_auto_msgs/msg/TrajectoryPoint']]), - ], - 'autoware_auto_msgs/msg/Route': [ - ('header', [2, 'std_msgs/msg/Header']), - ('start_point', [2, 'autoware_auto_msgs/msg/TrajectoryPoint']), - ('goal_point', [2, 'autoware_auto_msgs/msg/TrajectoryPoint']), - ('primitives', [4, [2, 'autoware_auto_msgs/msg/MapPrimitive']]), - ], - 'autoware_auto_msgs/msg/MapPrimitive': [ - ('id', [1, 'int64']), - ('primitive_type', [1, 'string']), - ], - 'autoware_auto_msgs/msg/TrajectoryPoint': [ - ('time_from_start', [2, 'builtin_interfaces/msg/Duration']), - ('x', [1, 'float32']), - ('y', [1, 'float32']), - ('heading', [2, 'autoware_auto_msgs/msg/Complex32']), - ('longitudinal_velocity_mps', [1, 'float32']), - ('lateral_velocity_mps', [1, 'float32']), - ('acceleration_mps2', [1, 'float32']), - ('heading_rate_rps', [1, 'float32']), - ('front_wheel_angle_rad', [1, 'float32']), - ('rear_wheel_angle_rad', [1, 'float32']), - ], - 'autoware_auto_msgs/msg/Complex32': [ - ('real', [1, 'float32']), - ('imag', [1, 'float32']), - ], - 'autoware_auto_msgs/msg/VehicleOdometry': [ - ('stamp', [2, 'builtin_interfaces/msg/Time']), - ('velocity_mps', [1, 'float32']), - ('front_wheel_angle_rad', [1, 'float32']), - ('rear_wheel_angle_rad', [1, 'float32']), - ], - 'autoware_auto_msgs/msg/DiagnosticHeader': [ - ('name', [1, 'string', [6, 256]]), - ('data_stamp', [2, 'builtin_interfaces/msg/Time']), - ('computation_start', [2, 'builtin_interfaces/msg/Time']), - ('runtime', [2, 'builtin_interfaces/msg/Duration']), - ('iterations', [1, 'uint32']), - ], - 'autoware_auto_msgs/msg/HighLevelControlCommand': [ - ('stamp', [2, 'builtin_interfaces/msg/Time']), - ('velocity_mps', [1, 'float32']), - ('curvature', [1, 'float32']), - ], - 'autoware_auto_msgs/msg/VehicleStateCommand': [ - ('stamp', [2, 'builtin_interfaces/msg/Time']), - ('blinker', [1, 'uint8']), - ('headlight', [1, 'uint8']), - ('wiper', [1, 'uint8']), - ('gear', [1, 'uint8']), - ('mode', [1, 'uint8']), - ('hand_brake', [1, 'bool']), - ('horn', [1, 'bool']), - ], - 'autoware_auto_msgs/msg/PointClusters': [ - ('clusters', [4, [2, 'sensor_msgs/msg/PointCloud2']]), - ], - 'autoware_auto_msgs/msg/RawControlCommand': [ - ('stamp', [2, 'builtin_interfaces/msg/Time']), - ('throttle', [1, 'uint32']), - ('brake', [1, 'uint32']), - ('front_steer', [1, 'int32']), - ('rear_steer', [1, 'int32']), - ], - 'autoware_auto_msgs/msg/VehicleStateReport': [ - ('stamp', [2, 'builtin_interfaces/msg/Time']), - ('fuel', [1, 'uint8']), - ('blinker', [1, 'uint8']), - ('headlight', [1, 'uint8']), - ('wiper', [1, 'uint8']), - ('gear', [1, 'uint8']), - ('mode', [1, 'uint8']), - ('hand_brake', [1, 'bool']), - ('horn', [1, 'bool']), - ], - 'autoware_auto_msgs/msg/ControlDiagnostic': [ - ('diag_header', [2, 'autoware_auto_msgs/msg/DiagnosticHeader']), - ('new_trajectory', [1, 'bool']), - ('trajectory_source', [1, 'string', [6, 256]]), - ('pose_source', [1, 'string', [6, 256]]), - ('lateral_error_m', [1, 'float32']), - ('longitudinal_error_m', [1, 'float32']), - ('velocity_error_mps', [1, 'float32']), - ('acceleration_error_mps2', [1, 'float32']), - ('yaw_error_rad', [1, 'float32']), - ('yaw_rate_error_rps', [1, 'float32']), - ], - 'autoware_auto_msgs/msg/VehicleKinematicState': [ - ('header', [2, 'std_msgs/msg/Header']), - ('state', [2, 'autoware_auto_msgs/msg/TrajectoryPoint']), - ('delta', [2, 'geometry_msgs/msg/Transform']), - ], - 'autoware_auto_msgs/msg/BoundingBoxArray': [ - ('header', [2, 'std_msgs/msg/Header']), - ('boxes', [4, [2, 'autoware_auto_msgs/msg/BoundingBox']]), - ], - 'autoware_auto_msgs/msg/VehicleControlCommand': [ - ('stamp', [2, 'builtin_interfaces/msg/Time']), - ('long_accel_mps2', [1, 'float32']), - ('velocity_mps', [1, 'float32']), - ('front_wheel_angle_rad', [1, 'float32']), - ('rear_wheel_angle_rad', [1, 'float32']), - ], } From 24cc3e0c2f6a1a17d6f34c42cb3690f3bb0fb3f1 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 5 Jul 2021 10:30:05 +0200 Subject: [PATCH 015/114] Add type hints to message classes --- src/rosbags/typesys/register.py | 35 +- src/rosbags/typesys/types.py | 931 ++++++++++++++++---------------- 2 files changed, 500 insertions(+), 466 deletions(-) diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index 875f930e..59ead36d 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -5,16 +5,44 @@ from __future__ import annotations import json +import re import sys from importlib.util import module_from_spec, spec_from_loader from typing import TYPE_CHECKING from . import types -from .base import TypesysError +from .base import Nodetype, TypesysError if TYPE_CHECKING: from .base import Typesdict +INTLIKE = re.compile('^u?(bool|int|float)') + + +def get_typehint(desc: tuple) -> str: + """Get python type hint for field. + + Args: + desc: Field descriptor. + + Returns: + Type hint for field. + + """ + if desc[0] == Nodetype.BASE: + if match := INTLIKE.match(desc[1]): + return match.group(1) + return 'str' + + if desc[0] == Nodetype.NAME: + return desc[1].replace('/', '__') + + sub = desc[2 if desc[0] == Nodetype.ARRAY else 1] + if INTLIKE.match(sub[1]): + typ = 'bool8' if sub[1] == 'bool' else sub[1] + return f'numpy.ndarray[Any, numpy.dtype[numpy.{typ}]]' + return f'list[{get_typehint(sub)}]' + def generate_python_code(typs: Typesdict) -> str: """Generate python code from types dictionary. @@ -35,6 +63,7 @@ def generate_python_code(typs: Typesdict) -> str: '', '# flake8: noqa N801', '# pylint: disable=invalid-name,too-many-instance-attributes,too-many-lines', + '# pylint: disable=unsubscriptable-object', '', 'from __future__ import annotations', '', @@ -44,6 +73,8 @@ def generate_python_code(typs: Typesdict) -> str: 'if TYPE_CHECKING:', ' from typing import Any', '', + ' import numpy', + '', '', ] @@ -54,7 +85,7 @@ def generate_python_code(typs: Typesdict) -> str: f'class {pyname}:', f' """Class for {name}."""', '', - *[f' {fname[1]}: Any' for _, fname in fields], + *[f' {fname[1]}: {get_typehint(desc)}' for desc, fname in fields], ] lines += [ diff --git a/src/rosbags/typesys/types.py b/src/rosbags/typesys/types.py index 861d911c..f6c6263d 100644 --- a/src/rosbags/typesys/types.py +++ b/src/rosbags/typesys/types.py @@ -6,6 +6,7 @@ # flake8: noqa N801 # pylint: disable=invalid-name,too-many-instance-attributes,too-many-lines +# pylint: disable=unsubscriptable-object from __future__ import annotations @@ -15,1315 +16,1317 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import Any + import numpy + @dataclass class builtin_interfaces__msg__Time: """Class for builtin_interfaces/msg/Time.""" - sec: Any - nanosec: Any + sec: int + nanosec: int @dataclass class builtin_interfaces__msg__Duration: """Class for builtin_interfaces/msg/Duration.""" - sec: Any - nanosec: Any + sec: int + nanosec: int @dataclass class diagnostic_msgs__msg__DiagnosticStatus: """Class for diagnostic_msgs/msg/DiagnosticStatus.""" - level: Any - name: Any - message: Any - hardware_id: Any - values: Any + level: int + name: str + message: str + hardware_id: str + values: list[diagnostic_msgs__msg__KeyValue] @dataclass class diagnostic_msgs__msg__DiagnosticArray: """Class for diagnostic_msgs/msg/DiagnosticArray.""" - header: Any - status: Any + header: std_msgs__msg__Header + status: list[diagnostic_msgs__msg__DiagnosticStatus] @dataclass class diagnostic_msgs__msg__KeyValue: """Class for diagnostic_msgs/msg/KeyValue.""" - key: Any - value: Any + key: str + value: str @dataclass class geometry_msgs__msg__AccelWithCovariance: """Class for geometry_msgs/msg/AccelWithCovariance.""" - accel: Any - covariance: Any + accel: geometry_msgs__msg__Accel + covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] @dataclass class geometry_msgs__msg__Point32: """Class for geometry_msgs/msg/Point32.""" - x: Any - y: Any - z: Any + x: float + y: float + z: float @dataclass class geometry_msgs__msg__Vector3: """Class for geometry_msgs/msg/Vector3.""" - x: Any - y: Any - z: Any + x: float + y: float + z: float @dataclass class geometry_msgs__msg__Inertia: """Class for geometry_msgs/msg/Inertia.""" - m: Any - com: Any - ixx: Any - ixy: Any - ixz: Any - iyy: Any - iyz: Any - izz: Any + m: float + com: geometry_msgs__msg__Vector3 + ixx: float + ixy: float + ixz: float + iyy: float + iyz: float + izz: float @dataclass class geometry_msgs__msg__PoseWithCovarianceStamped: """Class for geometry_msgs/msg/PoseWithCovarianceStamped.""" - header: Any - pose: Any + header: std_msgs__msg__Header + pose: geometry_msgs__msg__PoseWithCovariance @dataclass class geometry_msgs__msg__Twist: """Class for geometry_msgs/msg/Twist.""" - linear: Any - angular: Any + linear: geometry_msgs__msg__Vector3 + angular: geometry_msgs__msg__Vector3 @dataclass class geometry_msgs__msg__Pose: """Class for geometry_msgs/msg/Pose.""" - position: Any - orientation: Any + position: geometry_msgs__msg__Point + orientation: geometry_msgs__msg__Quaternion @dataclass class geometry_msgs__msg__Point: """Class for geometry_msgs/msg/Point.""" - x: Any - y: Any - z: Any + x: float + y: float + z: float @dataclass class geometry_msgs__msg__Vector3Stamped: """Class for geometry_msgs/msg/Vector3Stamped.""" - header: Any - vector: Any + header: std_msgs__msg__Header + vector: geometry_msgs__msg__Vector3 @dataclass class geometry_msgs__msg__Transform: """Class for geometry_msgs/msg/Transform.""" - translation: Any - rotation: Any + translation: geometry_msgs__msg__Vector3 + rotation: geometry_msgs__msg__Quaternion @dataclass class geometry_msgs__msg__PolygonStamped: """Class for geometry_msgs/msg/PolygonStamped.""" - header: Any - polygon: Any + header: std_msgs__msg__Header + polygon: geometry_msgs__msg__Polygon @dataclass class geometry_msgs__msg__Quaternion: """Class for geometry_msgs/msg/Quaternion.""" - x: Any - y: Any - z: Any - w: Any + x: float + y: float + z: float + w: float @dataclass class geometry_msgs__msg__Pose2D: """Class for geometry_msgs/msg/Pose2D.""" - x: Any - y: Any - theta: Any + x: float + y: float + theta: float @dataclass class geometry_msgs__msg__InertiaStamped: """Class for geometry_msgs/msg/InertiaStamped.""" - header: Any - inertia: Any + header: std_msgs__msg__Header + inertia: geometry_msgs__msg__Inertia @dataclass class geometry_msgs__msg__TwistStamped: """Class for geometry_msgs/msg/TwistStamped.""" - header: Any - twist: Any + header: std_msgs__msg__Header + twist: geometry_msgs__msg__Twist @dataclass class geometry_msgs__msg__PoseStamped: """Class for geometry_msgs/msg/PoseStamped.""" - header: Any - pose: Any + header: std_msgs__msg__Header + pose: geometry_msgs__msg__Pose @dataclass class geometry_msgs__msg__PointStamped: """Class for geometry_msgs/msg/PointStamped.""" - header: Any - point: Any + header: std_msgs__msg__Header + point: geometry_msgs__msg__Point @dataclass class geometry_msgs__msg__Polygon: """Class for geometry_msgs/msg/Polygon.""" - points: Any + points: list[geometry_msgs__msg__Point32] @dataclass class geometry_msgs__msg__PoseArray: """Class for geometry_msgs/msg/PoseArray.""" - header: Any - poses: Any + header: std_msgs__msg__Header + poses: list[geometry_msgs__msg__Pose] @dataclass class geometry_msgs__msg__AccelStamped: """Class for geometry_msgs/msg/AccelStamped.""" - header: Any - accel: Any + header: std_msgs__msg__Header + accel: geometry_msgs__msg__Accel @dataclass class geometry_msgs__msg__TwistWithCovarianceStamped: """Class for geometry_msgs/msg/TwistWithCovarianceStamped.""" - header: Any - twist: Any + header: std_msgs__msg__Header + twist: geometry_msgs__msg__TwistWithCovariance @dataclass class geometry_msgs__msg__QuaternionStamped: """Class for geometry_msgs/msg/QuaternionStamped.""" - header: Any - quaternion: Any + header: std_msgs__msg__Header + quaternion: geometry_msgs__msg__Quaternion @dataclass class geometry_msgs__msg__WrenchStamped: """Class for geometry_msgs/msg/WrenchStamped.""" - header: Any - wrench: Any + header: std_msgs__msg__Header + wrench: geometry_msgs__msg__Wrench @dataclass class geometry_msgs__msg__AccelWithCovarianceStamped: """Class for geometry_msgs/msg/AccelWithCovarianceStamped.""" - header: Any - accel: Any + header: std_msgs__msg__Header + accel: geometry_msgs__msg__AccelWithCovariance @dataclass class geometry_msgs__msg__PoseWithCovariance: """Class for geometry_msgs/msg/PoseWithCovariance.""" - pose: Any - covariance: Any + pose: geometry_msgs__msg__Pose + covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] @dataclass class geometry_msgs__msg__Wrench: """Class for geometry_msgs/msg/Wrench.""" - force: Any - torque: Any + force: geometry_msgs__msg__Vector3 + torque: geometry_msgs__msg__Vector3 @dataclass class geometry_msgs__msg__TransformStamped: """Class for geometry_msgs/msg/TransformStamped.""" - header: Any - child_frame_id: Any - transform: Any + header: std_msgs__msg__Header + child_frame_id: str + transform: geometry_msgs__msg__Transform @dataclass class geometry_msgs__msg__Accel: """Class for geometry_msgs/msg/Accel.""" - linear: Any - angular: Any + linear: geometry_msgs__msg__Vector3 + angular: geometry_msgs__msg__Vector3 @dataclass class geometry_msgs__msg__TwistWithCovariance: """Class for geometry_msgs/msg/TwistWithCovariance.""" - twist: Any - covariance: Any + twist: geometry_msgs__msg__Twist + covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] @dataclass class libstatistics_collector__msg__DummyMessage: """Class for libstatistics_collector/msg/DummyMessage.""" - header: Any + header: std_msgs__msg__Header @dataclass class lifecycle_msgs__msg__TransitionDescription: """Class for lifecycle_msgs/msg/TransitionDescription.""" - transition: Any - start_state: Any - goal_state: Any + transition: lifecycle_msgs__msg__Transition + start_state: lifecycle_msgs__msg__State + goal_state: lifecycle_msgs__msg__State @dataclass class lifecycle_msgs__msg__State: """Class for lifecycle_msgs/msg/State.""" - id: Any - label: Any + id: int + label: str @dataclass class lifecycle_msgs__msg__TransitionEvent: """Class for lifecycle_msgs/msg/TransitionEvent.""" - timestamp: Any - transition: Any - start_state: Any - goal_state: Any + timestamp: int + transition: lifecycle_msgs__msg__Transition + start_state: lifecycle_msgs__msg__State + goal_state: lifecycle_msgs__msg__State @dataclass class lifecycle_msgs__msg__Transition: """Class for lifecycle_msgs/msg/Transition.""" - id: Any - label: Any + id: int + label: str @dataclass class nav_msgs__msg__MapMetaData: """Class for nav_msgs/msg/MapMetaData.""" - map_load_time: Any - resolution: Any - width: Any - height: Any - origin: Any + map_load_time: builtin_interfaces__msg__Time + resolution: float + width: int + height: int + origin: geometry_msgs__msg__Pose @dataclass class nav_msgs__msg__GridCells: """Class for nav_msgs/msg/GridCells.""" - header: Any - cell_width: Any - cell_height: Any - cells: Any + header: std_msgs__msg__Header + cell_width: float + cell_height: float + cells: list[geometry_msgs__msg__Point] @dataclass class nav_msgs__msg__Odometry: """Class for nav_msgs/msg/Odometry.""" - header: Any - child_frame_id: Any - pose: Any - twist: Any + header: std_msgs__msg__Header + child_frame_id: str + pose: geometry_msgs__msg__PoseWithCovariance + twist: geometry_msgs__msg__TwistWithCovariance @dataclass class nav_msgs__msg__Path: """Class for nav_msgs/msg/Path.""" - header: Any - poses: Any + header: std_msgs__msg__Header + poses: list[geometry_msgs__msg__PoseStamped] @dataclass class nav_msgs__msg__OccupancyGrid: """Class for nav_msgs/msg/OccupancyGrid.""" - header: Any - info: Any - data: Any + header: std_msgs__msg__Header + info: nav_msgs__msg__MapMetaData + data: numpy.ndarray[Any, numpy.dtype[numpy.int8]] @dataclass class rcl_interfaces__msg__ListParametersResult: """Class for rcl_interfaces/msg/ListParametersResult.""" - names: Any - prefixes: Any + names: list[str] + prefixes: list[str] @dataclass class rcl_interfaces__msg__ParameterType: """Class for rcl_interfaces/msg/ParameterType.""" - structure_needs_at_least_one_member: Any + structure_needs_at_least_one_member: int @dataclass class rcl_interfaces__msg__ParameterEventDescriptors: """Class for rcl_interfaces/msg/ParameterEventDescriptors.""" - new_parameters: Any - changed_parameters: Any - deleted_parameters: Any + new_parameters: list[rcl_interfaces__msg__ParameterDescriptor] + changed_parameters: list[rcl_interfaces__msg__ParameterDescriptor] + deleted_parameters: list[rcl_interfaces__msg__ParameterDescriptor] @dataclass class rcl_interfaces__msg__ParameterEvent: """Class for rcl_interfaces/msg/ParameterEvent.""" - stamp: Any - node: Any - new_parameters: Any - changed_parameters: Any - deleted_parameters: Any + stamp: builtin_interfaces__msg__Time + node: str + new_parameters: list[rcl_interfaces__msg__Parameter] + changed_parameters: list[rcl_interfaces__msg__Parameter] + deleted_parameters: list[rcl_interfaces__msg__Parameter] @dataclass class rcl_interfaces__msg__IntegerRange: """Class for rcl_interfaces/msg/IntegerRange.""" - from_value: Any - to_value: Any - step: Any + from_value: int + to_value: int + step: int @dataclass class rcl_interfaces__msg__Parameter: """Class for rcl_interfaces/msg/Parameter.""" - name: Any - value: Any + name: str + value: rcl_interfaces__msg__ParameterValue @dataclass class rcl_interfaces__msg__ParameterValue: """Class for rcl_interfaces/msg/ParameterValue.""" - type: Any - bool_value: Any - integer_value: Any - double_value: Any - string_value: Any - byte_array_value: Any - bool_array_value: Any - integer_array_value: Any - double_array_value: Any - string_array_value: Any + type: int + bool_value: bool + integer_value: int + double_value: float + string_value: str + byte_array_value: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + bool_array_value: numpy.ndarray[Any, numpy.dtype[numpy.bool8]] + integer_array_value: numpy.ndarray[Any, numpy.dtype[numpy.int64]] + double_array_value: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + string_array_value: list[str] @dataclass class rcl_interfaces__msg__FloatingPointRange: """Class for rcl_interfaces/msg/FloatingPointRange.""" - from_value: Any - to_value: Any - step: Any + from_value: float + to_value: float + step: float @dataclass class rcl_interfaces__msg__SetParametersResult: """Class for rcl_interfaces/msg/SetParametersResult.""" - successful: Any - reason: Any + successful: bool + reason: str @dataclass class rcl_interfaces__msg__Log: """Class for rcl_interfaces/msg/Log.""" - stamp: Any - level: Any - name: Any - msg: Any - file: Any - function: Any - line: Any + stamp: builtin_interfaces__msg__Time + level: int + name: str + msg: str + file: str + function: str + line: int @dataclass class rcl_interfaces__msg__ParameterDescriptor: """Class for rcl_interfaces/msg/ParameterDescriptor.""" - name: Any - type: Any - description: Any - additional_constraints: Any - read_only: Any - floating_point_range: Any - integer_range: Any + name: str + type: int + description: str + additional_constraints: str + read_only: bool + floating_point_range: list[rcl_interfaces__msg__FloatingPointRange] + integer_range: list[rcl_interfaces__msg__IntegerRange] @dataclass class rmw_dds_common__msg__Gid: """Class for rmw_dds_common/msg/Gid.""" - data: Any + data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] @dataclass class rmw_dds_common__msg__NodeEntitiesInfo: """Class for rmw_dds_common/msg/NodeEntitiesInfo.""" - node_namespace: Any - node_name: Any - reader_gid_seq: Any - writer_gid_seq: Any + node_namespace: str + node_name: str + reader_gid_seq: list[rmw_dds_common__msg__Gid] + writer_gid_seq: list[rmw_dds_common__msg__Gid] @dataclass class rmw_dds_common__msg__ParticipantEntitiesInfo: """Class for rmw_dds_common/msg/ParticipantEntitiesInfo.""" - gid: Any - node_entities_info_seq: Any + gid: rmw_dds_common__msg__Gid + node_entities_info_seq: list[rmw_dds_common__msg__NodeEntitiesInfo] @dataclass class rosgraph_msgs__msg__Clock: """Class for rosgraph_msgs/msg/Clock.""" - clock: Any + clock: builtin_interfaces__msg__Time @dataclass class sensor_msgs__msg__Temperature: """Class for sensor_msgs/msg/Temperature.""" - header: Any - temperature: Any - variance: Any + header: std_msgs__msg__Header + temperature: float + variance: float @dataclass class sensor_msgs__msg__Range: """Class for sensor_msgs/msg/Range.""" - header: Any - radiation_type: Any - field_of_view: Any - min_range: Any - max_range: Any - range: Any + header: std_msgs__msg__Header + radiation_type: int + field_of_view: float + min_range: float + max_range: float + range: float @dataclass class sensor_msgs__msg__RegionOfInterest: """Class for sensor_msgs/msg/RegionOfInterest.""" - x_offset: Any - y_offset: Any - height: Any - width: Any - do_rectify: Any + x_offset: int + y_offset: int + height: int + width: int + do_rectify: bool @dataclass class sensor_msgs__msg__JoyFeedbackArray: """Class for sensor_msgs/msg/JoyFeedbackArray.""" - array: Any + array: list[sensor_msgs__msg__JoyFeedback] @dataclass class sensor_msgs__msg__TimeReference: """Class for sensor_msgs/msg/TimeReference.""" - header: Any - time_ref: Any - source: Any + header: std_msgs__msg__Header + time_ref: builtin_interfaces__msg__Time + source: str @dataclass class sensor_msgs__msg__CompressedImage: """Class for sensor_msgs/msg/CompressedImage.""" - header: Any - format: Any - data: Any + header: std_msgs__msg__Header + format: str + data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] @dataclass class sensor_msgs__msg__MultiEchoLaserScan: """Class for sensor_msgs/msg/MultiEchoLaserScan.""" - header: Any - angle_min: Any - angle_max: Any - angle_increment: Any - time_increment: Any - scan_time: Any - range_min: Any - range_max: Any - ranges: Any - intensities: Any + header: std_msgs__msg__Header + angle_min: float + angle_max: float + angle_increment: float + time_increment: float + scan_time: float + range_min: float + range_max: float + ranges: list[sensor_msgs__msg__LaserEcho] + intensities: list[sensor_msgs__msg__LaserEcho] @dataclass class sensor_msgs__msg__LaserEcho: """Class for sensor_msgs/msg/LaserEcho.""" - echoes: Any + echoes: numpy.ndarray[Any, numpy.dtype[numpy.float32]] @dataclass class sensor_msgs__msg__ChannelFloat32: """Class for sensor_msgs/msg/ChannelFloat32.""" - name: Any - values: Any + name: str + values: numpy.ndarray[Any, numpy.dtype[numpy.float32]] @dataclass class sensor_msgs__msg__CameraInfo: """Class for sensor_msgs/msg/CameraInfo.""" - header: Any - height: Any - width: Any - distortion_model: Any - d: Any - k: Any - r: Any - p: Any - binning_x: Any - binning_y: Any - roi: Any + header: std_msgs__msg__Header + height: int + width: int + distortion_model: str + d: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + k: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + r: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + p: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + binning_x: int + binning_y: int + roi: sensor_msgs__msg__RegionOfInterest @dataclass class sensor_msgs__msg__RelativeHumidity: """Class for sensor_msgs/msg/RelativeHumidity.""" - header: Any - relative_humidity: Any - variance: Any + header: std_msgs__msg__Header + relative_humidity: float + variance: float @dataclass class sensor_msgs__msg__FluidPressure: """Class for sensor_msgs/msg/FluidPressure.""" - header: Any - fluid_pressure: Any - variance: Any + header: std_msgs__msg__Header + fluid_pressure: float + variance: float @dataclass class sensor_msgs__msg__LaserScan: """Class for sensor_msgs/msg/LaserScan.""" - header: Any - angle_min: Any - angle_max: Any - angle_increment: Any - time_increment: Any - scan_time: Any - range_min: Any - range_max: Any - ranges: Any - intensities: Any + header: std_msgs__msg__Header + angle_min: float + angle_max: float + angle_increment: float + time_increment: float + scan_time: float + range_min: float + range_max: float + ranges: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + intensities: numpy.ndarray[Any, numpy.dtype[numpy.float32]] @dataclass class sensor_msgs__msg__BatteryState: """Class for sensor_msgs/msg/BatteryState.""" - header: Any - voltage: Any - temperature: Any - current: Any - charge: Any - capacity: Any - design_capacity: Any - percentage: Any - power_supply_status: Any - power_supply_health: Any - power_supply_technology: Any - present: Any - cell_voltage: Any - cell_temperature: Any - location: Any - serial_number: Any + header: std_msgs__msg__Header + voltage: float + temperature: float + current: float + charge: float + capacity: float + design_capacity: float + percentage: float + power_supply_status: int + power_supply_health: int + power_supply_technology: int + present: bool + cell_voltage: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + cell_temperature: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + location: str + serial_number: str @dataclass class sensor_msgs__msg__Image: """Class for sensor_msgs/msg/Image.""" - header: Any - height: Any - width: Any - encoding: Any - is_bigendian: Any - step: Any - data: Any + header: std_msgs__msg__Header + height: int + width: int + encoding: str + is_bigendian: int + step: int + data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] @dataclass class sensor_msgs__msg__PointCloud: """Class for sensor_msgs/msg/PointCloud.""" - header: Any - points: Any - channels: Any + header: std_msgs__msg__Header + points: list[geometry_msgs__msg__Point32] + channels: list[sensor_msgs__msg__ChannelFloat32] @dataclass class sensor_msgs__msg__Imu: """Class for sensor_msgs/msg/Imu.""" - header: Any - orientation: Any - orientation_covariance: Any - angular_velocity: Any - angular_velocity_covariance: Any - linear_acceleration: Any - linear_acceleration_covariance: Any + header: std_msgs__msg__Header + orientation: geometry_msgs__msg__Quaternion + orientation_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + angular_velocity: geometry_msgs__msg__Vector3 + angular_velocity_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + linear_acceleration: geometry_msgs__msg__Vector3 + linear_acceleration_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] @dataclass class sensor_msgs__msg__NavSatStatus: """Class for sensor_msgs/msg/NavSatStatus.""" - status: Any - service: Any + status: int + service: int @dataclass class sensor_msgs__msg__Illuminance: """Class for sensor_msgs/msg/Illuminance.""" - header: Any - illuminance: Any - variance: Any + header: std_msgs__msg__Header + illuminance: float + variance: float @dataclass class sensor_msgs__msg__Joy: """Class for sensor_msgs/msg/Joy.""" - header: Any - axes: Any - buttons: Any + header: std_msgs__msg__Header + axes: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + buttons: numpy.ndarray[Any, numpy.dtype[numpy.int32]] @dataclass class sensor_msgs__msg__NavSatFix: """Class for sensor_msgs/msg/NavSatFix.""" - header: Any - status: Any - latitude: Any - longitude: Any - altitude: Any - position_covariance: Any - position_covariance_type: Any + header: std_msgs__msg__Header + status: sensor_msgs__msg__NavSatStatus + latitude: float + longitude: float + altitude: float + position_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + position_covariance_type: int @dataclass class sensor_msgs__msg__MultiDOFJointState: """Class for sensor_msgs/msg/MultiDOFJointState.""" - header: Any - joint_names: Any - transforms: Any - twist: Any - wrench: Any + header: std_msgs__msg__Header + joint_names: list[str] + transforms: list[geometry_msgs__msg__Transform] + twist: list[geometry_msgs__msg__Twist] + wrench: list[geometry_msgs__msg__Wrench] @dataclass class sensor_msgs__msg__MagneticField: """Class for sensor_msgs/msg/MagneticField.""" - header: Any - magnetic_field: Any - magnetic_field_covariance: Any + header: std_msgs__msg__Header + magnetic_field: geometry_msgs__msg__Vector3 + magnetic_field_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] @dataclass class sensor_msgs__msg__JointState: """Class for sensor_msgs/msg/JointState.""" - header: Any - name: Any - position: Any - velocity: Any - effort: Any + header: std_msgs__msg__Header + name: list[str] + position: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + velocity: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + effort: numpy.ndarray[Any, numpy.dtype[numpy.float64]] @dataclass class sensor_msgs__msg__PointField: """Class for sensor_msgs/msg/PointField.""" - name: Any - offset: Any - datatype: Any - count: Any + name: str + offset: int + datatype: int + count: int @dataclass class sensor_msgs__msg__PointCloud2: """Class for sensor_msgs/msg/PointCloud2.""" - header: Any - height: Any - width: Any - fields: Any - is_bigendian: Any - point_step: Any - row_step: Any - data: Any - is_dense: Any + header: std_msgs__msg__Header + height: int + width: int + fields: list[sensor_msgs__msg__PointField] + is_bigendian: bool + point_step: int + row_step: int + data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + is_dense: bool @dataclass class sensor_msgs__msg__JoyFeedback: """Class for sensor_msgs/msg/JoyFeedback.""" - type: Any - id: Any - intensity: Any + type: int + id: int + intensity: float @dataclass class shape_msgs__msg__SolidPrimitive: """Class for shape_msgs/msg/SolidPrimitive.""" - type: Any - dimensions: Any + type: int + dimensions: numpy.ndarray[Any, numpy.dtype[numpy.float64]] @dataclass class shape_msgs__msg__Mesh: """Class for shape_msgs/msg/Mesh.""" - triangles: Any - vertices: Any + triangles: list[shape_msgs__msg__MeshTriangle] + vertices: list[geometry_msgs__msg__Point] @dataclass class shape_msgs__msg__Plane: """Class for shape_msgs/msg/Plane.""" - coef: Any + coef: numpy.ndarray[Any, numpy.dtype[numpy.float64]] @dataclass class shape_msgs__msg__MeshTriangle: """Class for shape_msgs/msg/MeshTriangle.""" - vertex_indices: Any + vertex_indices: numpy.ndarray[Any, numpy.dtype[numpy.uint32]] @dataclass class statistics_msgs__msg__StatisticDataType: """Class for statistics_msgs/msg/StatisticDataType.""" - structure_needs_at_least_one_member: Any + structure_needs_at_least_one_member: int @dataclass class statistics_msgs__msg__StatisticDataPoint: """Class for statistics_msgs/msg/StatisticDataPoint.""" - data_type: Any - data: Any + data_type: int + data: float @dataclass class statistics_msgs__msg__MetricsMessage: """Class for statistics_msgs/msg/MetricsMessage.""" - measurement_source_name: Any - metrics_source: Any - unit: Any - window_start: Any - window_stop: Any - statistics: Any + measurement_source_name: str + metrics_source: str + unit: str + window_start: builtin_interfaces__msg__Time + window_stop: builtin_interfaces__msg__Time + statistics: list[statistics_msgs__msg__StatisticDataPoint] @dataclass class std_msgs__msg__UInt8: """Class for std_msgs/msg/UInt8.""" - data: Any + data: int @dataclass class std_msgs__msg__Float32MultiArray: """Class for std_msgs/msg/Float32MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.float32]] @dataclass class std_msgs__msg__Int8: """Class for std_msgs/msg/Int8.""" - data: Any + data: int @dataclass class std_msgs__msg__Empty: """Class for std_msgs/msg/Empty.""" - structure_needs_at_least_one_member: Any + structure_needs_at_least_one_member: int @dataclass class std_msgs__msg__String: """Class for std_msgs/msg/String.""" - data: Any + data: str @dataclass class std_msgs__msg__MultiArrayDimension: """Class for std_msgs/msg/MultiArrayDimension.""" - label: Any - size: Any - stride: Any + label: str + size: int + stride: int @dataclass class std_msgs__msg__UInt64: """Class for std_msgs/msg/UInt64.""" - data: Any + data: int @dataclass class std_msgs__msg__UInt16: """Class for std_msgs/msg/UInt16.""" - data: Any + data: int @dataclass class std_msgs__msg__Float32: """Class for std_msgs/msg/Float32.""" - data: Any + data: float @dataclass class std_msgs__msg__Int64: """Class for std_msgs/msg/Int64.""" - data: Any + data: int @dataclass class std_msgs__msg__Int16MultiArray: """Class for std_msgs/msg/Int16MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.int16]] @dataclass class std_msgs__msg__Int16: """Class for std_msgs/msg/Int16.""" - data: Any + data: int @dataclass class std_msgs__msg__Float64MultiArray: """Class for std_msgs/msg/Float64MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.float64]] @dataclass class std_msgs__msg__MultiArrayLayout: """Class for std_msgs/msg/MultiArrayLayout.""" - dim: Any - data_offset: Any + dim: list[std_msgs__msg__MultiArrayDimension] + data_offset: int @dataclass class std_msgs__msg__UInt32MultiArray: """Class for std_msgs/msg/UInt32MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.uint32]] @dataclass class std_msgs__msg__Header: """Class for std_msgs/msg/Header.""" - stamp: Any - frame_id: Any + stamp: builtin_interfaces__msg__Time + frame_id: str @dataclass class std_msgs__msg__ByteMultiArray: """Class for std_msgs/msg/ByteMultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] @dataclass class std_msgs__msg__Int8MultiArray: """Class for std_msgs/msg/Int8MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.int8]] @dataclass class std_msgs__msg__Float64: """Class for std_msgs/msg/Float64.""" - data: Any + data: float @dataclass class std_msgs__msg__UInt8MultiArray: """Class for std_msgs/msg/UInt8MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] @dataclass class std_msgs__msg__Byte: """Class for std_msgs/msg/Byte.""" - data: Any + data: int @dataclass class std_msgs__msg__Char: """Class for std_msgs/msg/Char.""" - data: Any + data: int @dataclass class std_msgs__msg__UInt64MultiArray: """Class for std_msgs/msg/UInt64MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.uint64]] @dataclass class std_msgs__msg__Int32MultiArray: """Class for std_msgs/msg/Int32MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.int32]] @dataclass class std_msgs__msg__ColorRGBA: """Class for std_msgs/msg/ColorRGBA.""" - r: Any - g: Any - b: Any - a: Any + r: float + g: float + b: float + a: float @dataclass class std_msgs__msg__Bool: """Class for std_msgs/msg/Bool.""" - data: Any + data: bool @dataclass class std_msgs__msg__UInt32: """Class for std_msgs/msg/UInt32.""" - data: Any + data: int @dataclass class std_msgs__msg__Int64MultiArray: """Class for std_msgs/msg/Int64MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.int64]] @dataclass class std_msgs__msg__Int32: """Class for std_msgs/msg/Int32.""" - data: Any + data: int @dataclass class std_msgs__msg__UInt16MultiArray: """Class for std_msgs/msg/UInt16MultiArray.""" - layout: Any - data: Any + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.uint16]] @dataclass class stereo_msgs__msg__DisparityImage: """Class for stereo_msgs/msg/DisparityImage.""" - header: Any - image: Any - f: Any - t: Any - valid_window: Any - min_disparity: Any - max_disparity: Any - delta_d: Any + header: std_msgs__msg__Header + image: sensor_msgs__msg__Image + f: float + t: float + valid_window: sensor_msgs__msg__RegionOfInterest + min_disparity: float + max_disparity: float + delta_d: float @dataclass class tf2_msgs__msg__TF2Error: """Class for tf2_msgs/msg/TF2Error.""" - error: Any - error_string: Any + error: int + error_string: str @dataclass class tf2_msgs__msg__TFMessage: """Class for tf2_msgs/msg/TFMessage.""" - transforms: Any + transforms: list[geometry_msgs__msg__TransformStamped] @dataclass class trajectory_msgs__msg__MultiDOFJointTrajectory: """Class for trajectory_msgs/msg/MultiDOFJointTrajectory.""" - header: Any - joint_names: Any - points: Any + header: std_msgs__msg__Header + joint_names: list[str] + points: list[trajectory_msgs__msg__MultiDOFJointTrajectoryPoint] @dataclass class trajectory_msgs__msg__JointTrajectory: """Class for trajectory_msgs/msg/JointTrajectory.""" - header: Any - joint_names: Any - points: Any + header: std_msgs__msg__Header + joint_names: list[str] + points: list[trajectory_msgs__msg__JointTrajectoryPoint] @dataclass class trajectory_msgs__msg__JointTrajectoryPoint: """Class for trajectory_msgs/msg/JointTrajectoryPoint.""" - positions: Any - velocities: Any - accelerations: Any - effort: Any - time_from_start: Any + positions: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + velocities: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + accelerations: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + effort: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + time_from_start: builtin_interfaces__msg__Duration @dataclass class trajectory_msgs__msg__MultiDOFJointTrajectoryPoint: """Class for trajectory_msgs/msg/MultiDOFJointTrajectoryPoint.""" - transforms: Any - velocities: Any - accelerations: Any - time_from_start: Any + transforms: list[geometry_msgs__msg__Transform] + velocities: list[geometry_msgs__msg__Twist] + accelerations: list[geometry_msgs__msg__Twist] + time_from_start: builtin_interfaces__msg__Duration @dataclass class unique_identifier_msgs__msg__UUID: """Class for unique_identifier_msgs/msg/UUID.""" - uuid: Any + uuid: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] @dataclass class visualization_msgs__msg__Marker: """Class for visualization_msgs/msg/Marker.""" - header: Any - ns: Any - id: Any - type: Any - action: Any - pose: Any - scale: Any - color: Any - lifetime: Any - frame_locked: Any - points: Any - colors: Any - text: Any - mesh_resource: Any - mesh_use_embedded_materials: Any + header: std_msgs__msg__Header + ns: str + id: int + type: int + action: int + pose: geometry_msgs__msg__Pose + scale: geometry_msgs__msg__Vector3 + color: std_msgs__msg__ColorRGBA + lifetime: builtin_interfaces__msg__Duration + frame_locked: bool + points: list[geometry_msgs__msg__Point] + colors: list[std_msgs__msg__ColorRGBA] + text: str + mesh_resource: str + mesh_use_embedded_materials: bool @dataclass class visualization_msgs__msg__InteractiveMarkerInit: """Class for visualization_msgs/msg/InteractiveMarkerInit.""" - server_id: Any - seq_num: Any - markers: Any + server_id: str + seq_num: int + markers: list[visualization_msgs__msg__InteractiveMarker] @dataclass class visualization_msgs__msg__MenuEntry: """Class for visualization_msgs/msg/MenuEntry.""" - id: Any - parent_id: Any - title: Any - command: Any - command_type: Any + id: int + parent_id: int + title: str + command: str + command_type: int @dataclass class visualization_msgs__msg__MarkerArray: """Class for visualization_msgs/msg/MarkerArray.""" - markers: Any + markers: list[visualization_msgs__msg__Marker] @dataclass class visualization_msgs__msg__InteractiveMarkerUpdate: """Class for visualization_msgs/msg/InteractiveMarkerUpdate.""" - server_id: Any - seq_num: Any - type: Any - markers: Any - poses: Any - erases: Any + server_id: str + seq_num: int + type: int + markers: list[visualization_msgs__msg__InteractiveMarker] + poses: list[visualization_msgs__msg__InteractiveMarkerPose] + erases: list[str] @dataclass class visualization_msgs__msg__InteractiveMarker: """Class for visualization_msgs/msg/InteractiveMarker.""" - header: Any - pose: Any - name: Any - description: Any - scale: Any - menu_entries: Any - controls: Any + header: std_msgs__msg__Header + pose: geometry_msgs__msg__Pose + name: str + description: str + scale: float + menu_entries: list[visualization_msgs__msg__MenuEntry] + controls: list[visualization_msgs__msg__InteractiveMarkerControl] @dataclass class visualization_msgs__msg__InteractiveMarkerFeedback: """Class for visualization_msgs/msg/InteractiveMarkerFeedback.""" - header: Any - client_id: Any - marker_name: Any - control_name: Any - event_type: Any - pose: Any - menu_entry_id: Any - mouse_point: Any - mouse_point_valid: Any + header: std_msgs__msg__Header + client_id: str + marker_name: str + control_name: str + event_type: int + pose: geometry_msgs__msg__Pose + menu_entry_id: int + mouse_point: geometry_msgs__msg__Point + mouse_point_valid: bool @dataclass class visualization_msgs__msg__ImageMarker: """Class for visualization_msgs/msg/ImageMarker.""" - header: Any - ns: Any - id: Any - type: Any - action: Any - position: Any - scale: Any - outline_color: Any - filled: Any - fill_color: Any - lifetime: Any - points: Any - outline_colors: Any + header: std_msgs__msg__Header + ns: str + id: int + type: int + action: int + position: geometry_msgs__msg__Point + scale: float + outline_color: std_msgs__msg__ColorRGBA + filled: int + fill_color: std_msgs__msg__ColorRGBA + lifetime: builtin_interfaces__msg__Duration + points: list[geometry_msgs__msg__Point] + outline_colors: list[std_msgs__msg__ColorRGBA] @dataclass class visualization_msgs__msg__InteractiveMarkerControl: """Class for visualization_msgs/msg/InteractiveMarkerControl.""" - name: Any - orientation: Any - orientation_mode: Any - interaction_mode: Any - always_visible: Any - markers: Any - independent_marker_orientation: Any - description: Any + name: str + orientation: geometry_msgs__msg__Quaternion + orientation_mode: int + interaction_mode: int + always_visible: bool + markers: list[visualization_msgs__msg__Marker] + independent_marker_orientation: bool + description: str @dataclass class visualization_msgs__msg__InteractiveMarkerPose: """Class for visualization_msgs/msg/InteractiveMarkerPose.""" - header: Any - pose: Any - name: Any + header: std_msgs__msg__Header + pose: geometry_msgs__msg__Pose + name: str FIELDDEFS = { From 77e68e81911278ba20311fc265d3b4a5a35c4015 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sat, 3 Jul 2021 13:52:03 +0200 Subject: [PATCH 016/114] Fix rv tuple order of messages() in docs --- docs/topics/rosbag1.rst | 4 ++-- docs/topics/rosbag2.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/topics/rosbag1.rst b/docs/topics/rosbag1.rst index e718188e..02ab7144 100644 --- a/docs/topics/rosbag1.rst +++ b/docs/topics/rosbag1.rst @@ -18,10 +18,10 @@ Instances of the :py:class:`Reader ` class are typically print(topic, info) # iterate over messages - for topic, msgtype, rawdata, timestamp in reader.messages(): + for topic, msgtype, timestamp, rawdata in reader.messages(): if topic == '/imu_raw/Imu': print(timestamp) # messages() accepts topic filters - for topic, msgtype, rawdata, timestamp in reader.messages(['/imu_raw/Imu']): + for topic, msgtype, timestamp, rawdata in reader.messages(['/imu_raw/Imu']): print(timestamp) diff --git a/docs/topics/rosbag2.rst b/docs/topics/rosbag2.rst index 17bf3129..68c7edb2 100644 --- a/docs/topics/rosbag2.rst +++ b/docs/topics/rosbag2.rst @@ -60,6 +60,6 @@ Instances of the :py:class:`Reader ` class are used to r print(msg.header.frame_id) # messages() accepts topic filters - for topic, msgtype, rawdata, timestamp in reader.messages(['/imu_raw/Imu']): + for topic, msgtype, timestamp, rawdata in reader.messages(['/imu_raw/Imu']): msg = deserialize_cdr(rawdata, msgtype) print(msg.header.frame_id) From c2bbeec7aa91cbf1690b4efc7e5505fd0885bad6 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sat, 3 Jul 2021 14:27:11 +0200 Subject: [PATCH 017/114] Make optional connection header fields available --- src/rosbags/rosbag1/reader.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 56661ad9..cdab0ef6 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -69,6 +69,8 @@ class Connection(NamedTuple): msgtype: str md5sum: str msgdef: str + callerid: Optional[str] + latching: Optional[int] indexes: List @@ -490,7 +492,19 @@ class Reader: md5sum = header.get_string('md5sum') msgdef = header.get_string('message_definition') - return conn, Connection(conn, topic, normalize_msgtype(typ), md5sum, msgdef, []) + callerid = header.get_string('callerid') if 'callerid' in header else None + latching = int(header.get_string('latching')) if 'latching' in header else None + + return conn, Connection( + conn, + topic, + normalize_msgtype(typ), + md5sum, + msgdef, + callerid, + latching, + [], + ) def read_chunk_info(self) -> ChunkInfo: """Read chunk info record from current position.""" From 5175c349aa0df7e08b2a4eed8e1c384affecbe37 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sat, 3 Jul 2021 17:26:49 +0200 Subject: [PATCH 018/114] Translate latching info to equivalent QoS settings --- src/rosbags/convert/converter.py | 27 ++++++++++++++++++++++++++- tests/test_convert.py | 22 ++++++++++++++++++---- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index f1e4e9f3..8c46464f 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -15,6 +15,24 @@ if TYPE_CHECKING: from pathlib import Path from typing import Any, Dict, Optional +LATCH = """ +- history: 3 + depth: 0 + reliability: 1 + durability: 1 + deadline: + sec: 2147483647 + nsec: 4294967295 + lifespan: + sec: 2147483647 + nsec: 4294967295 + liveliness: 1 + liveliness_lease_duration: + sec: 2147483647 + nsec: 4294967295 + avoid_ros_namespace_conventions: false +""".strip() + class ConverterError(Exception): """Converter Error.""" @@ -40,7 +58,14 @@ def convert(src: Path, dst: Optional[Path]) -> None: with Reader(src) as reader, Writer(dst) as writer: typs: Dict[str, Any] = {} for name, topic in reader.topics.items(): - writer.add_topic(name, topic.msgtype) + connection = next( # pragma: no branch + x for x in reader.connections.values() if x.topic == name + ) + writer.add_topic( + name, + topic.msgtype, + offered_qos_profiles=LATCH if connection.latching else '', + ) typs.update(get_types_from_msg(topic.msgdef, topic.msgtype)) register_types(typs) diff --git a/tests/test_convert.py b/tests/test_convert.py index 1d015f35..db3cefc8 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -4,12 +4,13 @@ import sys from pathlib import Path -from unittest.mock import Mock, patch +from unittest.mock import Mock, call, patch import pytest from rosbags.convert import ConverterError, convert from rosbags.convert.__main__ import main +from rosbags.convert.converter import LATCH from rosbags.rosbag1 import ReaderError from rosbags.rosbag2 import WriterError @@ -75,11 +76,17 @@ def test_convert(tmp_path: Path): patch('rosbags.convert.converter.register_types') as register_types, \ patch('rosbags.convert.converter.ros1_to_cdr') as ros1_to_cdr: + reader.return_value.__enter__.return_value.connections = { + 0: Mock(topic='/topic', latching=False), + 1: Mock(topic='/latched', latching=True), + } reader.return_value.__enter__.return_value.topics = { '/topic': Mock(msgtype='typ', msgdef='def'), + '/latched': Mock(msgtype='typ', msgdef='def'), } reader.return_value.__enter__.return_value.messages.return_value = [ ('/topic', 'typ', 42, b'\x42'), + ('/latched', 'typ', 43, b'\x43'), ] ros1_to_cdr.return_value = b'666' @@ -90,11 +97,18 @@ def test_convert(tmp_path: Path): reader.return_value.__enter__.return_value.messages.assert_called_with() writer.assert_called_with(Path('foo')) - writer.return_value.__enter__.return_value.add_topic.assert_called_with('/topic', 'typ') - writer.return_value.__enter__.return_value.write.assert_called_with('/topic', 42, b'666') + writer.return_value.__enter__.return_value.add_topic.assert_has_calls( + [ + call('/topic', 'typ', offered_qos_profiles=''), + call('/latched', 'typ', offered_qos_profiles=LATCH), + ], + ) + writer.return_value.__enter__.return_value.write.assert_has_calls( + [call('/topic', 42, b'666'), call('/latched', 43, b'666')], + ) register_types.assert_called_with({'typ': 'def'}) - ros1_to_cdr.assert_called_with(b'\x42', 'typ') + ros1_to_cdr.assert_has_calls([call(b'\x42', 'typ'), call(b'\x43', 'typ')]) ros1_to_cdr.side_effect = KeyError('exc') with pytest.raises(ConverterError, match='Converting rosbag: '): From 12acb677e6545862b798aa59d732fd210c1664b3 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 4 Jul 2021 22:27:52 +0200 Subject: [PATCH 019/114] Use half-open intervals for time ranges --- src/rosbags/rosbag1/reader.py | 6 +++--- src/rosbags/rosbag2/reader.py | 4 ++-- src/rosbags/rosbag2/writer.py | 2 +- tests/test_reader.py | 4 ++-- tests/test_reader1.py | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index cdab0ef6..fb9fb0c4 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -94,7 +94,7 @@ class Chunk(NamedTuple): class TopicInfo(NamedTuple): """Topic information.""" - conn_count: int + conncount: int msgcount: int msgdef: str msgtype: str @@ -472,7 +472,7 @@ class Reader: @property def end_time(self) -> int: - """Timestamp in nanoseconds of the latest message.""" + """Timestamp in nanoseconds after the latest message.""" return max(x.end_time for x in self.chunk_infos) @property @@ -517,7 +517,7 @@ class Reader: chunk_pos = header.get_uint64('chunk_pos') start_time = header.get_time('start_time') - end_time = header.get_time('end_time') + end_time = header.get_time('end_time') + 1 count = header.get_uint32('count') self.bio.seek(4, os.SEEK_CUR) diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 3fac5ff2..a38e0f47 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -121,7 +121,7 @@ class Reader: @property def duration(self) -> int: """Duration in nanoseconds between earliest and latest messages.""" - return self.metadata['duration']['nanoseconds'] + return self.metadata['duration']['nanoseconds'] + 1 @property def start_time(self) -> int: @@ -130,7 +130,7 @@ class Reader: @property def end_time(self) -> int: - """Timestamp in nanoseconds of the latest message.""" + """Timestamp in nanoseconds after the latest message.""" return self.start_time + self.duration @property diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 042b096e..23d4c701 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -185,7 +185,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.cursor = None duration, start, count = self.conn.execute( - 'SELECT max(timestamp) - min(timestamp) + 1, min(timestamp), count(*) FROM messages', + 'SELECT max(timestamp) - min(timestamp), min(timestamp), count(*) FROM messages', ).fetchone() self.conn.commit() diff --git a/tests/test_reader.py b/tests/test_reader.py index d603e587..66d1d02d 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -120,9 +120,9 @@ def bag(request: SubRequest, tmp_path: Path) -> Path: def test_reader(bag: Path): """Test reader and deserializer on simple bag.""" with Reader(bag) as reader: - assert reader.duration == 42 + assert reader.duration == 43 assert reader.start_time == 666 - assert reader.end_time == 708 + assert reader.end_time == 709 assert reader.message_count == 4 if reader.compression_mode: assert reader.compression_format == 'zstd' diff --git a/tests/test_reader1.py b/tests/test_reader1.py index 9004f6f3..294e052b 100644 --- a/tests/test_reader1.py +++ b/tests/test_reader1.py @@ -198,9 +198,9 @@ def test_reader(tmp_path): # pylint: disable=too-many-statements ) with Reader(bag) as reader: assert reader.message_count == 1 - assert reader.duration == 0 + assert reader.duration == 1 assert reader.start_time == 42 * 10**9 - assert reader.end_time == 42 * 10**9 + assert reader.end_time == 42 * 10**9 + 1 assert len(reader.topics.keys()) == 1 assert reader.topics['/topic0'].msgcount == 1 msgs = list(reader.messages()) @@ -220,9 +220,9 @@ def test_reader(tmp_path): # pylint: disable=too-many-statements ) with Reader(bag) as reader: assert reader.message_count == 2 - assert reader.duration == 5 * 10**9 + assert reader.duration == 5 * 10**9 + 1 assert reader.start_time == 5 * 10**9 - assert reader.end_time == 10 * 10**9 + assert reader.end_time == 10 * 10**9 + 1 assert len(reader.topics.keys()) == 1 assert reader.topics['/topic0'].msgcount == 2 msgs = list(reader.messages()) From 19e81abef90e6c4f9f8c34c311055de3e390a713 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 5 Jul 2021 14:17:13 +0200 Subject: [PATCH 020/114] Fix sphinx circular imports --- docs/conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 5b0537c0..af0e7a8b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,6 +4,12 @@ import typing +# https://github.com/sphinx-doc/sphinx/issues/9243 +import sphinx.builders.html # noqa pylint: disable=unused-import +import sphinx.builders.latex # noqa pylint: disable=unused-import +import sphinx.builders.texinfo # noqa pylint: disable=unused-import +import sphinx.builders.text # noqa pylint: disable=unused-import +import sphinx.ext.autodoc # noqa pylint: disable=unused-import import sphinx_rtd_theme # noqa pylint: disable=unused-import # pylint: disable=invalid-name,redefined-builtin From 44a9c606b209aaa68687e6b680c1f5ed78cac9ce Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 5 Jul 2021 09:56:27 +0200 Subject: [PATCH 021/114] Update requirements --- requirements-dev.txt | 421 +++++++++++++++++++------------------------ requirements.txt | 114 ++++++------ 2 files changed, 238 insertions(+), 297 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 90af0108..7f9becd5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -12,13 +12,13 @@ astor==0.8.1 \ --hash=sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5 \ --hash=sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e # via flake8-simplify -astroid==2.5.6 \ - --hash=sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e \ - --hash=sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975 +astroid==2.6.2 \ + --hash=sha256:38b95085e9d92e2ca06cf8b35c12a74fa81da395a6f9e65803742e6509c05892 \ + --hash=sha256:606b2911d10c3dcf35e58d2ee5c97360e8477d7b9f3efc3f24811c93e6fc2cd9 # via pylint -attrs==20.3.0 \ - --hash=sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6 \ - --hash=sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700 +attrs==21.2.0 \ + --hash=sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1 \ + --hash=sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb # via # flake8-bugbear # pytest @@ -27,9 +27,9 @@ babel==2.9.1 \ --hash=sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9 \ --hash=sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0 # via sphinx -certifi==2020.12.5 \ - --hash=sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c \ - --hash=sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830 +certifi==2021.5.30 \ + --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \ + --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 # via requests chardet==4.0.0 \ --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \ @@ -115,9 +115,9 @@ flake8-commas==2.0.0 \ --hash=sha256:d3005899466f51380387df7151fb59afec666a0f4f4a2c6a8995b975de0f44b7 \ --hash=sha256:ee2141a3495ef9789a3894ed8802d03eff1eaaf98ce6d8653a7c573ef101935e # via rosbags -flake8-comprehensions==3.4.0 \ - --hash=sha256:7258a28e229fb9a8d16370b9c47a7d66396ba0201abb06c9d11df41b18ed64c4 \ - --hash=sha256:c00039be9f3959a26a98da3024f0fe809859bf1753ccb90e228cc40f3ac31ca7 +flake8-comprehensions==3.5.0 \ + --hash=sha256:b07aef3277623db32310aa241a1cec67212b53c1d18e767d7e26d4d83aa05bf7 \ + --hash=sha256:f24be9032587127f7a5bc6d066bf755b6e66834f694383adb8a673e229c1f559 # via rosbags flake8-docstrings==1.6.0 \ --hash=sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde \ @@ -135,9 +135,9 @@ flake8-mutable==1.2.0 \ --hash=sha256:38fd9dadcbcda6550a916197bc40ed76908119dabb37fbcca30873666c31d2d5 \ --hash=sha256:ee9b77111b867d845177bbc289d87d541445ffcc6029a0c5c65865b42b18c6a6 # via rosbags -flake8-plugin-utils==1.3.1 \ - --hash=sha256:6e996bc24ebe327558f24efd106f1be5f0c033c8cbb6eed815631f73d487f1c9 \ - --hash=sha256:efdbf9d15b18f72b7c348dd360f30e7cf3e73aa67ff832d5343eb5aa1115f250 +flake8-plugin-utils==1.3.2 \ + --hash=sha256:1fe43e3e9acf3a7c0f6b88f5338cad37044d2f156c43cb6b080b5f9da8a76f06 \ + --hash=sha256:20fa2a8ca2decac50116edb42e6af0a1253ef639ad79941249b840531889c65a # via # flake8-pytest-style # flake8-return @@ -149,31 +149,31 @@ flake8-print==4.0.0 \ --hash=sha256:5afac374b7dc49aac2c36d04b5eb1d746d72e6f5df75a6ecaecd99e9f79c6516 \ --hash=sha256:6c0efce658513169f96d7a24cf136c434dc711eb00ebd0a985eb1120103fe584 # via rosbags -flake8-pytest-style==1.4.1 \ - --hash=sha256:1bd3b7bb95608d6b70daebb0b0ee636be92d8175527f2e9919cbeb76f7515118 \ - --hash=sha256:bc4c10e5fcc2a20937bb6ce523579e9f91adfcb5403dce3adda5ac7c9bce364f +flake8-pytest-style==1.5.0 \ + --hash=sha256:668ce8f55edf7db4ac386d2735c3b354b5cb47aa341a4655d91a5788dd03124b \ + --hash=sha256:ec287a7dc4fe95082af5e408c8b2f8f4b6bcb366d5a17ff6c34112eb03446580 # via rosbags flake8-quotes==3.2.0 \ --hash=sha256:3f1116e985ef437c130431ac92f9b3155f8f652fda7405ac22ffdfd7a9d1055e # via rosbags -flake8-return==1.1.2 \ - --hash=sha256:183d0ad2f8553cb2c63c0cf288eb799d967577a74639599525adcd3860f6bb12 \ - --hash=sha256:d646d3b010a9736ddc23c24f98ad3282999f575da45d6eb9cefe4adddb44062d +flake8-return==1.1.3 \ + --hash=sha256:13a31edeb0c6157dd4f77166cdaa6141703d2b8b24def5558ae659852a003cb4 \ + --hash=sha256:4a266191f7ed69aa26b835ec90c5a5522fa8f79f5cf6363a877ac499f8eb418b # via rosbags -flake8-simplify==0.14.0 \ - --hash=sha256:365d0b812fc708af2286a8364ed7b23715bb873431c7f3188bc0b4cd6dcb3c0a \ - --hash=sha256:5793b3c7bd826d7489580f8146bbd40d5bface15d9be5020ddbf07980b844709 +flake8-simplify==0.14.1 \ + --hash=sha256:ba5d2905dc277cd4800c6e7f4859f96322d83dd37991ae5f8305750132084dd6 \ + --hash=sha256:d8b38feb97fd6c5943b5d9b2ab352acea83ec036a93215c0576b9e48c6aa2dcc # via rosbags -flake8-type-checking==1.0.0 \ - --hash=sha256:a39d5f46bc93a90e8bae85de644f0c24b8304ea675b30ec918213c3c69774604 \ - --hash=sha256:baacff5a7aacd11fb4c262c52a1c6dd030cd648934e4394b18a77cf3f2fe6458 +flake8-type-checking==1.0.3 \ + --hash=sha256:a236558b2b001b2f4909713342306c22d5c1fe8607053c3cf9d5abb13ba82ec7 \ + --hash=sha256:f76e71b0d9aae4dffe163dd62c2eacabbb3f94649cdd1f1bc6d16af34a2b7849 # via rosbags flake8-use-fstring==1.1 \ --hash=sha256:a0eea849ffe33fb6903c210c243c0f418da86a530e46cb13e64f1bdb045f22dc # via rosbags -flake8==3.9.1 \ - --hash=sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378 \ - --hash=sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a +flake8==3.9.2 \ + --hash=sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b \ + --hash=sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907 # via # flake8-annotations # flake8-bugbear @@ -202,15 +202,15 @@ iniconfig==1.1.1 \ --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 # via pytest -isort==5.8.0 \ - --hash=sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6 \ - --hash=sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d +isort==5.9.1 \ + --hash=sha256:83510593e07e433b77bd5bff0f6f607dbafa06d1a89022616f02d8b699cfcd56 \ + --hash=sha256:8e2c107091cfec7286bc0f68a547d0ba4c094d460b732075b6fba674f1035c0c # via # flake8-isort # pylint -jinja2==2.11.3 \ - --hash=sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419 \ - --hash=sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6 +jinja2==3.0.1 \ + --hash=sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4 \ + --hash=sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4 # via sphinx lazy-object-proxy==1.6.0 \ --hash=sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653 \ @@ -269,59 +269,41 @@ lz4==3.1.3 \ --hash=sha256:de2aac0cfda79c5a3eda9dbed21d78dc05c4a9ac00061748c3b57ea0e4a0b6a8 \ --hash=sha256:e3029738e64a0af1b04a32a39b32b0bba0e2088f61805e074c9a7e4bc212568f # via rosbags -markupsafe==1.1.1 \ - --hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \ - --hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \ - --hash=sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235 \ - --hash=sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5 \ - --hash=sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42 \ - --hash=sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f \ - --hash=sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39 \ - --hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \ - --hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b \ - --hash=sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014 \ - --hash=sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f \ - --hash=sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1 \ - --hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \ - --hash=sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183 \ - --hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \ - --hash=sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b \ - --hash=sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1 \ - --hash=sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15 \ - --hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \ - --hash=sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85 \ - --hash=sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1 \ - --hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \ - --hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \ - --hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \ - --hash=sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850 \ - --hash=sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0 \ - --hash=sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735 \ - --hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \ - --hash=sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb \ - --hash=sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e \ - --hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \ - --hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \ - --hash=sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1 \ - --hash=sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2 \ - --hash=sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21 \ - --hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \ - --hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \ - --hash=sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7 \ - --hash=sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b \ - --hash=sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8 \ - --hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \ - --hash=sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193 \ - --hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \ - --hash=sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b \ - --hash=sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f \ - --hash=sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2 \ - --hash=sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5 \ - --hash=sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c \ - --hash=sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032 \ - --hash=sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7 \ - --hash=sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be \ - --hash=sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621 +markupsafe==2.0.1 \ + --hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \ + --hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \ + --hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \ + --hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \ + --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \ + --hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \ + --hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \ + --hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \ + --hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \ + --hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \ + --hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \ + --hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \ + --hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \ + --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a \ + --hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \ + --hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \ + --hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \ + --hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \ + --hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \ + --hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \ + --hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \ + --hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \ + --hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \ + --hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \ + --hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \ + --hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \ + --hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \ + --hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \ + --hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \ + --hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \ + --hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \ + --hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \ + --hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \ + --hash=sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872 # via jinja2 mccabe==0.6.1 \ --hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \ @@ -333,59 +315,64 @@ mypy-extensions==0.4.3 \ --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 # via mypy -mypy==0.812 \ - --hash=sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e \ - --hash=sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064 \ - --hash=sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c \ - --hash=sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4 \ - --hash=sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97 \ - --hash=sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df \ - --hash=sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8 \ - --hash=sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a \ - --hash=sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56 \ - --hash=sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7 \ - --hash=sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6 \ - --hash=sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5 \ - --hash=sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a \ - --hash=sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521 \ - --hash=sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564 \ - --hash=sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49 \ - --hash=sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66 \ - --hash=sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a \ - --hash=sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119 \ - --hash=sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506 \ - --hash=sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c \ - --hash=sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb +mypy==0.910 \ + --hash=sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9 \ + --hash=sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a \ + --hash=sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9 \ + --hash=sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e \ + --hash=sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2 \ + --hash=sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212 \ + --hash=sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b \ + --hash=sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885 \ + --hash=sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150 \ + --hash=sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703 \ + --hash=sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072 \ + --hash=sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457 \ + --hash=sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e \ + --hash=sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0 \ + --hash=sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb \ + --hash=sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97 \ + --hash=sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8 \ + --hash=sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811 \ + --hash=sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6 \ + --hash=sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de \ + --hash=sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504 \ + --hash=sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921 \ + --hash=sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d # via pytest-mypy -numpy==1.20.2 \ - --hash=sha256:2428b109306075d89d21135bdd6b785f132a1f5a3260c371cee1fae427e12727 \ - --hash=sha256:377751954da04d4a6950191b20539066b4e19e3b559d4695399c5e8e3e683bf6 \ - --hash=sha256:4703b9e937df83f5b6b7447ca5912b5f5f297aba45f91dbbbc63ff9278c7aa98 \ - --hash=sha256:471c0571d0895c68da309dacee4e95a0811d0a9f9f532a48dc1bea5f3b7ad2b7 \ - --hash=sha256:61d5b4cf73622e4d0c6b83408a16631b670fc045afd6540679aa35591a17fe6d \ - --hash=sha256:6c915ee7dba1071554e70a3664a839fbc033e1d6528199d4621eeaaa5487ccd2 \ - --hash=sha256:6e51e417d9ae2e7848314994e6fc3832c9d426abce9328cf7571eefceb43e6c9 \ - --hash=sha256:719656636c48be22c23641859ff2419b27b6bdf844b36a2447cb39caceb00935 \ - --hash=sha256:780ae5284cb770ade51d4b4a7dce4faa554eb1d88a56d0e8b9f35fca9b0270ff \ - --hash=sha256:878922bf5ad7550aa044aa9301d417e2d3ae50f0f577de92051d739ac6096cee \ - --hash=sha256:924dc3f83de20437de95a73516f36e09918e9c9c18d5eac520062c49191025fb \ - --hash=sha256:97ce8b8ace7d3b9288d88177e66ee75480fb79b9cf745e91ecfe65d91a856042 \ - --hash=sha256:9c0fab855ae790ca74b27e55240fe4f2a36a364a3f1ebcfd1fb5ac4088f1cec3 \ - --hash=sha256:9cab23439eb1ebfed1aaec9cd42b7dc50fc96d5cd3147da348d9161f0501ada5 \ - --hash=sha256:a8e6859913ec8eeef3dbe9aed3bf475347642d1cdd6217c30f28dee8903528e6 \ - --hash=sha256:aa046527c04688af680217fffac61eec2350ef3f3d7320c07fd33f5c6e7b4d5f \ - --hash=sha256:abc81829c4039e7e4c30f7897938fa5d4916a09c2c7eb9b244b7a35ddc9656f4 \ - --hash=sha256:bad70051de2c50b1a6259a6df1daaafe8c480ca98132da98976d8591c412e737 \ - --hash=sha256:c73a7975d77f15f7f68dacfb2bca3d3f479f158313642e8ea9058eea06637931 \ - --hash=sha256:d15007f857d6995db15195217afdbddfcd203dfaa0ba6878a2f580eaf810ecd6 \ - --hash=sha256:d76061ae5cab49b83a8cf3feacefc2053fac672728802ac137dd8c4123397677 \ - --hash=sha256:e8e4fbbb7e7634f263c5b0150a629342cc19b47c5eba8d1cd4363ab3455ab576 \ - --hash=sha256:e9459f40244bb02b2f14f6af0cd0732791d72232bbb0dc4bab57ef88e75f6935 \ - --hash=sha256:edb1f041a9146dcf02cd7df7187db46ab524b9af2515f392f337c7cbbf5b52cd +numpy==1.21.0 \ + --hash=sha256:1a784e8ff7ea2a32e393cc53eb0003eca1597c7ca628227e34ce34eb11645a0e \ + --hash=sha256:2ba579dde0563f47021dcd652253103d6fd66165b18011dce1a0609215b2791e \ + --hash=sha256:3537b967b350ad17633b35c2f4b1a1bbd258c018910b518c30b48c8e41272717 \ + --hash=sha256:3c40e6b860220ed862e8097b8f81c9af6d7405b723f4a7af24a267b46f90e461 \ + --hash=sha256:598fe100b2948465cf3ed64b1a326424b5e4be2670552066e17dfaa67246011d \ + --hash=sha256:620732f42259eb2c4642761bd324462a01cdd13dd111740ce3d344992dd8492f \ + --hash=sha256:709884863def34d72b183d074d8ba5cfe042bc3ff8898f1ffad0209161caaa99 \ + --hash=sha256:75579acbadbf74e3afd1153da6177f846212ea2a0cc77de53523ae02c9256513 \ + --hash=sha256:7c55407f739f0bfcec67d0df49103f9333edc870061358ac8a8c9e37ea02fcd2 \ + --hash=sha256:a1f2fb2da242568af0271455b89aee0f71e4e032086ee2b4c5098945d0e11cf6 \ + --hash=sha256:a290989cd671cd0605e9c91a70e6df660f73ae87484218e8285c6522d29f6e38 \ + --hash=sha256:ac4fd578322842dbda8d968e3962e9f22e862b6ec6e3378e7415625915e2da4d \ + --hash=sha256:ad09f55cc95ed8d80d8ab2052f78cc21cb231764de73e229140d81ff49d8145e \ + --hash=sha256:b9205711e5440954f861ceeea8f1b415d7dd15214add2e878b4d1cf2bcb1a914 \ + --hash=sha256:bba474a87496d96e61461f7306fba2ebba127bed7836212c360f144d1e72ac54 \ + --hash=sha256:bebab3eaf0641bba26039fb0b2c5bf9b99407924b53b1ea86e03c32c64ef5aef \ + --hash=sha256:cc367c86eb87e5b7c9592935620f22d13b090c609f1b27e49600cd033b529f54 \ + --hash=sha256:ccc6c650f8700ce1e3a77668bb7c43e45c20ac06ae00d22bdf6760b38958c883 \ + --hash=sha256:cf680682ad0a3bef56dae200dbcbac2d57294a73e5b0f9864955e7dd7c2c2491 \ + --hash=sha256:d2910d0a075caed95de1a605df00ee03b599de5419d0b95d55342e9a33ad1fb3 \ + --hash=sha256:d5caa946a9f55511e76446e170bdad1d12d6b54e17a2afe7b189112ed4412bb8 \ + --hash=sha256:d89b0dc7f005090e32bb4f9bf796e1dcca6b52243caf1803fdd2b748d8561f63 \ + --hash=sha256:d95d16204cd51ff1a1c8d5f9958ce90ae190be81d348b514f9be39f878b8044a \ + --hash=sha256:e4d5a86a5257843a18fb1220c5f1c199532bc5d24e849ed4b0289fb59fbd4d8f \ + --hash=sha256:e58ddb53a7b4959932f5582ac455ff90dcb05fac3f8dcc8079498d43afbbde6c \ + --hash=sha256:e80fe25cba41c124d04c662f33f6364909b985f2eb5998aaa5ae4b9587242cce \ + --hash=sha256:eda2829af498946c59d8585a9fd74da3f810866e05f8df03a86f70079c7531dd \ + --hash=sha256:fd0a359c1c17f00cb37de2969984a74320970e0ceef4808c32e00773b06649d9 # via rosbags -packaging==20.9 \ - --hash=sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5 \ - --hash=sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a +packaging==21.0 \ + --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ + --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 # via # pytest # sphinx @@ -407,29 +394,29 @@ pycodestyle==2.7.0 \ # via # flake8 # flake8-print -pydocstyle==6.0.0 \ - --hash=sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f \ - --hash=sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d +pydocstyle==6.1.1 \ + --hash=sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc \ + --hash=sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4 # via flake8-docstrings pyflakes==2.3.1 \ --hash=sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3 \ --hash=sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db # via flake8 -pygments==2.8.1 \ - --hash=sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94 \ - --hash=sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8 +pygments==2.9.0 \ + --hash=sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f \ + --hash=sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e # via sphinx -pylint==2.8.2 \ - --hash=sha256:586d8fa9b1891f4b725f587ef267abe2a1bad89d6b184520c7f07a253dd6e217 \ - --hash=sha256:f7e2072654a6b6afdf5e2fb38147d3e2d2d43c89f648637baab63e026481279b +pylint==2.9.3 \ + --hash=sha256:23a1dc8b30459d78e9ff25942c61bb936108ccbe29dd9e71c01dc8274961709a \ + --hash=sha256:5d46330e6b8886c31b5e3aba5ff48c10f4aa5e76cbf9002c6544306221e63fbc # via pytest-pylint pyparsing==2.4.7 \ --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b # via packaging -pytest-cov==2.11.1 \ - --hash=sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7 \ - --hash=sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da +pytest-cov==2.12.1 \ + --hash=sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a \ + --hash=sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7 # via rosbags pytest-flake8==1.0.7 \ --hash=sha256:c28cf23e7d359753c896745fd4ba859495d02e16c84bac36caa8b1eec58f5bc1 \ @@ -443,9 +430,9 @@ pytest-pylint==0.18.0 \ --hash=sha256:790c7a8019fab08e59bd3812db1657a01995a975af8b1c6ce95b9aa39d61da27 \ --hash=sha256:b63aaf8b80ff33c8ceaa7f68323ed04102c7790093ccf6bdb261a4c2dc6fd564 # via rosbags -pytest==6.2.3 \ - --hash=sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634 \ - --hash=sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc +pytest==6.2.4 \ + --hash=sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b \ + --hash=sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890 # via # pytest-cov # pytest-flake8 @@ -460,46 +447,36 @@ requests==2.25.1 \ --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \ --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e # via sphinx -ruamel.yaml.clib==0.2.2 \ - --hash=sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b \ - --hash=sha256:1236df55e0f73cd138c0eca074ee086136c3f16a97c2ac719032c050f7e0622f \ - --hash=sha256:1f8c0a4577c0e6c99d208de5c4d3fd8aceed9574bb154d7a2b21c16bb924154c \ - --hash=sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91 \ - --hash=sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc \ - --hash=sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7 \ - --hash=sha256:2fd336a5c6415c82e2deb40d08c222087febe0aebe520f4d21910629018ab0f3 \ - --hash=sha256:30dca9bbcbb1cc858717438218d11eafb78666759e5094dd767468c0d577a7e7 \ - --hash=sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6 \ - --hash=sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6 \ - --hash=sha256:46d6d20815064e8bb023ea8628cfb7402c0f0e83de2c2227a88097e239a7dffd \ - --hash=sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0 \ - --hash=sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62 \ - --hash=sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99 \ - --hash=sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5 \ - --hash=sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026 \ - --hash=sha256:6c0a5dc52fc74eb87c67374a4e554d4761fd42a4d01390b7e868b30d21f4b8bb \ - --hash=sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2 \ - --hash=sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1 \ - --hash=sha256:75f0ee6839532e52a3a53f80ce64925ed4aed697dd3fa890c4c918f3304bd4f4 \ - --hash=sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b \ - --hash=sha256:8be05be57dc5c7b4a0b24edcaa2f7275866d9c907725226cdde46da09367d923 \ - --hash=sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e \ - --hash=sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c \ - --hash=sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988 \ - --hash=sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f \ - --hash=sha256:b4b0d31f2052b3f9f9b5327024dc629a253a83d8649d4734ca7f35b60ec3e9e5 \ - --hash=sha256:c6ac7e45367b1317e56f1461719c853fd6825226f45b835df7436bb04031fd8a \ - --hash=sha256:daf21aa33ee9b351f66deed30a3d450ab55c14242cfdfcd377798e2c0d25c9f1 \ - --hash=sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2 \ - --hash=sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f +ruamel.yaml.clib==0.2.6 \ + --hash=sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd \ + --hash=sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0 \ + --hash=sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277 \ + --hash=sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104 \ + --hash=sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd \ + --hash=sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78 \ + --hash=sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99 \ + --hash=sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527 \ + --hash=sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84 \ + --hash=sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7 \ + --hash=sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468 \ + --hash=sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b \ + --hash=sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94 \ + --hash=sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233 \ + --hash=sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb \ + --hash=sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5 \ + --hash=sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe \ + --hash=sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751 \ + --hash=sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502 \ + --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ + --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c # via ruamel.yaml -ruamel.yaml==0.17.4 \ - --hash=sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28 \ - --hash=sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22 +ruamel.yaml==0.17.10 \ + --hash=sha256:106bc8d6dc6a0ff7c9196a47570432036f41d556b779c6b4e618085f57e39e67 \ + --hash=sha256:ffb9b703853e9e8b7861606dfdab1026cf02505bade0653d1880f4b2db47f815 # via rosbags -six==1.15.0 \ - --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \ - --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via flake8-print snowballstemmer==2.1.0 \ --hash=sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2 \ @@ -515,9 +492,9 @@ sphinx-rtd-theme==0.5.2 \ --hash=sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a \ --hash=sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f # via rosbags -sphinx==3.5.4 \ - --hash=sha256:19010b7b9fa0dc7756a6e105b2aacd3a80f798af3c25c273be64d7beeb482cb1 \ - --hash=sha256:2320d4e994a191f4b4be27da514e46b3d6b420f2ff895d064f52415d342461e8 +sphinx==4.0.2 \ + --hash=sha256:b5c2ae4120bf00c799ba9b3699bc895816d272d120080fbc967292f29b52b48c \ + --hash=sha256:d1cb10bee9c4231f1700ec2e24a91be3f3a3aba066ea4ca9f3bbe47e59d5a1d4 # via # rosbags # sphinx-autodoc-typehints @@ -530,9 +507,9 @@ sphinxcontrib-devhelp==1.0.2 \ --hash=sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e \ --hash=sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4 # via sphinx -sphinxcontrib-htmlhelp==1.0.3 \ - --hash=sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f \ - --hash=sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b +sphinxcontrib-htmlhelp==2.0.0 \ + --hash=sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07 \ + --hash=sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2 # via sphinx sphinxcontrib-jsmath==1.0.1 \ --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \ @@ -542,9 +519,9 @@ sphinxcontrib-qthelp==1.0.3 \ --hash=sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72 \ --hash=sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6 # via sphinx -sphinxcontrib-serializinghtml==1.1.4 \ - --hash=sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc \ - --hash=sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a +sphinxcontrib-serializinghtml==1.1.5 \ + --hash=sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd \ + --hash=sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952 # via sphinx testfixtures==6.17.1 \ --hash=sha256:5ec3a0dd6f71cc4c304fbc024a10cc293d3e0b852c868014b9f233203e149bda \ @@ -554,49 +531,19 @@ toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f # via + # mypy # pylint # pytest + # pytest-cov # pytest-pylint -typed-ast==1.4.3 \ - --hash=sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace \ - --hash=sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff \ - --hash=sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266 \ - --hash=sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528 \ - --hash=sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6 \ - --hash=sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808 \ - --hash=sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4 \ - --hash=sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363 \ - --hash=sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341 \ - --hash=sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04 \ - --hash=sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41 \ - --hash=sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e \ - --hash=sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3 \ - --hash=sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899 \ - --hash=sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805 \ - --hash=sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c \ - --hash=sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c \ - --hash=sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39 \ - --hash=sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a \ - --hash=sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3 \ - --hash=sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7 \ - --hash=sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f \ - --hash=sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075 \ - --hash=sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0 \ - --hash=sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40 \ - --hash=sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428 \ - --hash=sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927 \ - --hash=sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3 \ - --hash=sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f \ - --hash=sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65 - # via mypy typing-extensions==3.10.0.0 \ --hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \ --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \ --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84 # via mypy -urllib3==1.26.4 \ - --hash=sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df \ - --hash=sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937 +urllib3==1.26.6 \ + --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ + --hash=sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f # via requests wrapt==1.12.1 \ --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7 diff --git a/requirements.txt b/requirements.txt index 18846300..cd17456a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,68 +37,62 @@ lz4==3.1.3 \ --hash=sha256:de2aac0cfda79c5a3eda9dbed21d78dc05c4a9ac00061748c3b57ea0e4a0b6a8 \ --hash=sha256:e3029738e64a0af1b04a32a39b32b0bba0e2088f61805e074c9a7e4bc212568f # via rosbags (setup.py) -numpy==1.20.2 \ - --hash=sha256:2428b109306075d89d21135bdd6b785f132a1f5a3260c371cee1fae427e12727 \ - --hash=sha256:377751954da04d4a6950191b20539066b4e19e3b559d4695399c5e8e3e683bf6 \ - --hash=sha256:4703b9e937df83f5b6b7447ca5912b5f5f297aba45f91dbbbc63ff9278c7aa98 \ - --hash=sha256:471c0571d0895c68da309dacee4e95a0811d0a9f9f532a48dc1bea5f3b7ad2b7 \ - --hash=sha256:61d5b4cf73622e4d0c6b83408a16631b670fc045afd6540679aa35591a17fe6d \ - --hash=sha256:6c915ee7dba1071554e70a3664a839fbc033e1d6528199d4621eeaaa5487ccd2 \ - --hash=sha256:6e51e417d9ae2e7848314994e6fc3832c9d426abce9328cf7571eefceb43e6c9 \ - --hash=sha256:719656636c48be22c23641859ff2419b27b6bdf844b36a2447cb39caceb00935 \ - --hash=sha256:780ae5284cb770ade51d4b4a7dce4faa554eb1d88a56d0e8b9f35fca9b0270ff \ - --hash=sha256:878922bf5ad7550aa044aa9301d417e2d3ae50f0f577de92051d739ac6096cee \ - --hash=sha256:924dc3f83de20437de95a73516f36e09918e9c9c18d5eac520062c49191025fb \ - --hash=sha256:97ce8b8ace7d3b9288d88177e66ee75480fb79b9cf745e91ecfe65d91a856042 \ - --hash=sha256:9c0fab855ae790ca74b27e55240fe4f2a36a364a3f1ebcfd1fb5ac4088f1cec3 \ - --hash=sha256:9cab23439eb1ebfed1aaec9cd42b7dc50fc96d5cd3147da348d9161f0501ada5 \ - --hash=sha256:a8e6859913ec8eeef3dbe9aed3bf475347642d1cdd6217c30f28dee8903528e6 \ - --hash=sha256:aa046527c04688af680217fffac61eec2350ef3f3d7320c07fd33f5c6e7b4d5f \ - --hash=sha256:abc81829c4039e7e4c30f7897938fa5d4916a09c2c7eb9b244b7a35ddc9656f4 \ - --hash=sha256:bad70051de2c50b1a6259a6df1daaafe8c480ca98132da98976d8591c412e737 \ - --hash=sha256:c73a7975d77f15f7f68dacfb2bca3d3f479f158313642e8ea9058eea06637931 \ - --hash=sha256:d15007f857d6995db15195217afdbddfcd203dfaa0ba6878a2f580eaf810ecd6 \ - --hash=sha256:d76061ae5cab49b83a8cf3feacefc2053fac672728802ac137dd8c4123397677 \ - --hash=sha256:e8e4fbbb7e7634f263c5b0150a629342cc19b47c5eba8d1cd4363ab3455ab576 \ - --hash=sha256:e9459f40244bb02b2f14f6af0cd0732791d72232bbb0dc4bab57ef88e75f6935 \ - --hash=sha256:edb1f041a9146dcf02cd7df7187db46ab524b9af2515f392f337c7cbbf5b52cd +numpy==1.21.0 \ + --hash=sha256:1a784e8ff7ea2a32e393cc53eb0003eca1597c7ca628227e34ce34eb11645a0e \ + --hash=sha256:2ba579dde0563f47021dcd652253103d6fd66165b18011dce1a0609215b2791e \ + --hash=sha256:3537b967b350ad17633b35c2f4b1a1bbd258c018910b518c30b48c8e41272717 \ + --hash=sha256:3c40e6b860220ed862e8097b8f81c9af6d7405b723f4a7af24a267b46f90e461 \ + --hash=sha256:598fe100b2948465cf3ed64b1a326424b5e4be2670552066e17dfaa67246011d \ + --hash=sha256:620732f42259eb2c4642761bd324462a01cdd13dd111740ce3d344992dd8492f \ + --hash=sha256:709884863def34d72b183d074d8ba5cfe042bc3ff8898f1ffad0209161caaa99 \ + --hash=sha256:75579acbadbf74e3afd1153da6177f846212ea2a0cc77de53523ae02c9256513 \ + --hash=sha256:7c55407f739f0bfcec67d0df49103f9333edc870061358ac8a8c9e37ea02fcd2 \ + --hash=sha256:a1f2fb2da242568af0271455b89aee0f71e4e032086ee2b4c5098945d0e11cf6 \ + --hash=sha256:a290989cd671cd0605e9c91a70e6df660f73ae87484218e8285c6522d29f6e38 \ + --hash=sha256:ac4fd578322842dbda8d968e3962e9f22e862b6ec6e3378e7415625915e2da4d \ + --hash=sha256:ad09f55cc95ed8d80d8ab2052f78cc21cb231764de73e229140d81ff49d8145e \ + --hash=sha256:b9205711e5440954f861ceeea8f1b415d7dd15214add2e878b4d1cf2bcb1a914 \ + --hash=sha256:bba474a87496d96e61461f7306fba2ebba127bed7836212c360f144d1e72ac54 \ + --hash=sha256:bebab3eaf0641bba26039fb0b2c5bf9b99407924b53b1ea86e03c32c64ef5aef \ + --hash=sha256:cc367c86eb87e5b7c9592935620f22d13b090c609f1b27e49600cd033b529f54 \ + --hash=sha256:ccc6c650f8700ce1e3a77668bb7c43e45c20ac06ae00d22bdf6760b38958c883 \ + --hash=sha256:cf680682ad0a3bef56dae200dbcbac2d57294a73e5b0f9864955e7dd7c2c2491 \ + --hash=sha256:d2910d0a075caed95de1a605df00ee03b599de5419d0b95d55342e9a33ad1fb3 \ + --hash=sha256:d5caa946a9f55511e76446e170bdad1d12d6b54e17a2afe7b189112ed4412bb8 \ + --hash=sha256:d89b0dc7f005090e32bb4f9bf796e1dcca6b52243caf1803fdd2b748d8561f63 \ + --hash=sha256:d95d16204cd51ff1a1c8d5f9958ce90ae190be81d348b514f9be39f878b8044a \ + --hash=sha256:e4d5a86a5257843a18fb1220c5f1c199532bc5d24e849ed4b0289fb59fbd4d8f \ + --hash=sha256:e58ddb53a7b4959932f5582ac455ff90dcb05fac3f8dcc8079498d43afbbde6c \ + --hash=sha256:e80fe25cba41c124d04c662f33f6364909b985f2eb5998aaa5ae4b9587242cce \ + --hash=sha256:eda2829af498946c59d8585a9fd74da3f810866e05f8df03a86f70079c7531dd \ + --hash=sha256:fd0a359c1c17f00cb37de2969984a74320970e0ceef4808c32e00773b06649d9 # via rosbags (setup.py) -ruamel.yaml.clib==0.2.2 \ - --hash=sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b \ - --hash=sha256:1236df55e0f73cd138c0eca074ee086136c3f16a97c2ac719032c050f7e0622f \ - --hash=sha256:1f8c0a4577c0e6c99d208de5c4d3fd8aceed9574bb154d7a2b21c16bb924154c \ - --hash=sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91 \ - --hash=sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc \ - --hash=sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7 \ - --hash=sha256:2fd336a5c6415c82e2deb40d08c222087febe0aebe520f4d21910629018ab0f3 \ - --hash=sha256:30dca9bbcbb1cc858717438218d11eafb78666759e5094dd767468c0d577a7e7 \ - --hash=sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6 \ - --hash=sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6 \ - --hash=sha256:46d6d20815064e8bb023ea8628cfb7402c0f0e83de2c2227a88097e239a7dffd \ - --hash=sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0 \ - --hash=sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62 \ - --hash=sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99 \ - --hash=sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5 \ - --hash=sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026 \ - --hash=sha256:6c0a5dc52fc74eb87c67374a4e554d4761fd42a4d01390b7e868b30d21f4b8bb \ - --hash=sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2 \ - --hash=sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1 \ - --hash=sha256:75f0ee6839532e52a3a53f80ce64925ed4aed697dd3fa890c4c918f3304bd4f4 \ - --hash=sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b \ - --hash=sha256:8be05be57dc5c7b4a0b24edcaa2f7275866d9c907725226cdde46da09367d923 \ - --hash=sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e \ - --hash=sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c \ - --hash=sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988 \ - --hash=sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f \ - --hash=sha256:b4b0d31f2052b3f9f9b5327024dc629a253a83d8649d4734ca7f35b60ec3e9e5 \ - --hash=sha256:c6ac7e45367b1317e56f1461719c853fd6825226f45b835df7436bb04031fd8a \ - --hash=sha256:daf21aa33ee9b351f66deed30a3d450ab55c14242cfdfcd377798e2c0d25c9f1 \ - --hash=sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2 \ - --hash=sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f +ruamel.yaml.clib==0.2.6 \ + --hash=sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd \ + --hash=sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0 \ + --hash=sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277 \ + --hash=sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104 \ + --hash=sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd \ + --hash=sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78 \ + --hash=sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99 \ + --hash=sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527 \ + --hash=sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84 \ + --hash=sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7 \ + --hash=sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468 \ + --hash=sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b \ + --hash=sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94 \ + --hash=sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233 \ + --hash=sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb \ + --hash=sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5 \ + --hash=sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe \ + --hash=sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751 \ + --hash=sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502 \ + --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ + --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c # via ruamel.yaml -ruamel.yaml==0.17.4 \ - --hash=sha256:44bc6b54fddd45e4bc0619059196679f9e8b79c027f4131bb072e6a22f4d5e28 \ - --hash=sha256:ac79fb25f5476e8e9ed1c53b8a2286d2c3f5dde49eb37dbcee5c7eb6a8415a22 +ruamel.yaml==0.17.10 \ + --hash=sha256:106bc8d6dc6a0ff7c9196a47570432036f41d556b779c6b4e618085f57e39e67 \ + --hash=sha256:ffb9b703853e9e8b7861606dfdab1026cf02505bade0653d1880f4b2db47f815 # via rosbags (setup.py) zstandard==0.15.2 \ --hash=sha256:1c5ef399f81204fbd9f0df3debf80389fd8aa9660fe1746d37c80b0d45f809e9 \ From 3d694b20f68a79284c04ccdcce7c671e1bea8282 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sat, 3 Jul 2021 17:33:03 +0200 Subject: [PATCH 022/114] Release 0.9.1 --- CHANGES.rst | 14 ++++++++++++++ setup.cfg | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 078a4749..c260aee2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,20 @@ Changes ======= +0.9.1 - 2021-07-05 +------------------ + +- Use half-open intervals for time ranges +- Create appropriate QoS profiles for latched topics in converted bags +- Fix return value tuple order of messages() in documentation `#2`_ +- Add type hints to message classes +- Remove non-default ROS2 message types +- Support multi-line comments in idl files +- Fix parsing of msg files on non-POSIX platforms `#4`_ + +.. _#2: https://gitlab.com/ternaris/rosbags/issues/2 +.. _#4: https://gitlab.com/ternaris/rosbags/issues/4 + 0.9.0 - 2021-05-16 ------------------ diff --git a/setup.cfg b/setup.cfg index 21c5151c..76af5e3c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.0 +version = 0.9.1 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 117a4f6348037d58b3a3c4a4aef868b74a7891ed Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 8 Jul 2021 09:18:54 +0200 Subject: [PATCH 023/114] Support relative type references in msg files --- src/rosbags/typesys/idl.py | 5 +++++ src/rosbags/typesys/msg.py | 14 +++++++++----- src/rosbags/typesys/peg.py | 32 +++++++++++++++++++++++++++----- tests/test_parse.py | 16 ++++++++++++++++ 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index b9542272..259f7ce5 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -253,6 +253,11 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods RULES = parse_grammar(GRAMMAR_IDL) + def __init__(self): + """Initialize.""" + super().__init__() + self.typedefs = {} + def visit_specification(self, children: Any) -> Typesdict: """Process start symbol, return only children of modules.""" children = [x[0] for x in children if x is not None] diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 7242d12a..59739c0f 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -89,10 +89,11 @@ def normalize_msgtype(name: str) -> str: return str(path) -def normalize_fieldtype(field: Any, names: List[str]): +def normalize_fieldtype(typename: str, field: Any, names: List[str]): """Normalize field typename. Args: + typename: Type name of field owner. field: Field definition. names: Valid message names. @@ -111,9 +112,12 @@ def normalize_fieldtype(field: Any, names: List[str]): else: if name in dct: name = dct[name] + elif name == 'Header': + name = 'std_msgs/msg/Header' + elif '/' not in name: + name = str(Path(typename).parent / name) elif '/msg/' not in name: - ptype = Path(name) - name = str(ptype.parent / 'msg' / ptype.name) + name = str((path := Path(name)).parent / 'msg' / path.name) inamedef = (Nodetype.NAME, name) if namedef[0] == Nodetype.NAME: @@ -159,9 +163,9 @@ class VisitorMSG(Visitor): typelist = [children[0], *[x[1] for x in children[1]]] typedict = dict(typelist) names = list(typedict.keys()) - for _, fields in typedict.items(): + for name, fields in typedict.items(): for field in fields: - normalize_fieldtype(field, names) + normalize_fieldtype(name, field, names) return typedict def visit_msgdef(self, children: Any) -> Any: diff --git a/src/rosbags/typesys/peg.py b/src/rosbags/typesys/peg.py index 95d1e731..92cc229b 100644 --- a/src/rosbags/typesys/peg.py +++ b/src/rosbags/typesys/peg.py @@ -58,10 +58,22 @@ class Rule: class RuleLiteral(Rule): """Rule to match string literal.""" + def __init__(self, value: Any, rules: Dict[str, Rule], name: Optional[str] = None): + """Initialize. + + Args: + value: Value of this rule. + rules: Grammar containing all rules. + name: Name of this rule. + + """ + super().__init__(value, rules, name) + self.value = value[1:-1].replace('\\\'', '\'') + def parse(self, text: str, pos: int) -> Tuple[int, Any]: """Apply rule at position.""" - value: str = self.value[1:-1].replace('\\\'', '\'') - if text[pos:].startswith(value): + value = self.value + if text[pos:pos + len(value)] == value: npos = pos + len(value) npos = self.skip_ws(text, npos) return npos, (self.LIT, value) @@ -71,10 +83,21 @@ class RuleLiteral(Rule): class RuleRegex(Rule): """Rule to match regular expression.""" + def __init__(self, value: Any, rules: Dict[str, Rule], name: Optional[str] = None): + """Initialize. + + Args: + value: Value of this rule. + rules: Grammar containing all rules. + name: Name of this rule. + + """ + super().__init__(value, rules, name) + self.value = re.compile(value[2:-1], re.M | re.S) + def parse(self, text: str, pos: int) -> Tuple[int, Any]: """Apply rule at position.""" - pattern = re.compile(self.value[2:-1], re.M | re.S) - match = pattern.match(text, pos) + match = self.value.match(text, pos) if not match: return -1, [] npos = self.skip_ws(text, match.span()[1]) @@ -171,7 +194,6 @@ class Visitor: # pylint: disable=too-few-public-methods def __init__(self): """Initialize.""" - self.typedefs = {} def visit(self, tree: Any) -> Any: """Visit all nodes in parse tree.""" diff --git a/tests/test_parse.py b/tests/test_parse.py index 014149ce..d5ea00fe 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -37,6 +37,11 @@ MSG: test_msgs/Other uint64[3] Header """ +RELSIBLING_MSG = """ +Header header +Other other +""" + IDL_LANG = """ // assign different literals and expressions @@ -122,6 +127,17 @@ def test_parse_multi_msg(): assert ret['test_msgs/msg/Foo'][2][0][1] == 'uint8' +def test_parse_relative_siblings_msg(): + """Test relative siblings with msg parser.""" + ret = get_types_from_msg(RELSIBLING_MSG, 'test_msgs/msg/Foo') + assert ret['test_msgs/msg/Foo'][0][0][1] == 'std_msgs/msg/Header' + assert ret['test_msgs/msg/Foo'][1][0][1] == 'test_msgs/msg/Other' + + ret = get_types_from_msg(RELSIBLING_MSG, 'rel_msgs/msg/Foo') + assert ret['rel_msgs/msg/Foo'][0][0][1] == 'std_msgs/msg/Header' + assert ret['rel_msgs/msg/Foo'][1][0][1] == 'rel_msgs/msg/Other' + + def test_parse_idl(): """Test idl parser.""" ret = get_types_from_idl(IDL_LANG) From fa57b167654dba237a06cf07e1615f2397b2a6eb Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 8 Jul 2021 09:29:44 +0200 Subject: [PATCH 024/114] Release 0.9.2 --- CHANGES.rst | 5 +++++ setup.cfg | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index c260aee2..1fa10673 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,11 @@ Changes ======= +0.9.2 - 2021-07-08 +------------------ + +- Support relative type references in msg files + 0.9.1 - 2021-07-05 ------------------ diff --git a/setup.cfg b/setup.cfg index 76af5e3c..5bc09b12 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.1 +version = 0.9.2 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 03b4d7e5c7d0da9aacaaa9cf5abd04abf0f6b14a Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 1 Aug 2021 17:38:18 +0200 Subject: [PATCH 025/114] Add const fields to type representations --- src/rosbags/serde/cdr.py | 38 +- src/rosbags/serde/messages.py | 11 +- src/rosbags/serde/ros1.py | 24 +- src/rosbags/serde/typing.py | 9 +- src/rosbags/serde/utils.py | 19 +- src/rosbags/typesys/base.py | 8 +- src/rosbags/typesys/idl.py | 47 +- src/rosbags/typesys/msg.py | 50 +- src/rosbags/typesys/register.py | 44 +- src/rosbags/typesys/types.py | 2135 ++++++++++++++++++++----------- tests/cdr.py | 18 +- tests/test_parse.py | 77 +- 12 files changed, 1573 insertions(+), 907 deletions(-) diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index ba9cc6d8..de3ff282 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -68,19 +68,19 @@ def generate_getsize_cdr(fields: List[Field]) -> Tuple[Callable, int]: size += SIZEMAP[desc.args] elif desc.valtype == Valtype.ARRAY: - subdesc = desc.args[1] + subdesc, length = desc.args if subdesc.valtype == Valtype.BASE: if subdesc.args == 'string': lines.append(f' val = message.{fieldname}') - for idx in range(desc.args[0]): + for idx in range(length): lines.append(' pos = (pos + 4 - 1) & -4') lines.append(f' pos += 4 + len(val[{idx}].encode()) + 1') aligned = 1 is_stat = False else: - lines.append(f' pos += {desc.args[0] * SIZEMAP[subdesc.args]}') - size += desc.args[0] * SIZEMAP[subdesc.args] + lines.append(f' pos += {length * SIZEMAP[subdesc.args]}') + size += length * SIZEMAP[subdesc.args] else: assert subdesc.valtype == Valtype.MESSAGE @@ -88,7 +88,7 @@ def generate_getsize_cdr(fields: List[Field]) -> Tuple[Callable, int]: anext_after = align_after(subdesc) if subdesc.args.size_cdr: - for _ in range(desc.args[0]): + for _ in range(length): if anext > anext_after: lines.append(f' pos = (pos + {anext} - 1) & -{anext}') size = (size + anext - 1) & -anext @@ -97,7 +97,7 @@ def generate_getsize_cdr(fields: List[Field]) -> Tuple[Callable, int]: else: lines.append(f' func = get_msgdef("{subdesc.args.name}").getsize_cdr') lines.append(f' val = message.{fieldname}') - for idx in range(desc.args[0]): + for idx in range(length): if anext > anext_after: lines.append(f' pos = (pos + {anext} - 1) & -{anext}') lines.append(f' pos = func(pos, val[{idx}])') @@ -107,7 +107,7 @@ def generate_getsize_cdr(fields: List[Field]) -> Tuple[Callable, int]: assert desc.valtype == Valtype.SEQUENCE lines.append(' pos += 4') aligned = 4 - subdesc = desc.args + subdesc = desc.args[0] if subdesc.valtype == Valtype.BASE: if subdesc.args == 'string': lines.append(f' for val in message.{fieldname}:') @@ -211,13 +211,13 @@ def generate_serialize_cdr(fields: List[Field], endianess: str) -> Callable: aligned = SIZEMAP[desc.args] elif desc.valtype == Valtype.ARRAY: - subdesc = desc.args[1] - lines.append(f' if len(val) != {desc.args[0]}:') + subdesc, length = desc.args + lines.append(f' if len(val) != {length}:') lines.append(' raise SerdeError(\'Unexpected array length\')') if subdesc.valtype == Valtype.BASE: if subdesc.args == 'string': - for idx in range(desc.args[0]): + for idx in range(length): lines.append(f' bval = memoryview(val[{idx}].encode())') lines.append(' length = len(bval) + 1') lines.append(' pos = (pos + 4 - 1) & -4') @@ -229,7 +229,7 @@ def generate_serialize_cdr(fields: List[Field], endianess: str) -> Callable: else: if (endianess == 'le') != (sys.byteorder == 'little'): lines.append(' val = val.byteswap()') - size = desc.args[0] * SIZEMAP[subdesc.args] + size = length * SIZEMAP[subdesc.args] lines.append(f' rawdata[pos:pos + {size}] = val.view(numpy.uint8)') lines.append(f' pos += {size}') @@ -240,7 +240,7 @@ def generate_serialize_cdr(fields: List[Field], endianess: str) -> Callable: lines.append( f' func = get_msgdef("{subdesc.args.name}").serialize_cdr_{endianess}', ) - for idx in range(desc.args[0]): + for idx in range(length): if anext > anext_after: lines.append(f' pos = (pos + {anext} - 1) & -{anext}') lines.append(f' pos = func(rawdata, pos, val[{idx}])') @@ -250,7 +250,7 @@ def generate_serialize_cdr(fields: List[Field], endianess: str) -> Callable: lines.append(f' pack_int32_{endianess}(rawdata, pos, len(val))') lines.append(' pos += 4') aligned = 4 - subdesc = desc.args + subdesc = desc.args[0] if subdesc.valtype == Valtype.BASE: if subdesc.args == 'string': @@ -350,11 +350,11 @@ def generate_deserialize_cdr(fields: List[Field], endianess: str) -> Callable: aligned = SIZEMAP[desc.args] elif desc.valtype == Valtype.ARRAY: - subdesc = desc.args[1] + subdesc, length = desc.args if subdesc.valtype == Valtype.BASE: if subdesc.args == 'string': lines.append(' value = []') - for idx in range(desc.args[0]): + for idx in range(length): if idx: lines.append(' pos = (pos + 4 - 1) & -4') lines.append(f' length = unpack_int32_{endianess}(rawdata, pos)[0]') @@ -365,10 +365,10 @@ def generate_deserialize_cdr(fields: List[Field], endianess: str) -> Callable: lines.append(' values.append(value)') aligned = 1 else: - size = desc.args[0] * SIZEMAP[subdesc.args] + size = length * SIZEMAP[subdesc.args] lines.append( f' val = numpy.frombuffer(rawdata, ' - f'dtype=numpy.{subdesc.args}, count={desc.args[0]}, offset=pos)', + f'dtype=numpy.{subdesc.args}, count={length}, offset=pos)', ) if (endianess == 'le') != (sys.byteorder == 'little'): lines.append(' val = val.byteswap()') @@ -380,7 +380,7 @@ def generate_deserialize_cdr(fields: List[Field], endianess: str) -> Callable: anext_after = align_after(subdesc) lines.append(f' msgdef = get_msgdef("{subdesc.args.name}")') lines.append(' value = []') - for _ in range(desc.args[0]): + for _ in range(length): if anext > anext_after: lines.append(f' pos = (pos + {anext} - 1) & -{anext}') lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls)') @@ -393,7 +393,7 @@ def generate_deserialize_cdr(fields: List[Field], endianess: str) -> Callable: lines.append(f' size = unpack_int32_{endianess}(rawdata, pos)[0]') lines.append(' pos += 4') aligned = 4 - subdesc = desc.args + subdesc = desc.args[0] if subdesc.valtype == Valtype.BASE: if subdesc.args == 'string': diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py index 3c2193d7..eae01a59 100644 --- a/src/rosbags/serde/messages.py +++ b/src/rosbags/serde/messages.py @@ -10,13 +10,12 @@ from rosbags.typesys import types from .cdr import generate_deserialize_cdr, generate_getsize_cdr, generate_serialize_cdr from .ros1 import generate_ros1_to_cdr -from .typing import Field, Msgdef -from .utils import Descriptor, Valtype +from .typing import Descriptor, Field, Msgdef +from .utils import Valtype if TYPE_CHECKING: from typing import Any, Dict - MSGDEFCACHE: Dict[str, Msgdef] = {} @@ -37,7 +36,7 @@ def get_msgdef(typename: str) -> Msgdef: """ if typename not in MSGDEFCACHE: - entries = types.FIELDDEFS[typename] + entries = types.FIELDDEFS[typename][1] def fixup(entry: Any) -> Descriptor: if entry[0] == Valtype.BASE: @@ -45,9 +44,9 @@ def get_msgdef(typename: str) -> Msgdef: if entry[0] == Valtype.MESSAGE: return Descriptor(Valtype.MESSAGE, get_msgdef(entry[1])) if entry[0] == Valtype.ARRAY: - return Descriptor(Valtype.ARRAY, (entry[1], fixup(entry[2]))) + return Descriptor(Valtype.ARRAY, (fixup(entry[1][0]), entry[1][1])) if entry[0] == Valtype.SEQUENCE: - return Descriptor(Valtype.SEQUENCE, fixup(entry[1])) + return Descriptor(Valtype.SEQUENCE, (fixup(entry[1][0]), entry[1][1])) raise SerdeError( # pragma: no cover f'Unknown field type {entry[0]!r} encountered.', ) diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index fb1c212b..e049adf6 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -22,12 +22,12 @@ if TYPE_CHECKING: def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Callable: - """Generate CDR serialization function. + """Generate ROS1 to CDR conversion function. Args: fields: Fields of message. typename: Message type name. - copy: Generate serialization or sizing function. + copy: Generate conversion or sizing function. Returns: ROS1 to CDR conversion function. @@ -42,17 +42,7 @@ def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Call 'import sys', 'import numpy', 'from rosbags.serde.messages import SerdeError, get_msgdef', - 'from rosbags.serde.primitives import pack_bool_le', - 'from rosbags.serde.primitives import pack_int8_le', - 'from rosbags.serde.primitives import pack_int16_le', 'from rosbags.serde.primitives import pack_int32_le', - 'from rosbags.serde.primitives import pack_int64_le', - 'from rosbags.serde.primitives import pack_uint8_le', - 'from rosbags.serde.primitives import pack_uint16_le', - 'from rosbags.serde.primitives import pack_uint32_le', - 'from rosbags.serde.primitives import pack_uint64_le', - 'from rosbags.serde.primitives import pack_float32_le', - 'from rosbags.serde.primitives import pack_float64_le', 'from rosbags.serde.primitives import unpack_int32_le', f'def {funcname}(input, ipos, output, opos):', ] @@ -89,11 +79,11 @@ def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Call aligned = size elif desc.valtype == Valtype.ARRAY: - subdesc = desc.args[1] + subdesc, length = desc.args if subdesc.valtype == Valtype.BASE: if subdesc.args == 'string': - for _ in range(desc.args[0]): + for _ in range(length): lines.append(' opos = (opos + 4 - 1) & -4') lines.append(' length = unpack_int32_le(input, ipos)[0] + 1') if copy: @@ -108,7 +98,7 @@ def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Call lines.append(' opos += length') aligned = 1 else: - size = desc.args[0] * SIZEMAP[subdesc.args] + size = length * SIZEMAP[subdesc.args] if copy: lines.append(f' output[opos:opos + {size}] = input[ipos:ipos + {size}]') lines.append(f' ipos += {size}') @@ -120,7 +110,7 @@ def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Call anext_after = align_after(subdesc) lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') - for _ in range(desc.args[0]): + for _ in range(length): if anext > anext_after: lines.append(f' opos = (opos + {anext} - 1) & -{anext}') lines.append(' ipos, opos = func(input, ipos, output, opos)') @@ -132,7 +122,7 @@ def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Call lines.append(' pack_int32_le(output, opos, size)') lines.append(' ipos += 4') lines.append(' opos += 4') - subdesc = desc.args + subdesc = desc.args[0] aligned = 4 if subdesc.valtype == Valtype.BASE: diff --git a/src/rosbags/serde/typing.py b/src/rosbags/serde/typing.py index a3b0d70c..3de40593 100644 --- a/src/rosbags/serde/typing.py +++ b/src/rosbags/serde/typing.py @@ -7,9 +7,14 @@ from __future__ import annotations from typing import TYPE_CHECKING, NamedTuple if TYPE_CHECKING: - from typing import Any, Callable, List # pylint: disable=ungrouped-imports + from typing import Any, Callable, List - from .utils import Descriptor + +class Descriptor(NamedTuple): + """Value type descriptor.""" + + valtype: int + args: Any class Field(NamedTuple): diff --git a/src/rosbags/serde/utils.py b/src/rosbags/serde/utils.py index 7ee18e49..5ebaa16c 100644 --- a/src/rosbags/serde/utils.py +++ b/src/rosbags/serde/utils.py @@ -6,11 +6,13 @@ from __future__ import annotations from enum import IntEnum from importlib.util import module_from_spec, spec_from_loader -from typing import TYPE_CHECKING, NamedTuple +from typing import TYPE_CHECKING if TYPE_CHECKING: from types import ModuleType - from typing import Any, Dict, List + from typing import Dict, List + + from .typing import Descriptor class Valtype(IntEnum): @@ -22,13 +24,6 @@ class Valtype(IntEnum): SEQUENCE = 4 -class Descriptor(NamedTuple): - """Value type descriptor.""" - - valtype: Valtype - args: Any # Union[Descriptor, Msgdef, Tuple[int, Descriptor], str] - - SIZEMAP: Dict[str, int] = { 'bool': 1, 'int8': 1, @@ -61,7 +56,7 @@ def align(entry: Descriptor) -> int: if entry.valtype == Valtype.MESSAGE: return align(entry.args.fields[0].descriptor) if entry.valtype == Valtype.ARRAY: - return align(entry.args[1]) + return align(entry.args[0]) assert entry.valtype == Valtype.SEQUENCE return 4 @@ -83,9 +78,9 @@ def align_after(entry: Descriptor) -> int: if entry.valtype == Valtype.MESSAGE: return align_after(entry.args.fields[-1].descriptor) if entry.valtype == Valtype.ARRAY: - return align_after(entry.args[1]) + return align_after(entry.args[0]) assert entry.valtype == Valtype.SEQUENCE - return min([4, align_after(entry.args)]) + return min([4, align_after(entry.args[0])]) def compile_lines(lines: List[str]) -> ModuleType: diff --git a/src/rosbags/typesys/base.py b/src/rosbags/typesys/base.py index d7d34377..02e0b3b9 100644 --- a/src/rosbags/typesys/base.py +++ b/src/rosbags/typesys/base.py @@ -8,12 +8,14 @@ from enum import IntEnum, auto from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Any, Dict, List, Tuple + from typing import Any, Dict, List, Optional, Tuple, Union from .peg import Visitor - Fielddefs = List[Tuple[Any, Any]] - Typesdict = Dict[str, Fielddefs] + Constdefs = List[Tuple[str, str, Any]] + Fielddesc = Tuple[int, Union[str, Tuple[Tuple[int, str], Optional[int]]]] + Fielddefs = List[Tuple[str, Fielddesc]] + Typesdict = Dict[str, Tuple[Constdefs, Fielddefs]] class TypesysError(Exception): diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index 259f7ce5..6c930c8f 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -261,8 +261,20 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods def visit_specification(self, children: Any) -> Typesdict: """Process start symbol, return only children of modules.""" children = [x[0] for x in children if x is not None] - modules = [y for t, x in children if t == Nodetype.MODULE for y in x] - return {x[1]: x[2] for x in modules if x[0] == Nodetype.STRUCT} + structs = {} + consts: dict[str, list[tuple[str, str, Any]]] = {} + for item in children: + if item[0] != Nodetype.MODULE: + continue + for subitem in item[1]: + if subitem[0] == Nodetype.STRUCT: + structs[subitem[1]] = subitem[2] + elif subitem[0] == Nodetype.CONST and '_Constants/' in subitem[1][1]: + structname, varname = subitem[1][1].split('_Constants/') + if structname not in consts: + consts[structname] = [] + consts[structname].append((varname, subitem[1][0], subitem[1][2])) + return {k: (consts.get(k, []), v) for k, v in structs.items()} def visit_comment(self, children: Any) -> Any: """Process comment, suppress output.""" @@ -273,12 +285,6 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods def visit_include(self, children: Any) -> Any: """Process include, suppress output.""" - def visit_type_dcl(self, children: Any) -> Any: - """Process typedef, pass structs, suppress otherwise.""" - if children[0] == Nodetype.STRUCT: - return children - return None - def visit_module_dcl(self, children: Any) -> Any: """Process module declaration.""" assert len(children) == 6 @@ -288,7 +294,6 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods children = children[4] consts = [] structs = [] - modules = [] for item in children: if not item or item[0] is None: continue @@ -299,20 +304,23 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods structs.append(item) else: assert item[0] == Nodetype.MODULE - modules.append(item) + consts += [x for x in item[1] if x[0] == Nodetype.CONST] + structs += [x for x in item[1] if x[0] == Nodetype.STRUCT] - for _, module in modules: - consts += [x for x in module if x[0] == Nodetype.CONST] - structs += [x for x in module if x[0] == Nodetype.STRUCT] - - consts = [(x[0], f'{name}/{x[1][0]}', *x[1][1:]) for x in consts] + consts = [(x[0], (x[1][0], f'{name}/{x[1][1]}', x[1][2])) for x in consts] structs = [(x[0], f'{name}/{x[1]}', *x[2:]) for x in structs] return (Nodetype.MODULE, consts + structs) def visit_const_dcl(self, children: Any) -> Any: """Process const declaration.""" - return (Nodetype.CONST, (children[1][1], *children[2:])) + return (Nodetype.CONST, (children[1][1], children[2][1], children[4][1])) + + def visit_type_dcl(self, children: Any) -> Any: + """Process type, pass structs, suppress otherwise.""" + if children[0] == Nodetype.STRUCT: + return children + return None def visit_type_declarator(self, children: Any) -> Any: """Process type declarator, register type mapping in instance typedef dictionary.""" @@ -323,7 +331,7 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods declarators = [children[1][0], *[x[1:][0] for x in children[1][1]]] for declarator in declarators: if declarator[0] == Nodetype.ADECLARATOR: - value = (Nodetype.ARRAY, declarator[2][1], base) + value = (Nodetype.ARRAY, (base, declarator[2][1])) else: value = base self.typedefs[declarator[1][1]] = value @@ -333,8 +341,7 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods assert len(children) in [4, 6] if len(children) == 6: assert children[4][0] == Nodetype.LITERAL_NUMBER - return (Nodetype.SEQUENCE, children[2]) - return (Nodetype.SEQUENCE, children[2]) + return (Nodetype.SEQUENCE, (children[2], None)) def create_struct_field(self, parts: Any) -> Any: """Create struct field and expand typedefs.""" @@ -346,7 +353,7 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods name = self.typedefs[name[1]] return name - yield from ((resolve_name(typename), x[1]) for x in params if x) + yield from ((x[1][1], resolve_name(typename)) for x in params if x) def visit_struct_dcl(self, children: Any) -> Any: """Process struct declaration.""" diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 59739c0f..75a24c4d 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -21,7 +21,7 @@ from .peg import Rule, Visitor, parse_grammar if TYPE_CHECKING: from typing import Any, List - from .base import Typesdict + from .base import Fielddesc, Typesdict GRAMMAR_MSG = r""" specification @@ -42,7 +42,7 @@ comment = r'#[^\n]*' const_dcl - = type_spec identifier '=' r'[^=][^\n]*' + = type_spec identifier '=' integer_literal field_dcl = type_spec identifier @@ -89,7 +89,7 @@ def normalize_msgtype(name: str) -> str: return str(path) -def normalize_fieldtype(typename: str, field: Any, names: List[str]): +def normalize_fieldtype(typename: str, field: Fielddesc, names: List[str]) -> Fielddesc: """Normalize field typename. Args: @@ -97,18 +97,20 @@ def normalize_fieldtype(typename: str, field: Any, names: List[str]): field: Field definition. names: Valid message names. + Returns: + Normalized fieldtype. + """ dct = {Path(name).name: name for name in names} - namedef = field[0] - if namedef[0] == Nodetype.NAME: - name = namedef[1] - elif namedef[0] == Nodetype.SEQUENCE: - name = namedef[1][1] + ftype, args = field + if ftype == Nodetype.NAME: + name = args else: - name = namedef[2][1] + name = args[0][1] + assert isinstance(name, str) if name in VisitorMSG.BASETYPES: - inamedef = (Nodetype.BASE, name) + ifield = (Nodetype.BASE, name) else: if name in dct: name = dct[name] @@ -118,16 +120,13 @@ def normalize_fieldtype(typename: str, field: Any, names: List[str]): name = str(Path(typename).parent / name) elif '/msg/' not in name: name = str((path := Path(name)).parent / 'msg' / path.name) - inamedef = (Nodetype.NAME, name) + ifield = (Nodetype.NAME, name) - if namedef[0] == Nodetype.NAME: - namedef = inamedef - elif namedef[0] == Nodetype.SEQUENCE: - namedef = (Nodetype.SEQUENCE, inamedef) - else: - namedef = (Nodetype.ARRAY, namedef[1], inamedef) + if ftype == Nodetype.NAME: + return ifield - field[0] = namedef + assert not isinstance(args, str) + return (ftype, (ifield, args[1])) class VisitorMSG(Visitor): @@ -157,6 +156,7 @@ class VisitorMSG(Visitor): def visit_const_dcl(self, children: Any) -> Any: """Process const declaration, suppress output.""" + return Nodetype.CONST, (children[0][1], children[1][1], children[3]) def visit_specification(self, children: Any) -> Typesdict: """Process start symbol.""" @@ -164,8 +164,10 @@ class VisitorMSG(Visitor): typedict = dict(typelist) names = list(typedict.keys()) for name, fields in typedict.items(): - for field in fields: - normalize_fieldtype(name, field, names) + consts = [(x[1][1], x[1][0], x[1][2]) for x in fields if x[0] == Nodetype.CONST] + fields = [x for x in fields if x[0] != Nodetype.CONST] + fields = [(field[1][1], normalize_fieldtype(name, field[0], names)) for field in fields] + typedict[name] = consts, fields return typedict def visit_msgdef(self, children: Any) -> Any: @@ -180,8 +182,8 @@ class VisitorMSG(Visitor): """Process array type specifier.""" length = children[1][1] if length: - return (Nodetype.ARRAY, int(length[0]), children[0]) - return (Nodetype.SEQUENCE, children[0]) + return Nodetype.ARRAY, (children[0], length[0]) + return Nodetype.SEQUENCE, (children[0], None) def visit_simple_type_spec(self, children: Any) -> Any: """Process simple type specifier.""" @@ -204,6 +206,10 @@ class VisitorMSG(Visitor): """Process identifier.""" return (Nodetype.NAME, children) + def visit_integer_literal(self, children: Any) -> Any: + """Process integer literal.""" + return int(children) + def get_types_from_msg(text: str, name: str) -> Typesdict: """Get type from msg message definition. diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index 59ead36d..cd433e5b 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -4,7 +4,6 @@ from __future__ import annotations -import json import re import sys from importlib.util import module_from_spec, spec_from_loader @@ -37,7 +36,7 @@ def get_typehint(desc: tuple) -> str: if desc[0] == Nodetype.NAME: return desc[1].replace('/', '__') - sub = desc[2 if desc[0] == Nodetype.ARRAY else 1] + sub = desc[1][0] if INTLIKE.match(sub[1]): typ = 'bool8' if sub[1] == 'bool' else sub[1] return f'numpy.ndarray[Any, numpy.dtype[numpy.{typ}]]' @@ -71,21 +70,27 @@ def generate_python_code(typs: Typesdict) -> str: 'from typing import TYPE_CHECKING', '', 'if TYPE_CHECKING:', - ' from typing import Any', + ' from typing import Any, ClassVar', '', ' import numpy', '', + ' from .base import Typesdict', '', ] - for name, fields in typs.items(): + for name, (consts, fields) in typs.items(): pyname = name.replace('/', '__') lines += [ '@dataclass', f'class {pyname}:', f' """Class for {name}."""', '', - *[f' {fname[1]}: {get_typehint(desc)}' for desc, fname in fields], + *[f' {fname}: {get_typehint(desc)}' for fname, desc in fields], + *[ + f' {fname}: ClassVar[{get_typehint((1, ftype))}] = {fvalue}' + for fname, ftype, fvalue in consts + ], + f' __msgtype__: ClassVar[str] = {name!r}', ] lines += [ @@ -93,16 +98,20 @@ def generate_python_code(typs: Typesdict) -> str: '', ] - lines += ['FIELDDEFS = {'] - for name, fields in typs.items(): + def get_ftype(ftype: tuple) -> tuple: + if ftype[0] <= 2: + return int(ftype[0]), ftype[1] + return int(ftype[0]), ((int(ftype[1][0][0]), ftype[1][0][1]), ftype[1][1]) + + lines += ['FIELDDEFS: Typesdict = {'] + for name, (consts, fields) in typs.items(): pyname = name.replace('/', '__') lines += [ - f' \'{name}\': [', - *[ - f' ({repr(fname[1])}, {json.loads(json.dumps(ftype))}),' - for ftype, fname in fields - ], - ' ],', + f' \'{name}\': ([', + *[f' ({fname!r}, {ftype!r}, {fvalue!r}),' for fname, ftype, fvalue in consts], + ' ], [', + *[f' ({fname!r}, {get_ftype(ftype)!r}),' for fname, ftype in fields], + ' ]),', ] lines += [ '}', @@ -127,15 +136,16 @@ def register_types(typs: Typesdict) -> None: module = module_from_spec(spec) sys.modules[name] = module exec(code, module.__dict__) # pylint: disable=exec-used - fielddefs = module.FIELDDEFS # type: ignore + fielddefs: Typesdict = module.FIELDDEFS # type: ignore - for name, fields in fielddefs.items(): + for name, (_, fields) in fielddefs.items(): if name == 'std_msgs/msg/Header': continue if have := types.FIELDDEFS.get(name): - have = [(x[0].lower(), x[1]) for x in have] + _, have_fields = have + have_fields = [(x[0].lower(), x[1]) for x in have_fields] fields = [(x[0].lower(), x[1]) for x in fields] - if have != fields: + if have_fields != fields: raise TypesysError(f'Type {name!r} is already present with different definition.') for name in fielddefs.keys() - types.FIELDDEFS.keys(): diff --git a/src/rosbags/typesys/types.py b/src/rosbags/typesys/types.py index f6c6263d..7c24dcf1 100644 --- a/src/rosbags/typesys/types.py +++ b/src/rosbags/typesys/types.py @@ -14,10 +14,11 @@ from dataclasses import dataclass from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Any + from typing import Any, ClassVar import numpy + from .base import Typesdict @dataclass class builtin_interfaces__msg__Time: @@ -25,6 +26,7 @@ class builtin_interfaces__msg__Time: sec: int nanosec: int + __msgtype__: ClassVar[str] = 'builtin_interfaces/msg/Time' @dataclass @@ -33,6 +35,7 @@ class builtin_interfaces__msg__Duration: sec: int nanosec: int + __msgtype__: ClassVar[str] = 'builtin_interfaces/msg/Duration' @dataclass @@ -44,6 +47,11 @@ class diagnostic_msgs__msg__DiagnosticStatus: message: str hardware_id: str values: list[diagnostic_msgs__msg__KeyValue] + OK: ClassVar[int] = 0 + WARN: ClassVar[int] = 1 + ERROR: ClassVar[int] = 2 + STALE: ClassVar[int] = 3 + __msgtype__: ClassVar[str] = 'diagnostic_msgs/msg/DiagnosticStatus' @dataclass @@ -52,6 +60,7 @@ class diagnostic_msgs__msg__DiagnosticArray: header: std_msgs__msg__Header status: list[diagnostic_msgs__msg__DiagnosticStatus] + __msgtype__: ClassVar[str] = 'diagnostic_msgs/msg/DiagnosticArray' @dataclass @@ -60,6 +69,7 @@ class diagnostic_msgs__msg__KeyValue: key: str value: str + __msgtype__: ClassVar[str] = 'diagnostic_msgs/msg/KeyValue' @dataclass @@ -68,6 +78,7 @@ class geometry_msgs__msg__AccelWithCovariance: accel: geometry_msgs__msg__Accel covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/AccelWithCovariance' @dataclass @@ -77,6 +88,7 @@ class geometry_msgs__msg__Point32: x: float y: float z: float + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Point32' @dataclass @@ -86,6 +98,7 @@ class geometry_msgs__msg__Vector3: x: float y: float z: float + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Vector3' @dataclass @@ -100,6 +113,7 @@ class geometry_msgs__msg__Inertia: iyy: float iyz: float izz: float + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Inertia' @dataclass @@ -108,6 +122,7 @@ class geometry_msgs__msg__PoseWithCovarianceStamped: header: std_msgs__msg__Header pose: geometry_msgs__msg__PoseWithCovariance + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PoseWithCovarianceStamped' @dataclass @@ -116,6 +131,7 @@ class geometry_msgs__msg__Twist: linear: geometry_msgs__msg__Vector3 angular: geometry_msgs__msg__Vector3 + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Twist' @dataclass @@ -124,6 +140,7 @@ class geometry_msgs__msg__Pose: position: geometry_msgs__msg__Point orientation: geometry_msgs__msg__Quaternion + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Pose' @dataclass @@ -133,6 +150,7 @@ class geometry_msgs__msg__Point: x: float y: float z: float + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Point' @dataclass @@ -141,6 +159,7 @@ class geometry_msgs__msg__Vector3Stamped: header: std_msgs__msg__Header vector: geometry_msgs__msg__Vector3 + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Vector3Stamped' @dataclass @@ -149,6 +168,7 @@ class geometry_msgs__msg__Transform: translation: geometry_msgs__msg__Vector3 rotation: geometry_msgs__msg__Quaternion + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Transform' @dataclass @@ -157,6 +177,7 @@ class geometry_msgs__msg__PolygonStamped: header: std_msgs__msg__Header polygon: geometry_msgs__msg__Polygon + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PolygonStamped' @dataclass @@ -167,6 +188,7 @@ class geometry_msgs__msg__Quaternion: y: float z: float w: float + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Quaternion' @dataclass @@ -176,6 +198,7 @@ class geometry_msgs__msg__Pose2D: x: float y: float theta: float + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Pose2D' @dataclass @@ -184,6 +207,7 @@ class geometry_msgs__msg__InertiaStamped: header: std_msgs__msg__Header inertia: geometry_msgs__msg__Inertia + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/InertiaStamped' @dataclass @@ -192,6 +216,7 @@ class geometry_msgs__msg__TwistStamped: header: std_msgs__msg__Header twist: geometry_msgs__msg__Twist + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/TwistStamped' @dataclass @@ -200,6 +225,7 @@ class geometry_msgs__msg__PoseStamped: header: std_msgs__msg__Header pose: geometry_msgs__msg__Pose + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PoseStamped' @dataclass @@ -208,6 +234,7 @@ class geometry_msgs__msg__PointStamped: header: std_msgs__msg__Header point: geometry_msgs__msg__Point + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PointStamped' @dataclass @@ -215,6 +242,7 @@ class geometry_msgs__msg__Polygon: """Class for geometry_msgs/msg/Polygon.""" points: list[geometry_msgs__msg__Point32] + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Polygon' @dataclass @@ -223,6 +251,7 @@ class geometry_msgs__msg__PoseArray: header: std_msgs__msg__Header poses: list[geometry_msgs__msg__Pose] + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PoseArray' @dataclass @@ -231,6 +260,7 @@ class geometry_msgs__msg__AccelStamped: header: std_msgs__msg__Header accel: geometry_msgs__msg__Accel + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/AccelStamped' @dataclass @@ -239,6 +269,7 @@ class geometry_msgs__msg__TwistWithCovarianceStamped: header: std_msgs__msg__Header twist: geometry_msgs__msg__TwistWithCovariance + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/TwistWithCovarianceStamped' @dataclass @@ -247,6 +278,7 @@ class geometry_msgs__msg__QuaternionStamped: header: std_msgs__msg__Header quaternion: geometry_msgs__msg__Quaternion + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/QuaternionStamped' @dataclass @@ -255,6 +287,7 @@ class geometry_msgs__msg__WrenchStamped: header: std_msgs__msg__Header wrench: geometry_msgs__msg__Wrench + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/WrenchStamped' @dataclass @@ -263,6 +296,7 @@ class geometry_msgs__msg__AccelWithCovarianceStamped: header: std_msgs__msg__Header accel: geometry_msgs__msg__AccelWithCovariance + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/AccelWithCovarianceStamped' @dataclass @@ -271,6 +305,7 @@ class geometry_msgs__msg__PoseWithCovariance: pose: geometry_msgs__msg__Pose covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PoseWithCovariance' @dataclass @@ -279,6 +314,7 @@ class geometry_msgs__msg__Wrench: force: geometry_msgs__msg__Vector3 torque: geometry_msgs__msg__Vector3 + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Wrench' @dataclass @@ -288,6 +324,7 @@ class geometry_msgs__msg__TransformStamped: header: std_msgs__msg__Header child_frame_id: str transform: geometry_msgs__msg__Transform + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/TransformStamped' @dataclass @@ -296,6 +333,7 @@ class geometry_msgs__msg__Accel: linear: geometry_msgs__msg__Vector3 angular: geometry_msgs__msg__Vector3 + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Accel' @dataclass @@ -304,6 +342,7 @@ class geometry_msgs__msg__TwistWithCovariance: twist: geometry_msgs__msg__Twist covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/TwistWithCovariance' @dataclass @@ -311,6 +350,7 @@ class libstatistics_collector__msg__DummyMessage: """Class for libstatistics_collector/msg/DummyMessage.""" header: std_msgs__msg__Header + __msgtype__: ClassVar[str] = 'libstatistics_collector/msg/DummyMessage' @dataclass @@ -320,6 +360,7 @@ class lifecycle_msgs__msg__TransitionDescription: transition: lifecycle_msgs__msg__Transition start_state: lifecycle_msgs__msg__State goal_state: lifecycle_msgs__msg__State + __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/TransitionDescription' @dataclass @@ -328,6 +369,18 @@ class lifecycle_msgs__msg__State: id: int label: str + PRIMARY_STATE_UNKNOWN: ClassVar[int] = 0 + PRIMARY_STATE_UNCONFIGURED: ClassVar[int] = 1 + PRIMARY_STATE_INACTIVE: ClassVar[int] = 2 + PRIMARY_STATE_ACTIVE: ClassVar[int] = 3 + PRIMARY_STATE_FINALIZED: ClassVar[int] = 4 + TRANSITION_STATE_CONFIGURING: ClassVar[int] = 10 + TRANSITION_STATE_CLEANINGUP: ClassVar[int] = 11 + TRANSITION_STATE_SHUTTINGDOWN: ClassVar[int] = 12 + TRANSITION_STATE_ACTIVATING: ClassVar[int] = 13 + TRANSITION_STATE_DEACTIVATING: ClassVar[int] = 14 + TRANSITION_STATE_ERRORPROCESSING: ClassVar[int] = 15 + __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/State' @dataclass @@ -338,6 +391,7 @@ class lifecycle_msgs__msg__TransitionEvent: transition: lifecycle_msgs__msg__Transition start_state: lifecycle_msgs__msg__State goal_state: lifecycle_msgs__msg__State + __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/TransitionEvent' @dataclass @@ -346,6 +400,37 @@ class lifecycle_msgs__msg__Transition: id: int label: str + TRANSITION_CREATE: ClassVar[int] = 0 + TRANSITION_CONFIGURE: ClassVar[int] = 1 + TRANSITION_CLEANUP: ClassVar[int] = 2 + TRANSITION_ACTIVATE: ClassVar[int] = 3 + TRANSITION_DEACTIVATE: ClassVar[int] = 4 + TRANSITION_UNCONFIGURED_SHUTDOWN: ClassVar[int] = 5 + TRANSITION_INACTIVE_SHUTDOWN: ClassVar[int] = 6 + TRANSITION_ACTIVE_SHUTDOWN: ClassVar[int] = 7 + TRANSITION_DESTROY: ClassVar[int] = 8 + TRANSITION_ON_CONFIGURE_SUCCESS: ClassVar[int] = 10 + TRANSITION_ON_CONFIGURE_FAILURE: ClassVar[int] = 11 + TRANSITION_ON_CONFIGURE_ERROR: ClassVar[int] = 12 + TRANSITION_ON_CLEANUP_SUCCESS: ClassVar[int] = 20 + TRANSITION_ON_CLEANUP_FAILURE: ClassVar[int] = 21 + TRANSITION_ON_CLEANUP_ERROR: ClassVar[int] = 22 + TRANSITION_ON_ACTIVATE_SUCCESS: ClassVar[int] = 30 + TRANSITION_ON_ACTIVATE_FAILURE: ClassVar[int] = 31 + TRANSITION_ON_ACTIVATE_ERROR: ClassVar[int] = 32 + TRANSITION_ON_DEACTIVATE_SUCCESS: ClassVar[int] = 40 + TRANSITION_ON_DEACTIVATE_FAILURE: ClassVar[int] = 41 + TRANSITION_ON_DEACTIVATE_ERROR: ClassVar[int] = 42 + TRANSITION_ON_SHUTDOWN_SUCCESS: ClassVar[int] = 50 + TRANSITION_ON_SHUTDOWN_FAILURE: ClassVar[int] = 51 + TRANSITION_ON_SHUTDOWN_ERROR: ClassVar[int] = 52 + TRANSITION_ON_ERROR_SUCCESS: ClassVar[int] = 60 + TRANSITION_ON_ERROR_FAILURE: ClassVar[int] = 61 + TRANSITION_ON_ERROR_ERROR: ClassVar[int] = 62 + TRANSITION_CALLBACK_SUCCESS: ClassVar[int] = 97 + TRANSITION_CALLBACK_FAILURE: ClassVar[int] = 98 + TRANSITION_CALLBACK_ERROR: ClassVar[int] = 99 + __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/Transition' @dataclass @@ -357,6 +442,7 @@ class nav_msgs__msg__MapMetaData: width: int height: int origin: geometry_msgs__msg__Pose + __msgtype__: ClassVar[str] = 'nav_msgs/msg/MapMetaData' @dataclass @@ -367,6 +453,7 @@ class nav_msgs__msg__GridCells: cell_width: float cell_height: float cells: list[geometry_msgs__msg__Point] + __msgtype__: ClassVar[str] = 'nav_msgs/msg/GridCells' @dataclass @@ -377,6 +464,7 @@ class nav_msgs__msg__Odometry: child_frame_id: str pose: geometry_msgs__msg__PoseWithCovariance twist: geometry_msgs__msg__TwistWithCovariance + __msgtype__: ClassVar[str] = 'nav_msgs/msg/Odometry' @dataclass @@ -385,6 +473,7 @@ class nav_msgs__msg__Path: header: std_msgs__msg__Header poses: list[geometry_msgs__msg__PoseStamped] + __msgtype__: ClassVar[str] = 'nav_msgs/msg/Path' @dataclass @@ -394,6 +483,7 @@ class nav_msgs__msg__OccupancyGrid: header: std_msgs__msg__Header info: nav_msgs__msg__MapMetaData data: numpy.ndarray[Any, numpy.dtype[numpy.int8]] + __msgtype__: ClassVar[str] = 'nav_msgs/msg/OccupancyGrid' @dataclass @@ -402,6 +492,7 @@ class rcl_interfaces__msg__ListParametersResult: names: list[str] prefixes: list[str] + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ListParametersResult' @dataclass @@ -409,6 +500,17 @@ class rcl_interfaces__msg__ParameterType: """Class for rcl_interfaces/msg/ParameterType.""" structure_needs_at_least_one_member: int + PARAMETER_NOT_SET: ClassVar[int] = 0 + PARAMETER_BOOL: ClassVar[int] = 1 + PARAMETER_INTEGER: ClassVar[int] = 2 + PARAMETER_DOUBLE: ClassVar[int] = 3 + PARAMETER_STRING: ClassVar[int] = 4 + PARAMETER_BYTE_ARRAY: ClassVar[int] = 5 + PARAMETER_BOOL_ARRAY: ClassVar[int] = 6 + PARAMETER_INTEGER_ARRAY: ClassVar[int] = 7 + PARAMETER_DOUBLE_ARRAY: ClassVar[int] = 8 + PARAMETER_STRING_ARRAY: ClassVar[int] = 9 + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterType' @dataclass @@ -418,6 +520,7 @@ class rcl_interfaces__msg__ParameterEventDescriptors: new_parameters: list[rcl_interfaces__msg__ParameterDescriptor] changed_parameters: list[rcl_interfaces__msg__ParameterDescriptor] deleted_parameters: list[rcl_interfaces__msg__ParameterDescriptor] + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterEventDescriptors' @dataclass @@ -429,6 +532,7 @@ class rcl_interfaces__msg__ParameterEvent: new_parameters: list[rcl_interfaces__msg__Parameter] changed_parameters: list[rcl_interfaces__msg__Parameter] deleted_parameters: list[rcl_interfaces__msg__Parameter] + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterEvent' @dataclass @@ -438,6 +542,7 @@ class rcl_interfaces__msg__IntegerRange: from_value: int to_value: int step: int + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/IntegerRange' @dataclass @@ -446,6 +551,7 @@ class rcl_interfaces__msg__Parameter: name: str value: rcl_interfaces__msg__ParameterValue + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/Parameter' @dataclass @@ -462,6 +568,7 @@ class rcl_interfaces__msg__ParameterValue: integer_array_value: numpy.ndarray[Any, numpy.dtype[numpy.int64]] double_array_value: numpy.ndarray[Any, numpy.dtype[numpy.float64]] string_array_value: list[str] + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterValue' @dataclass @@ -471,6 +578,7 @@ class rcl_interfaces__msg__FloatingPointRange: from_value: float to_value: float step: float + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/FloatingPointRange' @dataclass @@ -479,6 +587,7 @@ class rcl_interfaces__msg__SetParametersResult: successful: bool reason: str + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/SetParametersResult' @dataclass @@ -492,6 +601,12 @@ class rcl_interfaces__msg__Log: file: str function: str line: int + DEBUG: ClassVar[int] = 10 + INFO: ClassVar[int] = 20 + WARN: ClassVar[int] = 30 + ERROR: ClassVar[int] = 40 + FATAL: ClassVar[int] = 50 + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/Log' @dataclass @@ -505,6 +620,7 @@ class rcl_interfaces__msg__ParameterDescriptor: read_only: bool floating_point_range: list[rcl_interfaces__msg__FloatingPointRange] integer_range: list[rcl_interfaces__msg__IntegerRange] + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterDescriptor' @dataclass @@ -512,6 +628,7 @@ class rmw_dds_common__msg__Gid: """Class for rmw_dds_common/msg/Gid.""" data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + __msgtype__: ClassVar[str] = 'rmw_dds_common/msg/Gid' @dataclass @@ -522,6 +639,7 @@ class rmw_dds_common__msg__NodeEntitiesInfo: node_name: str reader_gid_seq: list[rmw_dds_common__msg__Gid] writer_gid_seq: list[rmw_dds_common__msg__Gid] + __msgtype__: ClassVar[str] = 'rmw_dds_common/msg/NodeEntitiesInfo' @dataclass @@ -530,6 +648,7 @@ class rmw_dds_common__msg__ParticipantEntitiesInfo: gid: rmw_dds_common__msg__Gid node_entities_info_seq: list[rmw_dds_common__msg__NodeEntitiesInfo] + __msgtype__: ClassVar[str] = 'rmw_dds_common/msg/ParticipantEntitiesInfo' @dataclass @@ -537,6 +656,7 @@ class rosgraph_msgs__msg__Clock: """Class for rosgraph_msgs/msg/Clock.""" clock: builtin_interfaces__msg__Time + __msgtype__: ClassVar[str] = 'rosgraph_msgs/msg/Clock' @dataclass @@ -546,6 +666,7 @@ class sensor_msgs__msg__Temperature: header: std_msgs__msg__Header temperature: float variance: float + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Temperature' @dataclass @@ -558,6 +679,9 @@ class sensor_msgs__msg__Range: min_range: float max_range: float range: float + ULTRASOUND: ClassVar[int] = 0 + INFRARED: ClassVar[int] = 1 + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Range' @dataclass @@ -569,6 +693,7 @@ class sensor_msgs__msg__RegionOfInterest: height: int width: int do_rectify: bool + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/RegionOfInterest' @dataclass @@ -576,6 +701,7 @@ class sensor_msgs__msg__JoyFeedbackArray: """Class for sensor_msgs/msg/JoyFeedbackArray.""" array: list[sensor_msgs__msg__JoyFeedback] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/JoyFeedbackArray' @dataclass @@ -585,6 +711,7 @@ class sensor_msgs__msg__TimeReference: header: std_msgs__msg__Header time_ref: builtin_interfaces__msg__Time source: str + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/TimeReference' @dataclass @@ -594,6 +721,7 @@ class sensor_msgs__msg__CompressedImage: header: std_msgs__msg__Header format: str data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/CompressedImage' @dataclass @@ -610,6 +738,7 @@ class sensor_msgs__msg__MultiEchoLaserScan: range_max: float ranges: list[sensor_msgs__msg__LaserEcho] intensities: list[sensor_msgs__msg__LaserEcho] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/MultiEchoLaserScan' @dataclass @@ -617,6 +746,7 @@ class sensor_msgs__msg__LaserEcho: """Class for sensor_msgs/msg/LaserEcho.""" echoes: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/LaserEcho' @dataclass @@ -625,6 +755,7 @@ class sensor_msgs__msg__ChannelFloat32: name: str values: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/ChannelFloat32' @dataclass @@ -642,6 +773,7 @@ class sensor_msgs__msg__CameraInfo: binning_x: int binning_y: int roi: sensor_msgs__msg__RegionOfInterest + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/CameraInfo' @dataclass @@ -651,6 +783,7 @@ class sensor_msgs__msg__RelativeHumidity: header: std_msgs__msg__Header relative_humidity: float variance: float + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/RelativeHumidity' @dataclass @@ -660,6 +793,7 @@ class sensor_msgs__msg__FluidPressure: header: std_msgs__msg__Header fluid_pressure: float variance: float + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/FluidPressure' @dataclass @@ -676,6 +810,7 @@ class sensor_msgs__msg__LaserScan: range_max: float ranges: numpy.ndarray[Any, numpy.dtype[numpy.float32]] intensities: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/LaserScan' @dataclass @@ -698,6 +833,28 @@ class sensor_msgs__msg__BatteryState: cell_temperature: numpy.ndarray[Any, numpy.dtype[numpy.float32]] location: str serial_number: str + POWER_SUPPLY_STATUS_UNKNOWN: ClassVar[int] = 0 + POWER_SUPPLY_STATUS_CHARGING: ClassVar[int] = 1 + POWER_SUPPLY_STATUS_DISCHARGING: ClassVar[int] = 2 + POWER_SUPPLY_STATUS_NOT_CHARGING: ClassVar[int] = 3 + POWER_SUPPLY_STATUS_FULL: ClassVar[int] = 4 + POWER_SUPPLY_HEALTH_UNKNOWN: ClassVar[int] = 0 + POWER_SUPPLY_HEALTH_GOOD: ClassVar[int] = 1 + POWER_SUPPLY_HEALTH_OVERHEAT: ClassVar[int] = 2 + POWER_SUPPLY_HEALTH_DEAD: ClassVar[int] = 3 + POWER_SUPPLY_HEALTH_OVERVOLTAGE: ClassVar[int] = 4 + POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: ClassVar[int] = 5 + POWER_SUPPLY_HEALTH_COLD: ClassVar[int] = 6 + POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE: ClassVar[int] = 7 + POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE: ClassVar[int] = 8 + POWER_SUPPLY_TECHNOLOGY_UNKNOWN: ClassVar[int] = 0 + POWER_SUPPLY_TECHNOLOGY_NIMH: ClassVar[int] = 1 + POWER_SUPPLY_TECHNOLOGY_LION: ClassVar[int] = 2 + POWER_SUPPLY_TECHNOLOGY_LIPO: ClassVar[int] = 3 + POWER_SUPPLY_TECHNOLOGY_LIFE: ClassVar[int] = 4 + POWER_SUPPLY_TECHNOLOGY_NICD: ClassVar[int] = 5 + POWER_SUPPLY_TECHNOLOGY_LIMN: ClassVar[int] = 6 + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/BatteryState' @dataclass @@ -711,6 +868,7 @@ class sensor_msgs__msg__Image: is_bigendian: int step: int data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Image' @dataclass @@ -720,6 +878,7 @@ class sensor_msgs__msg__PointCloud: header: std_msgs__msg__Header points: list[geometry_msgs__msg__Point32] channels: list[sensor_msgs__msg__ChannelFloat32] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/PointCloud' @dataclass @@ -733,6 +892,7 @@ class sensor_msgs__msg__Imu: angular_velocity_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] linear_acceleration: geometry_msgs__msg__Vector3 linear_acceleration_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Imu' @dataclass @@ -741,6 +901,15 @@ class sensor_msgs__msg__NavSatStatus: status: int service: int + STATUS_NO_FIX: ClassVar[int] = -1 + STATUS_FIX: ClassVar[int] = 0 + STATUS_SBAS_FIX: ClassVar[int] = 1 + STATUS_GBAS_FIX: ClassVar[int] = 2 + SERVICE_GPS: ClassVar[int] = 1 + SERVICE_GLONASS: ClassVar[int] = 2 + SERVICE_COMPASS: ClassVar[int] = 4 + SERVICE_GALILEO: ClassVar[int] = 8 + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/NavSatStatus' @dataclass @@ -750,6 +919,7 @@ class sensor_msgs__msg__Illuminance: header: std_msgs__msg__Header illuminance: float variance: float + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Illuminance' @dataclass @@ -759,6 +929,7 @@ class sensor_msgs__msg__Joy: header: std_msgs__msg__Header axes: numpy.ndarray[Any, numpy.dtype[numpy.float32]] buttons: numpy.ndarray[Any, numpy.dtype[numpy.int32]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Joy' @dataclass @@ -772,6 +943,11 @@ class sensor_msgs__msg__NavSatFix: altitude: float position_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] position_covariance_type: int + COVARIANCE_TYPE_UNKNOWN: ClassVar[int] = 0 + COVARIANCE_TYPE_APPROXIMATED: ClassVar[int] = 1 + COVARIANCE_TYPE_DIAGONAL_KNOWN: ClassVar[int] = 2 + COVARIANCE_TYPE_KNOWN: ClassVar[int] = 3 + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/NavSatFix' @dataclass @@ -783,6 +959,7 @@ class sensor_msgs__msg__MultiDOFJointState: transforms: list[geometry_msgs__msg__Transform] twist: list[geometry_msgs__msg__Twist] wrench: list[geometry_msgs__msg__Wrench] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/MultiDOFJointState' @dataclass @@ -792,6 +969,7 @@ class sensor_msgs__msg__MagneticField: header: std_msgs__msg__Header magnetic_field: geometry_msgs__msg__Vector3 magnetic_field_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/MagneticField' @dataclass @@ -803,6 +981,7 @@ class sensor_msgs__msg__JointState: position: numpy.ndarray[Any, numpy.dtype[numpy.float64]] velocity: numpy.ndarray[Any, numpy.dtype[numpy.float64]] effort: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/JointState' @dataclass @@ -813,6 +992,15 @@ class sensor_msgs__msg__PointField: offset: int datatype: int count: int + INT8: ClassVar[int] = 1 + UINT8: ClassVar[int] = 2 + INT16: ClassVar[int] = 3 + UINT16: ClassVar[int] = 4 + INT32: ClassVar[int] = 5 + UINT32: ClassVar[int] = 6 + FLOAT32: ClassVar[int] = 7 + FLOAT64: ClassVar[int] = 8 + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/PointField' @dataclass @@ -828,6 +1016,7 @@ class sensor_msgs__msg__PointCloud2: row_step: int data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] is_dense: bool + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/PointCloud2' @dataclass @@ -837,6 +1026,10 @@ class sensor_msgs__msg__JoyFeedback: type: int id: int intensity: float + TYPE_LED: ClassVar[int] = 0 + TYPE_RUMBLE: ClassVar[int] = 1 + TYPE_BUZZER: ClassVar[int] = 2 + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/JoyFeedback' @dataclass @@ -845,6 +1038,19 @@ class shape_msgs__msg__SolidPrimitive: type: int dimensions: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + BOX: ClassVar[int] = 1 + SPHERE: ClassVar[int] = 2 + CYLINDER: ClassVar[int] = 3 + CONE: ClassVar[int] = 4 + BOX_X: ClassVar[int] = 0 + BOX_Y: ClassVar[int] = 1 + BOX_Z: ClassVar[int] = 2 + SPHERE_RADIUS: ClassVar[int] = 0 + CYLINDER_HEIGHT: ClassVar[int] = 0 + CYLINDER_RADIUS: ClassVar[int] = 1 + CONE_HEIGHT: ClassVar[int] = 0 + CONE_RADIUS: ClassVar[int] = 1 + __msgtype__: ClassVar[str] = 'shape_msgs/msg/SolidPrimitive' @dataclass @@ -853,6 +1059,7 @@ class shape_msgs__msg__Mesh: triangles: list[shape_msgs__msg__MeshTriangle] vertices: list[geometry_msgs__msg__Point] + __msgtype__: ClassVar[str] = 'shape_msgs/msg/Mesh' @dataclass @@ -860,6 +1067,7 @@ class shape_msgs__msg__Plane: """Class for shape_msgs/msg/Plane.""" coef: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'shape_msgs/msg/Plane' @dataclass @@ -867,6 +1075,7 @@ class shape_msgs__msg__MeshTriangle: """Class for shape_msgs/msg/MeshTriangle.""" vertex_indices: numpy.ndarray[Any, numpy.dtype[numpy.uint32]] + __msgtype__: ClassVar[str] = 'shape_msgs/msg/MeshTriangle' @dataclass @@ -874,6 +1083,13 @@ class statistics_msgs__msg__StatisticDataType: """Class for statistics_msgs/msg/StatisticDataType.""" structure_needs_at_least_one_member: int + STATISTICS_DATA_TYPE_UNINITIALIZED: ClassVar[int] = 0 + STATISTICS_DATA_TYPE_AVERAGE: ClassVar[int] = 1 + STATISTICS_DATA_TYPE_MINIMUM: ClassVar[int] = 2 + STATISTICS_DATA_TYPE_MAXIMUM: ClassVar[int] = 3 + STATISTICS_DATA_TYPE_STDDEV: ClassVar[int] = 4 + STATISTICS_DATA_TYPE_SAMPLE_COUNT: ClassVar[int] = 5 + __msgtype__: ClassVar[str] = 'statistics_msgs/msg/StatisticDataType' @dataclass @@ -882,6 +1098,7 @@ class statistics_msgs__msg__StatisticDataPoint: data_type: int data: float + __msgtype__: ClassVar[str] = 'statistics_msgs/msg/StatisticDataPoint' @dataclass @@ -894,6 +1111,7 @@ class statistics_msgs__msg__MetricsMessage: window_start: builtin_interfaces__msg__Time window_stop: builtin_interfaces__msg__Time statistics: list[statistics_msgs__msg__StatisticDataPoint] + __msgtype__: ClassVar[str] = 'statistics_msgs/msg/MetricsMessage' @dataclass @@ -901,6 +1119,7 @@ class std_msgs__msg__UInt8: """Class for std_msgs/msg/UInt8.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt8' @dataclass @@ -909,6 +1128,7 @@ class std_msgs__msg__Float32MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Float32MultiArray' @dataclass @@ -916,6 +1136,7 @@ class std_msgs__msg__Int8: """Class for std_msgs/msg/Int8.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int8' @dataclass @@ -923,6 +1144,7 @@ class std_msgs__msg__Empty: """Class for std_msgs/msg/Empty.""" structure_needs_at_least_one_member: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Empty' @dataclass @@ -930,6 +1152,7 @@ class std_msgs__msg__String: """Class for std_msgs/msg/String.""" data: str + __msgtype__: ClassVar[str] = 'std_msgs/msg/String' @dataclass @@ -939,6 +1162,7 @@ class std_msgs__msg__MultiArrayDimension: label: str size: int stride: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/MultiArrayDimension' @dataclass @@ -946,6 +1170,7 @@ class std_msgs__msg__UInt64: """Class for std_msgs/msg/UInt64.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt64' @dataclass @@ -953,6 +1178,7 @@ class std_msgs__msg__UInt16: """Class for std_msgs/msg/UInt16.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt16' @dataclass @@ -960,6 +1186,7 @@ class std_msgs__msg__Float32: """Class for std_msgs/msg/Float32.""" data: float + __msgtype__: ClassVar[str] = 'std_msgs/msg/Float32' @dataclass @@ -967,6 +1194,7 @@ class std_msgs__msg__Int64: """Class for std_msgs/msg/Int64.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int64' @dataclass @@ -975,6 +1203,7 @@ class std_msgs__msg__Int16MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.int16]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int16MultiArray' @dataclass @@ -982,6 +1211,7 @@ class std_msgs__msg__Int16: """Class for std_msgs/msg/Int16.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int16' @dataclass @@ -990,6 +1220,7 @@ class std_msgs__msg__Float64MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Float64MultiArray' @dataclass @@ -998,6 +1229,7 @@ class std_msgs__msg__MultiArrayLayout: dim: list[std_msgs__msg__MultiArrayDimension] data_offset: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/MultiArrayLayout' @dataclass @@ -1006,6 +1238,7 @@ class std_msgs__msg__UInt32MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.uint32]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt32MultiArray' @dataclass @@ -1014,6 +1247,7 @@ class std_msgs__msg__Header: stamp: builtin_interfaces__msg__Time frame_id: str + __msgtype__: ClassVar[str] = 'std_msgs/msg/Header' @dataclass @@ -1022,6 +1256,7 @@ class std_msgs__msg__ByteMultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/ByteMultiArray' @dataclass @@ -1030,6 +1265,7 @@ class std_msgs__msg__Int8MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.int8]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int8MultiArray' @dataclass @@ -1037,6 +1273,7 @@ class std_msgs__msg__Float64: """Class for std_msgs/msg/Float64.""" data: float + __msgtype__: ClassVar[str] = 'std_msgs/msg/Float64' @dataclass @@ -1045,6 +1282,7 @@ class std_msgs__msg__UInt8MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt8MultiArray' @dataclass @@ -1052,6 +1290,7 @@ class std_msgs__msg__Byte: """Class for std_msgs/msg/Byte.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Byte' @dataclass @@ -1059,6 +1298,7 @@ class std_msgs__msg__Char: """Class for std_msgs/msg/Char.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Char' @dataclass @@ -1067,6 +1307,7 @@ class std_msgs__msg__UInt64MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.uint64]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt64MultiArray' @dataclass @@ -1075,6 +1316,7 @@ class std_msgs__msg__Int32MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.int32]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int32MultiArray' @dataclass @@ -1085,6 +1327,7 @@ class std_msgs__msg__ColorRGBA: g: float b: float a: float + __msgtype__: ClassVar[str] = 'std_msgs/msg/ColorRGBA' @dataclass @@ -1092,6 +1335,7 @@ class std_msgs__msg__Bool: """Class for std_msgs/msg/Bool.""" data: bool + __msgtype__: ClassVar[str] = 'std_msgs/msg/Bool' @dataclass @@ -1099,6 +1343,7 @@ class std_msgs__msg__UInt32: """Class for std_msgs/msg/UInt32.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt32' @dataclass @@ -1107,6 +1352,7 @@ class std_msgs__msg__Int64MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.int64]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int64MultiArray' @dataclass @@ -1114,6 +1360,7 @@ class std_msgs__msg__Int32: """Class for std_msgs/msg/Int32.""" data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int32' @dataclass @@ -1122,6 +1369,7 @@ class std_msgs__msg__UInt16MultiArray: layout: std_msgs__msg__MultiArrayLayout data: numpy.ndarray[Any, numpy.dtype[numpy.uint16]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt16MultiArray' @dataclass @@ -1136,6 +1384,7 @@ class stereo_msgs__msg__DisparityImage: min_disparity: float max_disparity: float delta_d: float + __msgtype__: ClassVar[str] = 'stereo_msgs/msg/DisparityImage' @dataclass @@ -1144,6 +1393,14 @@ class tf2_msgs__msg__TF2Error: error: int error_string: str + NO_ERROR: ClassVar[int] = 0 + LOOKUP_ERROR: ClassVar[int] = 1 + CONNECTIVITY_ERROR: ClassVar[int] = 2 + EXTRAPOLATION_ERROR: ClassVar[int] = 3 + INVALID_ARGUMENT_ERROR: ClassVar[int] = 4 + TIMEOUT_ERROR: ClassVar[int] = 5 + TRANSFORM_ERROR: ClassVar[int] = 6 + __msgtype__: ClassVar[str] = 'tf2_msgs/msg/TF2Error' @dataclass @@ -1151,6 +1408,7 @@ class tf2_msgs__msg__TFMessage: """Class for tf2_msgs/msg/TFMessage.""" transforms: list[geometry_msgs__msg__TransformStamped] + __msgtype__: ClassVar[str] = 'tf2_msgs/msg/TFMessage' @dataclass @@ -1160,6 +1418,7 @@ class trajectory_msgs__msg__MultiDOFJointTrajectory: header: std_msgs__msg__Header joint_names: list[str] points: list[trajectory_msgs__msg__MultiDOFJointTrajectoryPoint] + __msgtype__: ClassVar[str] = 'trajectory_msgs/msg/MultiDOFJointTrajectory' @dataclass @@ -1169,6 +1428,7 @@ class trajectory_msgs__msg__JointTrajectory: header: std_msgs__msg__Header joint_names: list[str] points: list[trajectory_msgs__msg__JointTrajectoryPoint] + __msgtype__: ClassVar[str] = 'trajectory_msgs/msg/JointTrajectory' @dataclass @@ -1180,6 +1440,7 @@ class trajectory_msgs__msg__JointTrajectoryPoint: accelerations: numpy.ndarray[Any, numpy.dtype[numpy.float64]] effort: numpy.ndarray[Any, numpy.dtype[numpy.float64]] time_from_start: builtin_interfaces__msg__Duration + __msgtype__: ClassVar[str] = 'trajectory_msgs/msg/JointTrajectoryPoint' @dataclass @@ -1190,6 +1451,7 @@ class trajectory_msgs__msg__MultiDOFJointTrajectoryPoint: velocities: list[geometry_msgs__msg__Twist] accelerations: list[geometry_msgs__msg__Twist] time_from_start: builtin_interfaces__msg__Duration + __msgtype__: ClassVar[str] = 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint' @dataclass @@ -1197,6 +1459,7 @@ class unique_identifier_msgs__msg__UUID: """Class for unique_identifier_msgs/msg/UUID.""" uuid: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + __msgtype__: ClassVar[str] = 'unique_identifier_msgs/msg/UUID' @dataclass @@ -1218,6 +1481,23 @@ class visualization_msgs__msg__Marker: text: str mesh_resource: str mesh_use_embedded_materials: bool + ARROW: ClassVar[int] = 0 + CUBE: ClassVar[int] = 1 + SPHERE: ClassVar[int] = 2 + CYLINDER: ClassVar[int] = 3 + LINE_STRIP: ClassVar[int] = 4 + LINE_LIST: ClassVar[int] = 5 + CUBE_LIST: ClassVar[int] = 6 + SPHERE_LIST: ClassVar[int] = 7 + POINTS: ClassVar[int] = 8 + TEXT_VIEW_FACING: ClassVar[int] = 9 + MESH_RESOURCE: ClassVar[int] = 10 + TRIANGLE_LIST: ClassVar[int] = 11 + ADD: ClassVar[int] = 0 + MODIFY: ClassVar[int] = 0 + DELETE: ClassVar[int] = 2 + DELETEALL: ClassVar[int] = 3 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/Marker' @dataclass @@ -1227,6 +1507,7 @@ class visualization_msgs__msg__InteractiveMarkerInit: server_id: str seq_num: int markers: list[visualization_msgs__msg__InteractiveMarker] + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerInit' @dataclass @@ -1238,6 +1519,10 @@ class visualization_msgs__msg__MenuEntry: title: str command: str command_type: int + FEEDBACK: ClassVar[int] = 0 + ROSRUN: ClassVar[int] = 1 + ROSLAUNCH: ClassVar[int] = 2 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/MenuEntry' @dataclass @@ -1245,6 +1530,7 @@ class visualization_msgs__msg__MarkerArray: """Class for visualization_msgs/msg/MarkerArray.""" markers: list[visualization_msgs__msg__Marker] + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/MarkerArray' @dataclass @@ -1257,6 +1543,9 @@ class visualization_msgs__msg__InteractiveMarkerUpdate: markers: list[visualization_msgs__msg__InteractiveMarker] poses: list[visualization_msgs__msg__InteractiveMarkerPose] erases: list[str] + KEEP_ALIVE: ClassVar[int] = 0 + UPDATE: ClassVar[int] = 1 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerUpdate' @dataclass @@ -1270,6 +1559,7 @@ class visualization_msgs__msg__InteractiveMarker: scale: float menu_entries: list[visualization_msgs__msg__MenuEntry] controls: list[visualization_msgs__msg__InteractiveMarkerControl] + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarker' @dataclass @@ -1285,6 +1575,13 @@ class visualization_msgs__msg__InteractiveMarkerFeedback: menu_entry_id: int mouse_point: geometry_msgs__msg__Point mouse_point_valid: bool + KEEP_ALIVE: ClassVar[int] = 0 + POSE_UPDATE: ClassVar[int] = 1 + MENU_SELECT: ClassVar[int] = 2 + BUTTON_CLICK: ClassVar[int] = 3 + MOUSE_DOWN: ClassVar[int] = 4 + MOUSE_UP: ClassVar[int] = 5 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerFeedback' @dataclass @@ -1304,6 +1601,14 @@ class visualization_msgs__msg__ImageMarker: lifetime: builtin_interfaces__msg__Duration points: list[geometry_msgs__msg__Point] outline_colors: list[std_msgs__msg__ColorRGBA] + CIRCLE: ClassVar[int] = 0 + LINE_STRIP: ClassVar[int] = 1 + LINE_LIST: ClassVar[int] = 2 + POLYGON: ClassVar[int] = 3 + POINTS: ClassVar[int] = 4 + ADD: ClassVar[int] = 0 + REMOVE: ClassVar[int] = 1 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/ImageMarker' @dataclass @@ -1318,6 +1623,20 @@ class visualization_msgs__msg__InteractiveMarkerControl: markers: list[visualization_msgs__msg__Marker] independent_marker_orientation: bool description: str + INHERIT: ClassVar[int] = 0 + FIXED: ClassVar[int] = 1 + VIEW_FACING: ClassVar[int] = 2 + NONE: ClassVar[int] = 0 + MENU: ClassVar[int] = 1 + BUTTON: ClassVar[int] = 2 + MOVE_AXIS: ClassVar[int] = 3 + MOVE_PLANE: ClassVar[int] = 4 + ROTATE_AXIS: ClassVar[int] = 5 + MOVE_ROTATE: ClassVar[int] = 6 + MOVE_3D: ClassVar[int] = 7 + ROTATE_3D: ClassVar[int] = 8 + MOVE_ROTATE_3D: ClassVar[int] = 9 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerControl' @dataclass @@ -1327,753 +1646,1073 @@ class visualization_msgs__msg__InteractiveMarkerPose: header: std_msgs__msg__Header pose: geometry_msgs__msg__Pose name: str + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerPose' -FIELDDEFS = { - 'builtin_interfaces/msg/Time': [ - ('sec', [1, 'int32']), - ('nanosec', [1, 'uint32']), - ], - 'builtin_interfaces/msg/Duration': [ - ('sec', [1, 'int32']), - ('nanosec', [1, 'uint32']), - ], - 'diagnostic_msgs/msg/DiagnosticStatus': [ - ('level', [1, 'uint8']), - ('name', [1, 'string']), - ('message', [1, 'string']), - ('hardware_id', [1, 'string']), - ('values', [4, [2, 'diagnostic_msgs/msg/KeyValue']]), - ], - 'diagnostic_msgs/msg/DiagnosticArray': [ - ('header', [2, 'std_msgs/msg/Header']), - ('status', [4, [2, 'diagnostic_msgs/msg/DiagnosticStatus']]), - ], - 'diagnostic_msgs/msg/KeyValue': [ - ('key', [1, 'string']), - ('value', [1, 'string']), - ], - 'geometry_msgs/msg/AccelWithCovariance': [ - ('accel', [2, 'geometry_msgs/msg/Accel']), - ('covariance', [3, 36, [1, 'float64']]), - ], - 'geometry_msgs/msg/Point32': [ - ('x', [1, 'float32']), - ('y', [1, 'float32']), - ('z', [1, 'float32']), - ], - 'geometry_msgs/msg/Vector3': [ - ('x', [1, 'float64']), - ('y', [1, 'float64']), - ('z', [1, 'float64']), - ], - 'geometry_msgs/msg/Inertia': [ - ('m', [1, 'float64']), - ('com', [2, 'geometry_msgs/msg/Vector3']), - ('ixx', [1, 'float64']), - ('ixy', [1, 'float64']), - ('ixz', [1, 'float64']), - ('iyy', [1, 'float64']), - ('iyz', [1, 'float64']), - ('izz', [1, 'float64']), - ], - 'geometry_msgs/msg/PoseWithCovarianceStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('pose', [2, 'geometry_msgs/msg/PoseWithCovariance']), - ], - 'geometry_msgs/msg/Twist': [ - ('linear', [2, 'geometry_msgs/msg/Vector3']), - ('angular', [2, 'geometry_msgs/msg/Vector3']), - ], - 'geometry_msgs/msg/Pose': [ - ('position', [2, 'geometry_msgs/msg/Point']), - ('orientation', [2, 'geometry_msgs/msg/Quaternion']), - ], - 'geometry_msgs/msg/Point': [ - ('x', [1, 'float64']), - ('y', [1, 'float64']), - ('z', [1, 'float64']), - ], - 'geometry_msgs/msg/Vector3Stamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('vector', [2, 'geometry_msgs/msg/Vector3']), - ], - 'geometry_msgs/msg/Transform': [ - ('translation', [2, 'geometry_msgs/msg/Vector3']), - ('rotation', [2, 'geometry_msgs/msg/Quaternion']), - ], - 'geometry_msgs/msg/PolygonStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('polygon', [2, 'geometry_msgs/msg/Polygon']), - ], - 'geometry_msgs/msg/Quaternion': [ - ('x', [1, 'float64']), - ('y', [1, 'float64']), - ('z', [1, 'float64']), - ('w', [1, 'float64']), - ], - 'geometry_msgs/msg/Pose2D': [ - ('x', [1, 'float64']), - ('y', [1, 'float64']), - ('theta', [1, 'float64']), - ], - 'geometry_msgs/msg/InertiaStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('inertia', [2, 'geometry_msgs/msg/Inertia']), - ], - 'geometry_msgs/msg/TwistStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('twist', [2, 'geometry_msgs/msg/Twist']), - ], - 'geometry_msgs/msg/PoseStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('pose', [2, 'geometry_msgs/msg/Pose']), - ], - 'geometry_msgs/msg/PointStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('point', [2, 'geometry_msgs/msg/Point']), - ], - 'geometry_msgs/msg/Polygon': [ - ('points', [4, [2, 'geometry_msgs/msg/Point32']]), - ], - 'geometry_msgs/msg/PoseArray': [ - ('header', [2, 'std_msgs/msg/Header']), - ('poses', [4, [2, 'geometry_msgs/msg/Pose']]), - ], - 'geometry_msgs/msg/AccelStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('accel', [2, 'geometry_msgs/msg/Accel']), - ], - 'geometry_msgs/msg/TwistWithCovarianceStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('twist', [2, 'geometry_msgs/msg/TwistWithCovariance']), - ], - 'geometry_msgs/msg/QuaternionStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('quaternion', [2, 'geometry_msgs/msg/Quaternion']), - ], - 'geometry_msgs/msg/WrenchStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('wrench', [2, 'geometry_msgs/msg/Wrench']), - ], - 'geometry_msgs/msg/AccelWithCovarianceStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('accel', [2, 'geometry_msgs/msg/AccelWithCovariance']), - ], - 'geometry_msgs/msg/PoseWithCovariance': [ - ('pose', [2, 'geometry_msgs/msg/Pose']), - ('covariance', [3, 36, [1, 'float64']]), - ], - 'geometry_msgs/msg/Wrench': [ - ('force', [2, 'geometry_msgs/msg/Vector3']), - ('torque', [2, 'geometry_msgs/msg/Vector3']), - ], - 'geometry_msgs/msg/TransformStamped': [ - ('header', [2, 'std_msgs/msg/Header']), - ('child_frame_id', [1, 'string']), - ('transform', [2, 'geometry_msgs/msg/Transform']), - ], - 'geometry_msgs/msg/Accel': [ - ('linear', [2, 'geometry_msgs/msg/Vector3']), - ('angular', [2, 'geometry_msgs/msg/Vector3']), - ], - 'geometry_msgs/msg/TwistWithCovariance': [ - ('twist', [2, 'geometry_msgs/msg/Twist']), - ('covariance', [3, 36, [1, 'float64']]), - ], - 'libstatistics_collector/msg/DummyMessage': [ - ('header', [2, 'std_msgs/msg/Header']), - ], - 'lifecycle_msgs/msg/TransitionDescription': [ - ('transition', [2, 'lifecycle_msgs/msg/Transition']), - ('start_state', [2, 'lifecycle_msgs/msg/State']), - ('goal_state', [2, 'lifecycle_msgs/msg/State']), - ], - 'lifecycle_msgs/msg/State': [ - ('id', [1, 'uint8']), - ('label', [1, 'string']), - ], - 'lifecycle_msgs/msg/TransitionEvent': [ - ('timestamp', [1, 'uint64']), - ('transition', [2, 'lifecycle_msgs/msg/Transition']), - ('start_state', [2, 'lifecycle_msgs/msg/State']), - ('goal_state', [2, 'lifecycle_msgs/msg/State']), - ], - 'lifecycle_msgs/msg/Transition': [ - ('id', [1, 'uint8']), - ('label', [1, 'string']), - ], - 'nav_msgs/msg/MapMetaData': [ - ('map_load_time', [2, 'builtin_interfaces/msg/Time']), - ('resolution', [1, 'float32']), - ('width', [1, 'uint32']), - ('height', [1, 'uint32']), - ('origin', [2, 'geometry_msgs/msg/Pose']), - ], - 'nav_msgs/msg/GridCells': [ - ('header', [2, 'std_msgs/msg/Header']), - ('cell_width', [1, 'float32']), - ('cell_height', [1, 'float32']), - ('cells', [4, [2, 'geometry_msgs/msg/Point']]), - ], - 'nav_msgs/msg/Odometry': [ - ('header', [2, 'std_msgs/msg/Header']), - ('child_frame_id', [1, 'string']), - ('pose', [2, 'geometry_msgs/msg/PoseWithCovariance']), - ('twist', [2, 'geometry_msgs/msg/TwistWithCovariance']), - ], - 'nav_msgs/msg/Path': [ - ('header', [2, 'std_msgs/msg/Header']), - ('poses', [4, [2, 'geometry_msgs/msg/PoseStamped']]), - ], - 'nav_msgs/msg/OccupancyGrid': [ - ('header', [2, 'std_msgs/msg/Header']), - ('info', [2, 'nav_msgs/msg/MapMetaData']), - ('data', [4, [1, 'int8']]), - ], - 'rcl_interfaces/msg/ListParametersResult': [ - ('names', [4, [1, 'string']]), - ('prefixes', [4, [1, 'string']]), - ], - 'rcl_interfaces/msg/ParameterType': [ - ('structure_needs_at_least_one_member', [1, 'uint8']), - ], - 'rcl_interfaces/msg/ParameterEventDescriptors': [ - ('new_parameters', [4, [2, 'rcl_interfaces/msg/ParameterDescriptor']]), - ('changed_parameters', [4, [2, 'rcl_interfaces/msg/ParameterDescriptor']]), - ('deleted_parameters', [4, [2, 'rcl_interfaces/msg/ParameterDescriptor']]), - ], - 'rcl_interfaces/msg/ParameterEvent': [ - ('stamp', [2, 'builtin_interfaces/msg/Time']), - ('node', [1, 'string']), - ('new_parameters', [4, [2, 'rcl_interfaces/msg/Parameter']]), - ('changed_parameters', [4, [2, 'rcl_interfaces/msg/Parameter']]), - ('deleted_parameters', [4, [2, 'rcl_interfaces/msg/Parameter']]), - ], - 'rcl_interfaces/msg/IntegerRange': [ - ('from_value', [1, 'int64']), - ('to_value', [1, 'int64']), - ('step', [1, 'uint64']), - ], - 'rcl_interfaces/msg/Parameter': [ - ('name', [1, 'string']), - ('value', [2, 'rcl_interfaces/msg/ParameterValue']), - ], - 'rcl_interfaces/msg/ParameterValue': [ - ('type', [1, 'uint8']), - ('bool_value', [1, 'bool']), - ('integer_value', [1, 'int64']), - ('double_value', [1, 'float64']), - ('string_value', [1, 'string']), - ('byte_array_value', [4, [1, 'uint8']]), - ('bool_array_value', [4, [1, 'bool']]), - ('integer_array_value', [4, [1, 'int64']]), - ('double_array_value', [4, [1, 'float64']]), - ('string_array_value', [4, [1, 'string']]), - ], - 'rcl_interfaces/msg/FloatingPointRange': [ - ('from_value', [1, 'float64']), - ('to_value', [1, 'float64']), - ('step', [1, 'float64']), - ], - 'rcl_interfaces/msg/SetParametersResult': [ - ('successful', [1, 'bool']), - ('reason', [1, 'string']), - ], - 'rcl_interfaces/msg/Log': [ - ('stamp', [2, 'builtin_interfaces/msg/Time']), - ('level', [1, 'uint8']), - ('name', [1, 'string']), - ('msg', [1, 'string']), - ('file', [1, 'string']), - ('function', [1, 'string']), - ('line', [1, 'uint32']), - ], - 'rcl_interfaces/msg/ParameterDescriptor': [ - ('name', [1, 'string']), - ('type', [1, 'uint8']), - ('description', [1, 'string']), - ('additional_constraints', [1, 'string']), - ('read_only', [1, 'bool']), - ('floating_point_range', [4, [2, 'rcl_interfaces/msg/FloatingPointRange']]), - ('integer_range', [4, [2, 'rcl_interfaces/msg/IntegerRange']]), - ], - 'rmw_dds_common/msg/Gid': [ - ('data', [3, 24, [1, 'uint8']]), - ], - 'rmw_dds_common/msg/NodeEntitiesInfo': [ - ('node_namespace', [1, 'string', [6, 256]]), - ('node_name', [1, 'string', [6, 256]]), - ('reader_gid_seq', [4, [2, 'rmw_dds_common/msg/Gid']]), - ('writer_gid_seq', [4, [2, 'rmw_dds_common/msg/Gid']]), - ], - 'rmw_dds_common/msg/ParticipantEntitiesInfo': [ - ('gid', [2, 'rmw_dds_common/msg/Gid']), - ('node_entities_info_seq', [4, [2, 'rmw_dds_common/msg/NodeEntitiesInfo']]), - ], - 'rosgraph_msgs/msg/Clock': [ - ('clock', [2, 'builtin_interfaces/msg/Time']), - ], - 'sensor_msgs/msg/Temperature': [ - ('header', [2, 'std_msgs/msg/Header']), - ('temperature', [1, 'float64']), - ('variance', [1, 'float64']), - ], - 'sensor_msgs/msg/Range': [ - ('header', [2, 'std_msgs/msg/Header']), - ('radiation_type', [1, 'uint8']), - ('field_of_view', [1, 'float32']), - ('min_range', [1, 'float32']), - ('max_range', [1, 'float32']), - ('range', [1, 'float32']), - ], - 'sensor_msgs/msg/RegionOfInterest': [ - ('x_offset', [1, 'uint32']), - ('y_offset', [1, 'uint32']), - ('height', [1, 'uint32']), - ('width', [1, 'uint32']), - ('do_rectify', [1, 'bool']), - ], - 'sensor_msgs/msg/JoyFeedbackArray': [ - ('array', [4, [2, 'sensor_msgs/msg/JoyFeedback']]), - ], - 'sensor_msgs/msg/TimeReference': [ - ('header', [2, 'std_msgs/msg/Header']), - ('time_ref', [2, 'builtin_interfaces/msg/Time']), - ('source', [1, 'string']), - ], - 'sensor_msgs/msg/CompressedImage': [ - ('header', [2, 'std_msgs/msg/Header']), - ('format', [1, 'string']), - ('data', [4, [1, 'uint8']]), - ], - 'sensor_msgs/msg/MultiEchoLaserScan': [ - ('header', [2, 'std_msgs/msg/Header']), - ('angle_min', [1, 'float32']), - ('angle_max', [1, 'float32']), - ('angle_increment', [1, 'float32']), - ('time_increment', [1, 'float32']), - ('scan_time', [1, 'float32']), - ('range_min', [1, 'float32']), - ('range_max', [1, 'float32']), - ('ranges', [4, [2, 'sensor_msgs/msg/LaserEcho']]), - ('intensities', [4, [2, 'sensor_msgs/msg/LaserEcho']]), - ], - 'sensor_msgs/msg/LaserEcho': [ - ('echoes', [4, [1, 'float32']]), - ], - 'sensor_msgs/msg/ChannelFloat32': [ - ('name', [1, 'string']), - ('values', [4, [1, 'float32']]), - ], - 'sensor_msgs/msg/CameraInfo': [ - ('header', [2, 'std_msgs/msg/Header']), - ('height', [1, 'uint32']), - ('width', [1, 'uint32']), - ('distortion_model', [1, 'string']), - ('d', [4, [1, 'float64']]), - ('k', [3, 9, [1, 'float64']]), - ('r', [3, 9, [1, 'float64']]), - ('p', [3, 12, [1, 'float64']]), - ('binning_x', [1, 'uint32']), - ('binning_y', [1, 'uint32']), - ('roi', [2, 'sensor_msgs/msg/RegionOfInterest']), - ], - 'sensor_msgs/msg/RelativeHumidity': [ - ('header', [2, 'std_msgs/msg/Header']), - ('relative_humidity', [1, 'float64']), - ('variance', [1, 'float64']), - ], - 'sensor_msgs/msg/FluidPressure': [ - ('header', [2, 'std_msgs/msg/Header']), - ('fluid_pressure', [1, 'float64']), - ('variance', [1, 'float64']), - ], - 'sensor_msgs/msg/LaserScan': [ - ('header', [2, 'std_msgs/msg/Header']), - ('angle_min', [1, 'float32']), - ('angle_max', [1, 'float32']), - ('angle_increment', [1, 'float32']), - ('time_increment', [1, 'float32']), - ('scan_time', [1, 'float32']), - ('range_min', [1, 'float32']), - ('range_max', [1, 'float32']), - ('ranges', [4, [1, 'float32']]), - ('intensities', [4, [1, 'float32']]), - ], - 'sensor_msgs/msg/BatteryState': [ - ('header', [2, 'std_msgs/msg/Header']), - ('voltage', [1, 'float32']), - ('temperature', [1, 'float32']), - ('current', [1, 'float32']), - ('charge', [1, 'float32']), - ('capacity', [1, 'float32']), - ('design_capacity', [1, 'float32']), - ('percentage', [1, 'float32']), - ('power_supply_status', [1, 'uint8']), - ('power_supply_health', [1, 'uint8']), - ('power_supply_technology', [1, 'uint8']), - ('present', [1, 'bool']), - ('cell_voltage', [4, [1, 'float32']]), - ('cell_temperature', [4, [1, 'float32']]), - ('location', [1, 'string']), - ('serial_number', [1, 'string']), - ], - 'sensor_msgs/msg/Image': [ - ('header', [2, 'std_msgs/msg/Header']), - ('height', [1, 'uint32']), - ('width', [1, 'uint32']), - ('encoding', [1, 'string']), - ('is_bigendian', [1, 'uint8']), - ('step', [1, 'uint32']), - ('data', [4, [1, 'uint8']]), - ], - 'sensor_msgs/msg/PointCloud': [ - ('header', [2, 'std_msgs/msg/Header']), - ('points', [4, [2, 'geometry_msgs/msg/Point32']]), - ('channels', [4, [2, 'sensor_msgs/msg/ChannelFloat32']]), - ], - 'sensor_msgs/msg/Imu': [ - ('header', [2, 'std_msgs/msg/Header']), - ('orientation', [2, 'geometry_msgs/msg/Quaternion']), - ('orientation_covariance', [3, 9, [1, 'float64']]), - ('angular_velocity', [2, 'geometry_msgs/msg/Vector3']), - ('angular_velocity_covariance', [3, 9, [1, 'float64']]), - ('linear_acceleration', [2, 'geometry_msgs/msg/Vector3']), - ('linear_acceleration_covariance', [3, 9, [1, 'float64']]), - ], - 'sensor_msgs/msg/NavSatStatus': [ - ('status', [1, 'int8']), - ('service', [1, 'uint16']), - ], - 'sensor_msgs/msg/Illuminance': [ - ('header', [2, 'std_msgs/msg/Header']), - ('illuminance', [1, 'float64']), - ('variance', [1, 'float64']), - ], - 'sensor_msgs/msg/Joy': [ - ('header', [2, 'std_msgs/msg/Header']), - ('axes', [4, [1, 'float32']]), - ('buttons', [4, [1, 'int32']]), - ], - 'sensor_msgs/msg/NavSatFix': [ - ('header', [2, 'std_msgs/msg/Header']), - ('status', [2, 'sensor_msgs/msg/NavSatStatus']), - ('latitude', [1, 'float64']), - ('longitude', [1, 'float64']), - ('altitude', [1, 'float64']), - ('position_covariance', [3, 9, [1, 'float64']]), - ('position_covariance_type', [1, 'uint8']), - ], - 'sensor_msgs/msg/MultiDOFJointState': [ - ('header', [2, 'std_msgs/msg/Header']), - ('joint_names', [4, [1, 'string']]), - ('transforms', [4, [2, 'geometry_msgs/msg/Transform']]), - ('twist', [4, [2, 'geometry_msgs/msg/Twist']]), - ('wrench', [4, [2, 'geometry_msgs/msg/Wrench']]), - ], - 'sensor_msgs/msg/MagneticField': [ - ('header', [2, 'std_msgs/msg/Header']), - ('magnetic_field', [2, 'geometry_msgs/msg/Vector3']), - ('magnetic_field_covariance', [3, 9, [1, 'float64']]), - ], - 'sensor_msgs/msg/JointState': [ - ('header', [2, 'std_msgs/msg/Header']), - ('name', [4, [1, 'string']]), - ('position', [4, [1, 'float64']]), - ('velocity', [4, [1, 'float64']]), - ('effort', [4, [1, 'float64']]), - ], - 'sensor_msgs/msg/PointField': [ - ('name', [1, 'string']), - ('offset', [1, 'uint32']), - ('datatype', [1, 'uint8']), - ('count', [1, 'uint32']), - ], - 'sensor_msgs/msg/PointCloud2': [ - ('header', [2, 'std_msgs/msg/Header']), - ('height', [1, 'uint32']), - ('width', [1, 'uint32']), - ('fields', [4, [2, 'sensor_msgs/msg/PointField']]), - ('is_bigendian', [1, 'bool']), - ('point_step', [1, 'uint32']), - ('row_step', [1, 'uint32']), - ('data', [4, [1, 'uint8']]), - ('is_dense', [1, 'bool']), - ], - 'sensor_msgs/msg/JoyFeedback': [ - ('type', [1, 'uint8']), - ('id', [1, 'uint8']), - ('intensity', [1, 'float32']), - ], - 'shape_msgs/msg/SolidPrimitive': [ - ('type', [1, 'uint8']), - ('dimensions', [4, [1, 'float64']]), - ], - 'shape_msgs/msg/Mesh': [ - ('triangles', [4, [2, 'shape_msgs/msg/MeshTriangle']]), - ('vertices', [4, [2, 'geometry_msgs/msg/Point']]), - ], - 'shape_msgs/msg/Plane': [ - ('coef', [3, 4, [1, 'float64']]), - ], - 'shape_msgs/msg/MeshTriangle': [ - ('vertex_indices', [3, 3, [1, 'uint32']]), - ], - 'statistics_msgs/msg/StatisticDataType': [ - ('structure_needs_at_least_one_member', [1, 'uint8']), - ], - 'statistics_msgs/msg/StatisticDataPoint': [ - ('data_type', [1, 'uint8']), - ('data', [1, 'float64']), - ], - 'statistics_msgs/msg/MetricsMessage': [ - ('measurement_source_name', [1, 'string']), - ('metrics_source', [1, 'string']), - ('unit', [1, 'string']), - ('window_start', [2, 'builtin_interfaces/msg/Time']), - ('window_stop', [2, 'builtin_interfaces/msg/Time']), - ('statistics', [4, [2, 'statistics_msgs/msg/StatisticDataPoint']]), - ], - 'std_msgs/msg/UInt8': [ - ('data', [1, 'uint8']), - ], - 'std_msgs/msg/Float32MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'float32']]), - ], - 'std_msgs/msg/Int8': [ - ('data', [1, 'int8']), - ], - 'std_msgs/msg/Empty': [ - ('structure_needs_at_least_one_member', [1, 'uint8']), - ], - 'std_msgs/msg/String': [ - ('data', [1, 'string']), - ], - 'std_msgs/msg/MultiArrayDimension': [ - ('label', [1, 'string']), - ('size', [1, 'uint32']), - ('stride', [1, 'uint32']), - ], - 'std_msgs/msg/UInt64': [ - ('data', [1, 'uint64']), - ], - 'std_msgs/msg/UInt16': [ - ('data', [1, 'uint16']), - ], - 'std_msgs/msg/Float32': [ - ('data', [1, 'float32']), - ], - 'std_msgs/msg/Int64': [ - ('data', [1, 'int64']), - ], - 'std_msgs/msg/Int16MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'int16']]), - ], - 'std_msgs/msg/Int16': [ - ('data', [1, 'int16']), - ], - 'std_msgs/msg/Float64MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'float64']]), - ], - 'std_msgs/msg/MultiArrayLayout': [ - ('dim', [4, [2, 'std_msgs/msg/MultiArrayDimension']]), - ('data_offset', [1, 'uint32']), - ], - 'std_msgs/msg/UInt32MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'uint32']]), - ], - 'std_msgs/msg/Header': [ - ('stamp', [2, 'builtin_interfaces/msg/Time']), - ('frame_id', [1, 'string']), - ], - 'std_msgs/msg/ByteMultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'uint8']]), - ], - 'std_msgs/msg/Int8MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'int8']]), - ], - 'std_msgs/msg/Float64': [ - ('data', [1, 'float64']), - ], - 'std_msgs/msg/UInt8MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'uint8']]), - ], - 'std_msgs/msg/Byte': [ - ('data', [1, 'uint8']), - ], - 'std_msgs/msg/Char': [ - ('data', [1, 'uint8']), - ], - 'std_msgs/msg/UInt64MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'uint64']]), - ], - 'std_msgs/msg/Int32MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'int32']]), - ], - 'std_msgs/msg/ColorRGBA': [ - ('r', [1, 'float32']), - ('g', [1, 'float32']), - ('b', [1, 'float32']), - ('a', [1, 'float32']), - ], - 'std_msgs/msg/Bool': [ - ('data', [1, 'bool']), - ], - 'std_msgs/msg/UInt32': [ - ('data', [1, 'uint32']), - ], - 'std_msgs/msg/Int64MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'int64']]), - ], - 'std_msgs/msg/Int32': [ - ('data', [1, 'int32']), - ], - 'std_msgs/msg/UInt16MultiArray': [ - ('layout', [2, 'std_msgs/msg/MultiArrayLayout']), - ('data', [4, [1, 'uint16']]), - ], - 'stereo_msgs/msg/DisparityImage': [ - ('header', [2, 'std_msgs/msg/Header']), - ('image', [2, 'sensor_msgs/msg/Image']), - ('f', [1, 'float32']), - ('t', [1, 'float32']), - ('valid_window', [2, 'sensor_msgs/msg/RegionOfInterest']), - ('min_disparity', [1, 'float32']), - ('max_disparity', [1, 'float32']), - ('delta_d', [1, 'float32']), - ], - 'tf2_msgs/msg/TF2Error': [ - ('error', [1, 'uint8']), - ('error_string', [1, 'string']), - ], - 'tf2_msgs/msg/TFMessage': [ - ('transforms', [4, [2, 'geometry_msgs/msg/TransformStamped']]), - ], - 'trajectory_msgs/msg/MultiDOFJointTrajectory': [ - ('header', [2, 'std_msgs/msg/Header']), - ('joint_names', [4, [1, 'string']]), - ('points', [4, [2, 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint']]), - ], - 'trajectory_msgs/msg/JointTrajectory': [ - ('header', [2, 'std_msgs/msg/Header']), - ('joint_names', [4, [1, 'string']]), - ('points', [4, [2, 'trajectory_msgs/msg/JointTrajectoryPoint']]), - ], - 'trajectory_msgs/msg/JointTrajectoryPoint': [ - ('positions', [4, [1, 'float64']]), - ('velocities', [4, [1, 'float64']]), - ('accelerations', [4, [1, 'float64']]), - ('effort', [4, [1, 'float64']]), - ('time_from_start', [2, 'builtin_interfaces/msg/Duration']), - ], - 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint': [ - ('transforms', [4, [2, 'geometry_msgs/msg/Transform']]), - ('velocities', [4, [2, 'geometry_msgs/msg/Twist']]), - ('accelerations', [4, [2, 'geometry_msgs/msg/Twist']]), - ('time_from_start', [2, 'builtin_interfaces/msg/Duration']), - ], - 'unique_identifier_msgs/msg/UUID': [ - ('uuid', [3, 16, [1, 'uint8']]), - ], - 'visualization_msgs/msg/Marker': [ - ('header', [2, 'std_msgs/msg/Header']), - ('ns', [1, 'string']), - ('id', [1, 'int32']), - ('type', [1, 'int32']), - ('action', [1, 'int32']), - ('pose', [2, 'geometry_msgs/msg/Pose']), - ('scale', [2, 'geometry_msgs/msg/Vector3']), - ('color', [2, 'std_msgs/msg/ColorRGBA']), - ('lifetime', [2, 'builtin_interfaces/msg/Duration']), - ('frame_locked', [1, 'bool']), - ('points', [4, [2, 'geometry_msgs/msg/Point']]), - ('colors', [4, [2, 'std_msgs/msg/ColorRGBA']]), - ('text', [1, 'string']), - ('mesh_resource', [1, 'string']), - ('mesh_use_embedded_materials', [1, 'bool']), - ], - 'visualization_msgs/msg/InteractiveMarkerInit': [ - ('server_id', [1, 'string']), - ('seq_num', [1, 'uint64']), - ('markers', [4, [2, 'visualization_msgs/msg/InteractiveMarker']]), - ], - 'visualization_msgs/msg/MenuEntry': [ - ('id', [1, 'uint32']), - ('parent_id', [1, 'uint32']), - ('title', [1, 'string']), - ('command', [1, 'string']), - ('command_type', [1, 'uint8']), - ], - 'visualization_msgs/msg/MarkerArray': [ - ('markers', [4, [2, 'visualization_msgs/msg/Marker']]), - ], - 'visualization_msgs/msg/InteractiveMarkerUpdate': [ - ('server_id', [1, 'string']), - ('seq_num', [1, 'uint64']), - ('type', [1, 'uint8']), - ('markers', [4, [2, 'visualization_msgs/msg/InteractiveMarker']]), - ('poses', [4, [2, 'visualization_msgs/msg/InteractiveMarkerPose']]), - ('erases', [4, [1, 'string']]), - ], - 'visualization_msgs/msg/InteractiveMarker': [ - ('header', [2, 'std_msgs/msg/Header']), - ('pose', [2, 'geometry_msgs/msg/Pose']), - ('name', [1, 'string']), - ('description', [1, 'string']), - ('scale', [1, 'float32']), - ('menu_entries', [4, [2, 'visualization_msgs/msg/MenuEntry']]), - ('controls', [4, [2, 'visualization_msgs/msg/InteractiveMarkerControl']]), - ], - 'visualization_msgs/msg/InteractiveMarkerFeedback': [ - ('header', [2, 'std_msgs/msg/Header']), - ('client_id', [1, 'string']), - ('marker_name', [1, 'string']), - ('control_name', [1, 'string']), - ('event_type', [1, 'uint8']), - ('pose', [2, 'geometry_msgs/msg/Pose']), - ('menu_entry_id', [1, 'uint32']), - ('mouse_point', [2, 'geometry_msgs/msg/Point']), - ('mouse_point_valid', [1, 'bool']), - ], - 'visualization_msgs/msg/ImageMarker': [ - ('header', [2, 'std_msgs/msg/Header']), - ('ns', [1, 'string']), - ('id', [1, 'int32']), - ('type', [1, 'int32']), - ('action', [1, 'int32']), - ('position', [2, 'geometry_msgs/msg/Point']), - ('scale', [1, 'float32']), - ('outline_color', [2, 'std_msgs/msg/ColorRGBA']), - ('filled', [1, 'uint8']), - ('fill_color', [2, 'std_msgs/msg/ColorRGBA']), - ('lifetime', [2, 'builtin_interfaces/msg/Duration']), - ('points', [4, [2, 'geometry_msgs/msg/Point']]), - ('outline_colors', [4, [2, 'std_msgs/msg/ColorRGBA']]), - ], - 'visualization_msgs/msg/InteractiveMarkerControl': [ - ('name', [1, 'string']), - ('orientation', [2, 'geometry_msgs/msg/Quaternion']), - ('orientation_mode', [1, 'uint8']), - ('interaction_mode', [1, 'uint8']), - ('always_visible', [1, 'bool']), - ('markers', [4, [2, 'visualization_msgs/msg/Marker']]), - ('independent_marker_orientation', [1, 'bool']), - ('description', [1, 'string']), - ], - 'visualization_msgs/msg/InteractiveMarkerPose': [ - ('header', [2, 'std_msgs/msg/Header']), - ('pose', [2, 'geometry_msgs/msg/Pose']), - ('name', [1, 'string']), - ], +FIELDDEFS: Typesdict = { + 'builtin_interfaces/msg/Time': ([ + ], [ + ('sec', (1, 'int32')), + ('nanosec', (1, 'uint32')), + ]), + 'builtin_interfaces/msg/Duration': ([ + ], [ + ('sec', (1, 'int32')), + ('nanosec', (1, 'uint32')), + ]), + 'diagnostic_msgs/msg/DiagnosticStatus': ([ + ('OK', 'uint8', 0), + ('WARN', 'uint8', 1), + ('ERROR', 'uint8', 2), + ('STALE', 'uint8', 3), + ], [ + ('level', (1, 'uint8')), + ('name', (1, 'string')), + ('message', (1, 'string')), + ('hardware_id', (1, 'string')), + ('values', (4, ((2, 'diagnostic_msgs/msg/KeyValue'), None))), + ]), + 'diagnostic_msgs/msg/DiagnosticArray': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('status', (4, ((2, 'diagnostic_msgs/msg/DiagnosticStatus'), None))), + ]), + 'diagnostic_msgs/msg/KeyValue': ([ + ], [ + ('key', (1, 'string')), + ('value', (1, 'string')), + ]), + 'geometry_msgs/msg/AccelWithCovariance': ([ + ], [ + ('accel', (2, 'geometry_msgs/msg/Accel')), + ('covariance', (3, ((1, 'float64'), 36))), + ]), + 'geometry_msgs/msg/Point32': ([ + ], [ + ('x', (1, 'float32')), + ('y', (1, 'float32')), + ('z', (1, 'float32')), + ]), + 'geometry_msgs/msg/Vector3': ([ + ], [ + ('x', (1, 'float64')), + ('y', (1, 'float64')), + ('z', (1, 'float64')), + ]), + 'geometry_msgs/msg/Inertia': ([ + ], [ + ('m', (1, 'float64')), + ('com', (2, 'geometry_msgs/msg/Vector3')), + ('ixx', (1, 'float64')), + ('ixy', (1, 'float64')), + ('ixz', (1, 'float64')), + ('iyy', (1, 'float64')), + ('iyz', (1, 'float64')), + ('izz', (1, 'float64')), + ]), + 'geometry_msgs/msg/PoseWithCovarianceStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('pose', (2, 'geometry_msgs/msg/PoseWithCovariance')), + ]), + 'geometry_msgs/msg/Twist': ([ + ], [ + ('linear', (2, 'geometry_msgs/msg/Vector3')), + ('angular', (2, 'geometry_msgs/msg/Vector3')), + ]), + 'geometry_msgs/msg/Pose': ([ + ], [ + ('position', (2, 'geometry_msgs/msg/Point')), + ('orientation', (2, 'geometry_msgs/msg/Quaternion')), + ]), + 'geometry_msgs/msg/Point': ([ + ], [ + ('x', (1, 'float64')), + ('y', (1, 'float64')), + ('z', (1, 'float64')), + ]), + 'geometry_msgs/msg/Vector3Stamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('vector', (2, 'geometry_msgs/msg/Vector3')), + ]), + 'geometry_msgs/msg/Transform': ([ + ], [ + ('translation', (2, 'geometry_msgs/msg/Vector3')), + ('rotation', (2, 'geometry_msgs/msg/Quaternion')), + ]), + 'geometry_msgs/msg/PolygonStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('polygon', (2, 'geometry_msgs/msg/Polygon')), + ]), + 'geometry_msgs/msg/Quaternion': ([ + ], [ + ('x', (1, 'float64')), + ('y', (1, 'float64')), + ('z', (1, 'float64')), + ('w', (1, 'float64')), + ]), + 'geometry_msgs/msg/Pose2D': ([ + ], [ + ('x', (1, 'float64')), + ('y', (1, 'float64')), + ('theta', (1, 'float64')), + ]), + 'geometry_msgs/msg/InertiaStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('inertia', (2, 'geometry_msgs/msg/Inertia')), + ]), + 'geometry_msgs/msg/TwistStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('twist', (2, 'geometry_msgs/msg/Twist')), + ]), + 'geometry_msgs/msg/PoseStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ]), + 'geometry_msgs/msg/PointStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('point', (2, 'geometry_msgs/msg/Point')), + ]), + 'geometry_msgs/msg/Polygon': ([ + ], [ + ('points', (4, ((2, 'geometry_msgs/msg/Point32'), None))), + ]), + 'geometry_msgs/msg/PoseArray': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('poses', (4, ((2, 'geometry_msgs/msg/Pose'), None))), + ]), + 'geometry_msgs/msg/AccelStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('accel', (2, 'geometry_msgs/msg/Accel')), + ]), + 'geometry_msgs/msg/TwistWithCovarianceStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('twist', (2, 'geometry_msgs/msg/TwistWithCovariance')), + ]), + 'geometry_msgs/msg/QuaternionStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('quaternion', (2, 'geometry_msgs/msg/Quaternion')), + ]), + 'geometry_msgs/msg/WrenchStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('wrench', (2, 'geometry_msgs/msg/Wrench')), + ]), + 'geometry_msgs/msg/AccelWithCovarianceStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('accel', (2, 'geometry_msgs/msg/AccelWithCovariance')), + ]), + 'geometry_msgs/msg/PoseWithCovariance': ([ + ], [ + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('covariance', (3, ((1, 'float64'), 36))), + ]), + 'geometry_msgs/msg/Wrench': ([ + ], [ + ('force', (2, 'geometry_msgs/msg/Vector3')), + ('torque', (2, 'geometry_msgs/msg/Vector3')), + ]), + 'geometry_msgs/msg/TransformStamped': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('child_frame_id', (1, 'string')), + ('transform', (2, 'geometry_msgs/msg/Transform')), + ]), + 'geometry_msgs/msg/Accel': ([ + ], [ + ('linear', (2, 'geometry_msgs/msg/Vector3')), + ('angular', (2, 'geometry_msgs/msg/Vector3')), + ]), + 'geometry_msgs/msg/TwistWithCovariance': ([ + ], [ + ('twist', (2, 'geometry_msgs/msg/Twist')), + ('covariance', (3, ((1, 'float64'), 36))), + ]), + 'libstatistics_collector/msg/DummyMessage': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ]), + 'lifecycle_msgs/msg/TransitionDescription': ([ + ], [ + ('transition', (2, 'lifecycle_msgs/msg/Transition')), + ('start_state', (2, 'lifecycle_msgs/msg/State')), + ('goal_state', (2, 'lifecycle_msgs/msg/State')), + ]), + 'lifecycle_msgs/msg/State': ([ + ('PRIMARY_STATE_UNKNOWN', 'uint8', 0), + ('PRIMARY_STATE_UNCONFIGURED', 'uint8', 1), + ('PRIMARY_STATE_INACTIVE', 'uint8', 2), + ('PRIMARY_STATE_ACTIVE', 'uint8', 3), + ('PRIMARY_STATE_FINALIZED', 'uint8', 4), + ('TRANSITION_STATE_CONFIGURING', 'uint8', 10), + ('TRANSITION_STATE_CLEANINGUP', 'uint8', 11), + ('TRANSITION_STATE_SHUTTINGDOWN', 'uint8', 12), + ('TRANSITION_STATE_ACTIVATING', 'uint8', 13), + ('TRANSITION_STATE_DEACTIVATING', 'uint8', 14), + ('TRANSITION_STATE_ERRORPROCESSING', 'uint8', 15), + ], [ + ('id', (1, 'uint8')), + ('label', (1, 'string')), + ]), + 'lifecycle_msgs/msg/TransitionEvent': ([ + ], [ + ('timestamp', (1, 'uint64')), + ('transition', (2, 'lifecycle_msgs/msg/Transition')), + ('start_state', (2, 'lifecycle_msgs/msg/State')), + ('goal_state', (2, 'lifecycle_msgs/msg/State')), + ]), + 'lifecycle_msgs/msg/Transition': ([ + ('TRANSITION_CREATE', 'uint8', 0), + ('TRANSITION_CONFIGURE', 'uint8', 1), + ('TRANSITION_CLEANUP', 'uint8', 2), + ('TRANSITION_ACTIVATE', 'uint8', 3), + ('TRANSITION_DEACTIVATE', 'uint8', 4), + ('TRANSITION_UNCONFIGURED_SHUTDOWN', 'uint8', 5), + ('TRANSITION_INACTIVE_SHUTDOWN', 'uint8', 6), + ('TRANSITION_ACTIVE_SHUTDOWN', 'uint8', 7), + ('TRANSITION_DESTROY', 'uint8', 8), + ('TRANSITION_ON_CONFIGURE_SUCCESS', 'uint8', 10), + ('TRANSITION_ON_CONFIGURE_FAILURE', 'uint8', 11), + ('TRANSITION_ON_CONFIGURE_ERROR', 'uint8', 12), + ('TRANSITION_ON_CLEANUP_SUCCESS', 'uint8', 20), + ('TRANSITION_ON_CLEANUP_FAILURE', 'uint8', 21), + ('TRANSITION_ON_CLEANUP_ERROR', 'uint8', 22), + ('TRANSITION_ON_ACTIVATE_SUCCESS', 'uint8', 30), + ('TRANSITION_ON_ACTIVATE_FAILURE', 'uint8', 31), + ('TRANSITION_ON_ACTIVATE_ERROR', 'uint8', 32), + ('TRANSITION_ON_DEACTIVATE_SUCCESS', 'uint8', 40), + ('TRANSITION_ON_DEACTIVATE_FAILURE', 'uint8', 41), + ('TRANSITION_ON_DEACTIVATE_ERROR', 'uint8', 42), + ('TRANSITION_ON_SHUTDOWN_SUCCESS', 'uint8', 50), + ('TRANSITION_ON_SHUTDOWN_FAILURE', 'uint8', 51), + ('TRANSITION_ON_SHUTDOWN_ERROR', 'uint8', 52), + ('TRANSITION_ON_ERROR_SUCCESS', 'uint8', 60), + ('TRANSITION_ON_ERROR_FAILURE', 'uint8', 61), + ('TRANSITION_ON_ERROR_ERROR', 'uint8', 62), + ('TRANSITION_CALLBACK_SUCCESS', 'uint8', 97), + ('TRANSITION_CALLBACK_FAILURE', 'uint8', 98), + ('TRANSITION_CALLBACK_ERROR', 'uint8', 99), + ], [ + ('id', (1, 'uint8')), + ('label', (1, 'string')), + ]), + 'nav_msgs/msg/MapMetaData': ([ + ], [ + ('map_load_time', (2, 'builtin_interfaces/msg/Time')), + ('resolution', (1, 'float32')), + ('width', (1, 'uint32')), + ('height', (1, 'uint32')), + ('origin', (2, 'geometry_msgs/msg/Pose')), + ]), + 'nav_msgs/msg/GridCells': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('cell_width', (1, 'float32')), + ('cell_height', (1, 'float32')), + ('cells', (4, ((2, 'geometry_msgs/msg/Point'), None))), + ]), + 'nav_msgs/msg/Odometry': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('child_frame_id', (1, 'string')), + ('pose', (2, 'geometry_msgs/msg/PoseWithCovariance')), + ('twist', (2, 'geometry_msgs/msg/TwistWithCovariance')), + ]), + 'nav_msgs/msg/Path': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('poses', (4, ((2, 'geometry_msgs/msg/PoseStamped'), None))), + ]), + 'nav_msgs/msg/OccupancyGrid': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('info', (2, 'nav_msgs/msg/MapMetaData')), + ('data', (4, ((1, 'int8'), None))), + ]), + 'rcl_interfaces/msg/ListParametersResult': ([ + ], [ + ('names', (4, ((1, 'string'), None))), + ('prefixes', (4, ((1, 'string'), None))), + ]), + 'rcl_interfaces/msg/ParameterType': ([ + ('PARAMETER_NOT_SET', 'uint8', 0), + ('PARAMETER_BOOL', 'uint8', 1), + ('PARAMETER_INTEGER', 'uint8', 2), + ('PARAMETER_DOUBLE', 'uint8', 3), + ('PARAMETER_STRING', 'uint8', 4), + ('PARAMETER_BYTE_ARRAY', 'uint8', 5), + ('PARAMETER_BOOL_ARRAY', 'uint8', 6), + ('PARAMETER_INTEGER_ARRAY', 'uint8', 7), + ('PARAMETER_DOUBLE_ARRAY', 'uint8', 8), + ('PARAMETER_STRING_ARRAY', 'uint8', 9), + ], [ + ('structure_needs_at_least_one_member', (1, 'uint8')), + ]), + 'rcl_interfaces/msg/ParameterEventDescriptors': ([ + ], [ + ('new_parameters', (4, ((2, 'rcl_interfaces/msg/ParameterDescriptor'), None))), + ('changed_parameters', (4, ((2, 'rcl_interfaces/msg/ParameterDescriptor'), None))), + ('deleted_parameters', (4, ((2, 'rcl_interfaces/msg/ParameterDescriptor'), None))), + ]), + 'rcl_interfaces/msg/ParameterEvent': ([ + ], [ + ('stamp', (2, 'builtin_interfaces/msg/Time')), + ('node', (1, 'string')), + ('new_parameters', (4, ((2, 'rcl_interfaces/msg/Parameter'), None))), + ('changed_parameters', (4, ((2, 'rcl_interfaces/msg/Parameter'), None))), + ('deleted_parameters', (4, ((2, 'rcl_interfaces/msg/Parameter'), None))), + ]), + 'rcl_interfaces/msg/IntegerRange': ([ + ], [ + ('from_value', (1, 'int64')), + ('to_value', (1, 'int64')), + ('step', (1, 'uint64')), + ]), + 'rcl_interfaces/msg/Parameter': ([ + ], [ + ('name', (1, 'string')), + ('value', (2, 'rcl_interfaces/msg/ParameterValue')), + ]), + 'rcl_interfaces/msg/ParameterValue': ([ + ], [ + ('type', (1, 'uint8')), + ('bool_value', (1, 'bool')), + ('integer_value', (1, 'int64')), + ('double_value', (1, 'float64')), + ('string_value', (1, 'string')), + ('byte_array_value', (4, ((1, 'uint8'), None))), + ('bool_array_value', (4, ((1, 'bool'), None))), + ('integer_array_value', (4, ((1, 'int64'), None))), + ('double_array_value', (4, ((1, 'float64'), None))), + ('string_array_value', (4, ((1, 'string'), None))), + ]), + 'rcl_interfaces/msg/FloatingPointRange': ([ + ], [ + ('from_value', (1, 'float64')), + ('to_value', (1, 'float64')), + ('step', (1, 'float64')), + ]), + 'rcl_interfaces/msg/SetParametersResult': ([ + ], [ + ('successful', (1, 'bool')), + ('reason', (1, 'string')), + ]), + 'rcl_interfaces/msg/Log': ([ + ('DEBUG', 'uint8', 10), + ('INFO', 'uint8', 20), + ('WARN', 'uint8', 30), + ('ERROR', 'uint8', 40), + ('FATAL', 'uint8', 50), + ], [ + ('stamp', (2, 'builtin_interfaces/msg/Time')), + ('level', (1, 'uint8')), + ('name', (1, 'string')), + ('msg', (1, 'string')), + ('file', (1, 'string')), + ('function', (1, 'string')), + ('line', (1, 'uint32')), + ]), + 'rcl_interfaces/msg/ParameterDescriptor': ([ + ], [ + ('name', (1, 'string')), + ('type', (1, 'uint8')), + ('description', (1, 'string')), + ('additional_constraints', (1, 'string')), + ('read_only', (1, 'bool')), + ('floating_point_range', (4, ((2, 'rcl_interfaces/msg/FloatingPointRange'), None))), + ('integer_range', (4, ((2, 'rcl_interfaces/msg/IntegerRange'), None))), + ]), + 'rmw_dds_common/msg/Gid': ([ + ], [ + ('data', (3, ((1, 'uint8'), 24))), + ]), + 'rmw_dds_common/msg/NodeEntitiesInfo': ([ + ], [ + ('node_namespace', (1, 'string')), + ('node_name', (1, 'string')), + ('reader_gid_seq', (4, ((2, 'rmw_dds_common/msg/Gid'), None))), + ('writer_gid_seq', (4, ((2, 'rmw_dds_common/msg/Gid'), None))), + ]), + 'rmw_dds_common/msg/ParticipantEntitiesInfo': ([ + ], [ + ('gid', (2, 'rmw_dds_common/msg/Gid')), + ('node_entities_info_seq', (4, ((2, 'rmw_dds_common/msg/NodeEntitiesInfo'), None))), + ]), + 'rosgraph_msgs/msg/Clock': ([ + ], [ + ('clock', (2, 'builtin_interfaces/msg/Time')), + ]), + 'sensor_msgs/msg/Temperature': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('temperature', (1, 'float64')), + ('variance', (1, 'float64')), + ]), + 'sensor_msgs/msg/Range': ([ + ('ULTRASOUND', 'uint8', 0), + ('INFRARED', 'uint8', 1), + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('radiation_type', (1, 'uint8')), + ('field_of_view', (1, 'float32')), + ('min_range', (1, 'float32')), + ('max_range', (1, 'float32')), + ('range', (1, 'float32')), + ]), + 'sensor_msgs/msg/RegionOfInterest': ([ + ], [ + ('x_offset', (1, 'uint32')), + ('y_offset', (1, 'uint32')), + ('height', (1, 'uint32')), + ('width', (1, 'uint32')), + ('do_rectify', (1, 'bool')), + ]), + 'sensor_msgs/msg/JoyFeedbackArray': ([ + ], [ + ('array', (4, ((2, 'sensor_msgs/msg/JoyFeedback'), None))), + ]), + 'sensor_msgs/msg/TimeReference': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('time_ref', (2, 'builtin_interfaces/msg/Time')), + ('source', (1, 'string')), + ]), + 'sensor_msgs/msg/CompressedImage': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('format', (1, 'string')), + ('data', (4, ((1, 'uint8'), None))), + ]), + 'sensor_msgs/msg/MultiEchoLaserScan': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('angle_min', (1, 'float32')), + ('angle_max', (1, 'float32')), + ('angle_increment', (1, 'float32')), + ('time_increment', (1, 'float32')), + ('scan_time', (1, 'float32')), + ('range_min', (1, 'float32')), + ('range_max', (1, 'float32')), + ('ranges', (4, ((2, 'sensor_msgs/msg/LaserEcho'), None))), + ('intensities', (4, ((2, 'sensor_msgs/msg/LaserEcho'), None))), + ]), + 'sensor_msgs/msg/LaserEcho': ([ + ], [ + ('echoes', (4, ((1, 'float32'), None))), + ]), + 'sensor_msgs/msg/ChannelFloat32': ([ + ], [ + ('name', (1, 'string')), + ('values', (4, ((1, 'float32'), None))), + ]), + 'sensor_msgs/msg/CameraInfo': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('height', (1, 'uint32')), + ('width', (1, 'uint32')), + ('distortion_model', (1, 'string')), + ('d', (4, ((1, 'float64'), None))), + ('k', (3, ((1, 'float64'), 9))), + ('r', (3, ((1, 'float64'), 9))), + ('p', (3, ((1, 'float64'), 12))), + ('binning_x', (1, 'uint32')), + ('binning_y', (1, 'uint32')), + ('roi', (2, 'sensor_msgs/msg/RegionOfInterest')), + ]), + 'sensor_msgs/msg/RelativeHumidity': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('relative_humidity', (1, 'float64')), + ('variance', (1, 'float64')), + ]), + 'sensor_msgs/msg/FluidPressure': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('fluid_pressure', (1, 'float64')), + ('variance', (1, 'float64')), + ]), + 'sensor_msgs/msg/LaserScan': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('angle_min', (1, 'float32')), + ('angle_max', (1, 'float32')), + ('angle_increment', (1, 'float32')), + ('time_increment', (1, 'float32')), + ('scan_time', (1, 'float32')), + ('range_min', (1, 'float32')), + ('range_max', (1, 'float32')), + ('ranges', (4, ((1, 'float32'), None))), + ('intensities', (4, ((1, 'float32'), None))), + ]), + 'sensor_msgs/msg/BatteryState': ([ + ('POWER_SUPPLY_STATUS_UNKNOWN', 'uint8', 0), + ('POWER_SUPPLY_STATUS_CHARGING', 'uint8', 1), + ('POWER_SUPPLY_STATUS_DISCHARGING', 'uint8', 2), + ('POWER_SUPPLY_STATUS_NOT_CHARGING', 'uint8', 3), + ('POWER_SUPPLY_STATUS_FULL', 'uint8', 4), + ('POWER_SUPPLY_HEALTH_UNKNOWN', 'uint8', 0), + ('POWER_SUPPLY_HEALTH_GOOD', 'uint8', 1), + ('POWER_SUPPLY_HEALTH_OVERHEAT', 'uint8', 2), + ('POWER_SUPPLY_HEALTH_DEAD', 'uint8', 3), + ('POWER_SUPPLY_HEALTH_OVERVOLTAGE', 'uint8', 4), + ('POWER_SUPPLY_HEALTH_UNSPEC_FAILURE', 'uint8', 5), + ('POWER_SUPPLY_HEALTH_COLD', 'uint8', 6), + ('POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE', 'uint8', 7), + ('POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE', 'uint8', 8), + ('POWER_SUPPLY_TECHNOLOGY_UNKNOWN', 'uint8', 0), + ('POWER_SUPPLY_TECHNOLOGY_NIMH', 'uint8', 1), + ('POWER_SUPPLY_TECHNOLOGY_LION', 'uint8', 2), + ('POWER_SUPPLY_TECHNOLOGY_LIPO', 'uint8', 3), + ('POWER_SUPPLY_TECHNOLOGY_LIFE', 'uint8', 4), + ('POWER_SUPPLY_TECHNOLOGY_NICD', 'uint8', 5), + ('POWER_SUPPLY_TECHNOLOGY_LIMN', 'uint8', 6), + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('voltage', (1, 'float32')), + ('temperature', (1, 'float32')), + ('current', (1, 'float32')), + ('charge', (1, 'float32')), + ('capacity', (1, 'float32')), + ('design_capacity', (1, 'float32')), + ('percentage', (1, 'float32')), + ('power_supply_status', (1, 'uint8')), + ('power_supply_health', (1, 'uint8')), + ('power_supply_technology', (1, 'uint8')), + ('present', (1, 'bool')), + ('cell_voltage', (4, ((1, 'float32'), None))), + ('cell_temperature', (4, ((1, 'float32'), None))), + ('location', (1, 'string')), + ('serial_number', (1, 'string')), + ]), + 'sensor_msgs/msg/Image': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('height', (1, 'uint32')), + ('width', (1, 'uint32')), + ('encoding', (1, 'string')), + ('is_bigendian', (1, 'uint8')), + ('step', (1, 'uint32')), + ('data', (4, ((1, 'uint8'), None))), + ]), + 'sensor_msgs/msg/PointCloud': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('points', (4, ((2, 'geometry_msgs/msg/Point32'), None))), + ('channels', (4, ((2, 'sensor_msgs/msg/ChannelFloat32'), None))), + ]), + 'sensor_msgs/msg/Imu': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('orientation', (2, 'geometry_msgs/msg/Quaternion')), + ('orientation_covariance', (3, ((1, 'float64'), 9))), + ('angular_velocity', (2, 'geometry_msgs/msg/Vector3')), + ('angular_velocity_covariance', (3, ((1, 'float64'), 9))), + ('linear_acceleration', (2, 'geometry_msgs/msg/Vector3')), + ('linear_acceleration_covariance', (3, ((1, 'float64'), 9))), + ]), + 'sensor_msgs/msg/NavSatStatus': ([ + ('STATUS_NO_FIX', 'int8', -1), + ('STATUS_FIX', 'int8', 0), + ('STATUS_SBAS_FIX', 'int8', 1), + ('STATUS_GBAS_FIX', 'int8', 2), + ('SERVICE_GPS', 'uint16', 1), + ('SERVICE_GLONASS', 'uint16', 2), + ('SERVICE_COMPASS', 'uint16', 4), + ('SERVICE_GALILEO', 'uint16', 8), + ], [ + ('status', (1, 'int8')), + ('service', (1, 'uint16')), + ]), + 'sensor_msgs/msg/Illuminance': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('illuminance', (1, 'float64')), + ('variance', (1, 'float64')), + ]), + 'sensor_msgs/msg/Joy': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('axes', (4, ((1, 'float32'), None))), + ('buttons', (4, ((1, 'int32'), None))), + ]), + 'sensor_msgs/msg/NavSatFix': ([ + ('COVARIANCE_TYPE_UNKNOWN', 'uint8', 0), + ('COVARIANCE_TYPE_APPROXIMATED', 'uint8', 1), + ('COVARIANCE_TYPE_DIAGONAL_KNOWN', 'uint8', 2), + ('COVARIANCE_TYPE_KNOWN', 'uint8', 3), + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('status', (2, 'sensor_msgs/msg/NavSatStatus')), + ('latitude', (1, 'float64')), + ('longitude', (1, 'float64')), + ('altitude', (1, 'float64')), + ('position_covariance', (3, ((1, 'float64'), 9))), + ('position_covariance_type', (1, 'uint8')), + ]), + 'sensor_msgs/msg/MultiDOFJointState': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('joint_names', (4, ((1, 'string'), None))), + ('transforms', (4, ((2, 'geometry_msgs/msg/Transform'), None))), + ('twist', (4, ((2, 'geometry_msgs/msg/Twist'), None))), + ('wrench', (4, ((2, 'geometry_msgs/msg/Wrench'), None))), + ]), + 'sensor_msgs/msg/MagneticField': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('magnetic_field', (2, 'geometry_msgs/msg/Vector3')), + ('magnetic_field_covariance', (3, ((1, 'float64'), 9))), + ]), + 'sensor_msgs/msg/JointState': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('name', (4, ((1, 'string'), None))), + ('position', (4, ((1, 'float64'), None))), + ('velocity', (4, ((1, 'float64'), None))), + ('effort', (4, ((1, 'float64'), None))), + ]), + 'sensor_msgs/msg/PointField': ([ + ('INT8', 'uint8', 1), + ('UINT8', 'uint8', 2), + ('INT16', 'uint8', 3), + ('UINT16', 'uint8', 4), + ('INT32', 'uint8', 5), + ('UINT32', 'uint8', 6), + ('FLOAT32', 'uint8', 7), + ('FLOAT64', 'uint8', 8), + ], [ + ('name', (1, 'string')), + ('offset', (1, 'uint32')), + ('datatype', (1, 'uint8')), + ('count', (1, 'uint32')), + ]), + 'sensor_msgs/msg/PointCloud2': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('height', (1, 'uint32')), + ('width', (1, 'uint32')), + ('fields', (4, ((2, 'sensor_msgs/msg/PointField'), None))), + ('is_bigendian', (1, 'bool')), + ('point_step', (1, 'uint32')), + ('row_step', (1, 'uint32')), + ('data', (4, ((1, 'uint8'), None))), + ('is_dense', (1, 'bool')), + ]), + 'sensor_msgs/msg/JoyFeedback': ([ + ('TYPE_LED', 'uint8', 0), + ('TYPE_RUMBLE', 'uint8', 1), + ('TYPE_BUZZER', 'uint8', 2), + ], [ + ('type', (1, 'uint8')), + ('id', (1, 'uint8')), + ('intensity', (1, 'float32')), + ]), + 'shape_msgs/msg/SolidPrimitive': ([ + ('BOX', 'uint8', 1), + ('SPHERE', 'uint8', 2), + ('CYLINDER', 'uint8', 3), + ('CONE', 'uint8', 4), + ('BOX_X', 'uint8', 0), + ('BOX_Y', 'uint8', 1), + ('BOX_Z', 'uint8', 2), + ('SPHERE_RADIUS', 'uint8', 0), + ('CYLINDER_HEIGHT', 'uint8', 0), + ('CYLINDER_RADIUS', 'uint8', 1), + ('CONE_HEIGHT', 'uint8', 0), + ('CONE_RADIUS', 'uint8', 1), + ], [ + ('type', (1, 'uint8')), + ('dimensions', (4, ((1, 'float64'), None))), + ]), + 'shape_msgs/msg/Mesh': ([ + ], [ + ('triangles', (4, ((2, 'shape_msgs/msg/MeshTriangle'), None))), + ('vertices', (4, ((2, 'geometry_msgs/msg/Point'), None))), + ]), + 'shape_msgs/msg/Plane': ([ + ], [ + ('coef', (3, ((1, 'float64'), 4))), + ]), + 'shape_msgs/msg/MeshTriangle': ([ + ], [ + ('vertex_indices', (3, ((1, 'uint32'), 3))), + ]), + 'statistics_msgs/msg/StatisticDataType': ([ + ('STATISTICS_DATA_TYPE_UNINITIALIZED', 'uint8', 0), + ('STATISTICS_DATA_TYPE_AVERAGE', 'uint8', 1), + ('STATISTICS_DATA_TYPE_MINIMUM', 'uint8', 2), + ('STATISTICS_DATA_TYPE_MAXIMUM', 'uint8', 3), + ('STATISTICS_DATA_TYPE_STDDEV', 'uint8', 4), + ('STATISTICS_DATA_TYPE_SAMPLE_COUNT', 'uint8', 5), + ], [ + ('structure_needs_at_least_one_member', (1, 'uint8')), + ]), + 'statistics_msgs/msg/StatisticDataPoint': ([ + ], [ + ('data_type', (1, 'uint8')), + ('data', (1, 'float64')), + ]), + 'statistics_msgs/msg/MetricsMessage': ([ + ], [ + ('measurement_source_name', (1, 'string')), + ('metrics_source', (1, 'string')), + ('unit', (1, 'string')), + ('window_start', (2, 'builtin_interfaces/msg/Time')), + ('window_stop', (2, 'builtin_interfaces/msg/Time')), + ('statistics', (4, ((2, 'statistics_msgs/msg/StatisticDataPoint'), None))), + ]), + 'std_msgs/msg/UInt8': ([ + ], [ + ('data', (1, 'uint8')), + ]), + 'std_msgs/msg/Float32MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'float32'), None))), + ]), + 'std_msgs/msg/Int8': ([ + ], [ + ('data', (1, 'int8')), + ]), + 'std_msgs/msg/Empty': ([ + ], [ + ('structure_needs_at_least_one_member', (1, 'uint8')), + ]), + 'std_msgs/msg/String': ([ + ], [ + ('data', (1, 'string')), + ]), + 'std_msgs/msg/MultiArrayDimension': ([ + ], [ + ('label', (1, 'string')), + ('size', (1, 'uint32')), + ('stride', (1, 'uint32')), + ]), + 'std_msgs/msg/UInt64': ([ + ], [ + ('data', (1, 'uint64')), + ]), + 'std_msgs/msg/UInt16': ([ + ], [ + ('data', (1, 'uint16')), + ]), + 'std_msgs/msg/Float32': ([ + ], [ + ('data', (1, 'float32')), + ]), + 'std_msgs/msg/Int64': ([ + ], [ + ('data', (1, 'int64')), + ]), + 'std_msgs/msg/Int16MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'int16'), None))), + ]), + 'std_msgs/msg/Int16': ([ + ], [ + ('data', (1, 'int16')), + ]), + 'std_msgs/msg/Float64MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'float64'), None))), + ]), + 'std_msgs/msg/MultiArrayLayout': ([ + ], [ + ('dim', (4, ((2, 'std_msgs/msg/MultiArrayDimension'), None))), + ('data_offset', (1, 'uint32')), + ]), + 'std_msgs/msg/UInt32MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint32'), None))), + ]), + 'std_msgs/msg/Header': ([ + ], [ + ('stamp', (2, 'builtin_interfaces/msg/Time')), + ('frame_id', (1, 'string')), + ]), + 'std_msgs/msg/ByteMultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint8'), None))), + ]), + 'std_msgs/msg/Int8MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'int8'), None))), + ]), + 'std_msgs/msg/Float64': ([ + ], [ + ('data', (1, 'float64')), + ]), + 'std_msgs/msg/UInt8MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint8'), None))), + ]), + 'std_msgs/msg/Byte': ([ + ], [ + ('data', (1, 'uint8')), + ]), + 'std_msgs/msg/Char': ([ + ], [ + ('data', (1, 'uint8')), + ]), + 'std_msgs/msg/UInt64MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint64'), None))), + ]), + 'std_msgs/msg/Int32MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'int32'), None))), + ]), + 'std_msgs/msg/ColorRGBA': ([ + ], [ + ('r', (1, 'float32')), + ('g', (1, 'float32')), + ('b', (1, 'float32')), + ('a', (1, 'float32')), + ]), + 'std_msgs/msg/Bool': ([ + ], [ + ('data', (1, 'bool')), + ]), + 'std_msgs/msg/UInt32': ([ + ], [ + ('data', (1, 'uint32')), + ]), + 'std_msgs/msg/Int64MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'int64'), None))), + ]), + 'std_msgs/msg/Int32': ([ + ], [ + ('data', (1, 'int32')), + ]), + 'std_msgs/msg/UInt16MultiArray': ([ + ], [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint16'), None))), + ]), + 'stereo_msgs/msg/DisparityImage': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('image', (2, 'sensor_msgs/msg/Image')), + ('f', (1, 'float32')), + ('t', (1, 'float32')), + ('valid_window', (2, 'sensor_msgs/msg/RegionOfInterest')), + ('min_disparity', (1, 'float32')), + ('max_disparity', (1, 'float32')), + ('delta_d', (1, 'float32')), + ]), + 'tf2_msgs/msg/TF2Error': ([ + ('NO_ERROR', 'uint8', 0), + ('LOOKUP_ERROR', 'uint8', 1), + ('CONNECTIVITY_ERROR', 'uint8', 2), + ('EXTRAPOLATION_ERROR', 'uint8', 3), + ('INVALID_ARGUMENT_ERROR', 'uint8', 4), + ('TIMEOUT_ERROR', 'uint8', 5), + ('TRANSFORM_ERROR', 'uint8', 6), + ], [ + ('error', (1, 'uint8')), + ('error_string', (1, 'string')), + ]), + 'tf2_msgs/msg/TFMessage': ([ + ], [ + ('transforms', (4, ((2, 'geometry_msgs/msg/TransformStamped'), None))), + ]), + 'trajectory_msgs/msg/MultiDOFJointTrajectory': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('joint_names', (4, ((1, 'string'), None))), + ('points', (4, ((2, 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint'), None))), + ]), + 'trajectory_msgs/msg/JointTrajectory': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('joint_names', (4, ((1, 'string'), None))), + ('points', (4, ((2, 'trajectory_msgs/msg/JointTrajectoryPoint'), None))), + ]), + 'trajectory_msgs/msg/JointTrajectoryPoint': ([ + ], [ + ('positions', (4, ((1, 'float64'), None))), + ('velocities', (4, ((1, 'float64'), None))), + ('accelerations', (4, ((1, 'float64'), None))), + ('effort', (4, ((1, 'float64'), None))), + ('time_from_start', (2, 'builtin_interfaces/msg/Duration')), + ]), + 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint': ([ + ], [ + ('transforms', (4, ((2, 'geometry_msgs/msg/Transform'), None))), + ('velocities', (4, ((2, 'geometry_msgs/msg/Twist'), None))), + ('accelerations', (4, ((2, 'geometry_msgs/msg/Twist'), None))), + ('time_from_start', (2, 'builtin_interfaces/msg/Duration')), + ]), + 'unique_identifier_msgs/msg/UUID': ([ + ], [ + ('uuid', (3, ((1, 'uint8'), 16))), + ]), + 'visualization_msgs/msg/Marker': ([ + ('ARROW', 'int32', 0), + ('CUBE', 'int32', 1), + ('SPHERE', 'int32', 2), + ('CYLINDER', 'int32', 3), + ('LINE_STRIP', 'int32', 4), + ('LINE_LIST', 'int32', 5), + ('CUBE_LIST', 'int32', 6), + ('SPHERE_LIST', 'int32', 7), + ('POINTS', 'int32', 8), + ('TEXT_VIEW_FACING', 'int32', 9), + ('MESH_RESOURCE', 'int32', 10), + ('TRIANGLE_LIST', 'int32', 11), + ('ADD', 'int32', 0), + ('MODIFY', 'int32', 0), + ('DELETE', 'int32', 2), + ('DELETEALL', 'int32', 3), + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('ns', (1, 'string')), + ('id', (1, 'int32')), + ('type', (1, 'int32')), + ('action', (1, 'int32')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('scale', (2, 'geometry_msgs/msg/Vector3')), + ('color', (2, 'std_msgs/msg/ColorRGBA')), + ('lifetime', (2, 'builtin_interfaces/msg/Duration')), + ('frame_locked', (1, 'bool')), + ('points', (4, ((2, 'geometry_msgs/msg/Point'), None))), + ('colors', (4, ((2, 'std_msgs/msg/ColorRGBA'), None))), + ('text', (1, 'string')), + ('mesh_resource', (1, 'string')), + ('mesh_use_embedded_materials', (1, 'bool')), + ]), + 'visualization_msgs/msg/InteractiveMarkerInit': ([ + ], [ + ('server_id', (1, 'string')), + ('seq_num', (1, 'uint64')), + ('markers', (4, ((2, 'visualization_msgs/msg/InteractiveMarker'), None))), + ]), + 'visualization_msgs/msg/MenuEntry': ([ + ('FEEDBACK', 'uint8', 0), + ('ROSRUN', 'uint8', 1), + ('ROSLAUNCH', 'uint8', 2), + ], [ + ('id', (1, 'uint32')), + ('parent_id', (1, 'uint32')), + ('title', (1, 'string')), + ('command', (1, 'string')), + ('command_type', (1, 'uint8')), + ]), + 'visualization_msgs/msg/MarkerArray': ([ + ], [ + ('markers', (4, ((2, 'visualization_msgs/msg/Marker'), None))), + ]), + 'visualization_msgs/msg/InteractiveMarkerUpdate': ([ + ('KEEP_ALIVE', 'uint8', 0), + ('UPDATE', 'uint8', 1), + ], [ + ('server_id', (1, 'string')), + ('seq_num', (1, 'uint64')), + ('type', (1, 'uint8')), + ('markers', (4, ((2, 'visualization_msgs/msg/InteractiveMarker'), None))), + ('poses', (4, ((2, 'visualization_msgs/msg/InteractiveMarkerPose'), None))), + ('erases', (4, ((1, 'string'), None))), + ]), + 'visualization_msgs/msg/InteractiveMarker': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('name', (1, 'string')), + ('description', (1, 'string')), + ('scale', (1, 'float32')), + ('menu_entries', (4, ((2, 'visualization_msgs/msg/MenuEntry'), None))), + ('controls', (4, ((2, 'visualization_msgs/msg/InteractiveMarkerControl'), None))), + ]), + 'visualization_msgs/msg/InteractiveMarkerFeedback': ([ + ('KEEP_ALIVE', 'uint8', 0), + ('POSE_UPDATE', 'uint8', 1), + ('MENU_SELECT', 'uint8', 2), + ('BUTTON_CLICK', 'uint8', 3), + ('MOUSE_DOWN', 'uint8', 4), + ('MOUSE_UP', 'uint8', 5), + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('client_id', (1, 'string')), + ('marker_name', (1, 'string')), + ('control_name', (1, 'string')), + ('event_type', (1, 'uint8')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('menu_entry_id', (1, 'uint32')), + ('mouse_point', (2, 'geometry_msgs/msg/Point')), + ('mouse_point_valid', (1, 'bool')), + ]), + 'visualization_msgs/msg/ImageMarker': ([ + ('CIRCLE', 'int32', 0), + ('LINE_STRIP', 'int32', 1), + ('LINE_LIST', 'int32', 2), + ('POLYGON', 'int32', 3), + ('POINTS', 'int32', 4), + ('ADD', 'int32', 0), + ('REMOVE', 'int32', 1), + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('ns', (1, 'string')), + ('id', (1, 'int32')), + ('type', (1, 'int32')), + ('action', (1, 'int32')), + ('position', (2, 'geometry_msgs/msg/Point')), + ('scale', (1, 'float32')), + ('outline_color', (2, 'std_msgs/msg/ColorRGBA')), + ('filled', (1, 'uint8')), + ('fill_color', (2, 'std_msgs/msg/ColorRGBA')), + ('lifetime', (2, 'builtin_interfaces/msg/Duration')), + ('points', (4, ((2, 'geometry_msgs/msg/Point'), None))), + ('outline_colors', (4, ((2, 'std_msgs/msg/ColorRGBA'), None))), + ]), + 'visualization_msgs/msg/InteractiveMarkerControl': ([ + ('INHERIT', 'uint8', 0), + ('FIXED', 'uint8', 1), + ('VIEW_FACING', 'uint8', 2), + ('NONE', 'uint8', 0), + ('MENU', 'uint8', 1), + ('BUTTON', 'uint8', 2), + ('MOVE_AXIS', 'uint8', 3), + ('MOVE_PLANE', 'uint8', 4), + ('ROTATE_AXIS', 'uint8', 5), + ('MOVE_ROTATE', 'uint8', 6), + ('MOVE_3D', 'uint8', 7), + ('ROTATE_3D', 'uint8', 8), + ('MOVE_ROTATE_3D', 'uint8', 9), + ], [ + ('name', (1, 'string')), + ('orientation', (2, 'geometry_msgs/msg/Quaternion')), + ('orientation_mode', (1, 'uint8')), + ('interaction_mode', (1, 'uint8')), + ('always_visible', (1, 'bool')), + ('markers', (4, ((2, 'visualization_msgs/msg/Marker'), None))), + ('independent_marker_orientation', (1, 'bool')), + ('description', (1, 'string')), + ]), + 'visualization_msgs/msg/InteractiveMarkerPose': ([ + ], [ + ('header', (2, 'std_msgs/msg/Header')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('name', (1, 'string')), + ]), } diff --git a/tests/cdr.py b/tests/cdr.py index f1361891..c3cbb249 100644 --- a/tests/cdr.py +++ b/tests/cdr.py @@ -161,12 +161,13 @@ def deserialize_message(rawdata: bytes, bmap: BasetypeMap, pos: int, msgdef: Msg values.append(num) elif desc.valtype == Valtype.ARRAY: - arr, pos = deserialize_array(rawdata, bmap, pos, *desc.args) + subdesc, length = desc.args + arr, pos = deserialize_array(rawdata, bmap, pos, length, subdesc) values.append(arr) elif desc.valtype == Valtype.SEQUENCE: size, pos = deserialize_number(rawdata, bmap, pos, 'int32') - arr, pos = deserialize_array(rawdata, bmap, pos, int(size), desc.args) + arr, pos = deserialize_array(rawdata, bmap, pos, int(size), desc.args[0]) values.append(arr) return msgdef.cls(*values), pos @@ -323,12 +324,12 @@ def serialize_message( pos = serialize_number(rawdata, bmap, pos, desc.args, val) elif desc.valtype == Valtype.ARRAY: - pos = serialize_array(rawdata, bmap, pos, desc.args[1], val) + pos = serialize_array(rawdata, bmap, pos, desc.args[0], val) elif desc.valtype == Valtype.SEQUENCE: size = len(val) pos = serialize_number(rawdata, bmap, pos, 'int32', size) - pos = serialize_array(rawdata, bmap, pos, desc.args, val) + pos = serialize_array(rawdata, bmap, pos, desc.args[0], val) return pos @@ -397,14 +398,15 @@ def get_size(message: Any, msgdef: Msgdef, size: int = 0) -> int: size += isize elif desc.valtype == Valtype.ARRAY: - if len(val) != desc.args[0]: - raise SerdeError(f'Unexpected array length: {len(val)} != {desc.args[0]}.') - size = get_array_size(desc.args[1], val, size) + subdesc, length = desc.args + if len(val) != length: + raise SerdeError(f'Unexpected array length: {len(val)} != {length}.') + size = get_array_size(subdesc, val, size) elif desc.valtype == Valtype.SEQUENCE: size = (size + 4 - 1) & -4 size += 4 - size = get_array_size(desc.args, val, size) + size = get_array_size(desc.args[0], val, size) return size diff --git a/tests/test_parse.py b/tests/test_parse.py index d5ea00fe..cc78bbfc 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -35,6 +35,7 @@ time time ================================================================================ MSG: test_msgs/Other uint64[3] Header +uint32 static = 42 """ RELSIBLING_MSG = """ @@ -81,6 +82,11 @@ module test_msgs { typedef test_msgs::msg::Bar Bar; typedef double d4[4]; + module Foo_Constants { + const int32 FOO = 32; + const int64 BAR = 64; + }; + @comment(type="text", text="ignore") struct Foo { std_msgs::msg::Header header; @@ -102,17 +108,18 @@ def test_parse_msg(): get_types_from_msg('', 'test_msgs/msg/Foo') ret = get_types_from_msg(MSG, 'test_msgs/msg/Foo') assert 'test_msgs/msg/Foo' in ret - fields = ret['test_msgs/msg/Foo'] - assert fields[0][0][1] == 'std_msgs/msg/Header' - assert fields[0][1][1] == 'header' - assert fields[1][0][1] == 'std_msgs/msg/Bool' - assert fields[1][1][1] == 'bool' - assert fields[2][0][1] == 'test_msgs/msg/Bar' - assert fields[2][1][1] == 'sibling' - assert fields[3][0][0] == Nodetype.BASE - assert fields[4][0][0] == Nodetype.SEQUENCE - assert fields[5][0][0] == Nodetype.SEQUENCE - assert fields[6][0][0] == Nodetype.ARRAY + consts, fields = ret['test_msgs/msg/Foo'] + assert consts == [('global', 'int32', 42)] + assert fields[0][0] == 'header' + assert fields[0][1][1] == 'std_msgs/msg/Header' + assert fields[1][0] == 'bool' + assert fields[1][1][1] == 'std_msgs/msg/Bool' + assert fields[2][0] == 'sibling' + assert fields[2][1][1] == 'test_msgs/msg/Bar' + assert fields[3][1][0] == Nodetype.BASE + assert fields[4][1][0] == Nodetype.SEQUENCE + assert fields[5][1][0] == Nodetype.SEQUENCE + assert fields[6][1][0] == Nodetype.ARRAY def test_parse_multi_msg(): @@ -122,20 +129,23 @@ def test_parse_multi_msg(): assert 'test_msgs/msg/Foo' in ret assert 'std_msgs/msg/Header' in ret assert 'test_msgs/msg/Other' in ret - assert ret['test_msgs/msg/Foo'][0][0][1] == 'std_msgs/msg/Header' - assert ret['test_msgs/msg/Foo'][1][0][1] == 'uint8' - assert ret['test_msgs/msg/Foo'][2][0][1] == 'uint8' + fields = ret['test_msgs/msg/Foo'][1] + assert fields[0][1][1] == 'std_msgs/msg/Header' + assert fields[1][1][1] == 'uint8' + assert fields[2][1][1] == 'uint8' + consts = ret['test_msgs/msg/Other'][0] + assert consts == [('static', 'uint32', 42)] def test_parse_relative_siblings_msg(): """Test relative siblings with msg parser.""" ret = get_types_from_msg(RELSIBLING_MSG, 'test_msgs/msg/Foo') - assert ret['test_msgs/msg/Foo'][0][0][1] == 'std_msgs/msg/Header' - assert ret['test_msgs/msg/Foo'][1][0][1] == 'test_msgs/msg/Other' + assert ret['test_msgs/msg/Foo'][1][0][1][1] == 'std_msgs/msg/Header' + assert ret['test_msgs/msg/Foo'][1][1][1][1] == 'test_msgs/msg/Other' ret = get_types_from_msg(RELSIBLING_MSG, 'rel_msgs/msg/Foo') - assert ret['rel_msgs/msg/Foo'][0][0][1] == 'std_msgs/msg/Header' - assert ret['rel_msgs/msg/Foo'][1][0][1] == 'rel_msgs/msg/Other' + assert ret['rel_msgs/msg/Foo'][1][0][1][1] == 'std_msgs/msg/Header' + assert ret['rel_msgs/msg/Foo'][1][1][1][1] == 'rel_msgs/msg/Other' def test_parse_idl(): @@ -145,28 +155,29 @@ def test_parse_idl(): ret = get_types_from_idl(IDL) assert 'test_msgs/msg/Foo' in ret - fields = ret['test_msgs/msg/Foo'] - assert fields[0][0][1] == 'std_msgs/msg/Header' - assert fields[0][1][1] == 'header' - assert fields[1][0][1] == 'std_msgs/msg/Bool' - assert fields[1][1][1] == 'bool' - assert fields[2][0][1] == 'test_msgs/msg/Bar' - assert fields[2][1][1] == 'sibling' - assert fields[3][0][0] == Nodetype.BASE - assert fields[4][0][0] == Nodetype.SEQUENCE - assert fields[5][0][0] == Nodetype.SEQUENCE - assert fields[6][0][0] == Nodetype.ARRAY + consts, fields = ret['test_msgs/msg/Foo'] + assert consts == [('FOO', 'int32', 32), ('BAR', 'int64', 64)] + assert fields[0][0] == 'header' + assert fields[0][1][1] == 'std_msgs/msg/Header' + assert fields[1][0] == 'bool' + assert fields[1][1][1] == 'std_msgs/msg/Bool' + assert fields[2][0] == 'sibling' + assert fields[2][1][1] == 'test_msgs/msg/Bar' + assert fields[3][1][0] == Nodetype.BASE + assert fields[4][1][0] == Nodetype.SEQUENCE + assert fields[5][1][0] == Nodetype.SEQUENCE + assert fields[6][1][0] == Nodetype.ARRAY def test_register_types(): """Test type registeration.""" assert 'foo' not in FIELDDEFS register_types({}) - register_types({'foo': [[(1, 'bool'), (2, 'b')]]}) + register_types({'foo': [[], [('b', (1, 'bool'))]]}) assert 'foo' in FIELDDEFS - register_types({'std_msgs/msg/Header': []}) - assert len(FIELDDEFS['std_msgs/msg/Header']) == 2 + register_types({'std_msgs/msg/Header': [[], []]}) + assert len(FIELDDEFS['std_msgs/msg/Header'][1]) == 2 with pytest.raises(TypesysError, match='different definition'): - register_types({'foo': [[(1, 'bool'), (2, 'x')]]}) + register_types({'foo': [[], [('x', (1, 'bool'))]]}) From ef97081e5a96daf05d2cfd5dc268d73830fbbabd Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 1 Aug 2021 18:00:51 +0200 Subject: [PATCH 026/114] Add CDR to ROS1 bytestream conversion --- src/rosbags/serde/__init__.py | 3 +- src/rosbags/serde/messages.py | 4 +- src/rosbags/serde/ros1.py | 147 ++++++++++++++++++++++++++++++++++ src/rosbags/serde/serdes.py | 41 ++++++++++ src/rosbags/serde/typing.py | 2 + tests/test_serde.py | 30 ++++++- 6 files changed, 224 insertions(+), 3 deletions(-) diff --git a/src/rosbags/serde/__init__.py b/src/rosbags/serde/__init__.py index 55cd0961..707afd50 100644 --- a/src/rosbags/serde/__init__.py +++ b/src/rosbags/serde/__init__.py @@ -9,10 +9,11 @@ convert directly between different serialization formats. """ from .messages import SerdeError -from .serdes import deserialize_cdr, ros1_to_cdr, serialize_cdr +from .serdes import cdr_to_ros1, deserialize_cdr, ros1_to_cdr, serialize_cdr __all__ = [ 'SerdeError', + 'cdr_to_ros1', 'deserialize_cdr', 'ros1_to_cdr', 'serialize_cdr', diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py index eae01a59..7c535023 100644 --- a/src/rosbags/serde/messages.py +++ b/src/rosbags/serde/messages.py @@ -9,7 +9,7 @@ from typing import TYPE_CHECKING from rosbags.typesys import types from .cdr import generate_deserialize_cdr, generate_getsize_cdr, generate_serialize_cdr -from .ros1 import generate_ros1_to_cdr +from .ros1 import generate_cdr_to_ros1, generate_ros1_to_cdr from .typing import Descriptor, Field, Msgdef from .utils import Valtype @@ -67,5 +67,7 @@ def get_msgdef(typename: str) -> Msgdef: generate_deserialize_cdr(fields, 'be'), generate_ros1_to_cdr(fields, typename, False), generate_ros1_to_cdr(fields, typename, True), + generate_cdr_to_ros1(fields, typename, False), + generate_cdr_to_ros1(fields, typename, True), ) return MSGDEFCACHE[typename] diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index e049adf6..5dd21967 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -168,3 +168,150 @@ def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Call lines.append(' return ipos, opos') return getattr(compile_lines(lines), funcname) + + +def generate_cdr_to_ros1(fields: List[Field], typename: str, copy: bool) -> Callable: + """Generate CDR to ROS1 conversion function. + + Args: + fields: Fields of message. + typename: Message type name. + copy: Generate conversion or sizing function. + + Returns: + CDR to ROS1 conversion function. + + """ + # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements + aligned = 8 + icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + next(inext) + funcname = 'cdr_to_ros1' if copy else 'getsize_cdr_to_ros1' + lines = [ + 'import sys', + 'import numpy', + 'from rosbags.serde.messages import SerdeError, get_msgdef', + 'from rosbags.serde.primitives import pack_int32_le', + 'from rosbags.serde.primitives import unpack_int32_le', + f'def {funcname}(input, ipos, output, opos):', + ] + + if typename == 'std_msgs/msg/Header': + lines.append(' opos += 4') + + for fcurr, fnext in zip(icurr, inext): + _, desc = fcurr + + if desc.valtype == Valtype.MESSAGE: + lines.append(f' func = get_msgdef("{desc.args.name}").{funcname}') + lines.append(' ipos, opos = func(input, ipos, output, opos)') + aligned = align_after(desc) + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + lines.append(' length = unpack_int32_le(input, ipos)[0] - 1') + if copy: + lines.append(' pack_int32_le(output, opos, length)') + lines.append(' ipos += 4') + lines.append(' opos += 4') + if copy: + lines.append(' output[opos:opos + length] = input[ipos:ipos + length]') + lines.append(' ipos += length + 1') + lines.append(' opos += length') + aligned = 1 + else: + size = SIZEMAP[desc.args] + if copy: + lines.append(f' output[opos:opos + {size}] = input[ipos:ipos + {size}]') + lines.append(f' ipos += {size}') + lines.append(f' opos += {size}') + aligned = size + + elif desc.valtype == Valtype.ARRAY: + subdesc, length = desc.args + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + for _ in range(length): + lines.append(' ipos = (ipos + 4 - 1) & -4') + lines.append(' length = unpack_int32_le(input, ipos)[0] - 1') + if copy: + lines.append(' pack_int32_le(output, opos, length)') + lines.append(' ipos += 4') + lines.append(' opos += 4') + if copy: + lines.append( + ' output[opos:opos + length] = input[ipos:ipos + length]', + ) + lines.append(' ipos += length + 1') + lines.append(' opos += length') + aligned = 1 + else: + size = length * SIZEMAP[subdesc.args] + if copy: + lines.append(f' output[opos:opos + {size}] = input[ipos:ipos + {size}]') + lines.append(f' ipos += {size}') + lines.append(f' opos += {size}') + aligned = SIZEMAP[subdesc.args] + + if subdesc.valtype == Valtype.MESSAGE: + anext = align(subdesc) + anext_after = align_after(subdesc) + + lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') + for _ in range(length): + if anext > anext_after: + lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') + lines.append(' ipos, opos = func(input, ipos, output, opos)') + aligned = anext_after + else: + assert desc.valtype == Valtype.SEQUENCE + lines.append(' size = unpack_int32_le(input, ipos)[0]') + if copy: + lines.append(' pack_int32_le(output, opos, size)') + lines.append(' ipos += 4') + lines.append(' opos += 4') + subdesc = desc.args[0] + aligned = 4 + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(' for _ in range(size):') + lines.append(' ipos = (ipos + 4 - 1) & -4') + lines.append(' length = unpack_int32_le(input, ipos)[0] - 1') + if copy: + lines.append(' pack_int32_le(output, opos, length)') + lines.append(' ipos += 4') + lines.append(' opos += 4') + if copy: + lines.append(' output[opos:opos + length] = input[ipos:ipos + length]') + lines.append(' ipos += length + 1') + lines.append(' opos += length') + aligned = 1 + else: + if aligned < (anext := align(subdesc)): + lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') + lines.append(f' length = size * {SIZEMAP[subdesc.args]}') + if copy: + lines.append(' output[opos:opos + length] = input[ipos:ipos + length]') + lines.append(' ipos += length') + lines.append(' opos += length') + aligned = anext + + else: + assert subdesc.valtype == Valtype.MESSAGE + anext = align(subdesc) + lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') + lines.append(' for _ in range(size):') + lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') + lines.append(' ipos, opos = func(input, ipos, output, opos)') + aligned = align_after(subdesc) + + aligned = min([aligned, 4]) + + if fnext and aligned < (anext := align(fnext.descriptor)): + lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') + aligned = anext + + lines.append(' return ipos, opos') + return getattr(compile_lines(lines), funcname) diff --git a/src/rosbags/serde/serdes.py b/src/rosbags/serde/serdes.py index 260edc08..b4977fc1 100644 --- a/src/rosbags/serde/serdes.py +++ b/src/rosbags/serde/serdes.py @@ -100,3 +100,44 @@ def ros1_to_cdr(raw: bytes, typename: str) -> memoryview: assert ipos == len(raw) assert opos + 4 == size return rawdata.toreadonly() + + +def cdr_to_ros1(raw: bytes, typename: str) -> memoryview: + """Convert serialized CDR message directly to ROS1. + + This should be reasonably fast as conversions happen on a byte-level + without going through deserialization and serialization. + + Args: + raw: CDR serialized message. + typename: Message type name. + + Returns: + ROS1 serialized message. + + """ + assert raw[1] == 1, 'Message byte order is not little endian' + + msgdef = get_msgdef(typename) + + ipos, opos = msgdef.getsize_cdr_to_ros1( + raw[4:], + 0, + None, + 0, + ) + assert ipos + 4 == len(raw) + + raw = memoryview(raw) + size = opos + rawdata = memoryview(bytearray(size)) + + ipos, opos = msgdef.cdr_to_ros1( + raw[4:], + 0, + rawdata, + 0, + ) + assert ipos + 4 == len(raw) + assert opos == size + return rawdata.toreadonly() diff --git a/src/rosbags/serde/typing.py b/src/rosbags/serde/typing.py index 3de40593..d4e8bd69 100644 --- a/src/rosbags/serde/typing.py +++ b/src/rosbags/serde/typing.py @@ -38,3 +38,5 @@ class Msgdef(NamedTuple): deserialize_cdr_be: Callable getsize_ros1_to_cdr: Callable ros1_to_cdr: Callable + getsize_cdr_to_ros1: Callable + cdr_to_ros1: Callable diff --git a/tests/test_serde.py b/tests/test_serde.py index 6353b277..a5e646ff 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -10,9 +10,10 @@ from unittest.mock import MagicMock, patch import numpy import pytest -from rosbags.serde import SerdeError, deserialize_cdr, ros1_to_cdr, serialize_cdr +from rosbags.serde import SerdeError, cdr_to_ros1, deserialize_cdr, ros1_to_cdr, serialize_cdr from rosbags.serde.messages import get_msgdef from rosbags.typesys import get_types_from_msg, register_types +from rosbags.typesys.types import builtin_interfaces__msg__Time, std_msgs__msg__Header from .cdr import deserialize, serialize @@ -380,3 +381,30 @@ def test_ros1_to_cdr(): b'\x00\x00\x00\x00\x00\x00\x00\x02' ) assert ros1_to_cdr(msg_ros, 'test_msgs/msg/dynamic_s_64') == msg_cdr + + +def test_cdr_to_ros1(): + """Test CDR to ROS1 conversion.""" + register_types(dict(get_types_from_msg(STATIC_16_64, 'test_msgs/msg/static_16_64'))) + msg_ros = (b'\x01\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02') + msg_cdr = ( + b'\x00\x01\x00\x00' + b'\x01\x00' + b'\x00\x00\x00\x00\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + ) + assert cdr_to_ros1(msg_cdr, 'test_msgs/msg/static_16_64') == msg_ros + + register_types(dict(get_types_from_msg(DYNAMIC_S_64, 'test_msgs/msg/dynamic_s_64'))) + msg_ros = (b'\x01\x00\x00\x00X' b'\x00\x00\x00\x00\x00\x00\x00\x02') + msg_cdr = ( + b'\x00\x01\x00\x00' + b'\x02\x00\x00\x00X\x00' + b'\x00\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x02' + ) + assert cdr_to_ros1(msg_cdr, 'test_msgs/msg/dynamic_s_64') == msg_ros + + header = std_msgs__msg__Header(stamp=builtin_interfaces__msg__Time(42, 666), frame_id='frame') + msg_ros = cdr_to_ros1(serialize_cdr(header, 'std_msgs/msg/Header'), 'std_msgs/msg/Header') + assert msg_ros == b'\x00\x00\x00\x00*\x00\x00\x00\x9a\x02\x00\x00\x05\x00\x00\x00frame' From ebf357a0c67cb58e9ca87ef7258e4a85017f741d Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 1 Aug 2021 18:03:18 +0200 Subject: [PATCH 027/114] Add ROS1 message definition generator --- src/rosbags/typesys/__init__.py | 3 +- src/rosbags/typesys/msg.py | 114 +++++++++++++++++++++++++++++++- tests/test_parse.py | 47 ++++++++++++- 3 files changed, 161 insertions(+), 3 deletions(-) diff --git a/src/rosbags/typesys/__init__.py b/src/rosbags/typesys/__init__.py index 1dc19c22..413df8b0 100644 --- a/src/rosbags/typesys/__init__.py +++ b/src/rosbags/typesys/__init__.py @@ -18,11 +18,12 @@ Supported formats: from .base import TypesysError from .idl import get_types_from_idl -from .msg import get_types_from_msg +from .msg import generate_msgdef, get_types_from_msg from .register import register_types __all__ = [ 'TypesysError', + 'generate_msgdef', 'get_types_from_idl', 'get_types_from_msg', 'register_types', diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 75a24c4d..514d77e7 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -12,11 +12,13 @@ Rosbag1 connection information. from __future__ import annotations +from hashlib import md5 from pathlib import PurePosixPath as Path from typing import TYPE_CHECKING -from .base import Nodetype, parse_message_definition +from .base import Nodetype, TypesysError, parse_message_definition from .peg import Rule, Visitor, parse_grammar +from .types import FIELDDEFS if TYPE_CHECKING: from typing import Any, List @@ -129,6 +131,20 @@ def normalize_fieldtype(typename: str, field: Fielddesc, names: List[str]) -> Fi return (ftype, (ifield, args[1])) +def denormalize_msgtype(typename: str) -> str: + """Undo message tyoename normalization. + + Args: + typename: Normalized message typename. + + Returns: + ROS1 style name. + + """ + assert '/msg/' in typename + return str((path := Path(typename)).parent.parent / path.name) + + class VisitorMSG(Visitor): """MSG file visitor.""" @@ -223,3 +239,99 @@ def get_types_from_msg(text: str, name: str) -> Typesdict: """ return parse_message_definition(VisitorMSG(), f'MSG: {name}\n{text}') + + +def gendefhash(typename: str, subdefs: dict[str, tuple[str, str]]) -> tuple[str, str]: + """Generate message definition and hash for type. + + The subdefs argument will be filled with child definitions. + + Args: + typename: Name of type to generate definition for. + subdefs: Child definitions. + + Returns: + Message definition and hash. + + Raises: + TypesysError: Type does not exist. + + """ + # pylint: disable=too-many-branches + typemap = { + 'builtin_interfaces/msg/Time': 'time', + 'builtin_interfaces/msg/Duration': 'duration', + } + + deftext: list[str] = [] + hashtext: list[str] = [] + if typename not in FIELDDEFS: + raise TypesysError(f'Type {typename!r} is unknown.') + + for name, typ, value in FIELDDEFS[typename][0]: + deftext.append(f'{typ} {name}={value}') + hashtext.append(f'{typ} {name}={value}') + + for name, (ftype, args) in FIELDDEFS[typename][1]: + if ftype == Nodetype.BASE: + deftext.append(f'{args} {name}') + hashtext.append(f'{args} {name}') + elif ftype == Nodetype.NAME: + assert isinstance(args, str) + subname = args + if subname in typemap: + deftext.append(f'{typemap[subname]} {name}') + hashtext.append(f'{typemap[subname]} {name}') + else: + if subname not in subdefs: + subdefs[subname] = ('', '') + subdefs[subname] = gendefhash(subname, subdefs) + deftext.append(f'{denormalize_msgtype(subname)} {name}') + hashtext.append(f'{subdefs[subname][1]} {name}') + else: + assert isinstance(args, tuple) + subdesc, num = args + count = '' if num is None else str(num) + subtype, subname = subdesc + if subtype == Nodetype.BASE: + deftext.append(f'{subname}[{count}] {name}') + hashtext.append(f'{subname}[{count}] {name}') + elif subname in typemap: + deftext.append(f'{typemap[subname]}[{count}] {name}') + hashtext.append(f'{typemap[subname]}[{count}] {name}') + else: + if subname not in subdefs: + subdefs[subname] = ('', '') + subdefs[subname] = gendefhash(subname, subdefs) + deftext.append(f'{denormalize_msgtype(subname)}[{count}] {name}') + hashtext.append(f'{subdefs[subname][1]} {name}') + + if typename == 'std_msgs/msg/Header': + deftext.insert(0, 'uint32 seq') + hashtext.insert(0, 'uint32 seq') + + deftext.append('') + return '\n'.join(deftext), md5('\n'.join(hashtext).encode()).hexdigest() + + +def generate_msgdef(typename: str) -> tuple[str, str]: + """Generate message definition for type. + + Args: + typename: Name of type to generate definition for. + + Returns: + Message definition. + + """ + subdefs: dict[str, tuple[str, str]] = {} + msgdef, md5sum = gendefhash(typename, subdefs) + + msgdef = ''.join( + [ + msgdef, + *[f'{"=" * 80}\nMSG: {denormalize_msgtype(k)}\n{v[0]}' for k, v in subdefs.items()], + ], + ) + + return msgdef, md5sum diff --git a/tests/test_parse.py b/tests/test_parse.py index cc78bbfc..e0fb995c 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -4,7 +4,13 @@ import pytest -from rosbags.typesys import TypesysError, get_types_from_idl, get_types_from_msg, register_types +from rosbags.typesys import ( + TypesysError, + generate_msgdef, + get_types_from_idl, + get_types_from_msg, + register_types, +) from rosbags.typesys.base import Nodetype from rosbags.typesys.types import FIELDDEFS @@ -181,3 +187,42 @@ def test_register_types(): with pytest.raises(TypesysError, match='different definition'): register_types({'foo': [[], [('x', (1, 'bool'))]]}) + + +def test_generate_msgdef(): + """Test message definition generator.""" + res = generate_msgdef('std_msgs/msg/Header') + assert res == ('uint32 seq\ntime stamp\nstring frame_id\n', '2176decaecbce78abc3b96ef049fabed') + + res = generate_msgdef('geometry_msgs/msg/PointStamped') + assert res[0].split(f'{"=" * 80}\n') == [ + 'std_msgs/Header header\ngeometry_msgs/Point point\n', + 'MSG: std_msgs/Header\nuint32 seq\ntime stamp\nstring frame_id\n', + 'MSG: geometry_msgs/Point\nfloat64 x\nfloat64 y\nfloat64 z\n', + ] + + res = generate_msgdef('geometry_msgs/msg/Twist') + assert res[0].split(f'{"=" * 80}\n') == [ + 'geometry_msgs/Vector3 linear\ngeometry_msgs/Vector3 angular\n', + 'MSG: geometry_msgs/Vector3\nfloat64 x\nfloat64 y\nfloat64 z\n', + ] + + res = generate_msgdef('shape_msgs/msg/Mesh') + assert res[0].split(f'{"=" * 80}\n') == [ + 'shape_msgs/MeshTriangle[] triangles\ngeometry_msgs/Point[] vertices\n', + 'MSG: shape_msgs/MeshTriangle\nuint32[3] vertex_indices\n', + 'MSG: geometry_msgs/Point\nfloat64 x\nfloat64 y\nfloat64 z\n', + ] + + res = generate_msgdef('shape_msgs/msg/Plane') + assert res[0] == 'float64[4] coef\n' + + res = generate_msgdef('sensor_msgs/msg/MultiEchoLaserScan') + assert len(res[0].split('=' * 80)) == 3 + + register_types(get_types_from_msg('time[3] times\nuint8 foo=42', 'foo_msgs/Timelist')) + res = generate_msgdef('foo_msgs/msg/Timelist') + assert res[0] == 'uint8 foo=42\ntime[3] times\n' + + with pytest.raises(TypesysError, match='is unknown'): + generate_msgdef('foo_msgs/msg/Badname') From f33e65b14ae17a7f0988630b4f1bf237645ff760 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 1 Aug 2021 18:22:36 +0200 Subject: [PATCH 028/114] Change to connection oriented reader API --- README.rst | 4 +- docs/topics/rosbag1.rst | 15 ++--- docs/topics/rosbag2.rst | 31 +++++----- src/rosbags/convert/converter.py | 48 +++++++++++----- src/rosbags/rosbag1/reader.py | 15 ++--- src/rosbags/rosbag2/connection.py | 18 ++++++ src/rosbags/rosbag2/reader.py | 94 ++++++++++++++++++++----------- src/rosbags/rosbag2/writer.py | 72 ++++++++++++++--------- tests/test_convert.py | 51 +++++++++++++---- tests/test_reader.py | 51 +++++++++-------- tests/test_reader1.py | 18 +++--- tests/test_roundtrip.py | 9 +-- tests/test_writer.py | 36 ++++++------ 13 files changed, 290 insertions(+), 172 deletions(-) create mode 100644 src/rosbags/rosbag2/connection.py diff --git a/README.rst b/README.rst index 7a8e2a1b..f25b40c1 100644 --- a/README.rst +++ b/README.rst @@ -41,8 +41,8 @@ Read and deserialize rosbag2 messages: # create reader instance and open for reading with Reader('/home/ros/rosbag_2020_03_24') as reader: - for topic, msgtype, timestamp, rawdata in reader.messages(['/imu_raw/Imu']): - msg = deserialize_cdr(rawdata, msgtype) + for connection, timestamp, rawdata in reader.messages(['/imu_raw/Imu']): + msg = deserialize_cdr(rawdata, connection.msgtype) print(msg.header.frame_id) diff --git a/docs/topics/rosbag1.rst b/docs/topics/rosbag1.rst index 02ab7144..33d5fdd3 100644 --- a/docs/topics/rosbag1.rst +++ b/docs/topics/rosbag1.rst @@ -13,15 +13,16 @@ Instances of the :py:class:`Reader ` class are typically # create reader instance with Reader('/home/ros/rosbag_2020_03_24.bag') as reader: - # topic and msgtype information is available on .topics dictionary - for topic, info in reader.topics.items(): - print(topic, info) + # topic and msgtype information is available on .connections dictionary + for connection in reader.connections.values(): + print(connection.topic, connection.msgtype) # iterate over messages - for topic, msgtype, timestamp, rawdata in reader.messages(): - if topic == '/imu_raw/Imu': + for connection, timestamp, rawdata in reader.messages(): + if connection.topic == '/imu_raw/Imu': print(timestamp) - # messages() accepts topic filters - for topic, msgtype, timestamp, rawdata in reader.messages(['/imu_raw/Imu']): + # messages() accepts connection filters + connections = [x for x in reader.connections.values() if x.topic == '/imu_raw/Imu'] + for connection, timestamp, rawdata in reader.messages(connections=connections): print(timestamp) diff --git a/docs/topics/rosbag2.rst b/docs/topics/rosbag2.rst index 68c7edb2..3e788bb3 100644 --- a/docs/topics/rosbag2.rst +++ b/docs/topics/rosbag2.rst @@ -27,16 +27,18 @@ Instances of the :py:class:`Writer ` class can create an from rosbags.rosbag2 import Writer from rosbags.serde import serialize_cdr + from rosbags.typesys.types import std_msgs__msg__String as String # create writer instance and open for writing with Writer('/home/ros/rosbag_2020_03_24') as writer: - # add new topic - topic = '/imu_raw/Imu' - msgtype = 'sensor_msgs/msg/Imu' - writer.add_topic(topic, msgtype, 'cdr') + # add new connection + topic = '/chatter' + msgtype = String.__msgtype__ + connection = writer.add_connection(topic, msgtype, 'cdr', '') # serialize and write message - writer.write(topic, timestamp, serialize_cdr(message, msgtype)) + message = String('hello world') + writer.write(connection, timestamp, serialize_cdr(message, msgtype)) Reading rosbag2 --------------- @@ -49,17 +51,18 @@ Instances of the :py:class:`Reader ` class are used to r # create reader instance and open for reading with Reader('/home/ros/rosbag_2020_03_24') as reader: - # topic and msgtype information is available on .topics dict - for topic, msgtype in reader.topics.items(): - print(topic, msgtype) + # topic and msgtype information is available on .connections dict + for connection in reader.connections.values(): + print(connection.topic, connection.msgtype) # iterate over messages - for topic, msgtype, timestamp, rawdata in reader.messages(): - if topic == '/imu_raw/Imu': - msg = deserialize_cdr(rawdata, msgtype) + for connection, timestamp, rawdata in reader.messages(): + if connection.topic == '/imu_raw/Imu': + msg = deserialize_cdr(rawdata, connection.msgtype) print(msg.header.frame_id) - # messages() accepts topic filters - for topic, msgtype, timestamp, rawdata in reader.messages(['/imu_raw/Imu']): - msg = deserialize_cdr(rawdata, msgtype) + # messages() accepts connection filters + connections = [x for x in reader.connections.values() if x.topic == '/imu_raw/Imu'] + for connection, timestamp, rawdata in reader.messages(connections=connections): + msg = deserialize_cdr(rawdata, connection.msgtype) print(msg.header.frame_id) diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index 8c46464f..8f76567a 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -4,10 +4,12 @@ from __future__ import annotations +from dataclasses import asdict from typing import TYPE_CHECKING from rosbags.rosbag1 import Reader, ReaderError from rosbags.rosbag2 import Writer, WriterError +from rosbags.rosbag2.connection import Connection as WConnection from rosbags.serde import ros1_to_cdr from rosbags.typesys import get_types_from_msg, register_types @@ -15,6 +17,8 @@ if TYPE_CHECKING: from pathlib import Path from typing import Any, Dict, Optional + from rosbags.rosbag1.reader import Connection as RConnection + LATCH = """ - history: 3 depth: 0 @@ -38,6 +42,26 @@ class ConverterError(Exception): """Converter Error.""" +def convert_connection(rconn: RConnection) -> WConnection: + """Convert rosbag1 connection to rosbag2 connection. + + Args: + rconn: Rosbag1 connection. + + Returns: + Rosbag2 connection. + + """ + return WConnection( + -1, + 0, + rconn.topic, + rconn.msgtype, + 'cdr', + LATCH if rconn.latching else '', + ) + + def convert(src: Path, dst: Optional[Path]) -> None: """Convert Rosbag1 to Rosbag2. @@ -57,21 +81,19 @@ def convert(src: Path, dst: Optional[Path]) -> None: try: with Reader(src) as reader, Writer(dst) as writer: typs: Dict[str, Any] = {} - for name, topic in reader.topics.items(): - connection = next( # pragma: no branch - x for x in reader.connections.values() if x.topic == name - ) - writer.add_topic( - name, - topic.msgtype, - offered_qos_profiles=LATCH if connection.latching else '', - ) - typs.update(get_types_from_msg(topic.msgdef, topic.msgtype)) + connmap: Dict[int, WConnection] = {} + + for rconn in reader.connections.values(): + candidate = convert_connection(rconn) + existing = next((x for x in writer.connections.values() if x == candidate), None) + wconn = existing if existing else writer.add_connection(**asdict(candidate)) + connmap[rconn.cid] = wconn + typs.update(get_types_from_msg(rconn.msgdef, rconn.msgtype)) register_types(typs) - for topic, msgtype, timestamp, data in reader.messages(): - data = ros1_to_cdr(data, msgtype) - writer.write(topic, timestamp, data) + for rconn, timestamp, data in reader.messages(): + data = ros1_to_cdr(data, rconn.msgtype) + writer.write(connmap[rconn.cid], timestamp, data) except ReaderError as err: raise ConverterError(f'Reading source bag: {err}') from err except WriterError as err: diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index fb9fb0c4..b928cff3 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -249,7 +249,7 @@ class Header(dict): raise ReaderError(f'Could not read time field {name!r}.') from err @classmethod - def read(cls: type, src: BinaryIO, expect: Optional[RecordType] = None) -> 'Header': + def read(cls: type, src: BinaryIO, expect: Optional[RecordType] = None) -> Header: """Read header from file handle. Args: @@ -588,7 +588,7 @@ class Reader: topics: Optional[Iterable[str]] = None, start: Optional[int] = None, stop: Optional[int] = None, - ) -> Generator[Tuple[str, str, int, bytes], None, None]: + ) -> Generator[Tuple[Connection, int, bytes], None, None]: """Read messages from bag. Args: @@ -598,7 +598,7 @@ class Reader: stop: Yield only messages before this timestamp (ns). Yields: - Tuples of topic name, type, timestamp (ns), and rawdata. + Tuples of connection, timestamp (ns), and rawdata. Raises: ReaderError: Bag not open or data corrupt. @@ -635,13 +635,10 @@ class Reader: if have != RecordType.MSGDATA: raise ReaderError('Expected to find message data.') - connection = self.connections[header.get_uint32('conn')] - time = header.get_time('time') - data = read_bytes(chunk, read_uint32(chunk)) - - assert entry.time == time - yield connection.topic, connection.msgtype, time, data + connection = self.connections[header.get_uint32('conn')] + assert entry.time == header.get_time('time') + yield connection, entry.time, data def __enter__(self) -> Reader: """Open rosbag1 when entering contextmanager.""" diff --git a/src/rosbags/rosbag2/connection.py b/src/rosbags/rosbag2/connection.py new file mode 100644 index 00000000..76d080fc --- /dev/null +++ b/src/rosbags/rosbag2/connection.py @@ -0,0 +1,18 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag2 connection.""" + +from __future__ import annotations + +from dataclasses import dataclass, field + + +@dataclass +class Connection: + """Connection metadata.""" + id: int = field(compare=False) # pylint: disable=invalid-name + count: int = field(compare=False) + topic: str + msgtype: str + serialization_format: str + offered_qos_profiles: str diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index a38e0f47..03973657 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -13,9 +13,11 @@ from typing import TYPE_CHECKING import zstandard from ruamel.yaml import YAML, YAMLError +from .connection import Connection + if TYPE_CHECKING: from types import TracebackType - from typing import Any, Generator, Iterable, List, Literal, Optional, Tuple, Type, Union + from typing import Any, Dict, Generator, Iterable, List, Literal, Optional, Tuple, Type, Union class ReaderError(Exception): @@ -96,11 +98,21 @@ class Reader: if missing: raise ReaderError(f'Some database files are missing: {[str(x) for x in missing]!r}') - topics = [x['topic_metadata'] for x in self.metadata['topics_with_message_count']] - noncdr = {y for x in topics if (y := x['serialization_format']) != 'cdr'} + self.connections = { + idx + 1: Connection( + id=idx + 1, + count=x['message_count'], + topic=x['topic_metadata']['name'], + msgtype=x['topic_metadata']['type'], + serialization_format=x['topic_metadata']['serialization_format'], + offered_qos_profiles=x['topic_metadata'].get('offered_qos_profiles', ''), + ) for idx, x in enumerate(self.metadata['topics_with_message_count']) + } + noncdr = { + y for x in self.connections.values() if (y := x.serialization_format) != 'cdr' + } if noncdr: raise ReaderError(f'Serialization format {noncdr!r} is not supported.') - self.topics = {x['name']: x['type'] for x in topics} if self.compression_mode and (cfmt := self.compression_format) != 'zstd': raise ReaderError(f'Compression format {cfmt!r} is not supported.') @@ -149,22 +161,31 @@ class Reader: mode = self.metadata.get('compression_mode', '').lower() return mode if mode != 'none' else None + @property + def topics(self) -> Dict[str, Connection]: + """Topic information. + + For the moment this a dictionary mapping topic names to connections. + + """ + return {x.topic: x for x in self.connections.values()} + def messages( # pylint: disable=too-many-locals self, - topics: Iterable[str] = (), + connections: Iterable[Connection] = (), start: Optional[int] = None, stop: Optional[int] = None, - ) -> Generator[Tuple[str, str, int, bytes], None, None]: + ) -> Generator[Tuple[Connection, int, bytes], None, None]: """Read messages from bag. Args: - topics: Iterable with topic names to filter for. An empty iterable - yields all messages. + connections: Iterable with connections to filter for. An empty + iterable disables filtering on connections. start: Yield only messages at or after this timestamp (ns). stop: Yield only messages before this timestamp (ns). Yields: - Tuples of topic name, type, timestamp (ns), and rawdata. + Tuples of connection, timestamp (ns), and rawdata. Raises: ReaderError: Bag not open. @@ -173,7 +194,32 @@ class Reader: if not self.bio: raise ReaderError('Rosbag is not open.') - topics = tuple(topics) + query = [ + 'SELECT topics.id,messages.timestamp,messages.data', + 'FROM messages JOIN topics ON messages.topic_id=topics.id', + ] + args: List[Any] = [] + clause = 'WHERE' + + if connections: + topics = {x.topic for x in connections} + query.append(f'{clause} topics.name IN ({",".join("?" for _ in topics)})') + args += topics + clause = 'AND' + + if start is not None: + query.append(f'{clause} messages.timestamp >= ?') + args.append(start) + clause = 'AND' + + if stop is not None: + query.append(f'{clause} messages.timestamp < ?') + args.append(stop) + clause = 'AND' + + query.append('ORDER BY timestamp') + querystr = ' '.join(query) + for filepath in self.paths: with decompress(filepath, self.compression_mode == 'file') as path: conn = sqlite3.connect(f'file:{path}?immutable=1', uri=True) @@ -186,34 +232,16 @@ class Reader: if cur.fetchone()[0] != 2: raise ReaderError(f'Cannot open database {path} or database missing tables.') - query = [ - 'SELECT topics.name,topics.type,messages.timestamp,messages.data', - 'FROM messages JOIN topics ON messages.topic_id=topics.id', - ] - args: List[Any] = [] - - if topics: - query.append(f'WHERE topics.name IN ({",".join("?" for _ in topics)})') - args += topics - - if start is not None: - query.append(f'{"AND" if args else "WHERE"} messages.timestamp >= ?') - args.append(start) - - if stop is not None: - query.append(f'{"AND" if args else "WHERE"} messages.timestamp < ?') - args.append(stop) - - query.append('ORDER BY timestamp') - cur.execute(' '.join(query), args) + cur.execute(querystr, args) if self.compression_mode == 'message': decomp = zstandard.ZstdDecompressor().decompress for row in cur: - topic, msgtype, timestamp, data = row - yield topic, msgtype, timestamp, decomp(data) + cid, timestamp, data = row + yield self.connections[cid], timestamp, decomp(data) else: - yield from cur + for cid, timestamp, data in cur: + yield self.connections[cid], timestamp, data def __enter__(self) -> Reader: """Open rosbag2 when entering contextmanager.""" diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 23d4c701..18becac6 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -1,6 +1,6 @@ # Copyright 2020-2021 Ternaris. # SPDX-License-Identifier: Apache-2.0 -"""Rosbag2 reader.""" +"""Rosbag2 writer.""" from __future__ import annotations @@ -12,6 +12,8 @@ from typing import TYPE_CHECKING import zstandard from ruamel.yaml import YAML +from .connection import Connection + if TYPE_CHECKING: from types import TracebackType from typing import Any, Dict, Literal, Optional, Type, Union @@ -77,10 +79,9 @@ class Writer: # pylint: disable=too-many-instance-attributes self.compression_mode = '' self.compression_format = '' self.compressor: Optional[zstandard.ZstdCompressor] = None - self.topics: Dict[str, Any] = {} + self.connections: Dict[int, Connection] = {} self.conn = None self.cursor: Optional[sqlite3.Cursor] = None - self.topics = {} def set_compression(self, mode: CompressionMode, fmt: CompressionFormat): """Enable compression on bag. @@ -118,22 +119,27 @@ class Writer: # pylint: disable=too-many-instance-attributes self.conn.executescript(self.SQLITE_SCHEMA) self.cursor = self.conn.cursor() - def add_topic( + def add_connection( self, - name: str, - typ: str, + topic: str, + msgtype: str, serialization_format: str = 'cdr', offered_qos_profiles: str = '', - ): - """Add a topic. + **_kw: Any, + ) -> Connection: + """Add a connection. This function can only be called after opening a bag. Args: - name: Topic name. - typ: Message type. + topic: Topic name. + msgtype: Message type. serialization_format: Serialization format. offered_qos_profiles: QOS Profile. + _kw: Ignored to allow consuming dicts from connection objects. + + Returns: + Connection object. Raises: WriterError: Bag not open or topic previously registered. @@ -141,17 +147,28 @@ class Writer: # pylint: disable=too-many-instance-attributes """ if not self.cursor: raise WriterError('Bag was not opened.') - if name in self.topics: - raise WriterError(f'Topics can only be added once: {name!r}.') - meta = (len(self.topics) + 1, name, typ, serialization_format, offered_qos_profiles) - self.cursor.execute('INSERT INTO topics VALUES(?, ?, ?, ?, ?)', meta) - self.topics[name] = [*meta, 0] - def write(self, topic: str, timestamp: int, data: bytes): + connection = Connection( + id=len(self.connections.values()) + 1, + count=0, + topic=topic, + msgtype=msgtype, + serialization_format=serialization_format, + offered_qos_profiles=offered_qos_profiles, + ) + if connection in self.connections.values(): + raise WriterError(f'Connection can only be added once: {connection!r}.') + + self.connections[connection.id] = connection + meta = (connection.id, topic, msgtype, serialization_format, offered_qos_profiles) + self.cursor.execute('INSERT INTO topics VALUES(?, ?, ?, ?, ?)', meta) + return connection + + def write(self, connection: Connection, timestamp: int, data: bytes): """Write message to rosbag2. Args: - topic: Topic message belongs to. + connection: Connection to write message to. timestamp: Message timestamp (ns). data: Serialized message data. @@ -161,19 +178,18 @@ class Writer: # pylint: disable=too-many-instance-attributes """ if not self.cursor: raise WriterError('Bag was not opened.') - if topic not in self.topics: - raise WriterError(f'Tried to write to unknown topic {topic!r}.') + if connection not in self.connections.values(): + raise WriterError(f'Tried to write to unknown connection {connection!r}.') if self.compression_mode == 'message': assert self.compressor data = self.compressor.compress(data) - tmeta = self.topics[topic] self.cursor.execute( 'INSERT INTO messages (topic_id, timestamp, data) VALUES(?, ?, ?)', - (tmeta[0], timestamp, data), + (connection.id, timestamp, data), ) - tmeta[-1] += 1 + connection.count += 1 def close(self): """Close rosbag2 after writing. @@ -214,13 +230,13 @@ class Writer: # pylint: disable=too-many-instance-attributes 'topics_with_message_count': [ { 'topic_metadata': { - 'name': x[1], - 'type': x[2], - 'serialization_format': x[3], - 'offered_qos_profiles': x[4], + 'name': x.topic, + 'type': x.msgtype, + 'serialization_format': x.serialization_format, + 'offered_qos_profiles': x.offered_qos_profiles, }, - 'message_count': x[5], - } for x in self.topics.values() + 'message_count': x.count, + } for x in self.connections.values() ], 'compression_format': self.compression_format, 'compression_mode': self.compression_mode, diff --git a/tests/test_convert.py b/tests/test_convert.py index db3cefc8..057a6092 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -76,17 +76,29 @@ def test_convert(tmp_path: Path): patch('rosbags.convert.converter.register_types') as register_types, \ patch('rosbags.convert.converter.ros1_to_cdr') as ros1_to_cdr: + connections = [ + Mock(topic='/topic', msgtype='typ', latching=False), + Mock(topic='/topic', msgtype='typ', latching=True), + ] + + wconnections = [ + Mock(topic='/topic', msgtype='typ'), + Mock(topic='/topic', msgtype='typ'), + ] + reader.return_value.__enter__.return_value.connections = { - 0: Mock(topic='/topic', latching=False), - 1: Mock(topic='/latched', latching=True), - } - reader.return_value.__enter__.return_value.topics = { - '/topic': Mock(msgtype='typ', msgdef='def'), - '/latched': Mock(msgtype='typ', msgdef='def'), + 1: connections[0], + 2: connections[1], } + reader.return_value.__enter__.return_value.messages.return_value = [ - ('/topic', 'typ', 42, b'\x42'), - ('/latched', 'typ', 43, b'\x43'), + (connections[0], 42, b'\x42'), + (connections[1], 43, b'\x43'), + ] + + writer.return_value.__enter__.return_value.add_connection.side_effect = [ + wconnections[0], + wconnections[1], ] ros1_to_cdr.return_value = b'666' @@ -97,14 +109,29 @@ def test_convert(tmp_path: Path): reader.return_value.__enter__.return_value.messages.assert_called_with() writer.assert_called_with(Path('foo')) - writer.return_value.__enter__.return_value.add_topic.assert_has_calls( + writer.return_value.__enter__.return_value.add_connection.assert_has_calls( [ - call('/topic', 'typ', offered_qos_profiles=''), - call('/latched', 'typ', offered_qos_profiles=LATCH), + call( + id=-1, + count=0, + topic='/topic', + msgtype='typ', + serialization_format='cdr', + offered_qos_profiles='', + ), + call( + id=-1, + count=0, + topic='/topic', + msgtype='typ', + serialization_format='cdr', + offered_qos_profiles=LATCH, + ), ], ) writer.return_value.__enter__.return_value.write.assert_has_calls( - [call('/topic', 42, b'666'), call('/latched', 43, b'666')], + [call(wconnections[0], 42, b'666'), + call(wconnections[1], 43, b'666')], ) register_types.assert_called_with({'typ': 'def'}) diff --git a/tests/test_reader.py b/tests/test_reader.py index 66d1d02d..2862954b 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -126,25 +126,26 @@ def test_reader(bag: Path): assert reader.message_count == 4 if reader.compression_mode: assert reader.compression_format == 'zstd' - + assert [*reader.connections.keys()] == [1, 2, 3] + assert [*reader.topics.keys()] == ['/poly', '/magn', '/joint'] gen = reader.messages() - topic, msgtype, timestamp, rawdata = next(gen) - assert topic == '/poly' - assert msgtype == 'geometry_msgs/msg/Polygon' + connection, timestamp, rawdata = next(gen) + assert connection.topic == '/poly' + assert connection.msgtype == 'geometry_msgs/msg/Polygon' assert timestamp == 666 assert rawdata == MSG_POLY[0] for idx in range(2): - topic, msgtype, timestamp, rawdata = next(gen) - assert topic == '/magn' - assert msgtype == 'sensor_msgs/msg/MagneticField' + connection, timestamp, rawdata = next(gen) + assert connection.topic == '/magn' + assert connection.msgtype == 'sensor_msgs/msg/MagneticField' assert timestamp == 708 assert rawdata == [MSG_MAGN, MSG_MAGN_BIG][idx][0] - topic, msgtype, timestamp, rawdata = next(gen) - assert topic == '/joint' - assert msgtype == 'trajectory_msgs/msg/JointTrajectory' + connection, timestamp, rawdata = next(gen) + assert connection.topic == '/joint' + assert connection.msgtype == 'trajectory_msgs/msg/JointTrajectory' with pytest.raises(StopIteration): next(gen) @@ -153,32 +154,32 @@ def test_reader(bag: Path): def test_message_filters(bag: Path): """Test reader filters messages.""" with Reader(bag) as reader: - - gen = reader.messages(['/magn']) - topic, _, _, _ = next(gen) - assert topic == '/magn' - topic, _, _, _ = next(gen) - assert topic == '/magn' + magn_connections = [x for x in reader.connections.values() if x.topic == '/magn'] + gen = reader.messages(connections=magn_connections) + connection, _, _ = next(gen) + assert connection.topic == '/magn' + connection, _, _ = next(gen) + assert connection.topic == '/magn' with pytest.raises(StopIteration): next(gen) gen = reader.messages(start=667) - topic, _, _, _ = next(gen) - assert topic == '/magn' - topic, _, _, _ = next(gen) - assert topic == '/magn' - topic, _, _, _ = next(gen) - assert topic == '/joint' + connection, _, _ = next(gen) + assert connection.topic == '/magn' + connection, _, _ = next(gen) + assert connection.topic == '/magn' + connection, _, _ = next(gen) + assert connection.topic == '/joint' with pytest.raises(StopIteration): next(gen) gen = reader.messages(stop=667) - topic, _, _, _ = next(gen) - assert topic == '/poly' + connection, _, _ = next(gen) + assert connection.topic == '/poly' with pytest.raises(StopIteration): next(gen) - gen = reader.messages(['/magn'], stop=667) + gen = reader.messages(connections=magn_connections, stop=667) with pytest.raises(StopIteration): next(gen) diff --git a/tests/test_reader1.py b/tests/test_reader1.py index 294e052b..f0a08124 100644 --- a/tests/test_reader1.py +++ b/tests/test_reader1.py @@ -141,7 +141,7 @@ def write_bag(bag, header, chunks=None): # pylint: disable=too-many-locals,too- header['index_pos'] = pack(' (path / 'compress_message.db3').stat().st_size @@ -76,7 +77,7 @@ def test_failure_cases(tmp_path: Path): bag = Writer(tmp_path / 'topic') with pytest.raises(WriterError, match='was not opened'): - bag.add_topic('/tf', 'tf_msgs/msg/tf2') + bag.add_connection('/tf', 'tf_msgs/msg/tf2') bag = Writer(tmp_path / 'write') with pytest.raises(WriterError, match='was not opened'): @@ -84,11 +85,12 @@ def test_failure_cases(tmp_path: Path): bag = Writer(tmp_path / 'topic') bag.open() - bag.add_topic('/tf', 'tf_msgs/msg/tf2') + bag.add_connection('/tf', 'tf_msgs/msg/tf2') with pytest.raises(WriterError, match='only be added once'): - bag.add_topic('/tf', 'tf_msgs/msg/tf2') + bag.add_connection('/tf', 'tf_msgs/msg/tf2') bag = Writer(tmp_path / 'notopic') bag.open() - with pytest.raises(WriterError, match='unknown topic'): - bag.write('/test', 42, b'\x00') + connection = Connection(1, 0, '/tf', 'tf_msgs/msg/tf2', 'cdr', '') + with pytest.raises(WriterError, match='unknown connection'): + bag.write(connection, 42, b'\x00') From 5bd1bcbd8303c12bca6acf7eabc44e49cce8c3f8 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 6 Aug 2021 12:03:29 +0200 Subject: [PATCH 029/114] Use built-in collections as generic types --- src/rosbags/convert/converter.py | 6 ++--- src/rosbags/rosbag1/reader.py | 42 +++++++++++------------------- src/rosbags/rosbag2/reader.py | 10 ++++---- src/rosbags/rosbag2/writer.py | 4 +-- src/rosbags/serde/cdr.py | 22 ++++++++++------ src/rosbags/serde/messages.py | 4 +-- src/rosbags/serde/ros1.py | 16 +++++++----- src/rosbags/serde/utils.py | 5 ++-- src/rosbags/typesys/msg.py | 6 ++--- src/rosbags/typesys/peg.py | 44 ++++++++++++++++---------------- 10 files changed, 78 insertions(+), 81 deletions(-) diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index 8f76567a..ef8565d4 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -15,7 +15,7 @@ from rosbags.typesys import get_types_from_msg, register_types if TYPE_CHECKING: from pathlib import Path - from typing import Any, Dict, Optional + from typing import Any, Optional from rosbags.rosbag1.reader import Connection as RConnection @@ -80,8 +80,8 @@ def convert(src: Path, dst: Optional[Path]) -> None: try: with Reader(src) as reader, Writer(dst) as writer: - typs: Dict[str, Any] = {} - connmap: Dict[int, WConnection] = {} + typs: dict[str, Any] = {} + connmap: dict[int, WConnection] = {} for rconn in reader.connections.values(): candidate = convert_connection(rconn) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index b928cff3..47223d07 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -23,19 +23,7 @@ from rosbags.typesys.msg import normalize_msgtype if TYPE_CHECKING: from types import TracebackType - from typing import ( - BinaryIO, - Callable, - Dict, - Generator, - Iterable, - List, - Literal, - Optional, - Tuple, - Type, - Union, - ) + from typing import BinaryIO, Callable, Generator, Iterable, Literal, Optional, Type, Union class ReaderError(Exception): @@ -71,7 +59,7 @@ class Connection(NamedTuple): msgdef: str callerid: Optional[str] latching: Optional[int] - indexes: List + indexes: list class ChunkInfo(NamedTuple): @@ -80,7 +68,7 @@ class ChunkInfo(NamedTuple): pos: int start_time: int end_time: int - connection_counts: Dict[int, int] + connection_counts: dict[int, int] class Chunk(NamedTuple): @@ -107,11 +95,11 @@ class IndexData(NamedTuple): chunk_pos: int offset: int - def __lt__(self, other: Tuple[int, ...]) -> bool: + def __lt__(self, other: tuple[int, ...]) -> bool: """Compare by time only.""" return self.time < other[0] - def __le__(self, other: Tuple[int, ...]) -> bool: + def __le__(self, other: tuple[int, ...]) -> bool: """Compare by time only.""" return self.time <= other[0] @@ -121,11 +109,11 @@ class IndexData(NamedTuple): return NotImplemented return self.time == other[0] - def __ge__(self, other: Tuple[int, ...]) -> bool: + def __ge__(self, other: tuple[int, ...]) -> bool: """Compare by time only.""" return self.time >= other[0] - def __gt__(self, other: Tuple[int, ...]) -> bool: + def __gt__(self, other: tuple[int, ...]) -> bool: """Compare by time only.""" return self.time > other[0] @@ -371,11 +359,11 @@ class Reader: raise ReaderError(f'File {str(self.path)!r} does not exist.') self.bio: Optional[BinaryIO] = None - self.connections: Dict[int, Connection] = {} - self.chunk_infos: List[ChunkInfo] = [] - self.chunks: Dict[int, Chunk] = {} + self.connections: dict[int, Connection] = {} + self.chunk_infos: list[ChunkInfo] = [] + self.chunks: dict[int, Chunk] = {} self.current_chunk = (-1, BytesIO()) - self.topics: Dict[str, TopicInfo] = {} + self.topics: dict[str, TopicInfo] = {} def open(self): # pylint: disable=too-many-branches,too-many-locals """Open rosbag and read metadata.""" @@ -480,7 +468,7 @@ class Reader: """Total message count.""" return reduce(lambda x, y: x + y, (x.msgcount for x in self.topics.values()), 0) - def read_connection(self) -> Tuple[int, Connection]: + def read_connection(self) -> tuple[int, Connection]: """Read connection record from current position.""" assert self.bio header = Header.read(self.bio, RecordType.CONNECTION) @@ -552,7 +540,7 @@ class Reader: decompressor, ) - def read_index_data(self, pos: int) -> Tuple[int, List[IndexData]]: + def read_index_data(self, pos: int) -> tuple[int, list[IndexData]]: """Read index data from position. Args: @@ -576,7 +564,7 @@ class Reader: self.bio.seek(4, os.SEEK_CUR) - index: List[IndexData] = [] + index: list[IndexData] = [] for _ in range(count): time = deserialize_time(self.bio.read(8)) offset = read_uint32(self.bio) @@ -588,7 +576,7 @@ class Reader: topics: Optional[Iterable[str]] = None, start: Optional[int] = None, stop: Optional[int] = None, - ) -> Generator[Tuple[Connection, int, bytes], None, None]: + ) -> Generator[tuple[Connection, int, bytes], None, None]: """Read messages from bag. Args: diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 03973657..636c0edc 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -17,7 +17,7 @@ from .connection import Connection if TYPE_CHECKING: from types import TracebackType - from typing import Any, Dict, Generator, Iterable, List, Literal, Optional, Tuple, Type, Union + from typing import Any, Generator, Iterable, Literal, Optional, Type, Union class ReaderError(Exception): @@ -162,7 +162,7 @@ class Reader: return mode if mode != 'none' else None @property - def topics(self) -> Dict[str, Connection]: + def topics(self) -> dict[str, Connection]: """Topic information. For the moment this a dictionary mapping topic names to connections. @@ -175,7 +175,7 @@ class Reader: connections: Iterable[Connection] = (), start: Optional[int] = None, stop: Optional[int] = None, - ) -> Generator[Tuple[Connection, int, bytes], None, None]: + ) -> Generator[tuple[Connection, int, bytes], None, None]: """Read messages from bag. Args: @@ -185,7 +185,7 @@ class Reader: stop: Yield only messages before this timestamp (ns). Yields: - Tuples of connection, timestamp (ns), and rawdata. + tuples of connection, timestamp (ns), and rawdata. Raises: ReaderError: Bag not open. @@ -198,7 +198,7 @@ class Reader: 'SELECT topics.id,messages.timestamp,messages.data', 'FROM messages JOIN topics ON messages.topic_id=topics.id', ] - args: List[Any] = [] + args: list[Any] = [] clause = 'WHERE' if connections: diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 18becac6..386aef23 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -16,7 +16,7 @@ from .connection import Connection if TYPE_CHECKING: from types import TracebackType - from typing import Any, Dict, Literal, Optional, Type, Union + from typing import Any, Literal, Optional, Type, Union class WriterError(Exception): @@ -79,7 +79,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.compression_mode = '' self.compression_format = '' self.compressor: Optional[zstandard.ZstdCompressor] = None - self.connections: Dict[int, Connection] = {} + self.connections: dict[int, Connection] = {} self.conn = None self.cursor: Optional[sqlite3.Cursor] = None diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index de3ff282..5b777878 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -13,16 +13,16 @@ from __future__ import annotations import sys from itertools import tee -from typing import TYPE_CHECKING, Iterator, Optional, Tuple, cast +from typing import TYPE_CHECKING, Iterator, cast from .typing import Field from .utils import SIZEMAP, Valtype, align, align_after, compile_lines if TYPE_CHECKING: - from typing import Callable, List + from typing import Callable -def generate_getsize_cdr(fields: List[Field]) -> Tuple[Callable, int]: +def generate_getsize_cdr(fields: list[Field]) -> tuple[Callable, int]: """Generate cdr size calculation function. Args: @@ -37,7 +37,9 @@ def generate_getsize_cdr(fields: List[Field]) -> Tuple[Callable, int]: is_stat = True aligned = 8 - icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + iterators = tee([*fields, None]) + icurr = cast(Iterator[Field], iterators[0]) + inext = iterators[1] next(inext) lines = [ 'import sys', @@ -155,7 +157,7 @@ def generate_getsize_cdr(fields: List[Field]) -> Tuple[Callable, int]: return compile_lines(lines).getsize_cdr, is_stat * size # type: ignore -def generate_serialize_cdr(fields: List[Field], endianess: str) -> Callable: +def generate_serialize_cdr(fields: list[Field], endianess: str) -> Callable: """Generate cdr serialization function. Args: @@ -168,7 +170,9 @@ def generate_serialize_cdr(fields: List[Field], endianess: str) -> Callable: """ # pylint: disable=too-many-branches,too-many-locals,too-many-statements aligned = 8 - icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + iterators = tee([*fields, None]) + icurr = cast(Iterator[Field], iterators[0]) + inext = iterators[1] next(inext) lines = [ 'import sys', @@ -292,7 +296,7 @@ def generate_serialize_cdr(fields: List[Field], endianess: str) -> Callable: return compile_lines(lines).serialize_cdr # type: ignore -def generate_deserialize_cdr(fields: List[Field], endianess: str) -> Callable: +def generate_deserialize_cdr(fields: list[Field], endianess: str) -> Callable: """Generate cdr deserialization function. Args: @@ -305,7 +309,9 @@ def generate_deserialize_cdr(fields: List[Field], endianess: str) -> Callable: """ # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements aligned = 8 - icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + iterators = tee([*fields, None]) + icurr = cast(Iterator[Field], iterators[0]) + inext = iterators[1] next(inext) lines = [ 'import sys', diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py index 7c535023..ed06449b 100644 --- a/src/rosbags/serde/messages.py +++ b/src/rosbags/serde/messages.py @@ -14,9 +14,9 @@ from .typing import Descriptor, Field, Msgdef from .utils import Valtype if TYPE_CHECKING: - from typing import Any, Dict + from typing import Any -MSGDEFCACHE: Dict[str, Msgdef] = {} +MSGDEFCACHE: dict[str, Msgdef] = {} class SerdeError(Exception): diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index 5dd21967..a12feef6 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -12,16 +12,16 @@ conversion of ROS1 to CDR. from __future__ import annotations from itertools import tee -from typing import TYPE_CHECKING, Iterator, Optional, Tuple, cast +from typing import TYPE_CHECKING, Iterator, cast from .typing import Field from .utils import SIZEMAP, Valtype, align, align_after, compile_lines if TYPE_CHECKING: - from typing import Callable, List # pylint: disable=ungrouped-imports + from typing import Callable # pylint: disable=ungrouped-imports -def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Callable: +def generate_ros1_to_cdr(fields: list[Field], typename: str, copy: bool) -> Callable: """Generate ROS1 to CDR conversion function. Args: @@ -35,7 +35,9 @@ def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Call """ # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements aligned = 8 - icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + iterators = tee([*fields, None]) + icurr = cast(Iterator[Field], iterators[0]) + inext = iterators[1] next(inext) funcname = 'ros1_to_cdr' if copy else 'getsize_ros1_to_cdr' lines = [ @@ -170,7 +172,7 @@ def generate_ros1_to_cdr(fields: List[Field], typename: str, copy: bool) -> Call return getattr(compile_lines(lines), funcname) -def generate_cdr_to_ros1(fields: List[Field], typename: str, copy: bool) -> Callable: +def generate_cdr_to_ros1(fields: list[Field], typename: str, copy: bool) -> Callable: """Generate CDR to ROS1 conversion function. Args: @@ -184,7 +186,9 @@ def generate_cdr_to_ros1(fields: List[Field], typename: str, copy: bool) -> Call """ # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements aligned = 8 - icurr, inext = cast(Tuple[Iterator[Field], Iterator[Optional[Field]]], tee([*fields, None])) + iterators = tee([*fields, None]) + icurr = cast(Iterator[Field], iterators[0]) + inext = iterators[1] next(inext) funcname = 'cdr_to_ros1' if copy else 'getsize_cdr_to_ros1' lines = [ diff --git a/src/rosbags/serde/utils.py b/src/rosbags/serde/utils.py index 5ebaa16c..41cdf3fe 100644 --- a/src/rosbags/serde/utils.py +++ b/src/rosbags/serde/utils.py @@ -10,7 +10,6 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from types import ModuleType - from typing import Dict, List from .typing import Descriptor @@ -24,7 +23,7 @@ class Valtype(IntEnum): SEQUENCE = 4 -SIZEMAP: Dict[str, int] = { +SIZEMAP: dict[str, int] = { 'bool': 1, 'int8': 1, 'int16': 2, @@ -83,7 +82,7 @@ def align_after(entry: Descriptor) -> int: return min([4, align_after(entry.args[0])]) -def compile_lines(lines: List[str]) -> ModuleType: +def compile_lines(lines: list[str]) -> ModuleType: """Compile lines of code to module. Args: diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 514d77e7..51936893 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -21,7 +21,7 @@ from .peg import Rule, Visitor, parse_grammar from .types import FIELDDEFS if TYPE_CHECKING: - from typing import Any, List + from typing import Any from .base import Fielddesc, Typesdict @@ -91,7 +91,7 @@ def normalize_msgtype(name: str) -> str: return str(path) -def normalize_fieldtype(typename: str, field: Fielddesc, names: List[str]) -> Fielddesc: +def normalize_fieldtype(typename: str, field: Fielddesc, names: list[str]) -> Fielddesc: """Normalize field typename. Args: @@ -235,7 +235,7 @@ def get_types_from_msg(text: str, name: str) -> Typesdict: name: Message typename. Returns: - List with single message name and parsetree. + list with single message name and parsetree. """ return parse_message_definition(VisitorMSG(), f'MSG: {name}\n{text}') diff --git a/src/rosbags/typesys/peg.py b/src/rosbags/typesys/peg.py index 92cc229b..3298b2f9 100644 --- a/src/rosbags/typesys/peg.py +++ b/src/rosbags/typesys/peg.py @@ -14,7 +14,7 @@ import re from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Any, Dict, List, Optional, Tuple + from typing import Any, Optional class Rule: @@ -23,7 +23,7 @@ class Rule: LIT = 'LITERAL' WS = re.compile(r'\s+', re.M | re.S) - def __init__(self, value: Any, rules: Dict[str, Rule], name: Optional[str] = None): + def __init__(self, value: Any, rules: dict[str, Rule], name: Optional[str] = None): """Initialize. Args: @@ -58,7 +58,7 @@ class Rule: class RuleLiteral(Rule): """Rule to match string literal.""" - def __init__(self, value: Any, rules: Dict[str, Rule], name: Optional[str] = None): + def __init__(self, value: Any, rules: dict[str, Rule], name: Optional[str] = None): """Initialize. Args: @@ -70,7 +70,7 @@ class RuleLiteral(Rule): super().__init__(value, rules, name) self.value = value[1:-1].replace('\\\'', '\'') - def parse(self, text: str, pos: int) -> Tuple[int, Any]: + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" value = self.value if text[pos:pos + len(value)] == value: @@ -83,7 +83,7 @@ class RuleLiteral(Rule): class RuleRegex(Rule): """Rule to match regular expression.""" - def __init__(self, value: Any, rules: Dict[str, Rule], name: Optional[str] = None): + def __init__(self, value: Any, rules: dict[str, Rule], name: Optional[str] = None): """Initialize. Args: @@ -95,7 +95,7 @@ class RuleRegex(Rule): super().__init__(value, rules, name) self.value = re.compile(value[2:-1], re.M | re.S) - def parse(self, text: str, pos: int) -> Tuple[int, Any]: + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" match = self.value.match(text, pos) if not match: @@ -107,7 +107,7 @@ class RuleRegex(Rule): class RuleToken(Rule): """Rule to match token.""" - def parse(self, text: str, pos: int) -> Tuple[int, Any]: + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" token = self.rules[self.value] npos, data = token.parse(text, pos) @@ -119,7 +119,7 @@ class RuleToken(Rule): class RuleOneof(Rule): """Rule to match first matching subrule.""" - def parse(self, text: str, pos: int) -> Tuple[int, Any]: + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" for value in self.value: npos, data = value.parse(text, pos) @@ -131,7 +131,7 @@ class RuleOneof(Rule): class RuleSequence(Rule): """Rule to match a sequence of subrules.""" - def parse(self, text: str, pos: int) -> Tuple[int, Any]: + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" data = [] npos = pos @@ -146,9 +146,9 @@ class RuleSequence(Rule): class RuleZeroPlus(Rule): """Rule to match zero or more occurences of subrule.""" - def parse(self, text: str, pos: int) -> Tuple[int, Any]: + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" - data: List[Any] = [] + data: list[Any] = [] lpos = pos while True: npos, node = self.value.parse(text, lpos) @@ -161,7 +161,7 @@ class RuleZeroPlus(Rule): class RuleOnePlus(Rule): """Rule to match one or more occurences of subrule.""" - def parse(self, text: str, pos: int) -> Tuple[int, Any]: + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" npos, node = self.value.parse(text, pos) if npos == -1: @@ -179,7 +179,7 @@ class RuleOnePlus(Rule): class RuleZeroOne(Rule): """Rule to match zero or one occurence of subrule.""" - def parse(self, text: str, pos: int) -> Tuple[int, Any]: + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" npos, node = self.value.parse(text, pos) if npos == -1: @@ -190,7 +190,7 @@ class RuleZeroOne(Rule): class Visitor: # pylint: disable=too-few-public-methods """Visitor transforming parse trees.""" - RULES: Dict[str, Rule] = {} + RULES: dict[str, Rule] = {} def __init__(self): """Initialize.""" @@ -208,15 +208,15 @@ class Visitor: # pylint: disable=too-few-public-methods return func(tree['data']) -def split_token(tok: str) -> List[str]: +def split_token(tok: str) -> list[str]: """Split repetition and grouping tokens.""" return list(filter(None, re.split(r'(^\()|(\)(?=[*+?]?$))|([*+?]$)', tok))) -def collapse_tokens(toks: List[Optional[Rule]], rules: Dict[str, Rule]) -> Rule: +def collapse_tokens(toks: list[Optional[Rule]], rules: dict[str, Rule]) -> Rule: """Collapse linear list of tokens to oneof of sequences.""" - value: List[Rule] = [] - seq: List[Rule] = [] + value: list[Rule] = [] + seq: list[Rule] = [] for tok in toks: if tok: seq.append(tok) @@ -227,9 +227,9 @@ def collapse_tokens(toks: List[Optional[Rule]], rules: Dict[str, Rule]) -> Rule: return RuleOneof(value, rules) if len(value) > 1 else value[0] -def parse_grammar(grammar: str) -> Dict[str, Rule]: +def parse_grammar(grammar: str) -> dict[str, Rule]: """Parse grammar into rule dictionary.""" - rules: Dict[str, Rule] = {} + rules: dict[str, Rule] = {} for token in grammar.split('\n\n'): lines = token.strip().split('\n') name, *defs = lines @@ -237,8 +237,8 @@ def parse_grammar(grammar: str) -> Dict[str, Rule]: assert items assert items[0] == '=' items.pop(0) - stack: List[Optional[Rule]] = [] - parens: List[int] = [] + stack: list[Optional[Rule]] = [] + parens: list[int] = [] while items: tok = items.pop(0) if tok in ['*', '+', '?']: From cc96973be3f00811cb65ce51b33e4ccdef6f4ea0 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 6 Aug 2021 09:39:55 +0200 Subject: [PATCH 030/114] Add rosbag1 writer --- README.rst | 2 +- docs/topics/rosbag1.rst | 29 ++- src/rosbags/rosbag1/__init__.py | 7 +- src/rosbags/rosbag1/writer.py | 403 ++++++++++++++++++++++++++++++++ tests/test_roundtrip1.py | 44 ++++ tests/test_writer1.py | 201 ++++++++++++++++ 6 files changed, 681 insertions(+), 5 deletions(-) create mode 100644 src/rosbags/rosbag1/writer.py create mode 100644 tests/test_roundtrip1.py create mode 100644 tests/test_writer1.py diff --git a/README.rst b/README.rst index f25b40c1..3a0c76cc 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ Rosbags Rosbags is the **pure python** library for everything rosbag. It contains: - **rosbag2** reader and writer, -- **rosbag1** reader for raw messages, +- **rosbag1** reader and writer, - **extensible** type system with serializers and deserializers, - **efficient converter** between rosbag1 and rosbag2, - and more. diff --git a/docs/topics/rosbag1.rst b/docs/topics/rosbag1.rst index 33d5fdd3..bbae2eb4 100644 --- a/docs/topics/rosbag1.rst +++ b/docs/topics/rosbag1.rst @@ -3,6 +3,27 @@ Rosbag1 The :py:mod:`rosbags.rosbag1` package provides fast read-only access to raw messages stored in the legacy bag format. The rosbag1 support is built for a ROS2 world and some APIs and values perform normalizations to mimic ROS2 behavior and make messages originating from rosbag1 and rosbag2 behave identically. Most notably message types are internally renamed to match their ROS2 counterparts. +Writing rosbag1 +--------------- +Instances of the :py:class:`Writer ` class can create and write to new rosbag1 files. It is usually used as a context manager. Before the first message of a topic can be written, its topic must first be added to the bag. The following example shows the typical usage pattern: + +.. code-block:: python + + from rosbags.rosbag1 import Writer + from rosbags.serde import cdr_to_ros1, serialize_cdr + from rosbags.typesys.types import std_msgs__msg__String as String + + # create writer instance and open for writing + with Writer('/home/ros/rosbag_2020_03_24.bag') as writer: + # add new connection + topic = '/chatter' + msgtype = String.__msgtype__ + connection = writer.add_connection(topic, msgtype, latching=True) + + # serialize and write message + message = String('hello world') + writer.write(connection, timestamp, cdr_to_ros1(serialize_cdr(message, msgtype), msgtype)) + Reading rosbag1 --------------- Instances of the :py:class:`Reader ` class are typically used as context managers and provide access to bag metadata and contents after the bag has been opened. The following example shows the typical usage pattern: @@ -10,6 +31,8 @@ Instances of the :py:class:`Reader ` class are typically .. code-block:: python from rosbags.rosbag1 import Reader + from rosbags.serde import deserialize_cdr, ros1_to_cdr + # create reader instance with Reader('/home/ros/rosbag_2020_03_24.bag') as reader: @@ -20,9 +43,11 @@ Instances of the :py:class:`Reader ` class are typically # iterate over messages for connection, timestamp, rawdata in reader.messages(): if connection.topic == '/imu_raw/Imu': - print(timestamp) + msg = deserialize_cdr(ros1_to_cdr(rawdata, connection.msgtype), connection.msgtype) + print(msg.header.frame_id) # messages() accepts connection filters connections = [x for x in reader.connections.values() if x.topic == '/imu_raw/Imu'] for connection, timestamp, rawdata in reader.messages(connections=connections): - print(timestamp) + msg = deserialize_cdr(ros1_to_cdr(rawdata, connection.msgtype), connection.msgtype) + print(msg.header.frame_id) diff --git a/src/rosbags/rosbag1/__init__.py b/src/rosbags/rosbag1/__init__.py index c9a93618..192bcf62 100644 --- a/src/rosbags/rosbag1/__init__.py +++ b/src/rosbags/rosbag1/__init__.py @@ -2,8 +2,8 @@ # SPDX-License-Identifier: Apache-2.0 """Rosbags support for rosbag1 files. -Reader provides access to metadata and raw message content saved in the -rosbag1 format. +Readers and writers provide access to metadata and raw message content saved +in the rosbag1 format. Supported versions: - Rosbag1 v2.0 @@ -11,8 +11,11 @@ Supported versions: """ from .reader import Reader, ReaderError +from .writer import Writer, WriterError __all__ = [ 'Reader', 'ReaderError', + 'Writer', + 'WriterError', ] diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py new file mode 100644 index 00000000..650d3c52 --- /dev/null +++ b/src/rosbags/rosbag1/writer.py @@ -0,0 +1,403 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag1 writer.""" + +from __future__ import annotations + +import struct +from bz2 import compress as bz2_compress +from collections import defaultdict +from dataclasses import dataclass +from enum import IntEnum, auto +from io import BytesIO +from pathlib import Path +from typing import TYPE_CHECKING + +from lz4.frame import compress as lz4_compress # type: ignore + +from rosbags.typesys.msg import denormalize_msgtype, generate_msgdef + +from .reader import Connection, RecordType + +if TYPE_CHECKING: + from types import TracebackType + from typing import Any, BinaryIO, Callable, Literal, Optional, Type, Union + + +class WriterError(Exception): + """Writer Error.""" + + +@dataclass +class WriteChunk: + """In progress chunk.""" + data: BytesIO + pos: int + start: int + end: int + connections: dict[int, list[tuple[int, int]]] + + +serialize_uint8 = struct.Struct(' bytes: + """Serialize time value. + + Args: + val: Time value. + + Returns: + Serialized bytes. + + """ + sec, nsec = val // 10**9, val % 10**9 + return struct.pack(' int: + """Write to file handle. + + Args: + dst: File handle. + opcode: Record type code. + + Returns: + Bytes written. + + """ + data = b'' + + if opcode: + keqv = 'op='.encode() + serialize_uint8(opcode) + data += serialize_uint32(len(keqv)) + keqv + + for key, value in self.items(): + keqv = f'{key}='.encode() + value + data += serialize_uint32(len(keqv)) + keqv + + size = len(data) + dst.write(serialize_uint32(size) + data) + return size + 4 + + +class Writer: # pylint: disable=too-many-instance-attributes + """Rosbag1 writer. + + This class implements writing of rosbag1 files in version 2.0. It should be + used as a contextmanager. + + """ + + class CompressionFormat(IntEnum): + """Compession formats.""" + + BZ2 = auto() + LZ4 = auto() + + def __init__(self, path: Union[Path, str]): + """Initialize writer. + + Args: + path: Filesystem path to bag. + + Raises: + WriterError: Target path exisits already, Writer can only create new rosbags. + + """ + path = Path(path) + self.path = path + if path.exists(): + raise WriterError(f'{path} exists already, not overwriting.') + self.bio: Optional[BinaryIO] = None + self.compressor: Callable[[bytes], bytes] = lambda x: x + self.compression_format = 'none' + self.connections: dict[int, Connection] = {} + self.chunks: list[WriteChunk] = [ + WriteChunk(BytesIO(), -1, 2**64, 0, defaultdict(list)), + ] + self.chunk_threshold = 1 * (1 << 20) + + def set_compression(self, fmt: CompressionFormat): + """Enable compression on rosbag1. + + This function has to be called before opening. + + Args: + fmt: Compressor to use, bz2 or lz4 + + Raises: + WriterError: Bag already open. + + """ + if self.bio: + raise WriterError(f'Cannot set compression, bag {self.path} already open.') + + self.compression_format = fmt.name.lower() + + bz2: Callable[[bytes], bytes] = lambda x: bz2_compress(x, compresslevel=9) + lz4: Callable[[bytes], bytes] = lambda x: lz4_compress(x, compression_level=16) + self.compressor = { + 'bz2': bz2, + 'lz4': lz4, + }[self.compression_format] + + def open(self): + """Open rosbag1 for writing.""" + try: + self.bio = self.path.open('xb') + except FileExistsError: + raise WriterError(f'{self.path} exists already, not overwriting.') from None + + self.bio.write(b'#ROSBAG V2.0\n') + header = Header() + header.set_uint64('index_pos', 0) + header.set_uint32('conn_count', 0) + header.set_uint32('chunk_count', 0) + size = header.write(self.bio, RecordType.BAGHEADER) + padsize = 4096 - 4 - size + self.bio.write(serialize_uint32(padsize) + b' ' * padsize) + + def add_connection( # pylint: disable=too-many-arguments + self, + topic: str, + msgtype: str, + msgdef: Optional[str] = None, + md5sum: Optional[str] = None, + callerid: Optional[str] = None, + latching: Optional[int] = None, + **_kw: Any, + ) -> Connection: + """Add a connection. + + This function can only be called after opening a bag. + + Args: + topic: Topic name. + msgtype: Message type. + msgdef: Message definiton. + md5sum: Message hash. + callerid: Caller id. + latching: Latching information. + _kw: Ignored to allow consuming dicts from connection objects. + + Returns: + Connection id. + + Raises: + WriterError: Bag not open or identical topic previously registered. + + """ + if not self.bio: + raise WriterError('Bag was not opened.') + + if msgdef is None or md5sum is None: + msgdef, md5sum = generate_msgdef(msgtype) + assert msgdef + assert md5sum + + connection = Connection( + len(self.connections), + topic, + denormalize_msgtype(msgtype), + md5sum, + msgdef, + callerid, + latching, + [], + ) + + if any(x[1:] == connection[1:] for x in self.connections.values()): + raise WriterError( + f'Connections can only be added once with same arguments: {connection!r}.', + ) + + bio = self.chunks[-1].data + self.write_connection(connection, bio) + + self.connections[connection.cid] = connection + return connection + + def write(self, connection: Connection, timestamp: int, data: bytes): + """Write message to rosbag1. + + Args: + connection: Connection to write message to. + timestamp: Message timestamp (ns). + data: Serialized message data. + + Raises: + WriterError: Bag not open or connection not registered. + + """ + if not self.bio: + raise WriterError('Bag was not opened.') + + if connection not in self.connections.values(): + raise WriterError(f'There is no connection {connection!r}.') from None + + chunk = self.chunks[-1] + chunk.connections[connection.cid].append((timestamp, chunk.data.tell())) + + if timestamp < chunk.start: + chunk.start = timestamp + + if timestamp > chunk.end: + chunk.end = timestamp + + header = Header() + header.set_uint32('conn', connection.cid) + header.set_time('time', timestamp) + + header.write(chunk.data, RecordType.MSGDATA) + chunk.data.write(serialize_uint32(len(data))) + chunk.data.write(data) + if chunk.data.tell() > self.chunk_threshold: + self.write_chunk(chunk) + + @staticmethod + def write_connection(connection: Connection, bio: BytesIO): + """Write connection record.""" + header = Header() + header.set_uint32('conn', connection.cid) + header.set_string('topic', connection.topic) + header.write(bio, RecordType.CONNECTION) + + header = Header() + header.set_string('topic', connection.topic) + header.set_string('type', connection.msgtype) + header.set_string('md5sum', connection.md5sum) + header.set_string('message_definition', connection.msgdef) + if connection.callerid is not None: + header.set_string('callerid', connection.callerid) + if connection.latching is not None: + header.set_string('latching', str(connection.latching)) + header.write(bio) + + def write_chunk(self, chunk: WriteChunk): + """Write open chunk to file.""" + assert self.bio + + if size := chunk.data.tell() > 0: + chunk.pos = self.bio.tell() + + header = Header() + header.set_string('compression', self.compression_format) + header.set_uint32('size', size) + header.write(self.bio, RecordType.CHUNK) + data = self.compressor(chunk.data.getvalue()) + self.bio.write(serialize_uint32(len(data))) + self.bio.write(data) + + for cid, items in chunk.connections.items(): + header = Header() + header.set_uint32('ver', 1) + header.set_uint32('conn', cid) + header.set_uint32('count', len(items)) + header.write(self.bio, RecordType.IDXDATA) + self.bio.write(serialize_uint32(len(items) * 12)) + for time, offset in items: + self.bio.write(serialize_time(time) + serialize_uint32(offset)) + + chunk.data.close() + self.chunks.append(WriteChunk(BytesIO(), -1, 2**64, 0, defaultdict(list))) + + def close(self): + """Close rosbag1 after writing. + + Closes open chunks and writes index. + + """ + for chunk in self.chunks: + if chunk.pos == -1: + self.write_chunk(chunk) + + index_pos = self.bio.tell() + + for connection in self.connections.values(): + self.write_connection(connection, self.bio) + + for chunk in self.chunks: + if chunk.pos == -1: + continue + header = Header() + header.set_uint32('ver', 1) + header.set_uint64('chunk_pos', chunk.pos) + header.set_time('start_time', 0 if chunk.start == 2**64 else chunk.start) + header.set_time('end_time', chunk.end) + header.set_uint32('count', len(chunk.connections)) + header.write(self.bio, RecordType.CHUNK_INFO) + self.bio.write(serialize_uint32(len(chunk.connections) * 8)) + for cid, items in chunk.connections.items(): + self.bio.write(serialize_uint32(cid) + serialize_uint32(len(items))) + + self.bio.seek(13) + header = Header() + header.set_uint64('index_pos', index_pos) + header.set_uint32('conn_count', len(self.connections)) + header.set_uint32('chunk_count', len([x for x in self.chunks if x.pos != -1])) + size = header.write(self.bio, RecordType.BAGHEADER) + padsize = 4096 - 4 - size + self.bio.write(serialize_uint32(padsize) + b' ' * padsize) + + self.bio.close() + + def __enter__(self) -> Writer: + """Open rosbag1 when entering contextmanager.""" + self.open() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Literal[False]: + """Close rosbag1 when exiting contextmanager.""" + self.close() + return False diff --git a/tests/test_roundtrip1.py b/tests/test_roundtrip1.py new file mode 100644 index 00000000..a85288f8 --- /dev/null +++ b/tests/test_roundtrip1.py @@ -0,0 +1,44 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Test full data roundtrip.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from rosbags.rosbag1 import Reader, Writer +from rosbags.serde import cdr_to_ros1, deserialize_cdr, ros1_to_cdr, serialize_cdr + +if TYPE_CHECKING: + from pathlib import Path + from typing import Optional + + +@pytest.mark.parametrize('fmt', [None, Writer.CompressionFormat.BZ2, Writer.CompressionFormat.LZ4]) +def test_roundtrip(tmp_path: Path, fmt: Optional[Writer.CompressionFormat]): + """Test full data roundtrip.""" + + class Foo: # pylint: disable=too-few-public-methods + """Dummy class.""" + + data = 1.25 + + path = tmp_path / 'test.bag' + wbag = Writer(path) + if fmt: + wbag.set_compression(fmt) + with wbag: + msgtype = 'std_msgs/msg/Float64' + conn = wbag.add_connection('/test', msgtype) + wbag.write(conn, 42, cdr_to_ros1(serialize_cdr(Foo, msgtype), msgtype)) + + rbag = Reader(path) + with rbag: + gen = rbag.messages() + connection, _, raw = next(gen) + msg = deserialize_cdr(ros1_to_cdr(raw, connection.msgtype), connection.msgtype) + assert msg.data == Foo.data + with pytest.raises(StopIteration): + next(gen) diff --git a/tests/test_writer1.py b/tests/test_writer1.py new file mode 100644 index 00000000..384ebadd --- /dev/null +++ b/tests/test_writer1.py @@ -0,0 +1,201 @@ +# Copyright 2020-2021 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Writer tests.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import Mock + +import pytest + +from rosbags.rosbag1 import Writer, WriterError + +if TYPE_CHECKING: + from pathlib import Path + from typing import Optional + + +def test_no_overwrite(tmp_path: Path): + """Test writer does not touch existing files.""" + path = tmp_path / 'test.bag' + path.write_text('foo') + with pytest.raises(WriterError, match='exists'): + Writer(path).open() + path.unlink() + + writer = Writer(path) + path.write_text('foo') + with pytest.raises(WriterError, match='exists'): + writer.open() + + +def test_empty(tmp_path: Path): + """Test empty bag.""" + path = tmp_path / 'test.bag' + + with Writer(path): + pass + data = path.read_bytes() + assert len(data) == 13 + 4096 + + +def test_add_connection(tmp_path: Path): + """Test adding of connections.""" + path = tmp_path / 'test.bag' + + with pytest.raises(WriterError, match='not opened'): + Writer(path).add_connection('/foo', 'test_msgs/msg/Test', 'MESSAGE_DEFINITION', 'HASH') + + with Writer(path) as writer: + res = writer.add_connection('/foo', 'test_msgs/msg/Test', 'MESSAGE_DEFINITION', 'HASH') + assert res.cid == 0 + data = path.read_bytes() + assert data.count(b'MESSAGE_DEFINITION') == 2 + assert data.count(b'HASH') == 2 + path.unlink() + + with Writer(path) as writer: + res = writer.add_connection('/foo', 'std_msgs/msg/Int8') + assert res.cid == 0 + data = path.read_bytes() + assert data.count(b'int8 data') == 2 + assert data.count(b'27ffa0c9c4b8fb8492252bcad9e5c57b') == 2 + path.unlink() + + with Writer(path) as writer: + writer.add_connection('/foo', 'test_msgs/msg/Test', 'MESSAGE_DEFINITION', 'HASH') + with pytest.raises(WriterError, match='can only be added once'): + writer.add_connection('/foo', 'test_msgs/msg/Test', 'MESSAGE_DEFINITION', 'HASH') + path.unlink() + + with Writer(path) as writer: + res1 = writer.add_connection('/foo', 'test_msgs/msg/Test', 'MESSAGE_DEFINITION', 'HASH') + res2 = writer.add_connection( + '/foo', + 'test_msgs/msg/Test', + 'MESSAGE_DEFINITION', + 'HASH', + callerid='src', + ) + res3 = writer.add_connection( + '/foo', + 'test_msgs/msg/Test', + 'MESSAGE_DEFINITION', + 'HASH', + latching=1, + ) + assert (res1.cid, res2.cid, res3.cid) == (0, 1, 2) + + +def test_write_errors(tmp_path: Path): + """Test write errors.""" + path = tmp_path / 'test.bag' + + with pytest.raises(WriterError, match='not opened'): + Writer(path).write(Mock(), 42, b'DEADBEEF') + + with Writer(path) as writer, \ + pytest.raises(WriterError, match='is no connection'): + writer.write(Mock(), 42, b'DEADBEEF') + path.unlink() + + +def test_write_simple(tmp_path: Path): + """Test writing of messages.""" + path = tmp_path / 'test.bag' + + with Writer(path) as writer: + conn_foo = writer.add_connection('/foo', 'test_msgs/msg/Test', 'MESSAGE_DEFINITION', 'HASH') + conn_latching = writer.add_connection( + '/foo', + 'test_msgs/msg/Test', + 'MESSAGE_DEFINITION', + 'HASH', + latching=1, + ) + conn_bar = writer.add_connection( + '/bar', + 'test_msgs/msg/Bar', + 'OTHER_DEFINITION', + 'HASH', + callerid='src', + ) + writer.add_connection('/baz', 'test_msgs/msg/Baz', 'NEVER_WRITTEN', 'HASH') + + writer.write(conn_foo, 42, b'DEADBEEF') + writer.write(conn_latching, 42, b'DEADBEEF') + writer.write(conn_bar, 43, b'SECRET') + writer.write(conn_bar, 43, b'SUBSEQUENT') + + res = path.read_bytes() + assert res.count(b'op=\x05') == 1 + assert res.count(b'op=\x06') == 1 + assert res.count(b'MESSAGE_DEFINITION') == 4 + assert res.count(b'latching=1') == 2 + assert res.count(b'OTHER_DEFINITION') == 2 + assert res.count(b'callerid=src') == 2 + assert res.count(b'NEVER_WRITTEN') == 2 + assert res.count(b'DEADBEEF') == 2 + assert res.count(b'SECRET') == 1 + assert res.count(b'SUBSEQUENT') == 1 + path.unlink() + + with Writer(path) as writer: + writer.chunk_threshold = 256 + conn_foo = writer.add_connection('/foo', 'test_msgs/msg/Test', 'MESSAGE_DEFINITION', 'HASH') + conn_latching = writer.add_connection( + '/foo', + 'test_msgs/msg/Test', + 'MESSAGE_DEFINITION', + 'HASH', + latching=1, + ) + conn_bar = writer.add_connection( + '/bar', + 'test_msgs/msg/Bar', + 'OTHER_DEFINITION', + 'HASH', + callerid='src', + ) + writer.add_connection('/baz', 'test_msgs/msg/Baz', 'NEVER_WRITTEN', 'HASH') + + writer.write(conn_foo, 42, b'DEADBEEF') + writer.write(conn_latching, 42, b'DEADBEEF') + writer.write(conn_bar, 43, b'SECRET') + writer.write(conn_bar, 43, b'SUBSEQUENT') + + res = path.read_bytes() + assert res.count(b'op=\x05') == 2 + assert res.count(b'op=\x06') == 2 + assert res.count(b'MESSAGE_DEFINITION') == 4 + assert res.count(b'latching=1') == 2 + assert res.count(b'OTHER_DEFINITION') == 2 + assert res.count(b'callerid=src') == 2 + assert res.count(b'NEVER_WRITTEN') == 2 + assert res.count(b'DEADBEEF') == 2 + assert res.count(b'SECRET') == 1 + assert res.count(b'SUBSEQUENT') == 1 + path.unlink() + + +def test_compression_errors(tmp_path: Path): + """Test compression modes.""" + path = tmp_path / 'test.bag' + with Writer(path) as writer, \ + pytest.raises(WriterError, match='already open'): + writer.set_compression(writer.CompressionFormat.BZ2) + + +@pytest.mark.parametrize('fmt', [None, Writer.CompressionFormat.BZ2, Writer.CompressionFormat.LZ4]) +def test_compression_modes(tmp_path: Path, fmt: Optional[Writer.CompressionFormat]): + """Test compression modes.""" + path = tmp_path / 'test.bag' + writer = Writer(path) + if fmt: + writer.set_compression(fmt) + with writer: + conn = writer.add_connection('/foo', 'std_msgs/msg/Int8') + writer.write(conn, 42, b'\x42') + data = path.read_bytes() + assert data.count(f'compression={fmt.name.lower() if fmt else "none"}'.encode()) == 1 From 5f99a3be8417a8e02fee02cc8b282713e83ddda6 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 6 Aug 2021 10:08:26 +0200 Subject: [PATCH 031/114] Add rosbag1 to rosbag1 comparison --- tools/compare/README.rst | 4 ++-- tools/compare/compare.py | 41 ++++++++++++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/tools/compare/README.rst b/tools/compare/README.rst index 8b581e53..9a96312a 100644 --- a/tools/compare/README.rst +++ b/tools/compare/README.rst @@ -2,10 +2,10 @@ Compare ======= -Check if the contents of a ``rosbag1`` and ``rosbag2`` file are identical. The provided Dockerfile creates an execution environment for the script. Run from the root of this repository:: +Check if the contents of a ``rosbag1`` and another ``rosbag1`` or ``rosbag2`` file are identical. The provided Dockerfile creates an execution environment for the script. Run from the root of this repository:: $ docker build -t rosbags/compare -f tools/compare/Dockerfile . -The docker image expects that the rosbag1 and rosbag2 files to be mounted ``/rosbag1`` and ``/rosbag2`` respectively:: +The docker image expects that the first rosbag1 and second rosbag1 or rosbag2 files to be mounted at ``/rosbag1`` and ``/rosbag2`` respectively:: $ docker run --rm -v /path/to/rosbag1.bag:/rosbag1 -v /path/to/rosbag2:/rosbag2 rosbags/compare diff --git a/tools/compare/compare.py b/tools/compare/compare.py index d79ba820..9649ea48 100644 --- a/tools/compare/compare.py +++ b/tools/compare/compare.py @@ -1,6 +1,6 @@ # Copyright 2020-2021 Ternaris. # SPDX-License-Identifier: Apache-2.0 -"""Tool checking if Rosbag1 and Rosbag2 contents are equal.""" +"""Tool checking if contents of two rosbags are equal.""" # pylint: disable=import-error @@ -9,6 +9,7 @@ from __future__ import annotations import array import math import sys +from pathlib import Path from typing import TYPE_CHECKING from unittest.mock import Mock @@ -24,15 +25,15 @@ rosgraph_msgs.msg.TopicStatistics = Mock() import rosbag.bag # type:ignore # noqa: E402 pylint: disable=wrong-import-position if TYPE_CHECKING: - from typing import Any, List + from typing import Any, List, Union from rosbag.bag import _Connection_Info class Reader: # pylint: disable=too-few-public-methods - """Mimimal shim using rosbag2_py to emulate rosbag2 API.""" + """Mimimal shim using rosbag2_py to emulate rosbags API.""" - def __init__(self, path: str): + def __init__(self, path: Union[str, Path]): """Initialize reader shim.""" self.reader = SequentialReader() self.reader.open(StorageOptions(path, 'sqlite3'), ConverterOptions('', '')) @@ -106,7 +107,32 @@ def compare(ref: Any, msg: Any): assert ref == msg -def main(path1: str, path2: str): +def main_bag1_bag1(path1: Path, path2: Path): + """Compare rosbag1 to rosbag1 message by message. + + Args: + path1: Rosbag1 filename. + path2: Rosbag1 filename. + + """ + reader1 = rosbag.bag.Bag(path1) + reader2 = rosbag.bag.Bag(path2) + src1 = reader1.read_messages(raw=True, return_connection_header=True) + src2 = reader2.read_messages(raw=True, return_connection_header=True) + + for msg1, msg2 in zip(src1, src2): + assert msg1.connection_header == msg2.connection_header + assert msg1.message[:-2] == msg2.message[:-2] + assert msg1.timestamp == msg2.timestamp + assert msg1.topic == msg2.topic + + assert next(src1, None) is None + assert next(src2, None) is None + + print('Bags are identical.') # noqa: T001 + + +def main_bag1_bag2(path1: Path, path2: Path): """Compare rosbag1 to rosbag2 message by message. Args: @@ -135,4 +161,7 @@ if __name__ == '__main__': if len(sys.argv) != 3: print(f'Usage: {sys.argv} [rosbag1] [rosbag2]') # noqa: T001 sys.exit(1) - main(sys.argv[1], sys.argv[2]) + arg1 = Path(sys.argv[1]) + arg2 = Path(sys.argv[2]) + main = main_bag1_bag2 if arg2.is_dir() else main_bag1_bag1 + main(arg1, arg2) From 4041e882c1d17dabccdfc048ab1d0a4b1182468e Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 6 Aug 2021 10:33:01 +0200 Subject: [PATCH 032/114] Add issue template --- .gitlab/issue_templates/bug.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .gitlab/issue_templates/bug.md diff --git a/.gitlab/issue_templates/bug.md b/.gitlab/issue_templates/bug.md new file mode 100644 index 00000000..62e6365d --- /dev/null +++ b/.gitlab/issue_templates/bug.md @@ -0,0 +1,28 @@ +## Your Environment + +Thank you for taking the time to report an issue. + +To more efficiently resolve this issue, we'd like to know some basic information about your system and setup. + +1) Your operating system: + +2) Version of python you are running (`python --version`): + +3) How did you install rosbags? Did you use pip to install from PyPI or a repository checkout or something else? + +4) Version of rosbags you have installed (`pip show rosbags | grep Version`): + + +If you're having issues with (de)serialization of custom message types please include a copy of the following: +* Message definition files (msg or idl) +* The bytes of an example message + + +## The Issue + +Please describe the issue that you are experiencing. + + +## Steps to Reproduce + +If the issue is predictable and consistently reproducible, please list the steps here. From 5d0aa8277c59501b63ff607d220f64416fcd0999 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 6 Aug 2021 12:23:19 +0200 Subject: [PATCH 033/114] Release 0.9.3 --- CHANGES.rst | 9 +++++++++ setup.cfg | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1fa10673..05f8277e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,15 @@ Changes ======= +0.9.3 - 2021-08-06 +------------------ + +- Add const fields to type classes +- Add CDR to ROS1 bytestream conversion +- Add ROS1 message definiton generator +- Use connection oriented APIs in readers and writers +- Add rosbag1 writer + 0.9.2 - 2021-07-08 ------------------ diff --git a/setup.cfg b/setup.cfg index 5bc09b12..6c15652e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.2 +version = 0.9.3 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 885900df398eb8d24a3432003aab8f545e5d4a9f Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 13 Sep 2021 10:49:57 +0200 Subject: [PATCH 034/114] Make reader1 API match reader2 --- src/rosbags/rosbag1/reader.py | 15 +++++++++------ src/rosbags/rosbag1/writer.py | 2 +- tests/test_reader1.py | 3 ++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 47223d07..31e3e4a0 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -55,8 +55,8 @@ class Connection(NamedTuple): cid: int topic: str msgtype: str - md5sum: str msgdef: str + md5sum: str callerid: Optional[str] latching: Optional[int] indexes: list @@ -487,8 +487,8 @@ class Reader: conn, topic, normalize_msgtype(typ), - md5sum, msgdef, + md5sum, callerid, latching, [], @@ -573,15 +573,15 @@ class Reader: def messages( self, - topics: Optional[Iterable[str]] = None, + connections: Iterable[Connection] = (), start: Optional[int] = None, stop: Optional[int] = None, ) -> Generator[tuple[Connection, int, bytes], None, None]: """Read messages from bag. Args: - topics: Iterable with topic names to filter for. An empty iterable - yields all messages. + connections: Iterable with connections to filter for. An empty + iterable disables filtering on connections. start: Yield only messages at or after this timestamp (ns). stop: Yield only messages before this timestamp (ns). @@ -595,7 +595,10 @@ class Reader: if not self.bio: raise ReaderError('Rosbag is not open.') - indexes = [x.indexes for x in self.connections.values() if not topics or x.topic in topics] + if not connections: + connections = self.connections.values() + + indexes = [x.indexes for x in connections] for entry in heapq.merge(*indexes): if start and entry.time < start: continue diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index 650d3c52..51a5e6bd 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -245,8 +245,8 @@ class Writer: # pylint: disable=too-many-instance-attributes len(self.connections), topic, denormalize_msgtype(msgtype), - md5sum, msgdef, + md5sum, callerid, latching, [], diff --git a/tests/test_reader1.py b/tests/test_reader1.py index f0a08124..5acbcf5b 100644 --- a/tests/test_reader1.py +++ b/tests/test_reader1.py @@ -254,7 +254,8 @@ def test_reader(tmp_path): # pylint: disable=too-many-statements assert msgs[0][2] == b'MSGCONTENT5' assert msgs[1][2] == b'MSGCONTENT10' - msgs = list(reader.messages(['/topic0'])) + connections = [x for x in reader.connections.values() if x.topic == '/topic0'] + msgs = list(reader.messages(connections)) assert len(msgs) == 1 assert msgs[0][2] == b'MSGCONTENT10' From be2d5675b3ae42c16afbfa2647a70be385ab9aa8 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 13 Sep 2021 17:30:09 +0200 Subject: [PATCH 035/114] Fix connection mapping for reader2 messages --- src/rosbags/rosbag2/reader.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 636c0edc..e5045e88 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -17,7 +17,7 @@ from .connection import Connection if TYPE_CHECKING: from types import TracebackType - from typing import Any, Generator, Iterable, Literal, Optional, Type, Union + from typing import Any, Dict, Generator, Iterable, Literal, Optional, Type, Union class ReaderError(Exception): @@ -232,16 +232,23 @@ class Reader: if cur.fetchone()[0] != 2: raise ReaderError(f'Cannot open database {path} or database missing tables.') + cur.execute('SELECT name,id FROM topics') + connmap: Dict[int, Connection] = { + row[1]: next((x for x in self.connections.values() if x.topic == row[0]), + None) # type: ignore + for row in cur + } + cur.execute(querystr, args) if self.compression_mode == 'message': decomp = zstandard.ZstdDecompressor().decompress for row in cur: cid, timestamp, data = row - yield self.connections[cid], timestamp, decomp(data) + yield connmap[cid], timestamp, decomp(data) else: for cid, timestamp, data in cur: - yield self.connections[cid], timestamp, data + yield connmap[cid], timestamp, data def __enter__(self) -> Reader: """Open rosbag2 when entering contextmanager.""" From 203131b777bae5686e5639dc178f737a3f760968 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 15 Sep 2021 14:47:38 +0200 Subject: [PATCH 036/114] Release 0.9.4 --- CHANGES.rst | 9 +++++++++ setup.cfg | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 05f8277e..859c11d4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,12 @@ Changes ======= +0.9.4 - 2021-09-15 +------------------ +- Make reader1 API match reader2 +- Fix connection mapping for reader2 messages + + 0.9.3 - 2021-08-06 ------------------ @@ -12,11 +18,13 @@ Changes - Use connection oriented APIs in readers and writers - Add rosbag1 writer + 0.9.2 - 2021-07-08 ------------------ - Support relative type references in msg files + 0.9.1 - 2021-07-05 ------------------ @@ -31,6 +39,7 @@ Changes .. _#2: https://gitlab.com/ternaris/rosbags/issues/2 .. _#4: https://gitlab.com/ternaris/rosbags/issues/4 + 0.9.0 - 2021-05-16 ------------------ diff --git a/setup.cfg b/setup.cfg index 6c15652e..86cba22a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.3 +version = 0.9.4 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 1333cf1168e645c82c620baa977ad9d777331f0b Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 3 Oct 2021 06:28:02 +0200 Subject: [PATCH 037/114] Add string constant support to msg parser --- src/rosbags/typesys/msg.py | 10 ++++++++-- tests/test_parse.py | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 51936893..8b326905 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -44,7 +44,8 @@ comment = r'#[^\n]*' const_dcl - = type_spec identifier '=' integer_literal + = 'string' identifier '=' r'[^\n]+' + / type_spec identifier '=' integer_literal field_dcl = type_spec identifier @@ -172,7 +173,12 @@ class VisitorMSG(Visitor): def visit_const_dcl(self, children: Any) -> Any: """Process const declaration, suppress output.""" - return Nodetype.CONST, (children[0][1], children[1][1], children[3]) + typ = children[0][1] + if typ == 'string': + value = children[3].strip() + else: + value = children[3] + return Nodetype.CONST, (typ, children[1][1], value) def visit_specification(self, children: Any) -> Typesdict: """Process start symbol.""" diff --git a/tests/test_parse.py b/tests/test_parse.py index e0fb995c..7f51ba85 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -18,6 +18,7 @@ MSG = """ # comment int32 global=42 +string str= foo bar\t std_msgs/Header header std_msgs/msg/Bool bool @@ -115,7 +116,7 @@ def test_parse_msg(): ret = get_types_from_msg(MSG, 'test_msgs/msg/Foo') assert 'test_msgs/msg/Foo' in ret consts, fields = ret['test_msgs/msg/Foo'] - assert consts == [('global', 'int32', 42)] + assert consts == [('global', 'int32', 42), ('str', 'string', 'foo bar')] assert fields[0][0] == 'header' assert fields[0][1][1] == 'std_msgs/msg/Header' assert fields[1][0] == 'bool' From 7bbc2914c4db5e7d9bf026e8509a304870f2b2f9 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 4 Oct 2021 11:54:39 +0200 Subject: [PATCH 038/114] Release 0.9.5 --- CHANGES.rst | 4 ++++ setup.cfg | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 859c11d4..b904f45a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,10 @@ Changes ======= +0.9.5 - 2021-10-04 +------------------ +- Add string constant support to msg parser + 0.9.4 - 2021-09-15 ------------------ - Make reader1 API match reader2 diff --git a/setup.cfg b/setup.cfg index 86cba22a..ed31f6e7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.4 +version = 0.9.5 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 75d98df4bf3cf894750fec142450c5c65d7b1e36 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 4 Oct 2021 16:46:22 +0200 Subject: [PATCH 039/114] Do not match msg separator as constant value --- src/rosbags/typesys/msg.py | 2 +- tests/test_parse.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 8b326905..9eda4e46 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -44,7 +44,7 @@ comment = r'#[^\n]*' const_dcl - = 'string' identifier '=' r'[^\n]+' + = 'string' identifier r'=(?!={79}\n)' r'[^\n]+' / type_spec identifier '=' integer_literal field_dcl diff --git a/tests/test_parse.py b/tests/test_parse.py index 7f51ba85..00783d91 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -45,6 +45,15 @@ uint64[3] Header uint32 static = 42 """ +CSTRING_CONFUSION_MSG = """ +std_msgs/Header header +string s + +================================================================================ +MSG: std_msgs/Header +time time +""" + RELSIBLING_MSG = """ Header header Other other @@ -144,6 +153,18 @@ def test_parse_multi_msg(): assert consts == [('static', 'uint32', 42)] +def test_parse_cstring_confusion(): + """Test if msg separator is confused with const string.""" + ret = get_types_from_msg(CSTRING_CONFUSION_MSG, 'test_msgs/msg/Foo') + assert len(ret) == 2 + assert 'test_msgs/msg/Foo' in ret + assert 'std_msgs/msg/Header' in ret + consts, fields = ret['test_msgs/msg/Foo'] + assert consts == [] + assert fields[0][1][1] == 'std_msgs/msg/Header' + assert fields[1][1][1] == 'string' + + def test_parse_relative_siblings_msg(): """Test relative siblings with msg parser.""" ret = get_types_from_msg(RELSIBLING_MSG, 'test_msgs/msg/Foo') From fa62876dd8cb639650182cc33e2769488113611e Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 4 Oct 2021 16:46:49 +0200 Subject: [PATCH 040/114] Release 0.9.6 --- CHANGES.rst | 4 ++++ setup.cfg | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index b904f45a..c0eece22 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,10 @@ Changes ======= +0.9.6 - 2021-10-04 +------------------ +- Do not match msg separator as constant value + 0.9.5 - 2021-10-04 ------------------ - Add string constant support to msg parser diff --git a/setup.cfg b/setup.cfg index ed31f6e7..70043a99 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.5 +version = 0.9.6 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 54a7eca6bee570a8c64a83511ce017e86999a6d7 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 21 Oct 2021 17:07:10 +0200 Subject: [PATCH 041/114] Use current APIs --- tests/test_writer.py | 2 +- tools/bench/bench.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_writer.py b/tests/test_writer.py index 7124ff37..5fac0e13 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -81,7 +81,7 @@ def test_failure_cases(tmp_path: Path): bag = Writer(tmp_path / 'write') with pytest.raises(WriterError, match='was not opened'): - bag.write('/tf', 0, b'') + bag.write(Connection(1, 0, '/tf', 'tf_msgs/msg/tf2', 'cdr', ''), 0, b'') bag = Writer(tmp_path / 'topic') bag.open() diff --git a/tools/bench/bench.py b/tools/bench/bench.py index d759a805..cb6d876a 100644 --- a/tools/bench/bench.py +++ b/tools/bench/bench.py @@ -84,16 +84,16 @@ def compare(path: Path): with Reader(path) as reader: gens = (reader.messages(), ReaderPy(path).messages()) for item, item_py in zip(*gens): - topic, msgtype, timestamp, data = item + connection, timestamp, data = item topic_py, msgtype_py, timestamp_py, data_py = item_py - assert topic == topic_py - assert msgtype == msgtype_py + assert connection.topic == topic_py + assert connection.msgtype == msgtype_py assert timestamp == timestamp_py assert data == data_py msg_py = deserialize_py(data_py, msgtype_py) - msg = deserialize_cdr(data, msgtype) + msg = deserialize_cdr(data, connection.msgtype) compare_msg(msg, msg_py) assert len(list(gens[0])) == 0 @@ -118,8 +118,8 @@ def read_deser_rosbag2_py(path: Path): def read_deser_rosbag2(path: Path): """Read testbag with rosbag2lite.""" with Reader(path) as reader: - for _, msgtype, _, data in reader.messages(): - deserialize_cdr(data, msgtype) + for connection, _, data in reader.messages(): + deserialize_cdr(data, connection.msgtype) def main(): From edf9db27244d1dcff5e25c58641b83c804e23a22 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 21 Oct 2021 17:07:43 +0200 Subject: [PATCH 042/114] Ignore typing error in docs config --- docs/conf.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index af0e7a8b..39841a8a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,12 +5,13 @@ import typing # https://github.com/sphinx-doc/sphinx/issues/9243 -import sphinx.builders.html # noqa pylint: disable=unused-import -import sphinx.builders.latex # noqa pylint: disable=unused-import -import sphinx.builders.texinfo # noqa pylint: disable=unused-import -import sphinx.builders.text # noqa pylint: disable=unused-import -import sphinx.ext.autodoc # noqa pylint: disable=unused-import -import sphinx_rtd_theme # noqa pylint: disable=unused-import +# pylint: disable=unused-import +import sphinx.builders.html # noqa +import sphinx.builders.latex # noqa +import sphinx.builders.texinfo # noqa +import sphinx.builders.text # noqa +import sphinx.ext.autodoc # noqa +import sphinx_rtd_theme # type: ignore # noqa # pylint: disable=invalid-name,redefined-builtin From 634a497dd5a41dd47f157f73200a462b360e2a21 Mon Sep 17 00:00:00 2001 From: Florian Friesdorf Date: Thu, 21 Oct 2021 12:03:51 +0200 Subject: [PATCH 043/114] Tweak mypy config to handle namespaces --- setup.cfg | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.cfg b/setup.cfg index 70043a99..c236305f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -125,6 +125,11 @@ line_length = 100 multi_line_output = 3 [mypy] +explicit_package_bases = True +mypy_path = $MYPY_CONFIG_FILE_DIR/src +namespace_packages = True + +[mypy-ruamel] ignore_missing_imports = True [pydocstyle] From 03e82a91adca8af0b404360912b4f78784d4103d Mon Sep 17 00:00:00 2001 From: Florian Friesdorf Date: Thu, 21 Oct 2021 18:15:43 +0200 Subject: [PATCH 044/114] Exclude venvs for flake8 --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index c236305f..ab7be1f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -103,6 +103,7 @@ exclude_lines = avoid-escape = False docstring_convention = google docstring_style = google +extend-exclude = venv*,.venv* extend-select = # docstrings D204, From 2f05cae883e7dc3cdd4457d9478227ced37c65a5 Mon Sep 17 00:00:00 2001 From: Florian Friesdorf Date: Thu, 21 Oct 2021 12:06:55 +0200 Subject: [PATCH 045/114] Add pytest-yapf3 --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index ab7be1f3..43a56f13 100644 --- a/setup.cfg +++ b/setup.cfg @@ -83,9 +83,11 @@ dev = pytest-flake8 pytest-mypy pytest-pylint + pytest-yapf3 sphinx sphinx-autodoc-typehints sphinx-rtd-theme + yapf [options.packages.find] where = src @@ -158,6 +160,7 @@ addopts = --flake8 --mypy --pylint + --yapf --cov=src --cov-branch --cov-report=html From 0e42f941d59badb33eef7c254c97cfef7875fbe0 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 21 Oct 2021 17:34:38 +0200 Subject: [PATCH 046/114] Update dependencies --- requirements-dev.txt | 525 ++++++++++++++++++++++++------------------- requirements.txt | 163 +++++++------- 2 files changed, 376 insertions(+), 312 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 7f9becd5..5107a052 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -12,9 +12,9 @@ astor==0.8.1 \ --hash=sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5 \ --hash=sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e # via flake8-simplify -astroid==2.6.2 \ - --hash=sha256:38b95085e9d92e2ca06cf8b35c12a74fa81da395a6f9e65803742e6509c05892 \ - --hash=sha256:606b2911d10c3dcf35e58d2ee5c97360e8477d7b9f3efc3f24811c93e6fc2cd9 +astroid==2.8.3 \ + --hash=sha256:0e361da0744d5011d4f5d57e64473ba9b7ab4da1e2d45d6631ebd67dd28c3cce \ + --hash=sha256:f9d66e3a4a0e5b52819b2ff41ac2b179df9d180697db71c92beb33a60c661794 # via pylint attrs==21.2.0 \ --hash=sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1 \ @@ -27,97 +27,78 @@ babel==2.9.1 \ --hash=sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9 \ --hash=sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0 # via sphinx -certifi==2021.5.30 \ - --hash=sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee \ - --hash=sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8 +certifi==2021.10.8 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 # via requests -chardet==4.0.0 \ - --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \ - --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5 +charset-normalizer==2.0.7 \ + --hash=sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0 \ + --hash=sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b # via requests -coverage==5.5 \ - --hash=sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c \ - --hash=sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6 \ - --hash=sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45 \ - --hash=sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a \ - --hash=sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03 \ - --hash=sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529 \ - --hash=sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a \ - --hash=sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a \ - --hash=sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2 \ - --hash=sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6 \ - --hash=sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759 \ - --hash=sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53 \ - --hash=sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a \ - --hash=sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4 \ - --hash=sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff \ - --hash=sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502 \ - --hash=sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793 \ - --hash=sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb \ - --hash=sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905 \ - --hash=sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821 \ - --hash=sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b \ - --hash=sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81 \ - --hash=sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0 \ - --hash=sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b \ - --hash=sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3 \ - --hash=sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184 \ - --hash=sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701 \ - --hash=sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a \ - --hash=sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82 \ - --hash=sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638 \ - --hash=sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5 \ - --hash=sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083 \ - --hash=sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6 \ - --hash=sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90 \ - --hash=sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465 \ - --hash=sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a \ - --hash=sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3 \ - --hash=sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e \ - --hash=sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066 \ - --hash=sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf \ - --hash=sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b \ - --hash=sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae \ - --hash=sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669 \ - --hash=sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873 \ - --hash=sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b \ - --hash=sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6 \ - --hash=sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb \ - --hash=sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160 \ - --hash=sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c \ - --hash=sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079 \ - --hash=sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d \ - --hash=sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6 +coverage[toml]==6.0.2 \ + --hash=sha256:04560539c19ec26995ecfb3d9307ff154fbb9a172cb57e3b3cfc4ced673103d1 \ + --hash=sha256:1549e1d08ce38259de2bc3e9a0d5f3642ff4a8f500ffc1b2df73fd621a6cdfc0 \ + --hash=sha256:1db67c497688fd4ba85b373b37cc52c50d437fd7267520ecd77bddbd89ea22c9 \ + --hash=sha256:30922626ce6f7a5a30bdba984ad21021529d3d05a68b4f71ea3b16bda35b8895 \ + --hash=sha256:36e9040a43d2017f2787b28d365a4bb33fcd792c7ff46a047a04094dc0e2a30d \ + --hash=sha256:381d773d896cc7f8ba4ff3b92dee4ed740fb88dfe33b6e42efc5e8ab6dfa1cfe \ + --hash=sha256:3bbda1b550e70fa6ac40533d3f23acd4f4e9cb4e6e77251ce77fdf41b3309fb2 \ + --hash=sha256:3be1206dc09fb6298de3fce70593e27436862331a85daee36270b6d0e1c251c4 \ + --hash=sha256:424c44f65e8be58b54e2b0bd1515e434b940679624b1b72726147cfc6a9fc7ce \ + --hash=sha256:4b34ae4f51bbfa5f96b758b55a163d502be3dcb24f505d0227858c2b3f94f5b9 \ + --hash=sha256:4e28d2a195c533b58fc94a12826f4431726d8eb029ac21d874345f943530c122 \ + --hash=sha256:53a294dc53cfb39c74758edaa6305193fb4258a30b1f6af24b360a6c8bd0ffa7 \ + --hash=sha256:60e51a3dd55540bec686d7fff61b05048ca31e804c1f32cbb44533e6372d9cc3 \ + --hash=sha256:61b598cbdbaae22d9e34e3f675997194342f866bb1d781da5d0be54783dce1ff \ + --hash=sha256:6807947a09510dc31fa86f43595bf3a14017cd60bf633cc746d52141bfa6b149 \ + --hash=sha256:6a6a9409223a27d5ef3cca57dd7cd4dfcb64aadf2fad5c3b787830ac9223e01a \ + --hash=sha256:7092eab374346121805fb637572483270324407bf150c30a3b161fc0c4ca5164 \ + --hash=sha256:77b1da5767ed2f44611bc9bc019bc93c03fa495728ec389759b6e9e5039ac6b1 \ + --hash=sha256:8251b37be1f2cd9c0e5ccd9ae0380909c24d2a5ed2162a41fcdbafaf59a85ebd \ + --hash=sha256:9f1627e162e3864a596486774876415a7410021f4b67fd2d9efdf93ade681afc \ + --hash=sha256:a1b73c7c4d2a42b9d37dd43199c5711d91424ff3c6c22681bc132db4a4afec6f \ + --hash=sha256:a82d79586a0a4f5fd1cf153e647464ced402938fbccb3ffc358c7babd4da1dd9 \ + --hash=sha256:abbff240f77347d17306d3201e14431519bf64495648ca5a49571f988f88dee9 \ + --hash=sha256:ad9b8c1206ae41d46ec7380b78ba735ebb77758a650643e841dd3894966c31d0 \ + --hash=sha256:bbffde2a68398682623d9dd8c0ca3f46fda074709b26fcf08ae7a4c431a6ab2d \ + --hash=sha256:bcae10fccb27ca2a5f456bf64d84110a5a74144be3136a5e598f9d9fb48c0caa \ + --hash=sha256:c9cd3828bbe1a40070c11fe16a51df733fd2f0cb0d745fb83b7b5c1f05967df7 \ + --hash=sha256:cd1cf1deb3d5544bd942356364a2fdc8959bad2b6cf6eb17f47d301ea34ae822 \ + --hash=sha256:d036dc1ed8e1388e995833c62325df3f996675779541f682677efc6af71e96cc \ + --hash=sha256:db42baa892cba723326284490283a68d4de516bfb5aaba369b4e3b2787a778b7 \ + --hash=sha256:e4fb7ced4d9dec77d6cf533acfbf8e1415fe799430366affb18d69ee8a3c6330 \ + --hash=sha256:e7a0b42db2a47ecb488cde14e0f6c7679a2c5a9f44814393b162ff6397fcdfbb \ + --hash=sha256:f2f184bf38e74f152eed7f87e345b51f3ab0b703842f447c22efe35e59942c24 # via pytest-cov -darglint==1.8.0 \ - --hash=sha256:aa605ef47817a6d14797d32b390466edab621768ea4ca5cc0f3c54f6d8dcaec8 \ - --hash=sha256:ac6797bcc918cd8d8f14c168a4a364f54e1aeb4ced59db58e7e4c6dfec2fe15c +darglint==1.8.1 \ + --hash=sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da \ + --hash=sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d # via rosbags -docutils==0.16 \ - --hash=sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af \ - --hash=sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc +docutils==0.17.1 \ + --hash=sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125 \ + --hash=sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61 # via # sphinx # sphinx-rtd-theme -filelock==3.0.12 \ - --hash=sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59 \ - --hash=sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836 +filelock==3.3.1 \ + --hash=sha256:2b5eb3589e7fdda14599e7eb1a50e09b4cc14f34ed98b8ba56d33bfaafcbef2f \ + --hash=sha256:34a9f35f95c441e7b38209775d6e0337f9a3759f3565f6c5798f19618527c76f # via pytest-mypy -flake8-annotations==2.6.2 \ - --hash=sha256:0d6cd2e770b5095f09689c9d84cc054c51b929c41a68969ea1beb4b825cac515 \ - --hash=sha256:d10c4638231f8a50c0a597c4efce42bd7b7d85df4f620a0ddaca526138936a4f +flake8-annotations==2.7.0 \ + --hash=sha256:3edfbbfb58e404868834fe6ec3eaf49c139f64f0701259f707d043185545151e \ + --hash=sha256:52e53c05b0c06cac1c2dec192ea2c36e85081238add3bd99421d56f574b9479b # via rosbags -flake8-bugbear==21.4.3 \ - --hash=sha256:2346c81f889955b39e4a368eb7d508de723d9de05716c287dc860a4073dc57e7 \ - --hash=sha256:4f305dca96be62bf732a218fe6f1825472a621d3452c5b994d8f89dae21dbafa +flake8-bugbear==21.9.2 \ + --hash=sha256:4f7eaa6f05b7d7ea4cbbde93f7bcdc5438e79320fa1ec420d860c181af38b769 \ + --hash=sha256:db9a09893a6c649a197f5350755100bb1dd84f110e60cf532fdfa07e41808ab2 # via rosbags -flake8-commas==2.0.0 \ - --hash=sha256:d3005899466f51380387df7151fb59afec666a0f4f4a2c6a8995b975de0f44b7 \ - --hash=sha256:ee2141a3495ef9789a3894ed8802d03eff1eaaf98ce6d8653a7c573ef101935e +flake8-commas==2.1.0 \ + --hash=sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263 \ + --hash=sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54 # via rosbags -flake8-comprehensions==3.5.0 \ - --hash=sha256:b07aef3277623db32310aa241a1cec67212b53c1d18e767d7e26d4d83aa05bf7 \ - --hash=sha256:f24be9032587127f7a5bc6d066bf755b6e66834f694383adb8a673e229c1f559 +flake8-comprehensions==3.7.0 \ + --hash=sha256:6b3218b2dde8ac5959c6476cde8f41a79e823c22feb656be2710cd2a3232cef9 \ + --hash=sha256:a5d7aea6315bbbd6fbcb2b4e80bff6a54d1600155e26236e555d0c6fe1d62522 # via rosbags flake8-docstrings==1.6.0 \ --hash=sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde \ @@ -127,9 +108,9 @@ flake8-fixme==1.1.1 \ --hash=sha256:226a6f2ef916730899f29ac140bed5d4a17e5aba79f00a0e3ae1eff1997cb1ac \ --hash=sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a # via rosbags -flake8-isort==4.0.0 \ - --hash=sha256:2b91300f4f1926b396c2c90185844eb1a3d5ec39ea6138832d119da0a208f4d9 \ - --hash=sha256:729cd6ef9ba3659512dee337687c05d79c78e1215fdf921ed67e5fe46cce2f3c +flake8-isort==4.1.1 \ + --hash=sha256:c4e8b6dcb7be9b71a02e6e5d4196cefcef0f3447be51e82730fb336fff164949 \ + --hash=sha256:d814304ab70e6e58859bc5c3e221e2e6e71c958e7005239202fee19c24f82717 # via rosbags flake8-mutable==1.2.0 \ --hash=sha256:38fd9dadcbcda6550a916197bc40ed76908119dabb37fbcca30873666c31d2d5 \ @@ -153,23 +134,23 @@ flake8-pytest-style==1.5.0 \ --hash=sha256:668ce8f55edf7db4ac386d2735c3b354b5cb47aa341a4655d91a5788dd03124b \ --hash=sha256:ec287a7dc4fe95082af5e408c8b2f8f4b6bcb366d5a17ff6c34112eb03446580 # via rosbags -flake8-quotes==3.2.0 \ - --hash=sha256:3f1116e985ef437c130431ac92f9b3155f8f652fda7405ac22ffdfd7a9d1055e +flake8-quotes==3.3.1 \ + --hash=sha256:633adca6fb8a08131536af0d750b44d6985b9aba46f498871e21588c3e6f525a # via rosbags flake8-return==1.1.3 \ --hash=sha256:13a31edeb0c6157dd4f77166cdaa6141703d2b8b24def5558ae659852a003cb4 \ --hash=sha256:4a266191f7ed69aa26b835ec90c5a5522fa8f79f5cf6363a877ac499f8eb418b # via rosbags -flake8-simplify==0.14.1 \ - --hash=sha256:ba5d2905dc277cd4800c6e7f4859f96322d83dd37991ae5f8305750132084dd6 \ - --hash=sha256:d8b38feb97fd6c5943b5d9b2ab352acea83ec036a93215c0576b9e48c6aa2dcc +flake8-simplify==0.14.2 \ + --hash=sha256:4a8f103607195c3d0743a2fd8beeebe24926e19fb3e24521042cfc35771a8d4d \ + --hash=sha256:6584f3d49350659caaf2a42129f820dc64e60d4bdcb316f621b4e1cfae27e33a # via rosbags flake8-type-checking==1.0.3 \ --hash=sha256:a236558b2b001b2f4909713342306c22d5c1fe8607053c3cf9d5abb13ba82ec7 \ --hash=sha256:f76e71b0d9aae4dffe163dd62c2eacabbb3f94649cdd1f1bc6d16af34a2b7849 # via rosbags -flake8-use-fstring==1.1 \ - --hash=sha256:a0eea849ffe33fb6903c210c243c0f418da86a530e46cb13e64f1bdb045f22dc +flake8-use-fstring==1.2.1 \ + --hash=sha256:d946ec744bf32e245d3aca1fbd95ddf9c43b27e69f8d5d26d83610ab5a5f4f2d # via rosbags flake8==3.9.2 \ --hash=sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b \ @@ -188,11 +169,12 @@ flake8==3.9.2 \ # flake8-simplify # flake8-type-checking # flake8-use-fstring + # pep8-naming # pytest-flake8 # rosbags -idna==2.10 \ - --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \ - --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 +idna==3.3 \ + --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ + --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d # via requests imagesize==1.2.0 \ --hash=sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1 \ @@ -202,15 +184,15 @@ iniconfig==1.1.1 \ --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 # via pytest -isort==5.9.1 \ - --hash=sha256:83510593e07e433b77bd5bff0f6f607dbafa06d1a89022616f02d8b699cfcd56 \ - --hash=sha256:8e2c107091cfec7286bc0f68a547d0ba4c094d460b732075b6fba674f1035c0c +isort==5.9.3 \ + --hash=sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899 \ + --hash=sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2 # via # flake8-isort # pylint -jinja2==3.0.1 \ - --hash=sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4 \ - --hash=sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4 +jinja2==3.0.2 \ + --hash=sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45 \ + --hash=sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c # via sphinx lazy-object-proxy==1.6.0 \ --hash=sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653 \ @@ -275,30 +257,50 @@ markupsafe==2.0.1 \ --hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \ --hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \ --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \ + --hash=sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724 \ --hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \ + --hash=sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646 \ --hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \ + --hash=sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6 \ + --hash=sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6 \ + --hash=sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad \ --hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \ + --hash=sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38 \ + --hash=sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac \ --hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \ + --hash=sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6 \ --hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \ --hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \ --hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \ --hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \ + --hash=sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a \ --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a \ + --hash=sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9 \ + --hash=sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864 \ --hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \ --hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \ --hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \ --hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \ --hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \ + --hash=sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b \ --hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \ --hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \ --hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \ --hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \ + --hash=sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28 \ --hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \ --hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \ + --hash=sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d \ --hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \ --hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \ + --hash=sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145 \ --hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \ + --hash=sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c \ + --hash=sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1 \ --hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \ + --hash=sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53 \ + --hash=sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134 \ + --hash=sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85 \ --hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \ --hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \ --hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \ @@ -340,35 +342,40 @@ mypy==0.910 \ --hash=sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921 \ --hash=sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d # via pytest-mypy -numpy==1.21.0 \ - --hash=sha256:1a784e8ff7ea2a32e393cc53eb0003eca1597c7ca628227e34ce34eb11645a0e \ - --hash=sha256:2ba579dde0563f47021dcd652253103d6fd66165b18011dce1a0609215b2791e \ - --hash=sha256:3537b967b350ad17633b35c2f4b1a1bbd258c018910b518c30b48c8e41272717 \ - --hash=sha256:3c40e6b860220ed862e8097b8f81c9af6d7405b723f4a7af24a267b46f90e461 \ - --hash=sha256:598fe100b2948465cf3ed64b1a326424b5e4be2670552066e17dfaa67246011d \ - --hash=sha256:620732f42259eb2c4642761bd324462a01cdd13dd111740ce3d344992dd8492f \ - --hash=sha256:709884863def34d72b183d074d8ba5cfe042bc3ff8898f1ffad0209161caaa99 \ - --hash=sha256:75579acbadbf74e3afd1153da6177f846212ea2a0cc77de53523ae02c9256513 \ - --hash=sha256:7c55407f739f0bfcec67d0df49103f9333edc870061358ac8a8c9e37ea02fcd2 \ - --hash=sha256:a1f2fb2da242568af0271455b89aee0f71e4e032086ee2b4c5098945d0e11cf6 \ - --hash=sha256:a290989cd671cd0605e9c91a70e6df660f73ae87484218e8285c6522d29f6e38 \ - --hash=sha256:ac4fd578322842dbda8d968e3962e9f22e862b6ec6e3378e7415625915e2da4d \ - --hash=sha256:ad09f55cc95ed8d80d8ab2052f78cc21cb231764de73e229140d81ff49d8145e \ - --hash=sha256:b9205711e5440954f861ceeea8f1b415d7dd15214add2e878b4d1cf2bcb1a914 \ - --hash=sha256:bba474a87496d96e61461f7306fba2ebba127bed7836212c360f144d1e72ac54 \ - --hash=sha256:bebab3eaf0641bba26039fb0b2c5bf9b99407924b53b1ea86e03c32c64ef5aef \ - --hash=sha256:cc367c86eb87e5b7c9592935620f22d13b090c609f1b27e49600cd033b529f54 \ - --hash=sha256:ccc6c650f8700ce1e3a77668bb7c43e45c20ac06ae00d22bdf6760b38958c883 \ - --hash=sha256:cf680682ad0a3bef56dae200dbcbac2d57294a73e5b0f9864955e7dd7c2c2491 \ - --hash=sha256:d2910d0a075caed95de1a605df00ee03b599de5419d0b95d55342e9a33ad1fb3 \ - --hash=sha256:d5caa946a9f55511e76446e170bdad1d12d6b54e17a2afe7b189112ed4412bb8 \ - --hash=sha256:d89b0dc7f005090e32bb4f9bf796e1dcca6b52243caf1803fdd2b748d8561f63 \ - --hash=sha256:d95d16204cd51ff1a1c8d5f9958ce90ae190be81d348b514f9be39f878b8044a \ - --hash=sha256:e4d5a86a5257843a18fb1220c5f1c199532bc5d24e849ed4b0289fb59fbd4d8f \ - --hash=sha256:e58ddb53a7b4959932f5582ac455ff90dcb05fac3f8dcc8079498d43afbbde6c \ - --hash=sha256:e80fe25cba41c124d04c662f33f6364909b985f2eb5998aaa5ae4b9587242cce \ - --hash=sha256:eda2829af498946c59d8585a9fd74da3f810866e05f8df03a86f70079c7531dd \ - --hash=sha256:fd0a359c1c17f00cb37de2969984a74320970e0ceef4808c32e00773b06649d9 +numpy==1.21.3 \ + --hash=sha256:043e83bfc274649c82a6f09836943e4a4aebe5e33656271c7dbf9621dd58b8ec \ + --hash=sha256:160ccc1bed3a8371bf0d760971f09bfe80a3e18646620e9ded0ad159d9749baa \ + --hash=sha256:188031f833bbb623637e66006cf75e933e00e7231f67e2b45cf8189612bb5dc3 \ + --hash=sha256:28f15209fb535dd4c504a7762d3bc440779b0e37d50ed810ced209e5cea60d96 \ + --hash=sha256:29fb3dcd0468b7715f8ce2c0c2d9bbbaf5ae686334951343a41bd8d155c6ea27 \ + --hash=sha256:2a6ee9620061b2a722749b391c0d80a0e2ae97290f1b32e28d5a362e21941ee4 \ + --hash=sha256:300321e3985c968e3ae7fbda187237b225f3ffe6528395a5b7a5407f73cf093e \ + --hash=sha256:32437f0b275c1d09d9c3add782516413e98cd7c09e6baf4715cbce781fc29912 \ + --hash=sha256:3c09418a14471c7ae69ba682e2428cae5b4420a766659605566c0fa6987f6b7e \ + --hash=sha256:49c6249260890e05b8111ebfc391ed58b3cb4b33e63197b2ec7f776e45330721 \ + --hash=sha256:4cc9b512e9fb590797474f58b7f6d1f1b654b3a94f4fa8558b48ca8b3cfc97cf \ + --hash=sha256:508b0b513fa1266875524ba8a9ecc27b02ad771fe1704a16314dc1a816a68737 \ + --hash=sha256:50cd26b0cf6664cb3b3dd161ba0a09c9c1343db064e7c69f9f8b551f5104d654 \ + --hash=sha256:5c4193f70f8069550a1788bd0cd3268ab7d3a2b70583dfe3b2e7f421e9aace06 \ + --hash=sha256:5dfe9d6a4c39b8b6edd7990091fea4f852888e41919d0e6722fe78dd421db0eb \ + --hash=sha256:63571bb7897a584ca3249c86dd01c10bcb5fe4296e3568b2e9c1a55356b6410e \ + --hash=sha256:75621882d2230ab77fb6a03d4cbccd2038511491076e7964ef87306623aa5272 \ + --hash=sha256:75eb7cadc8da49302f5b659d40ba4f6d94d5045fbd9569c9d058e77b0514c9e4 \ + --hash=sha256:88a5d6b268e9ad18f3533e184744acdaa2e913b13148160b1152300c949bbb5f \ + --hash=sha256:8a10968963640e75cc0193e1847616ab4c718e83b6938ae74dea44953950f6b7 \ + --hash=sha256:90bec6a86b348b4559b6482e2b684db4a9a7eed1fa054b86115a48d58fbbf62a \ + --hash=sha256:98339aa9911853f131de11010f6dd94c8cec254d3d1f7261528c3b3e3219f139 \ + --hash=sha256:a99a6b067e5190ac6d12005a4d85aa6227c5606fa93211f86b1dafb16233e57d \ + --hash=sha256:bffa2eee3b87376cc6b31eee36d05349571c236d1de1175b804b348dc0941e3f \ + --hash=sha256:c6c2d535a7beb1f8790aaa98fd089ceab2e3dd7ca48aca0af7dc60e6ef93ffe1 \ + --hash=sha256:cc14e7519fab2a4ed87d31f99c31a3796e4e1fe63a86ebdd1c5a1ea78ebd5896 \ + --hash=sha256:dd0482f3fc547f1b1b5d6a8b8e08f63fdc250c58ce688dedd8851e6e26cff0f3 \ + --hash=sha256:dde972a1e11bb7b702ed0e447953e7617723760f420decb97305e66fb4afc54f \ + --hash=sha256:e54af82d68ef8255535a6cdb353f55d6b8cf418a83e2be3569243787a4f4866f \ + --hash=sha256:e606e6316911471c8d9b4618e082635cfe98876007556e89ce03d52ff5e8fcf0 \ + --hash=sha256:f41b018f126aac18583956c54544db437f25c7ee4794bcb23eb38bef8e5e192a \ + --hash=sha256:f8f4625536926a155b80ad2bbff44f8cc59e9f2ad14cdda7acf4c135b4dc8ff2 \ + --hash=sha256:fe52dbe47d9deb69b05084abd4b0df7abb39a3c51957c09f635520abd49b29dd # via rosbags packaging==21.0 \ --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ @@ -376,13 +383,17 @@ packaging==21.0 \ # via # pytest # sphinx -pep8-naming==0.11.1 \ - --hash=sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724 \ - --hash=sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738 +pep8-naming==0.12.1 \ + --hash=sha256:4a8daeaeb33cfcde779309fc0c9c0a68a3bbe2ad8a8308b763c5068f86eb9f37 \ + --hash=sha256:bb2455947757d162aa4cad55dba4ce029005cd1692f2899a21d51d8630ca7841 # via rosbags -pluggy==0.13.1 \ - --hash=sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0 \ - --hash=sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d +platformdirs==2.4.0 \ + --hash=sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2 \ + --hash=sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d + # via pylint +pluggy==1.0.0 \ + --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ + --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 # via pytest py==1.10.0 \ --hash=sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3 \ @@ -402,21 +413,21 @@ pyflakes==2.3.1 \ --hash=sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3 \ --hash=sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db # via flake8 -pygments==2.9.0 \ - --hash=sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f \ - --hash=sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e +pygments==2.10.0 \ + --hash=sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380 \ + --hash=sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6 # via sphinx -pylint==2.9.3 \ - --hash=sha256:23a1dc8b30459d78e9ff25942c61bb936108ccbe29dd9e71c01dc8274961709a \ - --hash=sha256:5d46330e6b8886c31b5e3aba5ff48c10f4aa5e76cbf9002c6544306221e63fbc +pylint==2.11.1 \ + --hash=sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126 \ + --hash=sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436 # via pytest-pylint pyparsing==2.4.7 \ --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b # via packaging -pytest-cov==2.12.1 \ - --hash=sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a \ - --hash=sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7 +pytest-cov==3.0.0 \ + --hash=sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6 \ + --hash=sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470 # via rosbags pytest-flake8==1.0.7 \ --hash=sha256:c28cf23e7d359753c896745fd4ba859495d02e16c84bac36caa8b1eec58f5bc1 \ @@ -430,22 +441,27 @@ pytest-pylint==0.18.0 \ --hash=sha256:790c7a8019fab08e59bd3812db1657a01995a975af8b1c6ce95b9aa39d61da27 \ --hash=sha256:b63aaf8b80ff33c8ceaa7f68323ed04102c7790093ccf6bdb261a4c2dc6fd564 # via rosbags -pytest==6.2.4 \ - --hash=sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b \ - --hash=sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890 +pytest-yapf3==0.6.1 \ + --hash=sha256:63093e56f5c7baba4d221050c30e3d4e7132730741f95ec5de7a8c5fa3eb7821 \ + --hash=sha256:92b6b36d3b9435eedfb0072a8fb765496de1198c22d5cd3eb072359d35faba55 + # via rosbags +pytest==6.2.5 \ + --hash=sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89 \ + --hash=sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134 # via # pytest-cov # pytest-flake8 # pytest-mypy # pytest-pylint + # pytest-yapf3 # rosbags -pytz==2021.1 \ - --hash=sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da \ - --hash=sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798 +pytz==2021.3 \ + --hash=sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c \ + --hash=sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326 # via babel -requests==2.25.1 \ - --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \ - --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e +requests==2.26.0 \ + --hash=sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24 \ + --hash=sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7 # via sphinx ruamel.yaml.clib==0.2.6 \ --hash=sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd \ @@ -470,9 +486,9 @@ ruamel.yaml.clib==0.2.6 \ --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c # via ruamel.yaml -ruamel.yaml==0.17.10 \ - --hash=sha256:106bc8d6dc6a0ff7c9196a47570432036f41d556b779c6b4e618085f57e39e67 \ - --hash=sha256:ffb9b703853e9e8b7861606dfdab1026cf02505bade0653d1880f4b2db47f815 +ruamel.yaml==0.17.16 \ + --hash=sha256:1a771fc92d3823682b7f0893ad56cb5a5c87c48e62b5399d6f42c8759a583b33 \ + --hash=sha256:ea21da1198c4b41b8e7a259301cc9710d3b972bf8ba52f06218478e6802dd1f1 # via rosbags six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ @@ -488,13 +504,13 @@ sphinx-autodoc-typehints==1.12.0 \ --hash=sha256:193617d9dbe0847281b1399d369e74e34cd959c82e02c7efde077fca908a9f52 \ --hash=sha256:5e81776ec422dd168d688ab60f034fccfafbcd94329e9537712c93003bddc04a # via rosbags -sphinx-rtd-theme==0.5.2 \ - --hash=sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a \ - --hash=sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f +sphinx-rtd-theme==1.0.0 \ + --hash=sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8 \ + --hash=sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c # via rosbags -sphinx==4.0.2 \ - --hash=sha256:b5c2ae4120bf00c799ba9b3699bc895816d272d120080fbc967292f29b52b48c \ - --hash=sha256:d1cb10bee9c4231f1700ec2e24a91be3f3a3aba066ea4ca9f3bbe47e59d5a1d4 +sphinx==4.2.0 \ + --hash=sha256:94078db9184491e15bce0a56d9186e0aec95f16ac20b12d00e06d4e36f1058a6 \ + --hash=sha256:98a535c62a4fcfcc362528592f69b26f7caec587d32cd55688db580be0287ae0 # via # rosbags # sphinx-autodoc-typehints @@ -523,9 +539,9 @@ sphinxcontrib-serializinghtml==1.1.5 \ --hash=sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd \ --hash=sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952 # via sphinx -testfixtures==6.17.1 \ - --hash=sha256:5ec3a0dd6f71cc4c304fbc024a10cc293d3e0b852c868014b9f233203e149bda \ - --hash=sha256:9ed31e83f59619e2fa17df053b241e16e0608f4580f7b5a9333a0c9bdcc99137 +testfixtures==6.18.3 \ + --hash=sha256:2600100ae96ffd082334b378e355550fef8b4a529a6fa4c34f47130905c7426d \ + --hash=sha256:6ddb7f56a123e1a9339f130a200359092bd0a6455e31838d6c477e8729bb7763 # via flake8-isort toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ @@ -534,71 +550,118 @@ toml==0.10.2 \ # mypy # pylint # pytest - # pytest-cov # pytest-pylint -typing-extensions==3.10.0.0 \ - --hash=sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497 \ - --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \ - --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84 - # via mypy -urllib3==1.26.6 \ - --hash=sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4 \ - --hash=sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f +tomli==1.2.1 \ + --hash=sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f \ + --hash=sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442 + # via coverage +typing-extensions==3.10.0.2 \ + --hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \ + --hash=sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7 \ + --hash=sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34 + # via + # astroid + # mypy + # pylint +urllib3==1.26.7 \ + --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece \ + --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844 # via requests -wrapt==1.12.1 \ - --hash=sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7 +wrapt==1.13.2 \ + --hash=sha256:0473d1558b93e314e84313cc611f6c86be779369f9d3734302bf185a4d2625b1 \ + --hash=sha256:0582180566e7a13030f896c2f1ac6a56134ab5f3c3f4c5538086f758b1caf3f2 \ + --hash=sha256:15eee0e6fd07f48af2f66d0e6f2ff1916ffe9732d464d5e2390695296872cad9 \ + --hash=sha256:1c5c4cf188b5643a97e87e2110bbd4f5bc491d54a5b90633837b34d5df6a03fe \ + --hash=sha256:1eb657ed84f4d3e6ad648483c8a80a0cf0a78922ef94caa87d327e2e1ad49b48 \ + --hash=sha256:22142afab65daffc95863d78effcbd31c19a8003eca73de59f321ee77f73cadb \ + --hash=sha256:283e402e5357e104ac1e3fba5791220648e9af6fb14ad7d9cc059091af2b31d2 \ + --hash=sha256:3de7b4d3066cc610054e7aa2c005645e308df2f92be730aae3a47d42e910566a \ + --hash=sha256:3e0d16eedc242d01a6f8cf0623e9cdc3b869329da3f97a15961d8864111d8cf0 \ + --hash=sha256:3e33c138d1e3620b1e0cc6fd21e46c266393ed5dae0d595b7ed5a6b73ed57aa0 \ + --hash=sha256:3f87042623530bcffea038f824b63084180513c21e2e977291a9a7e65a66f13b \ + --hash=sha256:53c6706a1bcfb6436f1625511b95b812798a6d2ccc51359cd791e33722b5ea32 \ + --hash=sha256:593cb049ce1c391e0288523b30426c4430b26e74c7e6f6e2844bd99ac7ecc831 \ + --hash=sha256:6e6d1a8eeef415d7fb29fe017de0e48f45e45efd2d1bfda28fc50b7b330859ef \ + --hash=sha256:724ed2bc9c91a2b9026e5adce310fa60c6e7c8760b03391445730b9789b9d108 \ + --hash=sha256:728e2d9b7a99dd955d3426f237b940fc74017c4a39b125fec913f575619ddfe9 \ + --hash=sha256:7574de567dcd4858a2ffdf403088d6df8738b0e1eabea220553abf7c9048f59e \ + --hash=sha256:8164069f775c698d15582bf6320a4f308c50d048c1c10cf7d7a341feaccf5df7 \ + --hash=sha256:81a4cf257263b299263472d669692785f9c647e7dca01c18286b8f116dbf6b38 \ + --hash=sha256:82223f72eba6f63eafca87a0f614495ae5aa0126fe54947e2b8c023969e9f2d7 \ + --hash=sha256:8318088860968c07e741537030b1abdd8908ee2c71fbe4facdaade624a09e006 \ + --hash=sha256:83f2793ec6f3ef513ad8d5b9586f5ee6081cad132e6eae2ecb7eac1cc3decae0 \ + --hash=sha256:87ee3c73bdfb4367b26c57259995935501829f00c7b3eed373e2ad19ec21e4e4 \ + --hash=sha256:8860c8011a6961a651b1b9f46fdbc589ab63b0a50d645f7d92659618a3655867 \ + --hash=sha256:9adee1891253670575028279de8365c3a02d3489a74a66d774c321472939a0b1 \ + --hash=sha256:a0cdedf681db878416c05e1831ec69691b0e6577ac7dca9d4f815632e3549580 \ + --hash=sha256:a70d876c9aba12d3bd7f8f1b05b419322c6789beb717044eea2c8690d35cb91b \ + --hash=sha256:ada5e29e59e2feb710589ca1c79fd989b1dd94d27079dc1d199ec954a6ecc724 \ + --hash=sha256:af9480de8e63c5f959a092047aaf3d7077422ded84695b3398f5d49254af3e90 \ + --hash=sha256:b20703356cae1799080d0ad15085dc3213c1ac3f45e95afb9f12769b98231528 \ + --hash=sha256:bc85d17d90201afd88e3d25421da805e4e135012b5d1f149e4de2981394b2a52 \ + --hash=sha256:bff0a59387a0a2951cb869251257b6553663329a1b5525b5226cab8c88dcbe7e \ + --hash=sha256:c65e623ea7556e39c4f0818200a046cbba7575a6b570ff36122c276fdd30ab0a \ + --hash=sha256:c6ee5f8734820c21b9b8bf705e99faba87f21566d20626568eeb0d62cbeaf23c \ + --hash=sha256:c7ac2c7a8e34bd06710605b21dd1f3576764443d68e069d2afba9b116014d072 \ + --hash=sha256:ccb34ce599cab7f36a4c90318697ead18312c67a9a76327b3f4f902af8f68ea1 \ + --hash=sha256:d0d717e10f952df7ea41200c507cc7e24458f4c45b56c36ad418d2e79dacd1d4 \ + --hash=sha256:d90520616fce71c05dedeac3a0fe9991605f0acacd276e5f821842e454485a70 \ + --hash=sha256:dca56cc5963a5fd7c2aa8607017753f534ee514e09103a6c55d2db70b50e7447 \ + --hash=sha256:df3eae297a5f1594d1feb790338120f717dac1fa7d6feed7b411f87e0f2401c7 \ + --hash=sha256:e634136f700a21e1fcead0c137f433dde928979538c14907640607d43537d468 \ + --hash=sha256:fbad5ba74c46517e6488149514b2e2348d40df88cd6b52a83855b7a8bf04723f \ + --hash=sha256:fbe6aebc9559fed7ea27de51c2bf5c25ba2a4156cf0017556f72883f2496ee9a \ + --hash=sha256:fdede980273aeca591ad354608778365a3a310e0ecdd7a3587b38bc5be9b1808 # via astroid -zstandard==0.15.2 \ - --hash=sha256:1c5ef399f81204fbd9f0df3debf80389fd8aa9660fe1746d37c80b0d45f809e9 \ - --hash=sha256:1faefe33e3d6870a4dce637bcb41f7abb46a1872a595ecc7b034016081c37543 \ - --hash=sha256:1fb23b1754ce834a3a1a1e148cc2faad76eeadf9d889efe5e8199d3fb839d3c6 \ - --hash=sha256:22f127ff5da052ffba73af146d7d61db874f5edb468b36c9cb0b857316a21b3d \ - --hash=sha256:2353b61f249a5fc243aae3caa1207c80c7e6919a58b1f9992758fa496f61f839 \ - --hash=sha256:24cdcc6f297f7c978a40fb7706877ad33d8e28acc1786992a52199502d6da2a4 \ - --hash=sha256:31e35790434da54c106f05fa93ab4d0fab2798a6350e8a73928ec602e8505836 \ - --hash=sha256:3547ff4eee7175d944a865bbdf5529b0969c253e8a148c287f0668fe4eb9c935 \ - --hash=sha256:378ac053c0cfc74d115cbb6ee181540f3e793c7cca8ed8cd3893e338af9e942c \ - --hash=sha256:3e1cd2db25117c5b7c7e86a17cde6104a93719a9df7cb099d7498e4c1d13ee5c \ - --hash=sha256:3fe469a887f6142cc108e44c7f42c036e43620ebaf500747be2317c9f4615d4f \ - --hash=sha256:4800ab8ec94cbf1ed09c2b4686288750cab0642cb4d6fba2a56db66b923aeb92 \ - --hash=sha256:52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f \ - --hash=sha256:5752f44795b943c99be367fee5edf3122a1690b0d1ecd1bd5ec94c7fd2c39c94 \ - --hash=sha256:5d53f02aeb8fdd48b88bc80bece82542d084fb1a7ba03bf241fd53b63aee4f22 \ - --hash=sha256:69b7a5720b8dfab9005a43c7ddb2e3ccacbb9a2442908ae4ed49dd51ab19698a \ - --hash=sha256:6cc162b5b6e3c40b223163a9ea86cd332bd352ddadb5fd142fc0706e5e4eaaff \ - --hash=sha256:6f5d0330bc992b1e267a1b69fbdbb5ebe8c3a6af107d67e14c7a5b1ede2c5945 \ - --hash=sha256:6ffadd48e6fe85f27ca3ca10cfd3ef3d0f933bef7316870285ffeb58d791ca9c \ - --hash=sha256:72a011678c654df8323aa7b687e3147749034fdbe994d346f139ab9702b59cea \ - --hash=sha256:77d26452676f471223571efd73131fd4a626622c7960458aab2763e025836fc5 \ - --hash=sha256:7a88cc773ffe55992ff7259a8df5fb3570168d7138c69aadba40142d0e5ce39a \ - --hash=sha256:7b16bd74ae7bfbaca407a127e11058b287a4267caad13bd41305a5e630472549 \ - --hash=sha256:855d95ec78b6f0ff66e076d5461bf12d09d8e8f7e2b3fc9de7236d1464fd730e \ - --hash=sha256:8baf7991547441458325ca8fafeae79ef1501cb4354022724f3edd62279c5b2b \ - --hash=sha256:8fb77dd152054c6685639d855693579a92f276b38b8003be5942de31d241ebfb \ - --hash=sha256:92d49cc3b49372cfea2d42f43a2c16a98a32a6bc2f42abcde121132dbfc2f023 \ - --hash=sha256:94d0de65e37f5677165725f1fc7fb1616b9542d42a9832a9a0bdcba0ed68b63b \ - --hash=sha256:9867206093d7283d7de01bd2bf60389eb4d19b67306a0a763d1a8a4dbe2fb7c3 \ - --hash=sha256:9ee3c992b93e26c2ae827404a626138588e30bdabaaf7aa3aa25082a4e718790 \ - --hash=sha256:a4f8af277bb527fa3d56b216bda4da931b36b2d3fe416b6fc1744072b2c1dbd9 \ - --hash=sha256:ab9f19460dfa4c5dd25431b75bee28b5f018bf43476858d64b1aa1046196a2a0 \ - --hash=sha256:ac43c1821ba81e9344d818c5feed574a17f51fca27976ff7d022645c378fbbf5 \ - --hash=sha256:af5a011609206e390b44847da32463437505bf55fd8985e7a91c52d9da338d4b \ - --hash=sha256:b0975748bb6ec55b6d0f6665313c2cf7af6f536221dccd5879b967d76f6e7899 \ - --hash=sha256:b4963dad6cf28bfe0b61c3265d1c74a26a7605df3445bfcd3ba25de012330b2d \ - --hash=sha256:b7d3a484ace91ed827aa2ef3b44895e2ec106031012f14d28bd11a55f24fa734 \ - --hash=sha256:bd3c478a4a574f412efc58ba7e09ab4cd83484c545746a01601636e87e3dbf23 \ - --hash=sha256:c9e2dcb7f851f020232b991c226c5678dc07090256e929e45a89538d82f71d2e \ - --hash=sha256:d25c8eeb4720da41e7afbc404891e3a945b8bb6d5230e4c53d23ac4f4f9fc52c \ - --hash=sha256:dc8c03d0c5c10c200441ffb4cce46d869d9e5c4ef007f55856751dc288a2dffd \ - --hash=sha256:ec58e84d625553d191a23d5988a19c3ebfed519fff2a8b844223e3f074152163 \ - --hash=sha256:eda0719b29792f0fea04a853377cfff934660cb6cd72a0a0eeba7a1f0df4a16e \ - --hash=sha256:edde82ce3007a64e8434ccaf1b53271da4f255224d77b880b59e7d6d73df90c8 \ - --hash=sha256:f36722144bc0a5068934e51dca5a38a5b4daac1be84f4423244277e4baf24e7a \ - --hash=sha256:f8bb00ced04a8feff05989996db47906673ed45b11d86ad5ce892b5741e5f9dd \ - --hash=sha256:f98fc5750aac2d63d482909184aac72a979bfd123b112ec53fd365104ea15b1c \ - --hash=sha256:ff5b75f94101beaa373f1511319580a010f6e03458ee51b1a386d7de5331440a +yapf==0.31.0 \ + --hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \ + --hash=sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e + # via + # pytest-yapf3 + # rosbags +zstandard==0.16.0 \ + --hash=sha256:066488e721ec882485a500c216302b443f2eaef39356f7c65130e76c671e3ce2 \ + --hash=sha256:08a728715858f1477239887ba3c692bc462b2c86e7a8e467dc5affa7bba9093f \ + --hash=sha256:11216b47c62e9fc71a25f4b42f525a81da268071bdb434bc1e642ffc38a24a02 \ + --hash=sha256:127c4c93f578d9b509732c74ed9b44b23e94041ba11b13827be0a7d2e3869b39 \ + --hash=sha256:12dddee2574b00c262270cfb46bd0c048e92208b95fdd39ad2a9eac1cef30498 \ + --hash=sha256:1bdda52224043e13ed20f847e3b308de1c9372d1563824fad776b1cf1f847ef0 \ + --hash=sha256:2e31680d1bcf85e7a58a45df7365af894402ae77a9868c751dc991dd13099a5f \ + --hash=sha256:42992e89b250fe6878c175119af529775d4be7967cd9de86990145d615d6a444 \ + --hash=sha256:453e42af96923582ddbf3acf843f55d2dc534a3f7b345003852dd522aa51eae6 \ + --hash=sha256:4d8a296dab7f8f5d53acc693a6785751f43ca39b51c8eabc672f978306fb40e6 \ + --hash=sha256:5251ac352d8350869c404a0ca94457da018b726f692f6456ec82bbf907fbc956 \ + --hash=sha256:57a6cfc34d906d514358769ed6d510b312be1cf033aafb5db44865a6717579bd \ + --hash=sha256:6ed51162e270b9b8097dcae6f2c239ada05ec112194633193ec3241498988924 \ + --hash=sha256:74cbea966462afed5a89eb99e4577538d10d425e05bf6240a75c086d59ccaf89 \ + --hash=sha256:87bea44ad24c15cd872263c0d5f912186a4be3db361eab3b25f1a61dcb5ca014 \ + --hash=sha256:8a745862ed525eee4e28bdbd58bf3ea952bf9da3c31bb4e4ce11ef15aea5c625 \ + --hash=sha256:8b760fc8118b1a0aa1d8f4e2012622e8f5f178d4b8cb94f8c6d2948b6a49a485 \ + --hash=sha256:8c8c0e813b67de1c9d7f2760768c4ae53f011c75ace18d5cff4fb40d2173763f \ + --hash=sha256:8d5fe983e23b05f0e924fe8d0dd3935f0c9fd3266e4c6ff8621c12c350da299d \ + --hash=sha256:8f5785c0b9b71d49d789240ae16a636728596631cf100f32b963a6f9857af5a4 \ + --hash=sha256:91efd5ea5fb3c347e7ebb6d5622bfa37d72594a2dec37c5dde70b691edb6cc03 \ + --hash=sha256:92e6c1a656390176d51125847f2f422f9d8ed468c24b63958f6ee50d9aa98c83 \ + --hash=sha256:9bcbfe1ec89789239f63daeea8778488cb5ba9034a374d7753815935f83dad65 \ + --hash=sha256:a92aa26789f17ca3b1f45cc7e728597165e2b166b99d1204bb397a672edee761 \ + --hash=sha256:a9ec6de2c058e611e9dfe88d9809a5676bc1d2a53543c1273a90a60e41b8f43c \ + --hash=sha256:ac5d97f9dece91a1162f651da79b735c5cde4d5863477785962aad648b592446 \ + --hash=sha256:ae19628886d994ac1f3d2fc7f9ed5bb551d81000f7b4e0c57a0e88301aea2766 \ + --hash=sha256:b2ea1937eff0ed5621876dc377933fe76624abfb2ab5b418995f43af6bac50de \ + --hash=sha256:b46220bef7bf9271a2a05512e86acbabc86cca08bebde8447bdbb4acb3179447 \ + --hash=sha256:b61586b0ff55c4137e512f1e9df4e4d7a6e1e9df782b4b87652df27737c90cc1 \ + --hash=sha256:be68fbac1e88f0dbe033a2d2e3aaaf9c8307730b905f3cd3c698ca4b904f0702 \ + --hash=sha256:c75557d53bb2d064521ff20cce9b8a51ee8301e031b1d6bcedb6458dda3bc85d \ + --hash=sha256:c7e6b6ad58ae6f77872da9376ef0ecbf8c1ae7a0c8fc29a2473abc90f79a9a1b \ + --hash=sha256:c8828f4e78774a6c0b8d21e59677f8f48d2e17fe2ef72793c94c10abc032c41c \ + --hash=sha256:cae9bfcb9148152f8bfb9163b4b779326ca39fe9889e45e0572c56d25d5021be \ + --hash=sha256:ce61492764d0442ca1e81d38d7bf7847d7df5003bce28089bab64c0519749351 \ + --hash=sha256:d40447f4a44b442fa6715779ff49a1e319729d829198279927d18bca0d7ac32d \ + --hash=sha256:d9946cfe54bf3365f14a5aa233eb2425de3b77eac6a4c7d03dda7dbb6acd3267 \ + --hash=sha256:dd5a2287893e52204e4ce9d0e1bcea6240661dbb412efb53d5446b881d3c10a2 \ + --hash=sha256:e9456492eb13249841e53221e742bef93f4868122bfc26bafa12a07677619732 \ + --hash=sha256:eaae2d3e8fdf8bfe269628385087e4b648beef85bb0c187644e7df4fb0fe9046 \ + --hash=sha256:eba125d3899f2003debf97019cd6f46f841a405df067da23d11443ad17952a40 \ + --hash=sha256:ef759c1dfe78aa5a01747d3465d2585de14e08fc2b0195ce3f31f45477fc5a72 \ + --hash=sha256:ffe1d24c5e11e98e4c5f96f846cdd19619d8c7e5e8e5082bed62d39baa30cecb # via rosbags - -# WARNING: The following packages were not pinned, but pip requires them to be -# pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag. -# setuptools diff --git a/requirements.txt b/requirements.txt index cd17456a..1cb14697 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,35 +37,40 @@ lz4==3.1.3 \ --hash=sha256:de2aac0cfda79c5a3eda9dbed21d78dc05c4a9ac00061748c3b57ea0e4a0b6a8 \ --hash=sha256:e3029738e64a0af1b04a32a39b32b0bba0e2088f61805e074c9a7e4bc212568f # via rosbags (setup.py) -numpy==1.21.0 \ - --hash=sha256:1a784e8ff7ea2a32e393cc53eb0003eca1597c7ca628227e34ce34eb11645a0e \ - --hash=sha256:2ba579dde0563f47021dcd652253103d6fd66165b18011dce1a0609215b2791e \ - --hash=sha256:3537b967b350ad17633b35c2f4b1a1bbd258c018910b518c30b48c8e41272717 \ - --hash=sha256:3c40e6b860220ed862e8097b8f81c9af6d7405b723f4a7af24a267b46f90e461 \ - --hash=sha256:598fe100b2948465cf3ed64b1a326424b5e4be2670552066e17dfaa67246011d \ - --hash=sha256:620732f42259eb2c4642761bd324462a01cdd13dd111740ce3d344992dd8492f \ - --hash=sha256:709884863def34d72b183d074d8ba5cfe042bc3ff8898f1ffad0209161caaa99 \ - --hash=sha256:75579acbadbf74e3afd1153da6177f846212ea2a0cc77de53523ae02c9256513 \ - --hash=sha256:7c55407f739f0bfcec67d0df49103f9333edc870061358ac8a8c9e37ea02fcd2 \ - --hash=sha256:a1f2fb2da242568af0271455b89aee0f71e4e032086ee2b4c5098945d0e11cf6 \ - --hash=sha256:a290989cd671cd0605e9c91a70e6df660f73ae87484218e8285c6522d29f6e38 \ - --hash=sha256:ac4fd578322842dbda8d968e3962e9f22e862b6ec6e3378e7415625915e2da4d \ - --hash=sha256:ad09f55cc95ed8d80d8ab2052f78cc21cb231764de73e229140d81ff49d8145e \ - --hash=sha256:b9205711e5440954f861ceeea8f1b415d7dd15214add2e878b4d1cf2bcb1a914 \ - --hash=sha256:bba474a87496d96e61461f7306fba2ebba127bed7836212c360f144d1e72ac54 \ - --hash=sha256:bebab3eaf0641bba26039fb0b2c5bf9b99407924b53b1ea86e03c32c64ef5aef \ - --hash=sha256:cc367c86eb87e5b7c9592935620f22d13b090c609f1b27e49600cd033b529f54 \ - --hash=sha256:ccc6c650f8700ce1e3a77668bb7c43e45c20ac06ae00d22bdf6760b38958c883 \ - --hash=sha256:cf680682ad0a3bef56dae200dbcbac2d57294a73e5b0f9864955e7dd7c2c2491 \ - --hash=sha256:d2910d0a075caed95de1a605df00ee03b599de5419d0b95d55342e9a33ad1fb3 \ - --hash=sha256:d5caa946a9f55511e76446e170bdad1d12d6b54e17a2afe7b189112ed4412bb8 \ - --hash=sha256:d89b0dc7f005090e32bb4f9bf796e1dcca6b52243caf1803fdd2b748d8561f63 \ - --hash=sha256:d95d16204cd51ff1a1c8d5f9958ce90ae190be81d348b514f9be39f878b8044a \ - --hash=sha256:e4d5a86a5257843a18fb1220c5f1c199532bc5d24e849ed4b0289fb59fbd4d8f \ - --hash=sha256:e58ddb53a7b4959932f5582ac455ff90dcb05fac3f8dcc8079498d43afbbde6c \ - --hash=sha256:e80fe25cba41c124d04c662f33f6364909b985f2eb5998aaa5ae4b9587242cce \ - --hash=sha256:eda2829af498946c59d8585a9fd74da3f810866e05f8df03a86f70079c7531dd \ - --hash=sha256:fd0a359c1c17f00cb37de2969984a74320970e0ceef4808c32e00773b06649d9 +numpy==1.21.3 \ + --hash=sha256:043e83bfc274649c82a6f09836943e4a4aebe5e33656271c7dbf9621dd58b8ec \ + --hash=sha256:160ccc1bed3a8371bf0d760971f09bfe80a3e18646620e9ded0ad159d9749baa \ + --hash=sha256:188031f833bbb623637e66006cf75e933e00e7231f67e2b45cf8189612bb5dc3 \ + --hash=sha256:28f15209fb535dd4c504a7762d3bc440779b0e37d50ed810ced209e5cea60d96 \ + --hash=sha256:29fb3dcd0468b7715f8ce2c0c2d9bbbaf5ae686334951343a41bd8d155c6ea27 \ + --hash=sha256:2a6ee9620061b2a722749b391c0d80a0e2ae97290f1b32e28d5a362e21941ee4 \ + --hash=sha256:300321e3985c968e3ae7fbda187237b225f3ffe6528395a5b7a5407f73cf093e \ + --hash=sha256:32437f0b275c1d09d9c3add782516413e98cd7c09e6baf4715cbce781fc29912 \ + --hash=sha256:3c09418a14471c7ae69ba682e2428cae5b4420a766659605566c0fa6987f6b7e \ + --hash=sha256:49c6249260890e05b8111ebfc391ed58b3cb4b33e63197b2ec7f776e45330721 \ + --hash=sha256:4cc9b512e9fb590797474f58b7f6d1f1b654b3a94f4fa8558b48ca8b3cfc97cf \ + --hash=sha256:508b0b513fa1266875524ba8a9ecc27b02ad771fe1704a16314dc1a816a68737 \ + --hash=sha256:50cd26b0cf6664cb3b3dd161ba0a09c9c1343db064e7c69f9f8b551f5104d654 \ + --hash=sha256:5c4193f70f8069550a1788bd0cd3268ab7d3a2b70583dfe3b2e7f421e9aace06 \ + --hash=sha256:5dfe9d6a4c39b8b6edd7990091fea4f852888e41919d0e6722fe78dd421db0eb \ + --hash=sha256:63571bb7897a584ca3249c86dd01c10bcb5fe4296e3568b2e9c1a55356b6410e \ + --hash=sha256:75621882d2230ab77fb6a03d4cbccd2038511491076e7964ef87306623aa5272 \ + --hash=sha256:75eb7cadc8da49302f5b659d40ba4f6d94d5045fbd9569c9d058e77b0514c9e4 \ + --hash=sha256:88a5d6b268e9ad18f3533e184744acdaa2e913b13148160b1152300c949bbb5f \ + --hash=sha256:8a10968963640e75cc0193e1847616ab4c718e83b6938ae74dea44953950f6b7 \ + --hash=sha256:90bec6a86b348b4559b6482e2b684db4a9a7eed1fa054b86115a48d58fbbf62a \ + --hash=sha256:98339aa9911853f131de11010f6dd94c8cec254d3d1f7261528c3b3e3219f139 \ + --hash=sha256:a99a6b067e5190ac6d12005a4d85aa6227c5606fa93211f86b1dafb16233e57d \ + --hash=sha256:bffa2eee3b87376cc6b31eee36d05349571c236d1de1175b804b348dc0941e3f \ + --hash=sha256:c6c2d535a7beb1f8790aaa98fd089ceab2e3dd7ca48aca0af7dc60e6ef93ffe1 \ + --hash=sha256:cc14e7519fab2a4ed87d31f99c31a3796e4e1fe63a86ebdd1c5a1ea78ebd5896 \ + --hash=sha256:dd0482f3fc547f1b1b5d6a8b8e08f63fdc250c58ce688dedd8851e6e26cff0f3 \ + --hash=sha256:dde972a1e11bb7b702ed0e447953e7617723760f420decb97305e66fb4afc54f \ + --hash=sha256:e54af82d68ef8255535a6cdb353f55d6b8cf418a83e2be3569243787a4f4866f \ + --hash=sha256:e606e6316911471c8d9b4618e082635cfe98876007556e89ce03d52ff5e8fcf0 \ + --hash=sha256:f41b018f126aac18583956c54544db437f25c7ee4794bcb23eb38bef8e5e192a \ + --hash=sha256:f8f4625536926a155b80ad2bbff44f8cc59e9f2ad14cdda7acf4c135b4dc8ff2 \ + --hash=sha256:fe52dbe47d9deb69b05084abd4b0df7abb39a3c51957c09f635520abd49b29dd # via rosbags (setup.py) ruamel.yaml.clib==0.2.6 \ --hash=sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd \ @@ -90,57 +95,53 @@ ruamel.yaml.clib==0.2.6 \ --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c # via ruamel.yaml -ruamel.yaml==0.17.10 \ - --hash=sha256:106bc8d6dc6a0ff7c9196a47570432036f41d556b779c6b4e618085f57e39e67 \ - --hash=sha256:ffb9b703853e9e8b7861606dfdab1026cf02505bade0653d1880f4b2db47f815 +ruamel.yaml==0.17.16 \ + --hash=sha256:1a771fc92d3823682b7f0893ad56cb5a5c87c48e62b5399d6f42c8759a583b33 \ + --hash=sha256:ea21da1198c4b41b8e7a259301cc9710d3b972bf8ba52f06218478e6802dd1f1 # via rosbags (setup.py) -zstandard==0.15.2 \ - --hash=sha256:1c5ef399f81204fbd9f0df3debf80389fd8aa9660fe1746d37c80b0d45f809e9 \ - --hash=sha256:1faefe33e3d6870a4dce637bcb41f7abb46a1872a595ecc7b034016081c37543 \ - --hash=sha256:1fb23b1754ce834a3a1a1e148cc2faad76eeadf9d889efe5e8199d3fb839d3c6 \ - --hash=sha256:22f127ff5da052ffba73af146d7d61db874f5edb468b36c9cb0b857316a21b3d \ - --hash=sha256:2353b61f249a5fc243aae3caa1207c80c7e6919a58b1f9992758fa496f61f839 \ - --hash=sha256:24cdcc6f297f7c978a40fb7706877ad33d8e28acc1786992a52199502d6da2a4 \ - --hash=sha256:31e35790434da54c106f05fa93ab4d0fab2798a6350e8a73928ec602e8505836 \ - --hash=sha256:3547ff4eee7175d944a865bbdf5529b0969c253e8a148c287f0668fe4eb9c935 \ - --hash=sha256:378ac053c0cfc74d115cbb6ee181540f3e793c7cca8ed8cd3893e338af9e942c \ - --hash=sha256:3e1cd2db25117c5b7c7e86a17cde6104a93719a9df7cb099d7498e4c1d13ee5c \ - --hash=sha256:3fe469a887f6142cc108e44c7f42c036e43620ebaf500747be2317c9f4615d4f \ - --hash=sha256:4800ab8ec94cbf1ed09c2b4686288750cab0642cb4d6fba2a56db66b923aeb92 \ - --hash=sha256:52de08355fd5cfb3ef4533891092bb96229d43c2069703d4aff04fdbedf9c92f \ - --hash=sha256:5752f44795b943c99be367fee5edf3122a1690b0d1ecd1bd5ec94c7fd2c39c94 \ - --hash=sha256:5d53f02aeb8fdd48b88bc80bece82542d084fb1a7ba03bf241fd53b63aee4f22 \ - --hash=sha256:69b7a5720b8dfab9005a43c7ddb2e3ccacbb9a2442908ae4ed49dd51ab19698a \ - --hash=sha256:6cc162b5b6e3c40b223163a9ea86cd332bd352ddadb5fd142fc0706e5e4eaaff \ - --hash=sha256:6f5d0330bc992b1e267a1b69fbdbb5ebe8c3a6af107d67e14c7a5b1ede2c5945 \ - --hash=sha256:6ffadd48e6fe85f27ca3ca10cfd3ef3d0f933bef7316870285ffeb58d791ca9c \ - --hash=sha256:72a011678c654df8323aa7b687e3147749034fdbe994d346f139ab9702b59cea \ - --hash=sha256:77d26452676f471223571efd73131fd4a626622c7960458aab2763e025836fc5 \ - --hash=sha256:7a88cc773ffe55992ff7259a8df5fb3570168d7138c69aadba40142d0e5ce39a \ - --hash=sha256:7b16bd74ae7bfbaca407a127e11058b287a4267caad13bd41305a5e630472549 \ - --hash=sha256:855d95ec78b6f0ff66e076d5461bf12d09d8e8f7e2b3fc9de7236d1464fd730e \ - --hash=sha256:8baf7991547441458325ca8fafeae79ef1501cb4354022724f3edd62279c5b2b \ - --hash=sha256:8fb77dd152054c6685639d855693579a92f276b38b8003be5942de31d241ebfb \ - --hash=sha256:92d49cc3b49372cfea2d42f43a2c16a98a32a6bc2f42abcde121132dbfc2f023 \ - --hash=sha256:94d0de65e37f5677165725f1fc7fb1616b9542d42a9832a9a0bdcba0ed68b63b \ - --hash=sha256:9867206093d7283d7de01bd2bf60389eb4d19b67306a0a763d1a8a4dbe2fb7c3 \ - --hash=sha256:9ee3c992b93e26c2ae827404a626138588e30bdabaaf7aa3aa25082a4e718790 \ - --hash=sha256:a4f8af277bb527fa3d56b216bda4da931b36b2d3fe416b6fc1744072b2c1dbd9 \ - --hash=sha256:ab9f19460dfa4c5dd25431b75bee28b5f018bf43476858d64b1aa1046196a2a0 \ - --hash=sha256:ac43c1821ba81e9344d818c5feed574a17f51fca27976ff7d022645c378fbbf5 \ - --hash=sha256:af5a011609206e390b44847da32463437505bf55fd8985e7a91c52d9da338d4b \ - --hash=sha256:b0975748bb6ec55b6d0f6665313c2cf7af6f536221dccd5879b967d76f6e7899 \ - --hash=sha256:b4963dad6cf28bfe0b61c3265d1c74a26a7605df3445bfcd3ba25de012330b2d \ - --hash=sha256:b7d3a484ace91ed827aa2ef3b44895e2ec106031012f14d28bd11a55f24fa734 \ - --hash=sha256:bd3c478a4a574f412efc58ba7e09ab4cd83484c545746a01601636e87e3dbf23 \ - --hash=sha256:c9e2dcb7f851f020232b991c226c5678dc07090256e929e45a89538d82f71d2e \ - --hash=sha256:d25c8eeb4720da41e7afbc404891e3a945b8bb6d5230e4c53d23ac4f4f9fc52c \ - --hash=sha256:dc8c03d0c5c10c200441ffb4cce46d869d9e5c4ef007f55856751dc288a2dffd \ - --hash=sha256:ec58e84d625553d191a23d5988a19c3ebfed519fff2a8b844223e3f074152163 \ - --hash=sha256:eda0719b29792f0fea04a853377cfff934660cb6cd72a0a0eeba7a1f0df4a16e \ - --hash=sha256:edde82ce3007a64e8434ccaf1b53271da4f255224d77b880b59e7d6d73df90c8 \ - --hash=sha256:f36722144bc0a5068934e51dca5a38a5b4daac1be84f4423244277e4baf24e7a \ - --hash=sha256:f8bb00ced04a8feff05989996db47906673ed45b11d86ad5ce892b5741e5f9dd \ - --hash=sha256:f98fc5750aac2d63d482909184aac72a979bfd123b112ec53fd365104ea15b1c \ - --hash=sha256:ff5b75f94101beaa373f1511319580a010f6e03458ee51b1a386d7de5331440a +zstandard==0.16.0 \ + --hash=sha256:066488e721ec882485a500c216302b443f2eaef39356f7c65130e76c671e3ce2 \ + --hash=sha256:08a728715858f1477239887ba3c692bc462b2c86e7a8e467dc5affa7bba9093f \ + --hash=sha256:11216b47c62e9fc71a25f4b42f525a81da268071bdb434bc1e642ffc38a24a02 \ + --hash=sha256:127c4c93f578d9b509732c74ed9b44b23e94041ba11b13827be0a7d2e3869b39 \ + --hash=sha256:12dddee2574b00c262270cfb46bd0c048e92208b95fdd39ad2a9eac1cef30498 \ + --hash=sha256:1bdda52224043e13ed20f847e3b308de1c9372d1563824fad776b1cf1f847ef0 \ + --hash=sha256:2e31680d1bcf85e7a58a45df7365af894402ae77a9868c751dc991dd13099a5f \ + --hash=sha256:42992e89b250fe6878c175119af529775d4be7967cd9de86990145d615d6a444 \ + --hash=sha256:453e42af96923582ddbf3acf843f55d2dc534a3f7b345003852dd522aa51eae6 \ + --hash=sha256:4d8a296dab7f8f5d53acc693a6785751f43ca39b51c8eabc672f978306fb40e6 \ + --hash=sha256:5251ac352d8350869c404a0ca94457da018b726f692f6456ec82bbf907fbc956 \ + --hash=sha256:57a6cfc34d906d514358769ed6d510b312be1cf033aafb5db44865a6717579bd \ + --hash=sha256:6ed51162e270b9b8097dcae6f2c239ada05ec112194633193ec3241498988924 \ + --hash=sha256:74cbea966462afed5a89eb99e4577538d10d425e05bf6240a75c086d59ccaf89 \ + --hash=sha256:87bea44ad24c15cd872263c0d5f912186a4be3db361eab3b25f1a61dcb5ca014 \ + --hash=sha256:8a745862ed525eee4e28bdbd58bf3ea952bf9da3c31bb4e4ce11ef15aea5c625 \ + --hash=sha256:8b760fc8118b1a0aa1d8f4e2012622e8f5f178d4b8cb94f8c6d2948b6a49a485 \ + --hash=sha256:8c8c0e813b67de1c9d7f2760768c4ae53f011c75ace18d5cff4fb40d2173763f \ + --hash=sha256:8d5fe983e23b05f0e924fe8d0dd3935f0c9fd3266e4c6ff8621c12c350da299d \ + --hash=sha256:8f5785c0b9b71d49d789240ae16a636728596631cf100f32b963a6f9857af5a4 \ + --hash=sha256:91efd5ea5fb3c347e7ebb6d5622bfa37d72594a2dec37c5dde70b691edb6cc03 \ + --hash=sha256:92e6c1a656390176d51125847f2f422f9d8ed468c24b63958f6ee50d9aa98c83 \ + --hash=sha256:9bcbfe1ec89789239f63daeea8778488cb5ba9034a374d7753815935f83dad65 \ + --hash=sha256:a92aa26789f17ca3b1f45cc7e728597165e2b166b99d1204bb397a672edee761 \ + --hash=sha256:a9ec6de2c058e611e9dfe88d9809a5676bc1d2a53543c1273a90a60e41b8f43c \ + --hash=sha256:ac5d97f9dece91a1162f651da79b735c5cde4d5863477785962aad648b592446 \ + --hash=sha256:ae19628886d994ac1f3d2fc7f9ed5bb551d81000f7b4e0c57a0e88301aea2766 \ + --hash=sha256:b2ea1937eff0ed5621876dc377933fe76624abfb2ab5b418995f43af6bac50de \ + --hash=sha256:b46220bef7bf9271a2a05512e86acbabc86cca08bebde8447bdbb4acb3179447 \ + --hash=sha256:b61586b0ff55c4137e512f1e9df4e4d7a6e1e9df782b4b87652df27737c90cc1 \ + --hash=sha256:be68fbac1e88f0dbe033a2d2e3aaaf9c8307730b905f3cd3c698ca4b904f0702 \ + --hash=sha256:c75557d53bb2d064521ff20cce9b8a51ee8301e031b1d6bcedb6458dda3bc85d \ + --hash=sha256:c7e6b6ad58ae6f77872da9376ef0ecbf8c1ae7a0c8fc29a2473abc90f79a9a1b \ + --hash=sha256:c8828f4e78774a6c0b8d21e59677f8f48d2e17fe2ef72793c94c10abc032c41c \ + --hash=sha256:cae9bfcb9148152f8bfb9163b4b779326ca39fe9889e45e0572c56d25d5021be \ + --hash=sha256:ce61492764d0442ca1e81d38d7bf7847d7df5003bce28089bab64c0519749351 \ + --hash=sha256:d40447f4a44b442fa6715779ff49a1e319729d829198279927d18bca0d7ac32d \ + --hash=sha256:d9946cfe54bf3365f14a5aa233eb2425de3b77eac6a4c7d03dda7dbb6acd3267 \ + --hash=sha256:dd5a2287893e52204e4ce9d0e1bcea6240661dbb412efb53d5446b881d3c10a2 \ + --hash=sha256:e9456492eb13249841e53221e742bef93f4868122bfc26bafa12a07677619732 \ + --hash=sha256:eaae2d3e8fdf8bfe269628385087e4b648beef85bb0c187644e7df4fb0fe9046 \ + --hash=sha256:eba125d3899f2003debf97019cd6f46f841a405df067da23d11443ad17952a40 \ + --hash=sha256:ef759c1dfe78aa5a01747d3465d2585de14e08fc2b0195ce3f31f45477fc5a72 \ + --hash=sha256:ffe1d24c5e11e98e4c5f96f846cdd19619d8c7e5e8e5082bed62d39baa30cecb # via rosbags (setup.py) From 4f658378eb8732c6047c3cd12c462b0d20ea910f Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 21 Oct 2021 18:00:44 +0200 Subject: [PATCH 047/114] Fix lint for updated tools --- src/rosbags/typesys/__main__.py | 4 ++-- tests/test_serde.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rosbags/typesys/__main__.py b/src/rosbags/typesys/__main__.py index 4396fcaf..07593d2d 100644 --- a/src/rosbags/typesys/__main__.py +++ b/src/rosbags/typesys/__main__.py @@ -31,12 +31,12 @@ def main() -> None: # pragma: no cover for fname in files: path = Path(root, fname) if path.suffix == '.idl': - typs.update(get_types_from_idl(path.read_text())) + typs.update(get_types_from_idl(path.read_text(encoding='utf-8'))) elif path.suffix == '.msg': name = path.relative_to(path.parents[2]).with_suffix('') if '/msg/' not in str(name): name = name.parent / 'msg' / name.name - typs.update(get_types_from_msg(path.read_text(), str(name))) + typs.update(get_types_from_msg(path.read_text(encoding='utf-8'), str(name))) register_types(typs) (selfdir / 'types.py').write_text(generate_python_code(typs)) diff --git a/tests/test_serde.py b/tests/test_serde.py index a5e646ff..a8e95edf 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -178,7 +178,9 @@ def _comparable(): frombuffer = numpy.frombuffer def arreq(self: MagicMock, other: Union[MagicMock, Any]) -> bool: - return (getattr(self, '_mock_wraps') == getattr(other, '_mock_wraps', other)).all() + lhs = self._mock_wraps # pylint: disable=protected-access + rhs = getattr(other, '_mock_wraps', other) + return (lhs == rhs).all() class CNDArray(MagicMock): """Mock ndarray.""" From b14085019c318e03df2585c8778b23a85186f32c Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 21 Oct 2021 18:45:04 +0200 Subject: [PATCH 048/114] Generate style compliant code --- src/rosbags/typesys/__main__.py | 1 + src/rosbags/typesys/register.py | 21 +- src/rosbags/typesys/types.py | 4203 +++++++++++++++++-------------- 3 files changed, 2270 insertions(+), 1955 deletions(-) diff --git a/src/rosbags/typesys/__main__.py b/src/rosbags/typesys/__main__.py index 07593d2d..14178a7e 100644 --- a/src/rosbags/typesys/__main__.py +++ b/src/rosbags/typesys/__main__.py @@ -37,6 +37,7 @@ def main() -> None: # pragma: no cover if '/msg/' not in str(name): name = name.parent / 'msg' / name.name typs.update(get_types_from_msg(path.read_text(encoding='utf-8'), str(name))) + typs = dict(sorted(typs.items())) register_types(typs) (selfdir / 'types.py').write_text(generate_python_code(typs)) diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index cd433e5b..bbe11d03 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -76,6 +76,7 @@ def generate_python_code(typs: Typesdict) -> str: '', ' from .base import Typesdict', '', + '', ] for name, (consts, fields) in typs.items(): @@ -107,11 +108,21 @@ def generate_python_code(typs: Typesdict) -> str: for name, (consts, fields) in typs.items(): pyname = name.replace('/', '__') lines += [ - f' \'{name}\': ([', - *[f' ({fname!r}, {ftype!r}, {fvalue!r}),' for fname, ftype, fvalue in consts], - ' ], [', - *[f' ({fname!r}, {get_ftype(ftype)!r}),' for fname, ftype in fields], - ' ]),', + f' \'{name}\': (', + *( + [ + ' [', + *[ + f' ({fname!r}, {ftype!r}, {fvalue!r}),' + for fname, ftype, fvalue in consts + ], + ' ],', + ] if consts else [' [],'] + ), + ' [', + *[f' ({fname!r}, {get_ftype(ftype)!r}),' for fname, ftype in fields], + ' ],', + ' ),', ] lines += [ '}', diff --git a/src/rosbags/typesys/types.py b/src/rosbags/typesys/types.py index 7c24dcf1..63ece623 100644 --- a/src/rosbags/typesys/types.py +++ b/src/rosbags/typesys/types.py @@ -20,6 +20,16 @@ if TYPE_CHECKING: from .base import Typesdict + +@dataclass +class builtin_interfaces__msg__Duration: + """Class for builtin_interfaces/msg/Duration.""" + + sec: int + nanosec: int + __msgtype__: ClassVar[str] = 'builtin_interfaces/msg/Duration' + + @dataclass class builtin_interfaces__msg__Time: """Class for builtin_interfaces/msg/Time.""" @@ -30,12 +40,12 @@ class builtin_interfaces__msg__Time: @dataclass -class builtin_interfaces__msg__Duration: - """Class for builtin_interfaces/msg/Duration.""" +class diagnostic_msgs__msg__DiagnosticArray: + """Class for diagnostic_msgs/msg/DiagnosticArray.""" - sec: int - nanosec: int - __msgtype__: ClassVar[str] = 'builtin_interfaces/msg/Duration' + header: std_msgs__msg__Header + status: list[diagnostic_msgs__msg__DiagnosticStatus] + __msgtype__: ClassVar[str] = 'diagnostic_msgs/msg/DiagnosticArray' @dataclass @@ -54,15 +64,6 @@ class diagnostic_msgs__msg__DiagnosticStatus: __msgtype__: ClassVar[str] = 'diagnostic_msgs/msg/DiagnosticStatus' -@dataclass -class diagnostic_msgs__msg__DiagnosticArray: - """Class for diagnostic_msgs/msg/DiagnosticArray.""" - - header: std_msgs__msg__Header - status: list[diagnostic_msgs__msg__DiagnosticStatus] - __msgtype__: ClassVar[str] = 'diagnostic_msgs/msg/DiagnosticArray' - - @dataclass class diagnostic_msgs__msg__KeyValue: """Class for diagnostic_msgs/msg/KeyValue.""" @@ -72,6 +73,24 @@ class diagnostic_msgs__msg__KeyValue: __msgtype__: ClassVar[str] = 'diagnostic_msgs/msg/KeyValue' +@dataclass +class geometry_msgs__msg__Accel: + """Class for geometry_msgs/msg/Accel.""" + + linear: geometry_msgs__msg__Vector3 + angular: geometry_msgs__msg__Vector3 + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Accel' + + +@dataclass +class geometry_msgs__msg__AccelStamped: + """Class for geometry_msgs/msg/AccelStamped.""" + + header: std_msgs__msg__Header + accel: geometry_msgs__msg__Accel + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/AccelStamped' + + @dataclass class geometry_msgs__msg__AccelWithCovariance: """Class for geometry_msgs/msg/AccelWithCovariance.""" @@ -82,23 +101,12 @@ class geometry_msgs__msg__AccelWithCovariance: @dataclass -class geometry_msgs__msg__Point32: - """Class for geometry_msgs/msg/Point32.""" +class geometry_msgs__msg__AccelWithCovarianceStamped: + """Class for geometry_msgs/msg/AccelWithCovarianceStamped.""" - x: float - y: float - z: float - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Point32' - - -@dataclass -class geometry_msgs__msg__Vector3: - """Class for geometry_msgs/msg/Vector3.""" - - x: float - y: float - z: float - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Vector3' + header: std_msgs__msg__Header + accel: geometry_msgs__msg__AccelWithCovariance + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/AccelWithCovarianceStamped' @dataclass @@ -117,30 +125,12 @@ class geometry_msgs__msg__Inertia: @dataclass -class geometry_msgs__msg__PoseWithCovarianceStamped: - """Class for geometry_msgs/msg/PoseWithCovarianceStamped.""" +class geometry_msgs__msg__InertiaStamped: + """Class for geometry_msgs/msg/InertiaStamped.""" header: std_msgs__msg__Header - pose: geometry_msgs__msg__PoseWithCovariance - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PoseWithCovarianceStamped' - - -@dataclass -class geometry_msgs__msg__Twist: - """Class for geometry_msgs/msg/Twist.""" - - linear: geometry_msgs__msg__Vector3 - angular: geometry_msgs__msg__Vector3 - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Twist' - - -@dataclass -class geometry_msgs__msg__Pose: - """Class for geometry_msgs/msg/Pose.""" - - position: geometry_msgs__msg__Point - orientation: geometry_msgs__msg__Quaternion - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Pose' + inertia: geometry_msgs__msg__Inertia + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/InertiaStamped' @dataclass @@ -154,78 +144,13 @@ class geometry_msgs__msg__Point: @dataclass -class geometry_msgs__msg__Vector3Stamped: - """Class for geometry_msgs/msg/Vector3Stamped.""" - - header: std_msgs__msg__Header - vector: geometry_msgs__msg__Vector3 - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Vector3Stamped' - - -@dataclass -class geometry_msgs__msg__Transform: - """Class for geometry_msgs/msg/Transform.""" - - translation: geometry_msgs__msg__Vector3 - rotation: geometry_msgs__msg__Quaternion - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Transform' - - -@dataclass -class geometry_msgs__msg__PolygonStamped: - """Class for geometry_msgs/msg/PolygonStamped.""" - - header: std_msgs__msg__Header - polygon: geometry_msgs__msg__Polygon - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PolygonStamped' - - -@dataclass -class geometry_msgs__msg__Quaternion: - """Class for geometry_msgs/msg/Quaternion.""" +class geometry_msgs__msg__Point32: + """Class for geometry_msgs/msg/Point32.""" x: float y: float z: float - w: float - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Quaternion' - - -@dataclass -class geometry_msgs__msg__Pose2D: - """Class for geometry_msgs/msg/Pose2D.""" - - x: float - y: float - theta: float - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Pose2D' - - -@dataclass -class geometry_msgs__msg__InertiaStamped: - """Class for geometry_msgs/msg/InertiaStamped.""" - - header: std_msgs__msg__Header - inertia: geometry_msgs__msg__Inertia - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/InertiaStamped' - - -@dataclass -class geometry_msgs__msg__TwistStamped: - """Class for geometry_msgs/msg/TwistStamped.""" - - header: std_msgs__msg__Header - twist: geometry_msgs__msg__Twist - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/TwistStamped' - - -@dataclass -class geometry_msgs__msg__PoseStamped: - """Class for geometry_msgs/msg/PoseStamped.""" - - header: std_msgs__msg__Header - pose: geometry_msgs__msg__Pose - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PoseStamped' + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Point32' @dataclass @@ -245,6 +170,34 @@ class geometry_msgs__msg__Polygon: __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Polygon' +@dataclass +class geometry_msgs__msg__PolygonStamped: + """Class for geometry_msgs/msg/PolygonStamped.""" + + header: std_msgs__msg__Header + polygon: geometry_msgs__msg__Polygon + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PolygonStamped' + + +@dataclass +class geometry_msgs__msg__Pose: + """Class for geometry_msgs/msg/Pose.""" + + position: geometry_msgs__msg__Point + orientation: geometry_msgs__msg__Quaternion + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Pose' + + +@dataclass +class geometry_msgs__msg__Pose2D: + """Class for geometry_msgs/msg/Pose2D.""" + + x: float + y: float + theta: float + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Pose2D' + + @dataclass class geometry_msgs__msg__PoseArray: """Class for geometry_msgs/msg/PoseArray.""" @@ -255,48 +208,12 @@ class geometry_msgs__msg__PoseArray: @dataclass -class geometry_msgs__msg__AccelStamped: - """Class for geometry_msgs/msg/AccelStamped.""" +class geometry_msgs__msg__PoseStamped: + """Class for geometry_msgs/msg/PoseStamped.""" header: std_msgs__msg__Header - accel: geometry_msgs__msg__Accel - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/AccelStamped' - - -@dataclass -class geometry_msgs__msg__TwistWithCovarianceStamped: - """Class for geometry_msgs/msg/TwistWithCovarianceStamped.""" - - header: std_msgs__msg__Header - twist: geometry_msgs__msg__TwistWithCovariance - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/TwistWithCovarianceStamped' - - -@dataclass -class geometry_msgs__msg__QuaternionStamped: - """Class for geometry_msgs/msg/QuaternionStamped.""" - - header: std_msgs__msg__Header - quaternion: geometry_msgs__msg__Quaternion - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/QuaternionStamped' - - -@dataclass -class geometry_msgs__msg__WrenchStamped: - """Class for geometry_msgs/msg/WrenchStamped.""" - - header: std_msgs__msg__Header - wrench: geometry_msgs__msg__Wrench - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/WrenchStamped' - - -@dataclass -class geometry_msgs__msg__AccelWithCovarianceStamped: - """Class for geometry_msgs/msg/AccelWithCovarianceStamped.""" - - header: std_msgs__msg__Header - accel: geometry_msgs__msg__AccelWithCovariance - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/AccelWithCovarianceStamped' + pose: geometry_msgs__msg__Pose + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PoseStamped' @dataclass @@ -309,12 +226,41 @@ class geometry_msgs__msg__PoseWithCovariance: @dataclass -class geometry_msgs__msg__Wrench: - """Class for geometry_msgs/msg/Wrench.""" +class geometry_msgs__msg__PoseWithCovarianceStamped: + """Class for geometry_msgs/msg/PoseWithCovarianceStamped.""" - force: geometry_msgs__msg__Vector3 - torque: geometry_msgs__msg__Vector3 - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Wrench' + header: std_msgs__msg__Header + pose: geometry_msgs__msg__PoseWithCovariance + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/PoseWithCovarianceStamped' + + +@dataclass +class geometry_msgs__msg__Quaternion: + """Class for geometry_msgs/msg/Quaternion.""" + + x: float + y: float + z: float + w: float + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Quaternion' + + +@dataclass +class geometry_msgs__msg__QuaternionStamped: + """Class for geometry_msgs/msg/QuaternionStamped.""" + + header: std_msgs__msg__Header + quaternion: geometry_msgs__msg__Quaternion + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/QuaternionStamped' + + +@dataclass +class geometry_msgs__msg__Transform: + """Class for geometry_msgs/msg/Transform.""" + + translation: geometry_msgs__msg__Vector3 + rotation: geometry_msgs__msg__Quaternion + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Transform' @dataclass @@ -328,12 +274,21 @@ class geometry_msgs__msg__TransformStamped: @dataclass -class geometry_msgs__msg__Accel: - """Class for geometry_msgs/msg/Accel.""" +class geometry_msgs__msg__Twist: + """Class for geometry_msgs/msg/Twist.""" linear: geometry_msgs__msg__Vector3 angular: geometry_msgs__msg__Vector3 - __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Accel' + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Twist' + + +@dataclass +class geometry_msgs__msg__TwistStamped: + """Class for geometry_msgs/msg/TwistStamped.""" + + header: std_msgs__msg__Header + twist: geometry_msgs__msg__Twist + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/TwistStamped' @dataclass @@ -345,6 +300,52 @@ class geometry_msgs__msg__TwistWithCovariance: __msgtype__: ClassVar[str] = 'geometry_msgs/msg/TwistWithCovariance' +@dataclass +class geometry_msgs__msg__TwistWithCovarianceStamped: + """Class for geometry_msgs/msg/TwistWithCovarianceStamped.""" + + header: std_msgs__msg__Header + twist: geometry_msgs__msg__TwistWithCovariance + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/TwistWithCovarianceStamped' + + +@dataclass +class geometry_msgs__msg__Vector3: + """Class for geometry_msgs/msg/Vector3.""" + + x: float + y: float + z: float + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Vector3' + + +@dataclass +class geometry_msgs__msg__Vector3Stamped: + """Class for geometry_msgs/msg/Vector3Stamped.""" + + header: std_msgs__msg__Header + vector: geometry_msgs__msg__Vector3 + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Vector3Stamped' + + +@dataclass +class geometry_msgs__msg__Wrench: + """Class for geometry_msgs/msg/Wrench.""" + + force: geometry_msgs__msg__Vector3 + torque: geometry_msgs__msg__Vector3 + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/Wrench' + + +@dataclass +class geometry_msgs__msg__WrenchStamped: + """Class for geometry_msgs/msg/WrenchStamped.""" + + header: std_msgs__msg__Header + wrench: geometry_msgs__msg__Wrench + __msgtype__: ClassVar[str] = 'geometry_msgs/msg/WrenchStamped' + + @dataclass class libstatistics_collector__msg__DummyMessage: """Class for libstatistics_collector/msg/DummyMessage.""" @@ -353,16 +354,6 @@ class libstatistics_collector__msg__DummyMessage: __msgtype__: ClassVar[str] = 'libstatistics_collector/msg/DummyMessage' -@dataclass -class lifecycle_msgs__msg__TransitionDescription: - """Class for lifecycle_msgs/msg/TransitionDescription.""" - - transition: lifecycle_msgs__msg__Transition - start_state: lifecycle_msgs__msg__State - goal_state: lifecycle_msgs__msg__State - __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/TransitionDescription' - - @dataclass class lifecycle_msgs__msg__State: """Class for lifecycle_msgs/msg/State.""" @@ -383,17 +374,6 @@ class lifecycle_msgs__msg__State: __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/State' -@dataclass -class lifecycle_msgs__msg__TransitionEvent: - """Class for lifecycle_msgs/msg/TransitionEvent.""" - - timestamp: int - transition: lifecycle_msgs__msg__Transition - start_state: lifecycle_msgs__msg__State - goal_state: lifecycle_msgs__msg__State - __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/TransitionEvent' - - @dataclass class lifecycle_msgs__msg__Transition: """Class for lifecycle_msgs/msg/Transition.""" @@ -433,6 +413,38 @@ class lifecycle_msgs__msg__Transition: __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/Transition' +@dataclass +class lifecycle_msgs__msg__TransitionDescription: + """Class for lifecycle_msgs/msg/TransitionDescription.""" + + transition: lifecycle_msgs__msg__Transition + start_state: lifecycle_msgs__msg__State + goal_state: lifecycle_msgs__msg__State + __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/TransitionDescription' + + +@dataclass +class lifecycle_msgs__msg__TransitionEvent: + """Class for lifecycle_msgs/msg/TransitionEvent.""" + + timestamp: int + transition: lifecycle_msgs__msg__Transition + start_state: lifecycle_msgs__msg__State + goal_state: lifecycle_msgs__msg__State + __msgtype__: ClassVar[str] = 'lifecycle_msgs/msg/TransitionEvent' + + +@dataclass +class nav_msgs__msg__GridCells: + """Class for nav_msgs/msg/GridCells.""" + + header: std_msgs__msg__Header + cell_width: float + cell_height: float + cells: list[geometry_msgs__msg__Point] + __msgtype__: ClassVar[str] = 'nav_msgs/msg/GridCells' + + @dataclass class nav_msgs__msg__MapMetaData: """Class for nav_msgs/msg/MapMetaData.""" @@ -446,14 +458,13 @@ class nav_msgs__msg__MapMetaData: @dataclass -class nav_msgs__msg__GridCells: - """Class for nav_msgs/msg/GridCells.""" +class nav_msgs__msg__OccupancyGrid: + """Class for nav_msgs/msg/OccupancyGrid.""" header: std_msgs__msg__Header - cell_width: float - cell_height: float - cells: list[geometry_msgs__msg__Point] - __msgtype__: ClassVar[str] = 'nav_msgs/msg/GridCells' + info: nav_msgs__msg__MapMetaData + data: numpy.ndarray[Any, numpy.dtype[numpy.int8]] + __msgtype__: ClassVar[str] = 'nav_msgs/msg/OccupancyGrid' @dataclass @@ -477,62 +488,13 @@ class nav_msgs__msg__Path: @dataclass -class nav_msgs__msg__OccupancyGrid: - """Class for nav_msgs/msg/OccupancyGrid.""" +class rcl_interfaces__msg__FloatingPointRange: + """Class for rcl_interfaces/msg/FloatingPointRange.""" - header: std_msgs__msg__Header - info: nav_msgs__msg__MapMetaData - data: numpy.ndarray[Any, numpy.dtype[numpy.int8]] - __msgtype__: ClassVar[str] = 'nav_msgs/msg/OccupancyGrid' - - -@dataclass -class rcl_interfaces__msg__ListParametersResult: - """Class for rcl_interfaces/msg/ListParametersResult.""" - - names: list[str] - prefixes: list[str] - __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ListParametersResult' - - -@dataclass -class rcl_interfaces__msg__ParameterType: - """Class for rcl_interfaces/msg/ParameterType.""" - - structure_needs_at_least_one_member: int - PARAMETER_NOT_SET: ClassVar[int] = 0 - PARAMETER_BOOL: ClassVar[int] = 1 - PARAMETER_INTEGER: ClassVar[int] = 2 - PARAMETER_DOUBLE: ClassVar[int] = 3 - PARAMETER_STRING: ClassVar[int] = 4 - PARAMETER_BYTE_ARRAY: ClassVar[int] = 5 - PARAMETER_BOOL_ARRAY: ClassVar[int] = 6 - PARAMETER_INTEGER_ARRAY: ClassVar[int] = 7 - PARAMETER_DOUBLE_ARRAY: ClassVar[int] = 8 - PARAMETER_STRING_ARRAY: ClassVar[int] = 9 - __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterType' - - -@dataclass -class rcl_interfaces__msg__ParameterEventDescriptors: - """Class for rcl_interfaces/msg/ParameterEventDescriptors.""" - - new_parameters: list[rcl_interfaces__msg__ParameterDescriptor] - changed_parameters: list[rcl_interfaces__msg__ParameterDescriptor] - deleted_parameters: list[rcl_interfaces__msg__ParameterDescriptor] - __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterEventDescriptors' - - -@dataclass -class rcl_interfaces__msg__ParameterEvent: - """Class for rcl_interfaces/msg/ParameterEvent.""" - - stamp: builtin_interfaces__msg__Time - node: str - new_parameters: list[rcl_interfaces__msg__Parameter] - changed_parameters: list[rcl_interfaces__msg__Parameter] - deleted_parameters: list[rcl_interfaces__msg__Parameter] - __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterEvent' + from_value: float + to_value: float + step: float + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/FloatingPointRange' @dataclass @@ -546,48 +508,12 @@ class rcl_interfaces__msg__IntegerRange: @dataclass -class rcl_interfaces__msg__Parameter: - """Class for rcl_interfaces/msg/Parameter.""" +class rcl_interfaces__msg__ListParametersResult: + """Class for rcl_interfaces/msg/ListParametersResult.""" - name: str - value: rcl_interfaces__msg__ParameterValue - __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/Parameter' - - -@dataclass -class rcl_interfaces__msg__ParameterValue: - """Class for rcl_interfaces/msg/ParameterValue.""" - - type: int - bool_value: bool - integer_value: int - double_value: float - string_value: str - byte_array_value: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] - bool_array_value: numpy.ndarray[Any, numpy.dtype[numpy.bool8]] - integer_array_value: numpy.ndarray[Any, numpy.dtype[numpy.int64]] - double_array_value: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - string_array_value: list[str] - __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterValue' - - -@dataclass -class rcl_interfaces__msg__FloatingPointRange: - """Class for rcl_interfaces/msg/FloatingPointRange.""" - - from_value: float - to_value: float - step: float - __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/FloatingPointRange' - - -@dataclass -class rcl_interfaces__msg__SetParametersResult: - """Class for rcl_interfaces/msg/SetParametersResult.""" - - successful: bool - reason: str - __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/SetParametersResult' + names: list[str] + prefixes: list[str] + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ListParametersResult' @dataclass @@ -609,6 +535,15 @@ class rcl_interfaces__msg__Log: __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/Log' +@dataclass +class rcl_interfaces__msg__Parameter: + """Class for rcl_interfaces/msg/Parameter.""" + + name: str + value: rcl_interfaces__msg__ParameterValue + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/Parameter' + + @dataclass class rcl_interfaces__msg__ParameterDescriptor: """Class for rcl_interfaces/msg/ParameterDescriptor.""" @@ -623,6 +558,72 @@ class rcl_interfaces__msg__ParameterDescriptor: __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterDescriptor' +@dataclass +class rcl_interfaces__msg__ParameterEvent: + """Class for rcl_interfaces/msg/ParameterEvent.""" + + stamp: builtin_interfaces__msg__Time + node: str + new_parameters: list[rcl_interfaces__msg__Parameter] + changed_parameters: list[rcl_interfaces__msg__Parameter] + deleted_parameters: list[rcl_interfaces__msg__Parameter] + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterEvent' + + +@dataclass +class rcl_interfaces__msg__ParameterEventDescriptors: + """Class for rcl_interfaces/msg/ParameterEventDescriptors.""" + + new_parameters: list[rcl_interfaces__msg__ParameterDescriptor] + changed_parameters: list[rcl_interfaces__msg__ParameterDescriptor] + deleted_parameters: list[rcl_interfaces__msg__ParameterDescriptor] + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterEventDescriptors' + + +@dataclass +class rcl_interfaces__msg__ParameterType: + """Class for rcl_interfaces/msg/ParameterType.""" + + structure_needs_at_least_one_member: int + PARAMETER_NOT_SET: ClassVar[int] = 0 + PARAMETER_BOOL: ClassVar[int] = 1 + PARAMETER_INTEGER: ClassVar[int] = 2 + PARAMETER_DOUBLE: ClassVar[int] = 3 + PARAMETER_STRING: ClassVar[int] = 4 + PARAMETER_BYTE_ARRAY: ClassVar[int] = 5 + PARAMETER_BOOL_ARRAY: ClassVar[int] = 6 + PARAMETER_INTEGER_ARRAY: ClassVar[int] = 7 + PARAMETER_DOUBLE_ARRAY: ClassVar[int] = 8 + PARAMETER_STRING_ARRAY: ClassVar[int] = 9 + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterType' + + +@dataclass +class rcl_interfaces__msg__ParameterValue: + """Class for rcl_interfaces/msg/ParameterValue.""" + + type: int + bool_value: bool + integer_value: int + double_value: float + string_value: str + byte_array_value: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + bool_array_value: numpy.ndarray[Any, numpy.dtype[numpy.bool8]] + integer_array_value: numpy.ndarray[Any, numpy.dtype[numpy.int64]] + double_array_value: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + string_array_value: list[str] + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/ParameterValue' + + +@dataclass +class rcl_interfaces__msg__SetParametersResult: + """Class for rcl_interfaces/msg/SetParametersResult.""" + + successful: bool + reason: str + __msgtype__: ClassVar[str] = 'rcl_interfaces/msg/SetParametersResult' + + @dataclass class rmw_dds_common__msg__Gid: """Class for rmw_dds_common/msg/Gid.""" @@ -659,160 +660,6 @@ class rosgraph_msgs__msg__Clock: __msgtype__: ClassVar[str] = 'rosgraph_msgs/msg/Clock' -@dataclass -class sensor_msgs__msg__Temperature: - """Class for sensor_msgs/msg/Temperature.""" - - header: std_msgs__msg__Header - temperature: float - variance: float - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Temperature' - - -@dataclass -class sensor_msgs__msg__Range: - """Class for sensor_msgs/msg/Range.""" - - header: std_msgs__msg__Header - radiation_type: int - field_of_view: float - min_range: float - max_range: float - range: float - ULTRASOUND: ClassVar[int] = 0 - INFRARED: ClassVar[int] = 1 - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Range' - - -@dataclass -class sensor_msgs__msg__RegionOfInterest: - """Class for sensor_msgs/msg/RegionOfInterest.""" - - x_offset: int - y_offset: int - height: int - width: int - do_rectify: bool - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/RegionOfInterest' - - -@dataclass -class sensor_msgs__msg__JoyFeedbackArray: - """Class for sensor_msgs/msg/JoyFeedbackArray.""" - - array: list[sensor_msgs__msg__JoyFeedback] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/JoyFeedbackArray' - - -@dataclass -class sensor_msgs__msg__TimeReference: - """Class for sensor_msgs/msg/TimeReference.""" - - header: std_msgs__msg__Header - time_ref: builtin_interfaces__msg__Time - source: str - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/TimeReference' - - -@dataclass -class sensor_msgs__msg__CompressedImage: - """Class for sensor_msgs/msg/CompressedImage.""" - - header: std_msgs__msg__Header - format: str - data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/CompressedImage' - - -@dataclass -class sensor_msgs__msg__MultiEchoLaserScan: - """Class for sensor_msgs/msg/MultiEchoLaserScan.""" - - header: std_msgs__msg__Header - angle_min: float - angle_max: float - angle_increment: float - time_increment: float - scan_time: float - range_min: float - range_max: float - ranges: list[sensor_msgs__msg__LaserEcho] - intensities: list[sensor_msgs__msg__LaserEcho] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/MultiEchoLaserScan' - - -@dataclass -class sensor_msgs__msg__LaserEcho: - """Class for sensor_msgs/msg/LaserEcho.""" - - echoes: numpy.ndarray[Any, numpy.dtype[numpy.float32]] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/LaserEcho' - - -@dataclass -class sensor_msgs__msg__ChannelFloat32: - """Class for sensor_msgs/msg/ChannelFloat32.""" - - name: str - values: numpy.ndarray[Any, numpy.dtype[numpy.float32]] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/ChannelFloat32' - - -@dataclass -class sensor_msgs__msg__CameraInfo: - """Class for sensor_msgs/msg/CameraInfo.""" - - header: std_msgs__msg__Header - height: int - width: int - distortion_model: str - d: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - k: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - r: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - p: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - binning_x: int - binning_y: int - roi: sensor_msgs__msg__RegionOfInterest - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/CameraInfo' - - -@dataclass -class sensor_msgs__msg__RelativeHumidity: - """Class for sensor_msgs/msg/RelativeHumidity.""" - - header: std_msgs__msg__Header - relative_humidity: float - variance: float - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/RelativeHumidity' - - -@dataclass -class sensor_msgs__msg__FluidPressure: - """Class for sensor_msgs/msg/FluidPressure.""" - - header: std_msgs__msg__Header - fluid_pressure: float - variance: float - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/FluidPressure' - - -@dataclass -class sensor_msgs__msg__LaserScan: - """Class for sensor_msgs/msg/LaserScan.""" - - header: std_msgs__msg__Header - angle_min: float - angle_max: float - angle_increment: float - time_increment: float - scan_time: float - range_min: float - range_max: float - ranges: numpy.ndarray[Any, numpy.dtype[numpy.float32]] - intensities: numpy.ndarray[Any, numpy.dtype[numpy.float32]] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/LaserScan' - - @dataclass class sensor_msgs__msg__BatteryState: """Class for sensor_msgs/msg/BatteryState.""" @@ -857,6 +704,63 @@ class sensor_msgs__msg__BatteryState: __msgtype__: ClassVar[str] = 'sensor_msgs/msg/BatteryState' +@dataclass +class sensor_msgs__msg__CameraInfo: + """Class for sensor_msgs/msg/CameraInfo.""" + + header: std_msgs__msg__Header + height: int + width: int + distortion_model: str + d: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + k: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + r: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + p: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + binning_x: int + binning_y: int + roi: sensor_msgs__msg__RegionOfInterest + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/CameraInfo' + + +@dataclass +class sensor_msgs__msg__ChannelFloat32: + """Class for sensor_msgs/msg/ChannelFloat32.""" + + name: str + values: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/ChannelFloat32' + + +@dataclass +class sensor_msgs__msg__CompressedImage: + """Class for sensor_msgs/msg/CompressedImage.""" + + header: std_msgs__msg__Header + format: str + data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/CompressedImage' + + +@dataclass +class sensor_msgs__msg__FluidPressure: + """Class for sensor_msgs/msg/FluidPressure.""" + + header: std_msgs__msg__Header + fluid_pressure: float + variance: float + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/FluidPressure' + + +@dataclass +class sensor_msgs__msg__Illuminance: + """Class for sensor_msgs/msg/Illuminance.""" + + header: std_msgs__msg__Header + illuminance: float + variance: float + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Illuminance' + + @dataclass class sensor_msgs__msg__Image: """Class for sensor_msgs/msg/Image.""" @@ -871,16 +775,6 @@ class sensor_msgs__msg__Image: __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Image' -@dataclass -class sensor_msgs__msg__PointCloud: - """Class for sensor_msgs/msg/PointCloud.""" - - header: std_msgs__msg__Header - points: list[geometry_msgs__msg__Point32] - channels: list[sensor_msgs__msg__ChannelFloat32] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/PointCloud' - - @dataclass class sensor_msgs__msg__Imu: """Class for sensor_msgs/msg/Imu.""" @@ -896,30 +790,15 @@ class sensor_msgs__msg__Imu: @dataclass -class sensor_msgs__msg__NavSatStatus: - """Class for sensor_msgs/msg/NavSatStatus.""" - - status: int - service: int - STATUS_NO_FIX: ClassVar[int] = -1 - STATUS_FIX: ClassVar[int] = 0 - STATUS_SBAS_FIX: ClassVar[int] = 1 - STATUS_GBAS_FIX: ClassVar[int] = 2 - SERVICE_GPS: ClassVar[int] = 1 - SERVICE_GLONASS: ClassVar[int] = 2 - SERVICE_COMPASS: ClassVar[int] = 4 - SERVICE_GALILEO: ClassVar[int] = 8 - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/NavSatStatus' - - -@dataclass -class sensor_msgs__msg__Illuminance: - """Class for sensor_msgs/msg/Illuminance.""" +class sensor_msgs__msg__JointState: + """Class for sensor_msgs/msg/JointState.""" header: std_msgs__msg__Header - illuminance: float - variance: float - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Illuminance' + name: list[str] + position: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + velocity: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + effort: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/JointState' @dataclass @@ -932,6 +811,91 @@ class sensor_msgs__msg__Joy: __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Joy' +@dataclass +class sensor_msgs__msg__JoyFeedback: + """Class for sensor_msgs/msg/JoyFeedback.""" + + type: int + id: int + intensity: float + TYPE_LED: ClassVar[int] = 0 + TYPE_RUMBLE: ClassVar[int] = 1 + TYPE_BUZZER: ClassVar[int] = 2 + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/JoyFeedback' + + +@dataclass +class sensor_msgs__msg__JoyFeedbackArray: + """Class for sensor_msgs/msg/JoyFeedbackArray.""" + + array: list[sensor_msgs__msg__JoyFeedback] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/JoyFeedbackArray' + + +@dataclass +class sensor_msgs__msg__LaserEcho: + """Class for sensor_msgs/msg/LaserEcho.""" + + echoes: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/LaserEcho' + + +@dataclass +class sensor_msgs__msg__LaserScan: + """Class for sensor_msgs/msg/LaserScan.""" + + header: std_msgs__msg__Header + angle_min: float + angle_max: float + angle_increment: float + time_increment: float + scan_time: float + range_min: float + range_max: float + ranges: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + intensities: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/LaserScan' + + +@dataclass +class sensor_msgs__msg__MagneticField: + """Class for sensor_msgs/msg/MagneticField.""" + + header: std_msgs__msg__Header + magnetic_field: geometry_msgs__msg__Vector3 + magnetic_field_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/MagneticField' + + +@dataclass +class sensor_msgs__msg__MultiDOFJointState: + """Class for sensor_msgs/msg/MultiDOFJointState.""" + + header: std_msgs__msg__Header + joint_names: list[str] + transforms: list[geometry_msgs__msg__Transform] + twist: list[geometry_msgs__msg__Twist] + wrench: list[geometry_msgs__msg__Wrench] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/MultiDOFJointState' + + +@dataclass +class sensor_msgs__msg__MultiEchoLaserScan: + """Class for sensor_msgs/msg/MultiEchoLaserScan.""" + + header: std_msgs__msg__Header + angle_min: float + angle_max: float + angle_increment: float + time_increment: float + scan_time: float + range_min: float + range_max: float + ranges: list[sensor_msgs__msg__LaserEcho] + intensities: list[sensor_msgs__msg__LaserEcho] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/MultiEchoLaserScan' + + @dataclass class sensor_msgs__msg__NavSatFix: """Class for sensor_msgs/msg/NavSatFix.""" @@ -951,37 +915,46 @@ class sensor_msgs__msg__NavSatFix: @dataclass -class sensor_msgs__msg__MultiDOFJointState: - """Class for sensor_msgs/msg/MultiDOFJointState.""" +class sensor_msgs__msg__NavSatStatus: + """Class for sensor_msgs/msg/NavSatStatus.""" - header: std_msgs__msg__Header - joint_names: list[str] - transforms: list[geometry_msgs__msg__Transform] - twist: list[geometry_msgs__msg__Twist] - wrench: list[geometry_msgs__msg__Wrench] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/MultiDOFJointState' + status: int + service: int + STATUS_NO_FIX: ClassVar[int] = -1 + STATUS_FIX: ClassVar[int] = 0 + STATUS_SBAS_FIX: ClassVar[int] = 1 + STATUS_GBAS_FIX: ClassVar[int] = 2 + SERVICE_GPS: ClassVar[int] = 1 + SERVICE_GLONASS: ClassVar[int] = 2 + SERVICE_COMPASS: ClassVar[int] = 4 + SERVICE_GALILEO: ClassVar[int] = 8 + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/NavSatStatus' @dataclass -class sensor_msgs__msg__MagneticField: - """Class for sensor_msgs/msg/MagneticField.""" +class sensor_msgs__msg__PointCloud: + """Class for sensor_msgs/msg/PointCloud.""" header: std_msgs__msg__Header - magnetic_field: geometry_msgs__msg__Vector3 - magnetic_field_covariance: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/MagneticField' + points: list[geometry_msgs__msg__Point32] + channels: list[sensor_msgs__msg__ChannelFloat32] + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/PointCloud' @dataclass -class sensor_msgs__msg__JointState: - """Class for sensor_msgs/msg/JointState.""" +class sensor_msgs__msg__PointCloud2: + """Class for sensor_msgs/msg/PointCloud2.""" header: std_msgs__msg__Header - name: list[str] - position: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - velocity: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - effort: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/JointState' + height: int + width: int + fields: list[sensor_msgs__msg__PointField] + is_bigendian: bool + point_step: int + row_step: int + data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + is_dense: bool + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/PointCloud2' @dataclass @@ -1004,32 +977,85 @@ class sensor_msgs__msg__PointField: @dataclass -class sensor_msgs__msg__PointCloud2: - """Class for sensor_msgs/msg/PointCloud2.""" +class sensor_msgs__msg__Range: + """Class for sensor_msgs/msg/Range.""" header: std_msgs__msg__Header - height: int - width: int - fields: list[sensor_msgs__msg__PointField] - is_bigendian: bool - point_step: int - row_step: int - data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] - is_dense: bool - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/PointCloud2' + radiation_type: int + field_of_view: float + min_range: float + max_range: float + range: float + ULTRASOUND: ClassVar[int] = 0 + INFRARED: ClassVar[int] = 1 + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Range' @dataclass -class sensor_msgs__msg__JoyFeedback: - """Class for sensor_msgs/msg/JoyFeedback.""" +class sensor_msgs__msg__RegionOfInterest: + """Class for sensor_msgs/msg/RegionOfInterest.""" - type: int - id: int - intensity: float - TYPE_LED: ClassVar[int] = 0 - TYPE_RUMBLE: ClassVar[int] = 1 - TYPE_BUZZER: ClassVar[int] = 2 - __msgtype__: ClassVar[str] = 'sensor_msgs/msg/JoyFeedback' + x_offset: int + y_offset: int + height: int + width: int + do_rectify: bool + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/RegionOfInterest' + + +@dataclass +class sensor_msgs__msg__RelativeHumidity: + """Class for sensor_msgs/msg/RelativeHumidity.""" + + header: std_msgs__msg__Header + relative_humidity: float + variance: float + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/RelativeHumidity' + + +@dataclass +class sensor_msgs__msg__Temperature: + """Class for sensor_msgs/msg/Temperature.""" + + header: std_msgs__msg__Header + temperature: float + variance: float + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/Temperature' + + +@dataclass +class sensor_msgs__msg__TimeReference: + """Class for sensor_msgs/msg/TimeReference.""" + + header: std_msgs__msg__Header + time_ref: builtin_interfaces__msg__Time + source: str + __msgtype__: ClassVar[str] = 'sensor_msgs/msg/TimeReference' + + +@dataclass +class shape_msgs__msg__Mesh: + """Class for shape_msgs/msg/Mesh.""" + + triangles: list[shape_msgs__msg__MeshTriangle] + vertices: list[geometry_msgs__msg__Point] + __msgtype__: ClassVar[str] = 'shape_msgs/msg/Mesh' + + +@dataclass +class shape_msgs__msg__MeshTriangle: + """Class for shape_msgs/msg/MeshTriangle.""" + + vertex_indices: numpy.ndarray[Any, numpy.dtype[numpy.uint32]] + __msgtype__: ClassVar[str] = 'shape_msgs/msg/MeshTriangle' + + +@dataclass +class shape_msgs__msg__Plane: + """Class for shape_msgs/msg/Plane.""" + + coef: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'shape_msgs/msg/Plane' @dataclass @@ -1054,28 +1080,25 @@ class shape_msgs__msg__SolidPrimitive: @dataclass -class shape_msgs__msg__Mesh: - """Class for shape_msgs/msg/Mesh.""" +class statistics_msgs__msg__MetricsMessage: + """Class for statistics_msgs/msg/MetricsMessage.""" - triangles: list[shape_msgs__msg__MeshTriangle] - vertices: list[geometry_msgs__msg__Point] - __msgtype__: ClassVar[str] = 'shape_msgs/msg/Mesh' + measurement_source_name: str + metrics_source: str + unit: str + window_start: builtin_interfaces__msg__Time + window_stop: builtin_interfaces__msg__Time + statistics: list[statistics_msgs__msg__StatisticDataPoint] + __msgtype__: ClassVar[str] = 'statistics_msgs/msg/MetricsMessage' @dataclass -class shape_msgs__msg__Plane: - """Class for shape_msgs/msg/Plane.""" +class statistics_msgs__msg__StatisticDataPoint: + """Class for statistics_msgs/msg/StatisticDataPoint.""" - coef: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - __msgtype__: ClassVar[str] = 'shape_msgs/msg/Plane' - - -@dataclass -class shape_msgs__msg__MeshTriangle: - """Class for shape_msgs/msg/MeshTriangle.""" - - vertex_indices: numpy.ndarray[Any, numpy.dtype[numpy.uint32]] - __msgtype__: ClassVar[str] = 'shape_msgs/msg/MeshTriangle' + data_type: int + data: float + __msgtype__: ClassVar[str] = 'statistics_msgs/msg/StatisticDataPoint' @dataclass @@ -1093,161 +1116,19 @@ class statistics_msgs__msg__StatisticDataType: @dataclass -class statistics_msgs__msg__StatisticDataPoint: - """Class for statistics_msgs/msg/StatisticDataPoint.""" +class std_msgs__msg__Bool: + """Class for std_msgs/msg/Bool.""" - data_type: int - data: float - __msgtype__: ClassVar[str] = 'statistics_msgs/msg/StatisticDataPoint' + data: bool + __msgtype__: ClassVar[str] = 'std_msgs/msg/Bool' @dataclass -class statistics_msgs__msg__MetricsMessage: - """Class for statistics_msgs/msg/MetricsMessage.""" - - measurement_source_name: str - metrics_source: str - unit: str - window_start: builtin_interfaces__msg__Time - window_stop: builtin_interfaces__msg__Time - statistics: list[statistics_msgs__msg__StatisticDataPoint] - __msgtype__: ClassVar[str] = 'statistics_msgs/msg/MetricsMessage' - - -@dataclass -class std_msgs__msg__UInt8: - """Class for std_msgs/msg/UInt8.""" +class std_msgs__msg__Byte: + """Class for std_msgs/msg/Byte.""" data: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt8' - - -@dataclass -class std_msgs__msg__Float32MultiArray: - """Class for std_msgs/msg/Float32MultiArray.""" - - layout: std_msgs__msg__MultiArrayLayout - data: numpy.ndarray[Any, numpy.dtype[numpy.float32]] - __msgtype__: ClassVar[str] = 'std_msgs/msg/Float32MultiArray' - - -@dataclass -class std_msgs__msg__Int8: - """Class for std_msgs/msg/Int8.""" - - data: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/Int8' - - -@dataclass -class std_msgs__msg__Empty: - """Class for std_msgs/msg/Empty.""" - - structure_needs_at_least_one_member: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/Empty' - - -@dataclass -class std_msgs__msg__String: - """Class for std_msgs/msg/String.""" - - data: str - __msgtype__: ClassVar[str] = 'std_msgs/msg/String' - - -@dataclass -class std_msgs__msg__MultiArrayDimension: - """Class for std_msgs/msg/MultiArrayDimension.""" - - label: str - size: int - stride: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/MultiArrayDimension' - - -@dataclass -class std_msgs__msg__UInt64: - """Class for std_msgs/msg/UInt64.""" - - data: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt64' - - -@dataclass -class std_msgs__msg__UInt16: - """Class for std_msgs/msg/UInt16.""" - - data: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt16' - - -@dataclass -class std_msgs__msg__Float32: - """Class for std_msgs/msg/Float32.""" - - data: float - __msgtype__: ClassVar[str] = 'std_msgs/msg/Float32' - - -@dataclass -class std_msgs__msg__Int64: - """Class for std_msgs/msg/Int64.""" - - data: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/Int64' - - -@dataclass -class std_msgs__msg__Int16MultiArray: - """Class for std_msgs/msg/Int16MultiArray.""" - - layout: std_msgs__msg__MultiArrayLayout - data: numpy.ndarray[Any, numpy.dtype[numpy.int16]] - __msgtype__: ClassVar[str] = 'std_msgs/msg/Int16MultiArray' - - -@dataclass -class std_msgs__msg__Int16: - """Class for std_msgs/msg/Int16.""" - - data: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/Int16' - - -@dataclass -class std_msgs__msg__Float64MultiArray: - """Class for std_msgs/msg/Float64MultiArray.""" - - layout: std_msgs__msg__MultiArrayLayout - data: numpy.ndarray[Any, numpy.dtype[numpy.float64]] - __msgtype__: ClassVar[str] = 'std_msgs/msg/Float64MultiArray' - - -@dataclass -class std_msgs__msg__MultiArrayLayout: - """Class for std_msgs/msg/MultiArrayLayout.""" - - dim: list[std_msgs__msg__MultiArrayDimension] - data_offset: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/MultiArrayLayout' - - -@dataclass -class std_msgs__msg__UInt32MultiArray: - """Class for std_msgs/msg/UInt32MultiArray.""" - - layout: std_msgs__msg__MultiArrayLayout - data: numpy.ndarray[Any, numpy.dtype[numpy.uint32]] - __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt32MultiArray' - - -@dataclass -class std_msgs__msg__Header: - """Class for std_msgs/msg/Header.""" - - stamp: builtin_interfaces__msg__Time - frame_id: str - __msgtype__: ClassVar[str] = 'std_msgs/msg/Header' + __msgtype__: ClassVar[str] = 'std_msgs/msg/Byte' @dataclass @@ -1259,40 +1140,6 @@ class std_msgs__msg__ByteMultiArray: __msgtype__: ClassVar[str] = 'std_msgs/msg/ByteMultiArray' -@dataclass -class std_msgs__msg__Int8MultiArray: - """Class for std_msgs/msg/Int8MultiArray.""" - - layout: std_msgs__msg__MultiArrayLayout - data: numpy.ndarray[Any, numpy.dtype[numpy.int8]] - __msgtype__: ClassVar[str] = 'std_msgs/msg/Int8MultiArray' - - -@dataclass -class std_msgs__msg__Float64: - """Class for std_msgs/msg/Float64.""" - - data: float - __msgtype__: ClassVar[str] = 'std_msgs/msg/Float64' - - -@dataclass -class std_msgs__msg__UInt8MultiArray: - """Class for std_msgs/msg/UInt8MultiArray.""" - - layout: std_msgs__msg__MultiArrayLayout - data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] - __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt8MultiArray' - - -@dataclass -class std_msgs__msg__Byte: - """Class for std_msgs/msg/Byte.""" - - data: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/Byte' - - @dataclass class std_msgs__msg__Char: """Class for std_msgs/msg/Char.""" @@ -1301,24 +1148,6 @@ class std_msgs__msg__Char: __msgtype__: ClassVar[str] = 'std_msgs/msg/Char' -@dataclass -class std_msgs__msg__UInt64MultiArray: - """Class for std_msgs/msg/UInt64MultiArray.""" - - layout: std_msgs__msg__MultiArrayLayout - data: numpy.ndarray[Any, numpy.dtype[numpy.uint64]] - __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt64MultiArray' - - -@dataclass -class std_msgs__msg__Int32MultiArray: - """Class for std_msgs/msg/Int32MultiArray.""" - - layout: std_msgs__msg__MultiArrayLayout - data: numpy.ndarray[Any, numpy.dtype[numpy.int32]] - __msgtype__: ClassVar[str] = 'std_msgs/msg/Int32MultiArray' - - @dataclass class std_msgs__msg__ColorRGBA: """Class for std_msgs/msg/ColorRGBA.""" @@ -1331,19 +1160,96 @@ class std_msgs__msg__ColorRGBA: @dataclass -class std_msgs__msg__Bool: - """Class for std_msgs/msg/Bool.""" +class std_msgs__msg__Empty: + """Class for std_msgs/msg/Empty.""" - data: bool - __msgtype__: ClassVar[str] = 'std_msgs/msg/Bool' + structure_needs_at_least_one_member: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Empty' @dataclass -class std_msgs__msg__UInt32: - """Class for std_msgs/msg/UInt32.""" +class std_msgs__msg__Float32: + """Class for std_msgs/msg/Float32.""" + + data: float + __msgtype__: ClassVar[str] = 'std_msgs/msg/Float32' + + +@dataclass +class std_msgs__msg__Float32MultiArray: + """Class for std_msgs/msg/Float32MultiArray.""" + + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.float32]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Float32MultiArray' + + +@dataclass +class std_msgs__msg__Float64: + """Class for std_msgs/msg/Float64.""" + + data: float + __msgtype__: ClassVar[str] = 'std_msgs/msg/Float64' + + +@dataclass +class std_msgs__msg__Float64MultiArray: + """Class for std_msgs/msg/Float64MultiArray.""" + + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.float64]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Float64MultiArray' + + +@dataclass +class std_msgs__msg__Header: + """Class for std_msgs/msg/Header.""" + + stamp: builtin_interfaces__msg__Time + frame_id: str + __msgtype__: ClassVar[str] = 'std_msgs/msg/Header' + + +@dataclass +class std_msgs__msg__Int16: + """Class for std_msgs/msg/Int16.""" data: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt32' + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int16' + + +@dataclass +class std_msgs__msg__Int16MultiArray: + """Class for std_msgs/msg/Int16MultiArray.""" + + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.int16]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int16MultiArray' + + +@dataclass +class std_msgs__msg__Int32: + """Class for std_msgs/msg/Int32.""" + + data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int32' + + +@dataclass +class std_msgs__msg__Int32MultiArray: + """Class for std_msgs/msg/Int32MultiArray.""" + + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.int32]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int32MultiArray' + + +@dataclass +class std_msgs__msg__Int64: + """Class for std_msgs/msg/Int64.""" + + data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int64' @dataclass @@ -1356,11 +1262,55 @@ class std_msgs__msg__Int64MultiArray: @dataclass -class std_msgs__msg__Int32: - """Class for std_msgs/msg/Int32.""" +class std_msgs__msg__Int8: + """Class for std_msgs/msg/Int8.""" data: int - __msgtype__: ClassVar[str] = 'std_msgs/msg/Int32' + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int8' + + +@dataclass +class std_msgs__msg__Int8MultiArray: + """Class for std_msgs/msg/Int8MultiArray.""" + + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.int8]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/Int8MultiArray' + + +@dataclass +class std_msgs__msg__MultiArrayDimension: + """Class for std_msgs/msg/MultiArrayDimension.""" + + label: str + size: int + stride: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/MultiArrayDimension' + + +@dataclass +class std_msgs__msg__MultiArrayLayout: + """Class for std_msgs/msg/MultiArrayLayout.""" + + dim: list[std_msgs__msg__MultiArrayDimension] + data_offset: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/MultiArrayLayout' + + +@dataclass +class std_msgs__msg__String: + """Class for std_msgs/msg/String.""" + + data: str + __msgtype__: ClassVar[str] = 'std_msgs/msg/String' + + +@dataclass +class std_msgs__msg__UInt16: + """Class for std_msgs/msg/UInt16.""" + + data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt16' @dataclass @@ -1372,6 +1322,57 @@ class std_msgs__msg__UInt16MultiArray: __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt16MultiArray' +@dataclass +class std_msgs__msg__UInt32: + """Class for std_msgs/msg/UInt32.""" + + data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt32' + + +@dataclass +class std_msgs__msg__UInt32MultiArray: + """Class for std_msgs/msg/UInt32MultiArray.""" + + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.uint32]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt32MultiArray' + + +@dataclass +class std_msgs__msg__UInt64: + """Class for std_msgs/msg/UInt64.""" + + data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt64' + + +@dataclass +class std_msgs__msg__UInt64MultiArray: + """Class for std_msgs/msg/UInt64MultiArray.""" + + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.uint64]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt64MultiArray' + + +@dataclass +class std_msgs__msg__UInt8: + """Class for std_msgs/msg/UInt8.""" + + data: int + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt8' + + +@dataclass +class std_msgs__msg__UInt8MultiArray: + """Class for std_msgs/msg/UInt8MultiArray.""" + + layout: std_msgs__msg__MultiArrayLayout + data: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] + __msgtype__: ClassVar[str] = 'std_msgs/msg/UInt8MultiArray' + + @dataclass class stereo_msgs__msg__DisparityImage: """Class for stereo_msgs/msg/DisparityImage.""" @@ -1411,16 +1412,6 @@ class tf2_msgs__msg__TFMessage: __msgtype__: ClassVar[str] = 'tf2_msgs/msg/TFMessage' -@dataclass -class trajectory_msgs__msg__MultiDOFJointTrajectory: - """Class for trajectory_msgs/msg/MultiDOFJointTrajectory.""" - - header: std_msgs__msg__Header - joint_names: list[str] - points: list[trajectory_msgs__msg__MultiDOFJointTrajectoryPoint] - __msgtype__: ClassVar[str] = 'trajectory_msgs/msg/MultiDOFJointTrajectory' - - @dataclass class trajectory_msgs__msg__JointTrajectory: """Class for trajectory_msgs/msg/JointTrajectory.""" @@ -1443,6 +1434,16 @@ class trajectory_msgs__msg__JointTrajectoryPoint: __msgtype__: ClassVar[str] = 'trajectory_msgs/msg/JointTrajectoryPoint' +@dataclass +class trajectory_msgs__msg__MultiDOFJointTrajectory: + """Class for trajectory_msgs/msg/MultiDOFJointTrajectory.""" + + header: std_msgs__msg__Header + joint_names: list[str] + points: list[trajectory_msgs__msg__MultiDOFJointTrajectoryPoint] + __msgtype__: ClassVar[str] = 'trajectory_msgs/msg/MultiDOFJointTrajectory' + + @dataclass class trajectory_msgs__msg__MultiDOFJointTrajectoryPoint: """Class for trajectory_msgs/msg/MultiDOFJointTrajectoryPoint.""" @@ -1462,6 +1463,132 @@ class unique_identifier_msgs__msg__UUID: __msgtype__: ClassVar[str] = 'unique_identifier_msgs/msg/UUID' +@dataclass +class visualization_msgs__msg__ImageMarker: + """Class for visualization_msgs/msg/ImageMarker.""" + + header: std_msgs__msg__Header + ns: str + id: int + type: int + action: int + position: geometry_msgs__msg__Point + scale: float + outline_color: std_msgs__msg__ColorRGBA + filled: int + fill_color: std_msgs__msg__ColorRGBA + lifetime: builtin_interfaces__msg__Duration + points: list[geometry_msgs__msg__Point] + outline_colors: list[std_msgs__msg__ColorRGBA] + CIRCLE: ClassVar[int] = 0 + LINE_STRIP: ClassVar[int] = 1 + LINE_LIST: ClassVar[int] = 2 + POLYGON: ClassVar[int] = 3 + POINTS: ClassVar[int] = 4 + ADD: ClassVar[int] = 0 + REMOVE: ClassVar[int] = 1 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/ImageMarker' + + +@dataclass +class visualization_msgs__msg__InteractiveMarker: + """Class for visualization_msgs/msg/InteractiveMarker.""" + + header: std_msgs__msg__Header + pose: geometry_msgs__msg__Pose + name: str + description: str + scale: float + menu_entries: list[visualization_msgs__msg__MenuEntry] + controls: list[visualization_msgs__msg__InteractiveMarkerControl] + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarker' + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerControl: + """Class for visualization_msgs/msg/InteractiveMarkerControl.""" + + name: str + orientation: geometry_msgs__msg__Quaternion + orientation_mode: int + interaction_mode: int + always_visible: bool + markers: list[visualization_msgs__msg__Marker] + independent_marker_orientation: bool + description: str + INHERIT: ClassVar[int] = 0 + FIXED: ClassVar[int] = 1 + VIEW_FACING: ClassVar[int] = 2 + NONE: ClassVar[int] = 0 + MENU: ClassVar[int] = 1 + BUTTON: ClassVar[int] = 2 + MOVE_AXIS: ClassVar[int] = 3 + MOVE_PLANE: ClassVar[int] = 4 + ROTATE_AXIS: ClassVar[int] = 5 + MOVE_ROTATE: ClassVar[int] = 6 + MOVE_3D: ClassVar[int] = 7 + ROTATE_3D: ClassVar[int] = 8 + MOVE_ROTATE_3D: ClassVar[int] = 9 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerControl' + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerFeedback: + """Class for visualization_msgs/msg/InteractiveMarkerFeedback.""" + + header: std_msgs__msg__Header + client_id: str + marker_name: str + control_name: str + event_type: int + pose: geometry_msgs__msg__Pose + menu_entry_id: int + mouse_point: geometry_msgs__msg__Point + mouse_point_valid: bool + KEEP_ALIVE: ClassVar[int] = 0 + POSE_UPDATE: ClassVar[int] = 1 + MENU_SELECT: ClassVar[int] = 2 + BUTTON_CLICK: ClassVar[int] = 3 + MOUSE_DOWN: ClassVar[int] = 4 + MOUSE_UP: ClassVar[int] = 5 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerFeedback' + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerInit: + """Class for visualization_msgs/msg/InteractiveMarkerInit.""" + + server_id: str + seq_num: int + markers: list[visualization_msgs__msg__InteractiveMarker] + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerInit' + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerPose: + """Class for visualization_msgs/msg/InteractiveMarkerPose.""" + + header: std_msgs__msg__Header + pose: geometry_msgs__msg__Pose + name: str + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerPose' + + +@dataclass +class visualization_msgs__msg__InteractiveMarkerUpdate: + """Class for visualization_msgs/msg/InteractiveMarkerUpdate.""" + + server_id: str + seq_num: int + type: int + markers: list[visualization_msgs__msg__InteractiveMarker] + poses: list[visualization_msgs__msg__InteractiveMarkerPose] + erases: list[str] + KEEP_ALIVE: ClassVar[int] = 0 + UPDATE: ClassVar[int] = 1 + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerUpdate' + + @dataclass class visualization_msgs__msg__Marker: """Class for visualization_msgs/msg/Marker.""" @@ -1501,13 +1628,11 @@ class visualization_msgs__msg__Marker: @dataclass -class visualization_msgs__msg__InteractiveMarkerInit: - """Class for visualization_msgs/msg/InteractiveMarkerInit.""" +class visualization_msgs__msg__MarkerArray: + """Class for visualization_msgs/msg/MarkerArray.""" - server_id: str - seq_num: int - markers: list[visualization_msgs__msg__InteractiveMarker] - __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerInit' + markers: list[visualization_msgs__msg__Marker] + __msgtype__: ClassVar[str] = 'visualization_msgs/msg/MarkerArray' @dataclass @@ -1525,1194 +1650,1372 @@ class visualization_msgs__msg__MenuEntry: __msgtype__: ClassVar[str] = 'visualization_msgs/msg/MenuEntry' -@dataclass -class visualization_msgs__msg__MarkerArray: - """Class for visualization_msgs/msg/MarkerArray.""" - - markers: list[visualization_msgs__msg__Marker] - __msgtype__: ClassVar[str] = 'visualization_msgs/msg/MarkerArray' - - -@dataclass -class visualization_msgs__msg__InteractiveMarkerUpdate: - """Class for visualization_msgs/msg/InteractiveMarkerUpdate.""" - - server_id: str - seq_num: int - type: int - markers: list[visualization_msgs__msg__InteractiveMarker] - poses: list[visualization_msgs__msg__InteractiveMarkerPose] - erases: list[str] - KEEP_ALIVE: ClassVar[int] = 0 - UPDATE: ClassVar[int] = 1 - __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerUpdate' - - -@dataclass -class visualization_msgs__msg__InteractiveMarker: - """Class for visualization_msgs/msg/InteractiveMarker.""" - - header: std_msgs__msg__Header - pose: geometry_msgs__msg__Pose - name: str - description: str - scale: float - menu_entries: list[visualization_msgs__msg__MenuEntry] - controls: list[visualization_msgs__msg__InteractiveMarkerControl] - __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarker' - - -@dataclass -class visualization_msgs__msg__InteractiveMarkerFeedback: - """Class for visualization_msgs/msg/InteractiveMarkerFeedback.""" - - header: std_msgs__msg__Header - client_id: str - marker_name: str - control_name: str - event_type: int - pose: geometry_msgs__msg__Pose - menu_entry_id: int - mouse_point: geometry_msgs__msg__Point - mouse_point_valid: bool - KEEP_ALIVE: ClassVar[int] = 0 - POSE_UPDATE: ClassVar[int] = 1 - MENU_SELECT: ClassVar[int] = 2 - BUTTON_CLICK: ClassVar[int] = 3 - MOUSE_DOWN: ClassVar[int] = 4 - MOUSE_UP: ClassVar[int] = 5 - __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerFeedback' - - -@dataclass -class visualization_msgs__msg__ImageMarker: - """Class for visualization_msgs/msg/ImageMarker.""" - - header: std_msgs__msg__Header - ns: str - id: int - type: int - action: int - position: geometry_msgs__msg__Point - scale: float - outline_color: std_msgs__msg__ColorRGBA - filled: int - fill_color: std_msgs__msg__ColorRGBA - lifetime: builtin_interfaces__msg__Duration - points: list[geometry_msgs__msg__Point] - outline_colors: list[std_msgs__msg__ColorRGBA] - CIRCLE: ClassVar[int] = 0 - LINE_STRIP: ClassVar[int] = 1 - LINE_LIST: ClassVar[int] = 2 - POLYGON: ClassVar[int] = 3 - POINTS: ClassVar[int] = 4 - ADD: ClassVar[int] = 0 - REMOVE: ClassVar[int] = 1 - __msgtype__: ClassVar[str] = 'visualization_msgs/msg/ImageMarker' - - -@dataclass -class visualization_msgs__msg__InteractiveMarkerControl: - """Class for visualization_msgs/msg/InteractiveMarkerControl.""" - - name: str - orientation: geometry_msgs__msg__Quaternion - orientation_mode: int - interaction_mode: int - always_visible: bool - markers: list[visualization_msgs__msg__Marker] - independent_marker_orientation: bool - description: str - INHERIT: ClassVar[int] = 0 - FIXED: ClassVar[int] = 1 - VIEW_FACING: ClassVar[int] = 2 - NONE: ClassVar[int] = 0 - MENU: ClassVar[int] = 1 - BUTTON: ClassVar[int] = 2 - MOVE_AXIS: ClassVar[int] = 3 - MOVE_PLANE: ClassVar[int] = 4 - ROTATE_AXIS: ClassVar[int] = 5 - MOVE_ROTATE: ClassVar[int] = 6 - MOVE_3D: ClassVar[int] = 7 - ROTATE_3D: ClassVar[int] = 8 - MOVE_ROTATE_3D: ClassVar[int] = 9 - __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerControl' - - -@dataclass -class visualization_msgs__msg__InteractiveMarkerPose: - """Class for visualization_msgs/msg/InteractiveMarkerPose.""" - - header: std_msgs__msg__Header - pose: geometry_msgs__msg__Pose - name: str - __msgtype__: ClassVar[str] = 'visualization_msgs/msg/InteractiveMarkerPose' - - FIELDDEFS: Typesdict = { - 'builtin_interfaces/msg/Time': ([ - ], [ - ('sec', (1, 'int32')), - ('nanosec', (1, 'uint32')), - ]), - 'builtin_interfaces/msg/Duration': ([ - ], [ - ('sec', (1, 'int32')), - ('nanosec', (1, 'uint32')), - ]), - 'diagnostic_msgs/msg/DiagnosticStatus': ([ - ('OK', 'uint8', 0), - ('WARN', 'uint8', 1), - ('ERROR', 'uint8', 2), - ('STALE', 'uint8', 3), - ], [ - ('level', (1, 'uint8')), - ('name', (1, 'string')), - ('message', (1, 'string')), - ('hardware_id', (1, 'string')), - ('values', (4, ((2, 'diagnostic_msgs/msg/KeyValue'), None))), - ]), - 'diagnostic_msgs/msg/DiagnosticArray': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('status', (4, ((2, 'diagnostic_msgs/msg/DiagnosticStatus'), None))), - ]), - 'diagnostic_msgs/msg/KeyValue': ([ - ], [ - ('key', (1, 'string')), - ('value', (1, 'string')), - ]), - 'geometry_msgs/msg/AccelWithCovariance': ([ - ], [ - ('accel', (2, 'geometry_msgs/msg/Accel')), - ('covariance', (3, ((1, 'float64'), 36))), - ]), - 'geometry_msgs/msg/Point32': ([ - ], [ - ('x', (1, 'float32')), - ('y', (1, 'float32')), - ('z', (1, 'float32')), - ]), - 'geometry_msgs/msg/Vector3': ([ - ], [ - ('x', (1, 'float64')), - ('y', (1, 'float64')), - ('z', (1, 'float64')), - ]), - 'geometry_msgs/msg/Inertia': ([ - ], [ - ('m', (1, 'float64')), - ('com', (2, 'geometry_msgs/msg/Vector3')), - ('ixx', (1, 'float64')), - ('ixy', (1, 'float64')), - ('ixz', (1, 'float64')), - ('iyy', (1, 'float64')), - ('iyz', (1, 'float64')), - ('izz', (1, 'float64')), - ]), - 'geometry_msgs/msg/PoseWithCovarianceStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('pose', (2, 'geometry_msgs/msg/PoseWithCovariance')), - ]), - 'geometry_msgs/msg/Twist': ([ - ], [ - ('linear', (2, 'geometry_msgs/msg/Vector3')), - ('angular', (2, 'geometry_msgs/msg/Vector3')), - ]), - 'geometry_msgs/msg/Pose': ([ - ], [ - ('position', (2, 'geometry_msgs/msg/Point')), - ('orientation', (2, 'geometry_msgs/msg/Quaternion')), - ]), - 'geometry_msgs/msg/Point': ([ - ], [ - ('x', (1, 'float64')), - ('y', (1, 'float64')), - ('z', (1, 'float64')), - ]), - 'geometry_msgs/msg/Vector3Stamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('vector', (2, 'geometry_msgs/msg/Vector3')), - ]), - 'geometry_msgs/msg/Transform': ([ - ], [ - ('translation', (2, 'geometry_msgs/msg/Vector3')), - ('rotation', (2, 'geometry_msgs/msg/Quaternion')), - ]), - 'geometry_msgs/msg/PolygonStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('polygon', (2, 'geometry_msgs/msg/Polygon')), - ]), - 'geometry_msgs/msg/Quaternion': ([ - ], [ - ('x', (1, 'float64')), - ('y', (1, 'float64')), - ('z', (1, 'float64')), - ('w', (1, 'float64')), - ]), - 'geometry_msgs/msg/Pose2D': ([ - ], [ - ('x', (1, 'float64')), - ('y', (1, 'float64')), - ('theta', (1, 'float64')), - ]), - 'geometry_msgs/msg/InertiaStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('inertia', (2, 'geometry_msgs/msg/Inertia')), - ]), - 'geometry_msgs/msg/TwistStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('twist', (2, 'geometry_msgs/msg/Twist')), - ]), - 'geometry_msgs/msg/PoseStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('pose', (2, 'geometry_msgs/msg/Pose')), - ]), - 'geometry_msgs/msg/PointStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('point', (2, 'geometry_msgs/msg/Point')), - ]), - 'geometry_msgs/msg/Polygon': ([ - ], [ - ('points', (4, ((2, 'geometry_msgs/msg/Point32'), None))), - ]), - 'geometry_msgs/msg/PoseArray': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('poses', (4, ((2, 'geometry_msgs/msg/Pose'), None))), - ]), - 'geometry_msgs/msg/AccelStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('accel', (2, 'geometry_msgs/msg/Accel')), - ]), - 'geometry_msgs/msg/TwistWithCovarianceStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('twist', (2, 'geometry_msgs/msg/TwistWithCovariance')), - ]), - 'geometry_msgs/msg/QuaternionStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('quaternion', (2, 'geometry_msgs/msg/Quaternion')), - ]), - 'geometry_msgs/msg/WrenchStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('wrench', (2, 'geometry_msgs/msg/Wrench')), - ]), - 'geometry_msgs/msg/AccelWithCovarianceStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('accel', (2, 'geometry_msgs/msg/AccelWithCovariance')), - ]), - 'geometry_msgs/msg/PoseWithCovariance': ([ - ], [ - ('pose', (2, 'geometry_msgs/msg/Pose')), - ('covariance', (3, ((1, 'float64'), 36))), - ]), - 'geometry_msgs/msg/Wrench': ([ - ], [ - ('force', (2, 'geometry_msgs/msg/Vector3')), - ('torque', (2, 'geometry_msgs/msg/Vector3')), - ]), - 'geometry_msgs/msg/TransformStamped': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('child_frame_id', (1, 'string')), - ('transform', (2, 'geometry_msgs/msg/Transform')), - ]), - 'geometry_msgs/msg/Accel': ([ - ], [ - ('linear', (2, 'geometry_msgs/msg/Vector3')), - ('angular', (2, 'geometry_msgs/msg/Vector3')), - ]), - 'geometry_msgs/msg/TwistWithCovariance': ([ - ], [ - ('twist', (2, 'geometry_msgs/msg/Twist')), - ('covariance', (3, ((1, 'float64'), 36))), - ]), - 'libstatistics_collector/msg/DummyMessage': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ]), - 'lifecycle_msgs/msg/TransitionDescription': ([ - ], [ - ('transition', (2, 'lifecycle_msgs/msg/Transition')), - ('start_state', (2, 'lifecycle_msgs/msg/State')), - ('goal_state', (2, 'lifecycle_msgs/msg/State')), - ]), - 'lifecycle_msgs/msg/State': ([ - ('PRIMARY_STATE_UNKNOWN', 'uint8', 0), - ('PRIMARY_STATE_UNCONFIGURED', 'uint8', 1), - ('PRIMARY_STATE_INACTIVE', 'uint8', 2), - ('PRIMARY_STATE_ACTIVE', 'uint8', 3), - ('PRIMARY_STATE_FINALIZED', 'uint8', 4), - ('TRANSITION_STATE_CONFIGURING', 'uint8', 10), - ('TRANSITION_STATE_CLEANINGUP', 'uint8', 11), - ('TRANSITION_STATE_SHUTTINGDOWN', 'uint8', 12), - ('TRANSITION_STATE_ACTIVATING', 'uint8', 13), - ('TRANSITION_STATE_DEACTIVATING', 'uint8', 14), - ('TRANSITION_STATE_ERRORPROCESSING', 'uint8', 15), - ], [ - ('id', (1, 'uint8')), - ('label', (1, 'string')), - ]), - 'lifecycle_msgs/msg/TransitionEvent': ([ - ], [ - ('timestamp', (1, 'uint64')), - ('transition', (2, 'lifecycle_msgs/msg/Transition')), - ('start_state', (2, 'lifecycle_msgs/msg/State')), - ('goal_state', (2, 'lifecycle_msgs/msg/State')), - ]), - 'lifecycle_msgs/msg/Transition': ([ - ('TRANSITION_CREATE', 'uint8', 0), - ('TRANSITION_CONFIGURE', 'uint8', 1), - ('TRANSITION_CLEANUP', 'uint8', 2), - ('TRANSITION_ACTIVATE', 'uint8', 3), - ('TRANSITION_DEACTIVATE', 'uint8', 4), - ('TRANSITION_UNCONFIGURED_SHUTDOWN', 'uint8', 5), - ('TRANSITION_INACTIVE_SHUTDOWN', 'uint8', 6), - ('TRANSITION_ACTIVE_SHUTDOWN', 'uint8', 7), - ('TRANSITION_DESTROY', 'uint8', 8), - ('TRANSITION_ON_CONFIGURE_SUCCESS', 'uint8', 10), - ('TRANSITION_ON_CONFIGURE_FAILURE', 'uint8', 11), - ('TRANSITION_ON_CONFIGURE_ERROR', 'uint8', 12), - ('TRANSITION_ON_CLEANUP_SUCCESS', 'uint8', 20), - ('TRANSITION_ON_CLEANUP_FAILURE', 'uint8', 21), - ('TRANSITION_ON_CLEANUP_ERROR', 'uint8', 22), - ('TRANSITION_ON_ACTIVATE_SUCCESS', 'uint8', 30), - ('TRANSITION_ON_ACTIVATE_FAILURE', 'uint8', 31), - ('TRANSITION_ON_ACTIVATE_ERROR', 'uint8', 32), - ('TRANSITION_ON_DEACTIVATE_SUCCESS', 'uint8', 40), - ('TRANSITION_ON_DEACTIVATE_FAILURE', 'uint8', 41), - ('TRANSITION_ON_DEACTIVATE_ERROR', 'uint8', 42), - ('TRANSITION_ON_SHUTDOWN_SUCCESS', 'uint8', 50), - ('TRANSITION_ON_SHUTDOWN_FAILURE', 'uint8', 51), - ('TRANSITION_ON_SHUTDOWN_ERROR', 'uint8', 52), - ('TRANSITION_ON_ERROR_SUCCESS', 'uint8', 60), - ('TRANSITION_ON_ERROR_FAILURE', 'uint8', 61), - ('TRANSITION_ON_ERROR_ERROR', 'uint8', 62), - ('TRANSITION_CALLBACK_SUCCESS', 'uint8', 97), - ('TRANSITION_CALLBACK_FAILURE', 'uint8', 98), - ('TRANSITION_CALLBACK_ERROR', 'uint8', 99), - ], [ - ('id', (1, 'uint8')), - ('label', (1, 'string')), - ]), - 'nav_msgs/msg/MapMetaData': ([ - ], [ - ('map_load_time', (2, 'builtin_interfaces/msg/Time')), - ('resolution', (1, 'float32')), - ('width', (1, 'uint32')), - ('height', (1, 'uint32')), - ('origin', (2, 'geometry_msgs/msg/Pose')), - ]), - 'nav_msgs/msg/GridCells': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('cell_width', (1, 'float32')), - ('cell_height', (1, 'float32')), - ('cells', (4, ((2, 'geometry_msgs/msg/Point'), None))), - ]), - 'nav_msgs/msg/Odometry': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('child_frame_id', (1, 'string')), - ('pose', (2, 'geometry_msgs/msg/PoseWithCovariance')), - ('twist', (2, 'geometry_msgs/msg/TwistWithCovariance')), - ]), - 'nav_msgs/msg/Path': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('poses', (4, ((2, 'geometry_msgs/msg/PoseStamped'), None))), - ]), - 'nav_msgs/msg/OccupancyGrid': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('info', (2, 'nav_msgs/msg/MapMetaData')), - ('data', (4, ((1, 'int8'), None))), - ]), - 'rcl_interfaces/msg/ListParametersResult': ([ - ], [ - ('names', (4, ((1, 'string'), None))), - ('prefixes', (4, ((1, 'string'), None))), - ]), - 'rcl_interfaces/msg/ParameterType': ([ - ('PARAMETER_NOT_SET', 'uint8', 0), - ('PARAMETER_BOOL', 'uint8', 1), - ('PARAMETER_INTEGER', 'uint8', 2), - ('PARAMETER_DOUBLE', 'uint8', 3), - ('PARAMETER_STRING', 'uint8', 4), - ('PARAMETER_BYTE_ARRAY', 'uint8', 5), - ('PARAMETER_BOOL_ARRAY', 'uint8', 6), - ('PARAMETER_INTEGER_ARRAY', 'uint8', 7), - ('PARAMETER_DOUBLE_ARRAY', 'uint8', 8), - ('PARAMETER_STRING_ARRAY', 'uint8', 9), - ], [ - ('structure_needs_at_least_one_member', (1, 'uint8')), - ]), - 'rcl_interfaces/msg/ParameterEventDescriptors': ([ - ], [ - ('new_parameters', (4, ((2, 'rcl_interfaces/msg/ParameterDescriptor'), None))), - ('changed_parameters', (4, ((2, 'rcl_interfaces/msg/ParameterDescriptor'), None))), - ('deleted_parameters', (4, ((2, 'rcl_interfaces/msg/ParameterDescriptor'), None))), - ]), - 'rcl_interfaces/msg/ParameterEvent': ([ - ], [ - ('stamp', (2, 'builtin_interfaces/msg/Time')), - ('node', (1, 'string')), - ('new_parameters', (4, ((2, 'rcl_interfaces/msg/Parameter'), None))), - ('changed_parameters', (4, ((2, 'rcl_interfaces/msg/Parameter'), None))), - ('deleted_parameters', (4, ((2, 'rcl_interfaces/msg/Parameter'), None))), - ]), - 'rcl_interfaces/msg/IntegerRange': ([ - ], [ - ('from_value', (1, 'int64')), - ('to_value', (1, 'int64')), - ('step', (1, 'uint64')), - ]), - 'rcl_interfaces/msg/Parameter': ([ - ], [ - ('name', (1, 'string')), - ('value', (2, 'rcl_interfaces/msg/ParameterValue')), - ]), - 'rcl_interfaces/msg/ParameterValue': ([ - ], [ - ('type', (1, 'uint8')), - ('bool_value', (1, 'bool')), - ('integer_value', (1, 'int64')), - ('double_value', (1, 'float64')), - ('string_value', (1, 'string')), - ('byte_array_value', (4, ((1, 'uint8'), None))), - ('bool_array_value', (4, ((1, 'bool'), None))), - ('integer_array_value', (4, ((1, 'int64'), None))), - ('double_array_value', (4, ((1, 'float64'), None))), - ('string_array_value', (4, ((1, 'string'), None))), - ]), - 'rcl_interfaces/msg/FloatingPointRange': ([ - ], [ - ('from_value', (1, 'float64')), - ('to_value', (1, 'float64')), - ('step', (1, 'float64')), - ]), - 'rcl_interfaces/msg/SetParametersResult': ([ - ], [ - ('successful', (1, 'bool')), - ('reason', (1, 'string')), - ]), - 'rcl_interfaces/msg/Log': ([ - ('DEBUG', 'uint8', 10), - ('INFO', 'uint8', 20), - ('WARN', 'uint8', 30), - ('ERROR', 'uint8', 40), - ('FATAL', 'uint8', 50), - ], [ - ('stamp', (2, 'builtin_interfaces/msg/Time')), - ('level', (1, 'uint8')), - ('name', (1, 'string')), - ('msg', (1, 'string')), - ('file', (1, 'string')), - ('function', (1, 'string')), - ('line', (1, 'uint32')), - ]), - 'rcl_interfaces/msg/ParameterDescriptor': ([ - ], [ - ('name', (1, 'string')), - ('type', (1, 'uint8')), - ('description', (1, 'string')), - ('additional_constraints', (1, 'string')), - ('read_only', (1, 'bool')), - ('floating_point_range', (4, ((2, 'rcl_interfaces/msg/FloatingPointRange'), None))), - ('integer_range', (4, ((2, 'rcl_interfaces/msg/IntegerRange'), None))), - ]), - 'rmw_dds_common/msg/Gid': ([ - ], [ - ('data', (3, ((1, 'uint8'), 24))), - ]), - 'rmw_dds_common/msg/NodeEntitiesInfo': ([ - ], [ - ('node_namespace', (1, 'string')), - ('node_name', (1, 'string')), - ('reader_gid_seq', (4, ((2, 'rmw_dds_common/msg/Gid'), None))), - ('writer_gid_seq', (4, ((2, 'rmw_dds_common/msg/Gid'), None))), - ]), - 'rmw_dds_common/msg/ParticipantEntitiesInfo': ([ - ], [ - ('gid', (2, 'rmw_dds_common/msg/Gid')), - ('node_entities_info_seq', (4, ((2, 'rmw_dds_common/msg/NodeEntitiesInfo'), None))), - ]), - 'rosgraph_msgs/msg/Clock': ([ - ], [ - ('clock', (2, 'builtin_interfaces/msg/Time')), - ]), - 'sensor_msgs/msg/Temperature': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('temperature', (1, 'float64')), - ('variance', (1, 'float64')), - ]), - 'sensor_msgs/msg/Range': ([ - ('ULTRASOUND', 'uint8', 0), - ('INFRARED', 'uint8', 1), - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('radiation_type', (1, 'uint8')), - ('field_of_view', (1, 'float32')), - ('min_range', (1, 'float32')), - ('max_range', (1, 'float32')), - ('range', (1, 'float32')), - ]), - 'sensor_msgs/msg/RegionOfInterest': ([ - ], [ - ('x_offset', (1, 'uint32')), - ('y_offset', (1, 'uint32')), - ('height', (1, 'uint32')), - ('width', (1, 'uint32')), - ('do_rectify', (1, 'bool')), - ]), - 'sensor_msgs/msg/JoyFeedbackArray': ([ - ], [ - ('array', (4, ((2, 'sensor_msgs/msg/JoyFeedback'), None))), - ]), - 'sensor_msgs/msg/TimeReference': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('time_ref', (2, 'builtin_interfaces/msg/Time')), - ('source', (1, 'string')), - ]), - 'sensor_msgs/msg/CompressedImage': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('format', (1, 'string')), - ('data', (4, ((1, 'uint8'), None))), - ]), - 'sensor_msgs/msg/MultiEchoLaserScan': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('angle_min', (1, 'float32')), - ('angle_max', (1, 'float32')), - ('angle_increment', (1, 'float32')), - ('time_increment', (1, 'float32')), - ('scan_time', (1, 'float32')), - ('range_min', (1, 'float32')), - ('range_max', (1, 'float32')), - ('ranges', (4, ((2, 'sensor_msgs/msg/LaserEcho'), None))), - ('intensities', (4, ((2, 'sensor_msgs/msg/LaserEcho'), None))), - ]), - 'sensor_msgs/msg/LaserEcho': ([ - ], [ - ('echoes', (4, ((1, 'float32'), None))), - ]), - 'sensor_msgs/msg/ChannelFloat32': ([ - ], [ - ('name', (1, 'string')), - ('values', (4, ((1, 'float32'), None))), - ]), - 'sensor_msgs/msg/CameraInfo': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('height', (1, 'uint32')), - ('width', (1, 'uint32')), - ('distortion_model', (1, 'string')), - ('d', (4, ((1, 'float64'), None))), - ('k', (3, ((1, 'float64'), 9))), - ('r', (3, ((1, 'float64'), 9))), - ('p', (3, ((1, 'float64'), 12))), - ('binning_x', (1, 'uint32')), - ('binning_y', (1, 'uint32')), - ('roi', (2, 'sensor_msgs/msg/RegionOfInterest')), - ]), - 'sensor_msgs/msg/RelativeHumidity': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('relative_humidity', (1, 'float64')), - ('variance', (1, 'float64')), - ]), - 'sensor_msgs/msg/FluidPressure': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('fluid_pressure', (1, 'float64')), - ('variance', (1, 'float64')), - ]), - 'sensor_msgs/msg/LaserScan': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('angle_min', (1, 'float32')), - ('angle_max', (1, 'float32')), - ('angle_increment', (1, 'float32')), - ('time_increment', (1, 'float32')), - ('scan_time', (1, 'float32')), - ('range_min', (1, 'float32')), - ('range_max', (1, 'float32')), - ('ranges', (4, ((1, 'float32'), None))), - ('intensities', (4, ((1, 'float32'), None))), - ]), - 'sensor_msgs/msg/BatteryState': ([ - ('POWER_SUPPLY_STATUS_UNKNOWN', 'uint8', 0), - ('POWER_SUPPLY_STATUS_CHARGING', 'uint8', 1), - ('POWER_SUPPLY_STATUS_DISCHARGING', 'uint8', 2), - ('POWER_SUPPLY_STATUS_NOT_CHARGING', 'uint8', 3), - ('POWER_SUPPLY_STATUS_FULL', 'uint8', 4), - ('POWER_SUPPLY_HEALTH_UNKNOWN', 'uint8', 0), - ('POWER_SUPPLY_HEALTH_GOOD', 'uint8', 1), - ('POWER_SUPPLY_HEALTH_OVERHEAT', 'uint8', 2), - ('POWER_SUPPLY_HEALTH_DEAD', 'uint8', 3), - ('POWER_SUPPLY_HEALTH_OVERVOLTAGE', 'uint8', 4), - ('POWER_SUPPLY_HEALTH_UNSPEC_FAILURE', 'uint8', 5), - ('POWER_SUPPLY_HEALTH_COLD', 'uint8', 6), - ('POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE', 'uint8', 7), - ('POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE', 'uint8', 8), - ('POWER_SUPPLY_TECHNOLOGY_UNKNOWN', 'uint8', 0), - ('POWER_SUPPLY_TECHNOLOGY_NIMH', 'uint8', 1), - ('POWER_SUPPLY_TECHNOLOGY_LION', 'uint8', 2), - ('POWER_SUPPLY_TECHNOLOGY_LIPO', 'uint8', 3), - ('POWER_SUPPLY_TECHNOLOGY_LIFE', 'uint8', 4), - ('POWER_SUPPLY_TECHNOLOGY_NICD', 'uint8', 5), - ('POWER_SUPPLY_TECHNOLOGY_LIMN', 'uint8', 6), - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('voltage', (1, 'float32')), - ('temperature', (1, 'float32')), - ('current', (1, 'float32')), - ('charge', (1, 'float32')), - ('capacity', (1, 'float32')), - ('design_capacity', (1, 'float32')), - ('percentage', (1, 'float32')), - ('power_supply_status', (1, 'uint8')), - ('power_supply_health', (1, 'uint8')), - ('power_supply_technology', (1, 'uint8')), - ('present', (1, 'bool')), - ('cell_voltage', (4, ((1, 'float32'), None))), - ('cell_temperature', (4, ((1, 'float32'), None))), - ('location', (1, 'string')), - ('serial_number', (1, 'string')), - ]), - 'sensor_msgs/msg/Image': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('height', (1, 'uint32')), - ('width', (1, 'uint32')), - ('encoding', (1, 'string')), - ('is_bigendian', (1, 'uint8')), - ('step', (1, 'uint32')), - ('data', (4, ((1, 'uint8'), None))), - ]), - 'sensor_msgs/msg/PointCloud': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('points', (4, ((2, 'geometry_msgs/msg/Point32'), None))), - ('channels', (4, ((2, 'sensor_msgs/msg/ChannelFloat32'), None))), - ]), - 'sensor_msgs/msg/Imu': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('orientation', (2, 'geometry_msgs/msg/Quaternion')), - ('orientation_covariance', (3, ((1, 'float64'), 9))), - ('angular_velocity', (2, 'geometry_msgs/msg/Vector3')), - ('angular_velocity_covariance', (3, ((1, 'float64'), 9))), - ('linear_acceleration', (2, 'geometry_msgs/msg/Vector3')), - ('linear_acceleration_covariance', (3, ((1, 'float64'), 9))), - ]), - 'sensor_msgs/msg/NavSatStatus': ([ - ('STATUS_NO_FIX', 'int8', -1), - ('STATUS_FIX', 'int8', 0), - ('STATUS_SBAS_FIX', 'int8', 1), - ('STATUS_GBAS_FIX', 'int8', 2), - ('SERVICE_GPS', 'uint16', 1), - ('SERVICE_GLONASS', 'uint16', 2), - ('SERVICE_COMPASS', 'uint16', 4), - ('SERVICE_GALILEO', 'uint16', 8), - ], [ - ('status', (1, 'int8')), - ('service', (1, 'uint16')), - ]), - 'sensor_msgs/msg/Illuminance': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('illuminance', (1, 'float64')), - ('variance', (1, 'float64')), - ]), - 'sensor_msgs/msg/Joy': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('axes', (4, ((1, 'float32'), None))), - ('buttons', (4, ((1, 'int32'), None))), - ]), - 'sensor_msgs/msg/NavSatFix': ([ - ('COVARIANCE_TYPE_UNKNOWN', 'uint8', 0), - ('COVARIANCE_TYPE_APPROXIMATED', 'uint8', 1), - ('COVARIANCE_TYPE_DIAGONAL_KNOWN', 'uint8', 2), - ('COVARIANCE_TYPE_KNOWN', 'uint8', 3), - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('status', (2, 'sensor_msgs/msg/NavSatStatus')), - ('latitude', (1, 'float64')), - ('longitude', (1, 'float64')), - ('altitude', (1, 'float64')), - ('position_covariance', (3, ((1, 'float64'), 9))), - ('position_covariance_type', (1, 'uint8')), - ]), - 'sensor_msgs/msg/MultiDOFJointState': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('joint_names', (4, ((1, 'string'), None))), - ('transforms', (4, ((2, 'geometry_msgs/msg/Transform'), None))), - ('twist', (4, ((2, 'geometry_msgs/msg/Twist'), None))), - ('wrench', (4, ((2, 'geometry_msgs/msg/Wrench'), None))), - ]), - 'sensor_msgs/msg/MagneticField': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('magnetic_field', (2, 'geometry_msgs/msg/Vector3')), - ('magnetic_field_covariance', (3, ((1, 'float64'), 9))), - ]), - 'sensor_msgs/msg/JointState': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('name', (4, ((1, 'string'), None))), - ('position', (4, ((1, 'float64'), None))), - ('velocity', (4, ((1, 'float64'), None))), - ('effort', (4, ((1, 'float64'), None))), - ]), - 'sensor_msgs/msg/PointField': ([ - ('INT8', 'uint8', 1), - ('UINT8', 'uint8', 2), - ('INT16', 'uint8', 3), - ('UINT16', 'uint8', 4), - ('INT32', 'uint8', 5), - ('UINT32', 'uint8', 6), - ('FLOAT32', 'uint8', 7), - ('FLOAT64', 'uint8', 8), - ], [ - ('name', (1, 'string')), - ('offset', (1, 'uint32')), - ('datatype', (1, 'uint8')), - ('count', (1, 'uint32')), - ]), - 'sensor_msgs/msg/PointCloud2': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('height', (1, 'uint32')), - ('width', (1, 'uint32')), - ('fields', (4, ((2, 'sensor_msgs/msg/PointField'), None))), - ('is_bigendian', (1, 'bool')), - ('point_step', (1, 'uint32')), - ('row_step', (1, 'uint32')), - ('data', (4, ((1, 'uint8'), None))), - ('is_dense', (1, 'bool')), - ]), - 'sensor_msgs/msg/JoyFeedback': ([ - ('TYPE_LED', 'uint8', 0), - ('TYPE_RUMBLE', 'uint8', 1), - ('TYPE_BUZZER', 'uint8', 2), - ], [ - ('type', (1, 'uint8')), - ('id', (1, 'uint8')), - ('intensity', (1, 'float32')), - ]), - 'shape_msgs/msg/SolidPrimitive': ([ - ('BOX', 'uint8', 1), - ('SPHERE', 'uint8', 2), - ('CYLINDER', 'uint8', 3), - ('CONE', 'uint8', 4), - ('BOX_X', 'uint8', 0), - ('BOX_Y', 'uint8', 1), - ('BOX_Z', 'uint8', 2), - ('SPHERE_RADIUS', 'uint8', 0), - ('CYLINDER_HEIGHT', 'uint8', 0), - ('CYLINDER_RADIUS', 'uint8', 1), - ('CONE_HEIGHT', 'uint8', 0), - ('CONE_RADIUS', 'uint8', 1), - ], [ - ('type', (1, 'uint8')), - ('dimensions', (4, ((1, 'float64'), None))), - ]), - 'shape_msgs/msg/Mesh': ([ - ], [ - ('triangles', (4, ((2, 'shape_msgs/msg/MeshTriangle'), None))), - ('vertices', (4, ((2, 'geometry_msgs/msg/Point'), None))), - ]), - 'shape_msgs/msg/Plane': ([ - ], [ - ('coef', (3, ((1, 'float64'), 4))), - ]), - 'shape_msgs/msg/MeshTriangle': ([ - ], [ - ('vertex_indices', (3, ((1, 'uint32'), 3))), - ]), - 'statistics_msgs/msg/StatisticDataType': ([ - ('STATISTICS_DATA_TYPE_UNINITIALIZED', 'uint8', 0), - ('STATISTICS_DATA_TYPE_AVERAGE', 'uint8', 1), - ('STATISTICS_DATA_TYPE_MINIMUM', 'uint8', 2), - ('STATISTICS_DATA_TYPE_MAXIMUM', 'uint8', 3), - ('STATISTICS_DATA_TYPE_STDDEV', 'uint8', 4), - ('STATISTICS_DATA_TYPE_SAMPLE_COUNT', 'uint8', 5), - ], [ - ('structure_needs_at_least_one_member', (1, 'uint8')), - ]), - 'statistics_msgs/msg/StatisticDataPoint': ([ - ], [ - ('data_type', (1, 'uint8')), - ('data', (1, 'float64')), - ]), - 'statistics_msgs/msg/MetricsMessage': ([ - ], [ - ('measurement_source_name', (1, 'string')), - ('metrics_source', (1, 'string')), - ('unit', (1, 'string')), - ('window_start', (2, 'builtin_interfaces/msg/Time')), - ('window_stop', (2, 'builtin_interfaces/msg/Time')), - ('statistics', (4, ((2, 'statistics_msgs/msg/StatisticDataPoint'), None))), - ]), - 'std_msgs/msg/UInt8': ([ - ], [ - ('data', (1, 'uint8')), - ]), - 'std_msgs/msg/Float32MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'float32'), None))), - ]), - 'std_msgs/msg/Int8': ([ - ], [ - ('data', (1, 'int8')), - ]), - 'std_msgs/msg/Empty': ([ - ], [ - ('structure_needs_at_least_one_member', (1, 'uint8')), - ]), - 'std_msgs/msg/String': ([ - ], [ - ('data', (1, 'string')), - ]), - 'std_msgs/msg/MultiArrayDimension': ([ - ], [ - ('label', (1, 'string')), - ('size', (1, 'uint32')), - ('stride', (1, 'uint32')), - ]), - 'std_msgs/msg/UInt64': ([ - ], [ - ('data', (1, 'uint64')), - ]), - 'std_msgs/msg/UInt16': ([ - ], [ - ('data', (1, 'uint16')), - ]), - 'std_msgs/msg/Float32': ([ - ], [ - ('data', (1, 'float32')), - ]), - 'std_msgs/msg/Int64': ([ - ], [ - ('data', (1, 'int64')), - ]), - 'std_msgs/msg/Int16MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'int16'), None))), - ]), - 'std_msgs/msg/Int16': ([ - ], [ - ('data', (1, 'int16')), - ]), - 'std_msgs/msg/Float64MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'float64'), None))), - ]), - 'std_msgs/msg/MultiArrayLayout': ([ - ], [ - ('dim', (4, ((2, 'std_msgs/msg/MultiArrayDimension'), None))), - ('data_offset', (1, 'uint32')), - ]), - 'std_msgs/msg/UInt32MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'uint32'), None))), - ]), - 'std_msgs/msg/Header': ([ - ], [ - ('stamp', (2, 'builtin_interfaces/msg/Time')), - ('frame_id', (1, 'string')), - ]), - 'std_msgs/msg/ByteMultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'uint8'), None))), - ]), - 'std_msgs/msg/Int8MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'int8'), None))), - ]), - 'std_msgs/msg/Float64': ([ - ], [ - ('data', (1, 'float64')), - ]), - 'std_msgs/msg/UInt8MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'uint8'), None))), - ]), - 'std_msgs/msg/Byte': ([ - ], [ - ('data', (1, 'uint8')), - ]), - 'std_msgs/msg/Char': ([ - ], [ - ('data', (1, 'uint8')), - ]), - 'std_msgs/msg/UInt64MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'uint64'), None))), - ]), - 'std_msgs/msg/Int32MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'int32'), None))), - ]), - 'std_msgs/msg/ColorRGBA': ([ - ], [ - ('r', (1, 'float32')), - ('g', (1, 'float32')), - ('b', (1, 'float32')), - ('a', (1, 'float32')), - ]), - 'std_msgs/msg/Bool': ([ - ], [ - ('data', (1, 'bool')), - ]), - 'std_msgs/msg/UInt32': ([ - ], [ - ('data', (1, 'uint32')), - ]), - 'std_msgs/msg/Int64MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'int64'), None))), - ]), - 'std_msgs/msg/Int32': ([ - ], [ - ('data', (1, 'int32')), - ]), - 'std_msgs/msg/UInt16MultiArray': ([ - ], [ - ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), - ('data', (4, ((1, 'uint16'), None))), - ]), - 'stereo_msgs/msg/DisparityImage': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('image', (2, 'sensor_msgs/msg/Image')), - ('f', (1, 'float32')), - ('t', (1, 'float32')), - ('valid_window', (2, 'sensor_msgs/msg/RegionOfInterest')), - ('min_disparity', (1, 'float32')), - ('max_disparity', (1, 'float32')), - ('delta_d', (1, 'float32')), - ]), - 'tf2_msgs/msg/TF2Error': ([ - ('NO_ERROR', 'uint8', 0), - ('LOOKUP_ERROR', 'uint8', 1), - ('CONNECTIVITY_ERROR', 'uint8', 2), - ('EXTRAPOLATION_ERROR', 'uint8', 3), - ('INVALID_ARGUMENT_ERROR', 'uint8', 4), - ('TIMEOUT_ERROR', 'uint8', 5), - ('TRANSFORM_ERROR', 'uint8', 6), - ], [ - ('error', (1, 'uint8')), - ('error_string', (1, 'string')), - ]), - 'tf2_msgs/msg/TFMessage': ([ - ], [ - ('transforms', (4, ((2, 'geometry_msgs/msg/TransformStamped'), None))), - ]), - 'trajectory_msgs/msg/MultiDOFJointTrajectory': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('joint_names', (4, ((1, 'string'), None))), - ('points', (4, ((2, 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint'), None))), - ]), - 'trajectory_msgs/msg/JointTrajectory': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('joint_names', (4, ((1, 'string'), None))), - ('points', (4, ((2, 'trajectory_msgs/msg/JointTrajectoryPoint'), None))), - ]), - 'trajectory_msgs/msg/JointTrajectoryPoint': ([ - ], [ - ('positions', (4, ((1, 'float64'), None))), - ('velocities', (4, ((1, 'float64'), None))), - ('accelerations', (4, ((1, 'float64'), None))), - ('effort', (4, ((1, 'float64'), None))), - ('time_from_start', (2, 'builtin_interfaces/msg/Duration')), - ]), - 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint': ([ - ], [ - ('transforms', (4, ((2, 'geometry_msgs/msg/Transform'), None))), - ('velocities', (4, ((2, 'geometry_msgs/msg/Twist'), None))), - ('accelerations', (4, ((2, 'geometry_msgs/msg/Twist'), None))), - ('time_from_start', (2, 'builtin_interfaces/msg/Duration')), - ]), - 'unique_identifier_msgs/msg/UUID': ([ - ], [ - ('uuid', (3, ((1, 'uint8'), 16))), - ]), - 'visualization_msgs/msg/Marker': ([ - ('ARROW', 'int32', 0), - ('CUBE', 'int32', 1), - ('SPHERE', 'int32', 2), - ('CYLINDER', 'int32', 3), - ('LINE_STRIP', 'int32', 4), - ('LINE_LIST', 'int32', 5), - ('CUBE_LIST', 'int32', 6), - ('SPHERE_LIST', 'int32', 7), - ('POINTS', 'int32', 8), - ('TEXT_VIEW_FACING', 'int32', 9), - ('MESH_RESOURCE', 'int32', 10), - ('TRIANGLE_LIST', 'int32', 11), - ('ADD', 'int32', 0), - ('MODIFY', 'int32', 0), - ('DELETE', 'int32', 2), - ('DELETEALL', 'int32', 3), - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('ns', (1, 'string')), - ('id', (1, 'int32')), - ('type', (1, 'int32')), - ('action', (1, 'int32')), - ('pose', (2, 'geometry_msgs/msg/Pose')), - ('scale', (2, 'geometry_msgs/msg/Vector3')), - ('color', (2, 'std_msgs/msg/ColorRGBA')), - ('lifetime', (2, 'builtin_interfaces/msg/Duration')), - ('frame_locked', (1, 'bool')), - ('points', (4, ((2, 'geometry_msgs/msg/Point'), None))), - ('colors', (4, ((2, 'std_msgs/msg/ColorRGBA'), None))), - ('text', (1, 'string')), - ('mesh_resource', (1, 'string')), - ('mesh_use_embedded_materials', (1, 'bool')), - ]), - 'visualization_msgs/msg/InteractiveMarkerInit': ([ - ], [ - ('server_id', (1, 'string')), - ('seq_num', (1, 'uint64')), - ('markers', (4, ((2, 'visualization_msgs/msg/InteractiveMarker'), None))), - ]), - 'visualization_msgs/msg/MenuEntry': ([ - ('FEEDBACK', 'uint8', 0), - ('ROSRUN', 'uint8', 1), - ('ROSLAUNCH', 'uint8', 2), - ], [ - ('id', (1, 'uint32')), - ('parent_id', (1, 'uint32')), - ('title', (1, 'string')), - ('command', (1, 'string')), - ('command_type', (1, 'uint8')), - ]), - 'visualization_msgs/msg/MarkerArray': ([ - ], [ - ('markers', (4, ((2, 'visualization_msgs/msg/Marker'), None))), - ]), - 'visualization_msgs/msg/InteractiveMarkerUpdate': ([ - ('KEEP_ALIVE', 'uint8', 0), - ('UPDATE', 'uint8', 1), - ], [ - ('server_id', (1, 'string')), - ('seq_num', (1, 'uint64')), - ('type', (1, 'uint8')), - ('markers', (4, ((2, 'visualization_msgs/msg/InteractiveMarker'), None))), - ('poses', (4, ((2, 'visualization_msgs/msg/InteractiveMarkerPose'), None))), - ('erases', (4, ((1, 'string'), None))), - ]), - 'visualization_msgs/msg/InteractiveMarker': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('pose', (2, 'geometry_msgs/msg/Pose')), - ('name', (1, 'string')), - ('description', (1, 'string')), - ('scale', (1, 'float32')), - ('menu_entries', (4, ((2, 'visualization_msgs/msg/MenuEntry'), None))), - ('controls', (4, ((2, 'visualization_msgs/msg/InteractiveMarkerControl'), None))), - ]), - 'visualization_msgs/msg/InteractiveMarkerFeedback': ([ - ('KEEP_ALIVE', 'uint8', 0), - ('POSE_UPDATE', 'uint8', 1), - ('MENU_SELECT', 'uint8', 2), - ('BUTTON_CLICK', 'uint8', 3), - ('MOUSE_DOWN', 'uint8', 4), - ('MOUSE_UP', 'uint8', 5), - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('client_id', (1, 'string')), - ('marker_name', (1, 'string')), - ('control_name', (1, 'string')), - ('event_type', (1, 'uint8')), - ('pose', (2, 'geometry_msgs/msg/Pose')), - ('menu_entry_id', (1, 'uint32')), - ('mouse_point', (2, 'geometry_msgs/msg/Point')), - ('mouse_point_valid', (1, 'bool')), - ]), - 'visualization_msgs/msg/ImageMarker': ([ - ('CIRCLE', 'int32', 0), - ('LINE_STRIP', 'int32', 1), - ('LINE_LIST', 'int32', 2), - ('POLYGON', 'int32', 3), - ('POINTS', 'int32', 4), - ('ADD', 'int32', 0), - ('REMOVE', 'int32', 1), - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('ns', (1, 'string')), - ('id', (1, 'int32')), - ('type', (1, 'int32')), - ('action', (1, 'int32')), - ('position', (2, 'geometry_msgs/msg/Point')), - ('scale', (1, 'float32')), - ('outline_color', (2, 'std_msgs/msg/ColorRGBA')), - ('filled', (1, 'uint8')), - ('fill_color', (2, 'std_msgs/msg/ColorRGBA')), - ('lifetime', (2, 'builtin_interfaces/msg/Duration')), - ('points', (4, ((2, 'geometry_msgs/msg/Point'), None))), - ('outline_colors', (4, ((2, 'std_msgs/msg/ColorRGBA'), None))), - ]), - 'visualization_msgs/msg/InteractiveMarkerControl': ([ - ('INHERIT', 'uint8', 0), - ('FIXED', 'uint8', 1), - ('VIEW_FACING', 'uint8', 2), - ('NONE', 'uint8', 0), - ('MENU', 'uint8', 1), - ('BUTTON', 'uint8', 2), - ('MOVE_AXIS', 'uint8', 3), - ('MOVE_PLANE', 'uint8', 4), - ('ROTATE_AXIS', 'uint8', 5), - ('MOVE_ROTATE', 'uint8', 6), - ('MOVE_3D', 'uint8', 7), - ('ROTATE_3D', 'uint8', 8), - ('MOVE_ROTATE_3D', 'uint8', 9), - ], [ - ('name', (1, 'string')), - ('orientation', (2, 'geometry_msgs/msg/Quaternion')), - ('orientation_mode', (1, 'uint8')), - ('interaction_mode', (1, 'uint8')), - ('always_visible', (1, 'bool')), - ('markers', (4, ((2, 'visualization_msgs/msg/Marker'), None))), - ('independent_marker_orientation', (1, 'bool')), - ('description', (1, 'string')), - ]), - 'visualization_msgs/msg/InteractiveMarkerPose': ([ - ], [ - ('header', (2, 'std_msgs/msg/Header')), - ('pose', (2, 'geometry_msgs/msg/Pose')), - ('name', (1, 'string')), - ]), + 'builtin_interfaces/msg/Duration': ( + [], + [ + ('sec', (1, 'int32')), + ('nanosec', (1, 'uint32')), + ], + ), + 'builtin_interfaces/msg/Time': ( + [], + [ + ('sec', (1, 'int32')), + ('nanosec', (1, 'uint32')), + ], + ), + 'diagnostic_msgs/msg/DiagnosticArray': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('status', (4, ((2, 'diagnostic_msgs/msg/DiagnosticStatus'), None))), + ], + ), + 'diagnostic_msgs/msg/DiagnosticStatus': ( + [ + ('OK', 'uint8', 0), + ('WARN', 'uint8', 1), + ('ERROR', 'uint8', 2), + ('STALE', 'uint8', 3), + ], + [ + ('level', (1, 'uint8')), + ('name', (1, 'string')), + ('message', (1, 'string')), + ('hardware_id', (1, 'string')), + ('values', (4, ((2, 'diagnostic_msgs/msg/KeyValue'), None))), + ], + ), + 'diagnostic_msgs/msg/KeyValue': ( + [], + [ + ('key', (1, 'string')), + ('value', (1, 'string')), + ], + ), + 'geometry_msgs/msg/Accel': ( + [], + [ + ('linear', (2, 'geometry_msgs/msg/Vector3')), + ('angular', (2, 'geometry_msgs/msg/Vector3')), + ], + ), + 'geometry_msgs/msg/AccelStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('accel', (2, 'geometry_msgs/msg/Accel')), + ], + ), + 'geometry_msgs/msg/AccelWithCovariance': ( + [], + [ + ('accel', (2, 'geometry_msgs/msg/Accel')), + ('covariance', (3, ((1, 'float64'), 36))), + ], + ), + 'geometry_msgs/msg/AccelWithCovarianceStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('accel', (2, 'geometry_msgs/msg/AccelWithCovariance')), + ], + ), + 'geometry_msgs/msg/Inertia': ( + [], + [ + ('m', (1, 'float64')), + ('com', (2, 'geometry_msgs/msg/Vector3')), + ('ixx', (1, 'float64')), + ('ixy', (1, 'float64')), + ('ixz', (1, 'float64')), + ('iyy', (1, 'float64')), + ('iyz', (1, 'float64')), + ('izz', (1, 'float64')), + ], + ), + 'geometry_msgs/msg/InertiaStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('inertia', (2, 'geometry_msgs/msg/Inertia')), + ], + ), + 'geometry_msgs/msg/Point': ( + [], + [ + ('x', (1, 'float64')), + ('y', (1, 'float64')), + ('z', (1, 'float64')), + ], + ), + 'geometry_msgs/msg/Point32': ( + [], + [ + ('x', (1, 'float32')), + ('y', (1, 'float32')), + ('z', (1, 'float32')), + ], + ), + 'geometry_msgs/msg/PointStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('point', (2, 'geometry_msgs/msg/Point')), + ], + ), + 'geometry_msgs/msg/Polygon': ( + [], + [ + ('points', (4, ((2, 'geometry_msgs/msg/Point32'), None))), + ], + ), + 'geometry_msgs/msg/PolygonStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('polygon', (2, 'geometry_msgs/msg/Polygon')), + ], + ), + 'geometry_msgs/msg/Pose': ( + [], + [ + ('position', (2, 'geometry_msgs/msg/Point')), + ('orientation', (2, 'geometry_msgs/msg/Quaternion')), + ], + ), + 'geometry_msgs/msg/Pose2D': ( + [], + [ + ('x', (1, 'float64')), + ('y', (1, 'float64')), + ('theta', (1, 'float64')), + ], + ), + 'geometry_msgs/msg/PoseArray': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('poses', (4, ((2, 'geometry_msgs/msg/Pose'), None))), + ], + ), + 'geometry_msgs/msg/PoseStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ], + ), + 'geometry_msgs/msg/PoseWithCovariance': ( + [], + [ + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('covariance', (3, ((1, 'float64'), 36))), + ], + ), + 'geometry_msgs/msg/PoseWithCovarianceStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('pose', (2, 'geometry_msgs/msg/PoseWithCovariance')), + ], + ), + 'geometry_msgs/msg/Quaternion': ( + [], + [ + ('x', (1, 'float64')), + ('y', (1, 'float64')), + ('z', (1, 'float64')), + ('w', (1, 'float64')), + ], + ), + 'geometry_msgs/msg/QuaternionStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('quaternion', (2, 'geometry_msgs/msg/Quaternion')), + ], + ), + 'geometry_msgs/msg/Transform': ( + [], + [ + ('translation', (2, 'geometry_msgs/msg/Vector3')), + ('rotation', (2, 'geometry_msgs/msg/Quaternion')), + ], + ), + 'geometry_msgs/msg/TransformStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('child_frame_id', (1, 'string')), + ('transform', (2, 'geometry_msgs/msg/Transform')), + ], + ), + 'geometry_msgs/msg/Twist': ( + [], + [ + ('linear', (2, 'geometry_msgs/msg/Vector3')), + ('angular', (2, 'geometry_msgs/msg/Vector3')), + ], + ), + 'geometry_msgs/msg/TwistStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('twist', (2, 'geometry_msgs/msg/Twist')), + ], + ), + 'geometry_msgs/msg/TwistWithCovariance': ( + [], + [ + ('twist', (2, 'geometry_msgs/msg/Twist')), + ('covariance', (3, ((1, 'float64'), 36))), + ], + ), + 'geometry_msgs/msg/TwistWithCovarianceStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('twist', (2, 'geometry_msgs/msg/TwistWithCovariance')), + ], + ), + 'geometry_msgs/msg/Vector3': ( + [], + [ + ('x', (1, 'float64')), + ('y', (1, 'float64')), + ('z', (1, 'float64')), + ], + ), + 'geometry_msgs/msg/Vector3Stamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('vector', (2, 'geometry_msgs/msg/Vector3')), + ], + ), + 'geometry_msgs/msg/Wrench': ( + [], + [ + ('force', (2, 'geometry_msgs/msg/Vector3')), + ('torque', (2, 'geometry_msgs/msg/Vector3')), + ], + ), + 'geometry_msgs/msg/WrenchStamped': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('wrench', (2, 'geometry_msgs/msg/Wrench')), + ], + ), + 'libstatistics_collector/msg/DummyMessage': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ], + ), + 'lifecycle_msgs/msg/State': ( + [ + ('PRIMARY_STATE_UNKNOWN', 'uint8', 0), + ('PRIMARY_STATE_UNCONFIGURED', 'uint8', 1), + ('PRIMARY_STATE_INACTIVE', 'uint8', 2), + ('PRIMARY_STATE_ACTIVE', 'uint8', 3), + ('PRIMARY_STATE_FINALIZED', 'uint8', 4), + ('TRANSITION_STATE_CONFIGURING', 'uint8', 10), + ('TRANSITION_STATE_CLEANINGUP', 'uint8', 11), + ('TRANSITION_STATE_SHUTTINGDOWN', 'uint8', 12), + ('TRANSITION_STATE_ACTIVATING', 'uint8', 13), + ('TRANSITION_STATE_DEACTIVATING', 'uint8', 14), + ('TRANSITION_STATE_ERRORPROCESSING', 'uint8', 15), + ], + [ + ('id', (1, 'uint8')), + ('label', (1, 'string')), + ], + ), + 'lifecycle_msgs/msg/Transition': ( + [ + ('TRANSITION_CREATE', 'uint8', 0), + ('TRANSITION_CONFIGURE', 'uint8', 1), + ('TRANSITION_CLEANUP', 'uint8', 2), + ('TRANSITION_ACTIVATE', 'uint8', 3), + ('TRANSITION_DEACTIVATE', 'uint8', 4), + ('TRANSITION_UNCONFIGURED_SHUTDOWN', 'uint8', 5), + ('TRANSITION_INACTIVE_SHUTDOWN', 'uint8', 6), + ('TRANSITION_ACTIVE_SHUTDOWN', 'uint8', 7), + ('TRANSITION_DESTROY', 'uint8', 8), + ('TRANSITION_ON_CONFIGURE_SUCCESS', 'uint8', 10), + ('TRANSITION_ON_CONFIGURE_FAILURE', 'uint8', 11), + ('TRANSITION_ON_CONFIGURE_ERROR', 'uint8', 12), + ('TRANSITION_ON_CLEANUP_SUCCESS', 'uint8', 20), + ('TRANSITION_ON_CLEANUP_FAILURE', 'uint8', 21), + ('TRANSITION_ON_CLEANUP_ERROR', 'uint8', 22), + ('TRANSITION_ON_ACTIVATE_SUCCESS', 'uint8', 30), + ('TRANSITION_ON_ACTIVATE_FAILURE', 'uint8', 31), + ('TRANSITION_ON_ACTIVATE_ERROR', 'uint8', 32), + ('TRANSITION_ON_DEACTIVATE_SUCCESS', 'uint8', 40), + ('TRANSITION_ON_DEACTIVATE_FAILURE', 'uint8', 41), + ('TRANSITION_ON_DEACTIVATE_ERROR', 'uint8', 42), + ('TRANSITION_ON_SHUTDOWN_SUCCESS', 'uint8', 50), + ('TRANSITION_ON_SHUTDOWN_FAILURE', 'uint8', 51), + ('TRANSITION_ON_SHUTDOWN_ERROR', 'uint8', 52), + ('TRANSITION_ON_ERROR_SUCCESS', 'uint8', 60), + ('TRANSITION_ON_ERROR_FAILURE', 'uint8', 61), + ('TRANSITION_ON_ERROR_ERROR', 'uint8', 62), + ('TRANSITION_CALLBACK_SUCCESS', 'uint8', 97), + ('TRANSITION_CALLBACK_FAILURE', 'uint8', 98), + ('TRANSITION_CALLBACK_ERROR', 'uint8', 99), + ], + [ + ('id', (1, 'uint8')), + ('label', (1, 'string')), + ], + ), + 'lifecycle_msgs/msg/TransitionDescription': ( + [], + [ + ('transition', (2, 'lifecycle_msgs/msg/Transition')), + ('start_state', (2, 'lifecycle_msgs/msg/State')), + ('goal_state', (2, 'lifecycle_msgs/msg/State')), + ], + ), + 'lifecycle_msgs/msg/TransitionEvent': ( + [], + [ + ('timestamp', (1, 'uint64')), + ('transition', (2, 'lifecycle_msgs/msg/Transition')), + ('start_state', (2, 'lifecycle_msgs/msg/State')), + ('goal_state', (2, 'lifecycle_msgs/msg/State')), + ], + ), + 'nav_msgs/msg/GridCells': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('cell_width', (1, 'float32')), + ('cell_height', (1, 'float32')), + ('cells', (4, ((2, 'geometry_msgs/msg/Point'), None))), + ], + ), + 'nav_msgs/msg/MapMetaData': ( + [], + [ + ('map_load_time', (2, 'builtin_interfaces/msg/Time')), + ('resolution', (1, 'float32')), + ('width', (1, 'uint32')), + ('height', (1, 'uint32')), + ('origin', (2, 'geometry_msgs/msg/Pose')), + ], + ), + 'nav_msgs/msg/OccupancyGrid': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('info', (2, 'nav_msgs/msg/MapMetaData')), + ('data', (4, ((1, 'int8'), None))), + ], + ), + 'nav_msgs/msg/Odometry': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('child_frame_id', (1, 'string')), + ('pose', (2, 'geometry_msgs/msg/PoseWithCovariance')), + ('twist', (2, 'geometry_msgs/msg/TwistWithCovariance')), + ], + ), + 'nav_msgs/msg/Path': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('poses', (4, ((2, 'geometry_msgs/msg/PoseStamped'), None))), + ], + ), + 'rcl_interfaces/msg/FloatingPointRange': ( + [], + [ + ('from_value', (1, 'float64')), + ('to_value', (1, 'float64')), + ('step', (1, 'float64')), + ], + ), + 'rcl_interfaces/msg/IntegerRange': ( + [], + [ + ('from_value', (1, 'int64')), + ('to_value', (1, 'int64')), + ('step', (1, 'uint64')), + ], + ), + 'rcl_interfaces/msg/ListParametersResult': ( + [], + [ + ('names', (4, ((1, 'string'), None))), + ('prefixes', (4, ((1, 'string'), None))), + ], + ), + 'rcl_interfaces/msg/Log': ( + [ + ('DEBUG', 'uint8', 10), + ('INFO', 'uint8', 20), + ('WARN', 'uint8', 30), + ('ERROR', 'uint8', 40), + ('FATAL', 'uint8', 50), + ], + [ + ('stamp', (2, 'builtin_interfaces/msg/Time')), + ('level', (1, 'uint8')), + ('name', (1, 'string')), + ('msg', (1, 'string')), + ('file', (1, 'string')), + ('function', (1, 'string')), + ('line', (1, 'uint32')), + ], + ), + 'rcl_interfaces/msg/Parameter': ( + [], + [ + ('name', (1, 'string')), + ('value', (2, 'rcl_interfaces/msg/ParameterValue')), + ], + ), + 'rcl_interfaces/msg/ParameterDescriptor': ( + [], + [ + ('name', (1, 'string')), + ('type', (1, 'uint8')), + ('description', (1, 'string')), + ('additional_constraints', (1, 'string')), + ('read_only', (1, 'bool')), + ('floating_point_range', (4, ((2, 'rcl_interfaces/msg/FloatingPointRange'), None))), + ('integer_range', (4, ((2, 'rcl_interfaces/msg/IntegerRange'), None))), + ], + ), + 'rcl_interfaces/msg/ParameterEvent': ( + [], + [ + ('stamp', (2, 'builtin_interfaces/msg/Time')), + ('node', (1, 'string')), + ('new_parameters', (4, ((2, 'rcl_interfaces/msg/Parameter'), None))), + ('changed_parameters', (4, ((2, 'rcl_interfaces/msg/Parameter'), None))), + ('deleted_parameters', (4, ((2, 'rcl_interfaces/msg/Parameter'), None))), + ], + ), + 'rcl_interfaces/msg/ParameterEventDescriptors': ( + [], + [ + ('new_parameters', (4, ((2, 'rcl_interfaces/msg/ParameterDescriptor'), None))), + ('changed_parameters', (4, ((2, 'rcl_interfaces/msg/ParameterDescriptor'), None))), + ('deleted_parameters', (4, ((2, 'rcl_interfaces/msg/ParameterDescriptor'), None))), + ], + ), + 'rcl_interfaces/msg/ParameterType': ( + [ + ('PARAMETER_NOT_SET', 'uint8', 0), + ('PARAMETER_BOOL', 'uint8', 1), + ('PARAMETER_INTEGER', 'uint8', 2), + ('PARAMETER_DOUBLE', 'uint8', 3), + ('PARAMETER_STRING', 'uint8', 4), + ('PARAMETER_BYTE_ARRAY', 'uint8', 5), + ('PARAMETER_BOOL_ARRAY', 'uint8', 6), + ('PARAMETER_INTEGER_ARRAY', 'uint8', 7), + ('PARAMETER_DOUBLE_ARRAY', 'uint8', 8), + ('PARAMETER_STRING_ARRAY', 'uint8', 9), + ], + [ + ('structure_needs_at_least_one_member', (1, 'uint8')), + ], + ), + 'rcl_interfaces/msg/ParameterValue': ( + [], + [ + ('type', (1, 'uint8')), + ('bool_value', (1, 'bool')), + ('integer_value', (1, 'int64')), + ('double_value', (1, 'float64')), + ('string_value', (1, 'string')), + ('byte_array_value', (4, ((1, 'uint8'), None))), + ('bool_array_value', (4, ((1, 'bool'), None))), + ('integer_array_value', (4, ((1, 'int64'), None))), + ('double_array_value', (4, ((1, 'float64'), None))), + ('string_array_value', (4, ((1, 'string'), None))), + ], + ), + 'rcl_interfaces/msg/SetParametersResult': ( + [], + [ + ('successful', (1, 'bool')), + ('reason', (1, 'string')), + ], + ), + 'rmw_dds_common/msg/Gid': ( + [], + [ + ('data', (3, ((1, 'uint8'), 24))), + ], + ), + 'rmw_dds_common/msg/NodeEntitiesInfo': ( + [], + [ + ('node_namespace', (1, 'string')), + ('node_name', (1, 'string')), + ('reader_gid_seq', (4, ((2, 'rmw_dds_common/msg/Gid'), None))), + ('writer_gid_seq', (4, ((2, 'rmw_dds_common/msg/Gid'), None))), + ], + ), + 'rmw_dds_common/msg/ParticipantEntitiesInfo': ( + [], + [ + ('gid', (2, 'rmw_dds_common/msg/Gid')), + ('node_entities_info_seq', (4, ((2, 'rmw_dds_common/msg/NodeEntitiesInfo'), None))), + ], + ), + 'rosgraph_msgs/msg/Clock': ( + [], + [ + ('clock', (2, 'builtin_interfaces/msg/Time')), + ], + ), + 'sensor_msgs/msg/BatteryState': ( + [ + ('POWER_SUPPLY_STATUS_UNKNOWN', 'uint8', 0), + ('POWER_SUPPLY_STATUS_CHARGING', 'uint8', 1), + ('POWER_SUPPLY_STATUS_DISCHARGING', 'uint8', 2), + ('POWER_SUPPLY_STATUS_NOT_CHARGING', 'uint8', 3), + ('POWER_SUPPLY_STATUS_FULL', 'uint8', 4), + ('POWER_SUPPLY_HEALTH_UNKNOWN', 'uint8', 0), + ('POWER_SUPPLY_HEALTH_GOOD', 'uint8', 1), + ('POWER_SUPPLY_HEALTH_OVERHEAT', 'uint8', 2), + ('POWER_SUPPLY_HEALTH_DEAD', 'uint8', 3), + ('POWER_SUPPLY_HEALTH_OVERVOLTAGE', 'uint8', 4), + ('POWER_SUPPLY_HEALTH_UNSPEC_FAILURE', 'uint8', 5), + ('POWER_SUPPLY_HEALTH_COLD', 'uint8', 6), + ('POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE', 'uint8', 7), + ('POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE', 'uint8', 8), + ('POWER_SUPPLY_TECHNOLOGY_UNKNOWN', 'uint8', 0), + ('POWER_SUPPLY_TECHNOLOGY_NIMH', 'uint8', 1), + ('POWER_SUPPLY_TECHNOLOGY_LION', 'uint8', 2), + ('POWER_SUPPLY_TECHNOLOGY_LIPO', 'uint8', 3), + ('POWER_SUPPLY_TECHNOLOGY_LIFE', 'uint8', 4), + ('POWER_SUPPLY_TECHNOLOGY_NICD', 'uint8', 5), + ('POWER_SUPPLY_TECHNOLOGY_LIMN', 'uint8', 6), + ], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('voltage', (1, 'float32')), + ('temperature', (1, 'float32')), + ('current', (1, 'float32')), + ('charge', (1, 'float32')), + ('capacity', (1, 'float32')), + ('design_capacity', (1, 'float32')), + ('percentage', (1, 'float32')), + ('power_supply_status', (1, 'uint8')), + ('power_supply_health', (1, 'uint8')), + ('power_supply_technology', (1, 'uint8')), + ('present', (1, 'bool')), + ('cell_voltage', (4, ((1, 'float32'), None))), + ('cell_temperature', (4, ((1, 'float32'), None))), + ('location', (1, 'string')), + ('serial_number', (1, 'string')), + ], + ), + 'sensor_msgs/msg/CameraInfo': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('height', (1, 'uint32')), + ('width', (1, 'uint32')), + ('distortion_model', (1, 'string')), + ('d', (4, ((1, 'float64'), None))), + ('k', (3, ((1, 'float64'), 9))), + ('r', (3, ((1, 'float64'), 9))), + ('p', (3, ((1, 'float64'), 12))), + ('binning_x', (1, 'uint32')), + ('binning_y', (1, 'uint32')), + ('roi', (2, 'sensor_msgs/msg/RegionOfInterest')), + ], + ), + 'sensor_msgs/msg/ChannelFloat32': ( + [], + [ + ('name', (1, 'string')), + ('values', (4, ((1, 'float32'), None))), + ], + ), + 'sensor_msgs/msg/CompressedImage': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('format', (1, 'string')), + ('data', (4, ((1, 'uint8'), None))), + ], + ), + 'sensor_msgs/msg/FluidPressure': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('fluid_pressure', (1, 'float64')), + ('variance', (1, 'float64')), + ], + ), + 'sensor_msgs/msg/Illuminance': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('illuminance', (1, 'float64')), + ('variance', (1, 'float64')), + ], + ), + 'sensor_msgs/msg/Image': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('height', (1, 'uint32')), + ('width', (1, 'uint32')), + ('encoding', (1, 'string')), + ('is_bigendian', (1, 'uint8')), + ('step', (1, 'uint32')), + ('data', (4, ((1, 'uint8'), None))), + ], + ), + 'sensor_msgs/msg/Imu': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('orientation', (2, 'geometry_msgs/msg/Quaternion')), + ('orientation_covariance', (3, ((1, 'float64'), 9))), + ('angular_velocity', (2, 'geometry_msgs/msg/Vector3')), + ('angular_velocity_covariance', (3, ((1, 'float64'), 9))), + ('linear_acceleration', (2, 'geometry_msgs/msg/Vector3')), + ('linear_acceleration_covariance', (3, ((1, 'float64'), 9))), + ], + ), + 'sensor_msgs/msg/JointState': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('name', (4, ((1, 'string'), None))), + ('position', (4, ((1, 'float64'), None))), + ('velocity', (4, ((1, 'float64'), None))), + ('effort', (4, ((1, 'float64'), None))), + ], + ), + 'sensor_msgs/msg/Joy': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('axes', (4, ((1, 'float32'), None))), + ('buttons', (4, ((1, 'int32'), None))), + ], + ), + 'sensor_msgs/msg/JoyFeedback': ( + [ + ('TYPE_LED', 'uint8', 0), + ('TYPE_RUMBLE', 'uint8', 1), + ('TYPE_BUZZER', 'uint8', 2), + ], + [ + ('type', (1, 'uint8')), + ('id', (1, 'uint8')), + ('intensity', (1, 'float32')), + ], + ), + 'sensor_msgs/msg/JoyFeedbackArray': ( + [], + [ + ('array', (4, ((2, 'sensor_msgs/msg/JoyFeedback'), None))), + ], + ), + 'sensor_msgs/msg/LaserEcho': ( + [], + [ + ('echoes', (4, ((1, 'float32'), None))), + ], + ), + 'sensor_msgs/msg/LaserScan': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('angle_min', (1, 'float32')), + ('angle_max', (1, 'float32')), + ('angle_increment', (1, 'float32')), + ('time_increment', (1, 'float32')), + ('scan_time', (1, 'float32')), + ('range_min', (1, 'float32')), + ('range_max', (1, 'float32')), + ('ranges', (4, ((1, 'float32'), None))), + ('intensities', (4, ((1, 'float32'), None))), + ], + ), + 'sensor_msgs/msg/MagneticField': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('magnetic_field', (2, 'geometry_msgs/msg/Vector3')), + ('magnetic_field_covariance', (3, ((1, 'float64'), 9))), + ], + ), + 'sensor_msgs/msg/MultiDOFJointState': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('joint_names', (4, ((1, 'string'), None))), + ('transforms', (4, ((2, 'geometry_msgs/msg/Transform'), None))), + ('twist', (4, ((2, 'geometry_msgs/msg/Twist'), None))), + ('wrench', (4, ((2, 'geometry_msgs/msg/Wrench'), None))), + ], + ), + 'sensor_msgs/msg/MultiEchoLaserScan': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('angle_min', (1, 'float32')), + ('angle_max', (1, 'float32')), + ('angle_increment', (1, 'float32')), + ('time_increment', (1, 'float32')), + ('scan_time', (1, 'float32')), + ('range_min', (1, 'float32')), + ('range_max', (1, 'float32')), + ('ranges', (4, ((2, 'sensor_msgs/msg/LaserEcho'), None))), + ('intensities', (4, ((2, 'sensor_msgs/msg/LaserEcho'), None))), + ], + ), + 'sensor_msgs/msg/NavSatFix': ( + [ + ('COVARIANCE_TYPE_UNKNOWN', 'uint8', 0), + ('COVARIANCE_TYPE_APPROXIMATED', 'uint8', 1), + ('COVARIANCE_TYPE_DIAGONAL_KNOWN', 'uint8', 2), + ('COVARIANCE_TYPE_KNOWN', 'uint8', 3), + ], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('status', (2, 'sensor_msgs/msg/NavSatStatus')), + ('latitude', (1, 'float64')), + ('longitude', (1, 'float64')), + ('altitude', (1, 'float64')), + ('position_covariance', (3, ((1, 'float64'), 9))), + ('position_covariance_type', (1, 'uint8')), + ], + ), + 'sensor_msgs/msg/NavSatStatus': ( + [ + ('STATUS_NO_FIX', 'int8', -1), + ('STATUS_FIX', 'int8', 0), + ('STATUS_SBAS_FIX', 'int8', 1), + ('STATUS_GBAS_FIX', 'int8', 2), + ('SERVICE_GPS', 'uint16', 1), + ('SERVICE_GLONASS', 'uint16', 2), + ('SERVICE_COMPASS', 'uint16', 4), + ('SERVICE_GALILEO', 'uint16', 8), + ], + [ + ('status', (1, 'int8')), + ('service', (1, 'uint16')), + ], + ), + 'sensor_msgs/msg/PointCloud': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('points', (4, ((2, 'geometry_msgs/msg/Point32'), None))), + ('channels', (4, ((2, 'sensor_msgs/msg/ChannelFloat32'), None))), + ], + ), + 'sensor_msgs/msg/PointCloud2': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('height', (1, 'uint32')), + ('width', (1, 'uint32')), + ('fields', (4, ((2, 'sensor_msgs/msg/PointField'), None))), + ('is_bigendian', (1, 'bool')), + ('point_step', (1, 'uint32')), + ('row_step', (1, 'uint32')), + ('data', (4, ((1, 'uint8'), None))), + ('is_dense', (1, 'bool')), + ], + ), + 'sensor_msgs/msg/PointField': ( + [ + ('INT8', 'uint8', 1), + ('UINT8', 'uint8', 2), + ('INT16', 'uint8', 3), + ('UINT16', 'uint8', 4), + ('INT32', 'uint8', 5), + ('UINT32', 'uint8', 6), + ('FLOAT32', 'uint8', 7), + ('FLOAT64', 'uint8', 8), + ], + [ + ('name', (1, 'string')), + ('offset', (1, 'uint32')), + ('datatype', (1, 'uint8')), + ('count', (1, 'uint32')), + ], + ), + 'sensor_msgs/msg/Range': ( + [ + ('ULTRASOUND', 'uint8', 0), + ('INFRARED', 'uint8', 1), + ], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('radiation_type', (1, 'uint8')), + ('field_of_view', (1, 'float32')), + ('min_range', (1, 'float32')), + ('max_range', (1, 'float32')), + ('range', (1, 'float32')), + ], + ), + 'sensor_msgs/msg/RegionOfInterest': ( + [], + [ + ('x_offset', (1, 'uint32')), + ('y_offset', (1, 'uint32')), + ('height', (1, 'uint32')), + ('width', (1, 'uint32')), + ('do_rectify', (1, 'bool')), + ], + ), + 'sensor_msgs/msg/RelativeHumidity': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('relative_humidity', (1, 'float64')), + ('variance', (1, 'float64')), + ], + ), + 'sensor_msgs/msg/Temperature': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('temperature', (1, 'float64')), + ('variance', (1, 'float64')), + ], + ), + 'sensor_msgs/msg/TimeReference': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('time_ref', (2, 'builtin_interfaces/msg/Time')), + ('source', (1, 'string')), + ], + ), + 'shape_msgs/msg/Mesh': ( + [], + [ + ('triangles', (4, ((2, 'shape_msgs/msg/MeshTriangle'), None))), + ('vertices', (4, ((2, 'geometry_msgs/msg/Point'), None))), + ], + ), + 'shape_msgs/msg/MeshTriangle': ( + [], + [ + ('vertex_indices', (3, ((1, 'uint32'), 3))), + ], + ), + 'shape_msgs/msg/Plane': ( + [], + [ + ('coef', (3, ((1, 'float64'), 4))), + ], + ), + 'shape_msgs/msg/SolidPrimitive': ( + [ + ('BOX', 'uint8', 1), + ('SPHERE', 'uint8', 2), + ('CYLINDER', 'uint8', 3), + ('CONE', 'uint8', 4), + ('BOX_X', 'uint8', 0), + ('BOX_Y', 'uint8', 1), + ('BOX_Z', 'uint8', 2), + ('SPHERE_RADIUS', 'uint8', 0), + ('CYLINDER_HEIGHT', 'uint8', 0), + ('CYLINDER_RADIUS', 'uint8', 1), + ('CONE_HEIGHT', 'uint8', 0), + ('CONE_RADIUS', 'uint8', 1), + ], + [ + ('type', (1, 'uint8')), + ('dimensions', (4, ((1, 'float64'), None))), + ], + ), + 'statistics_msgs/msg/MetricsMessage': ( + [], + [ + ('measurement_source_name', (1, 'string')), + ('metrics_source', (1, 'string')), + ('unit', (1, 'string')), + ('window_start', (2, 'builtin_interfaces/msg/Time')), + ('window_stop', (2, 'builtin_interfaces/msg/Time')), + ('statistics', (4, ((2, 'statistics_msgs/msg/StatisticDataPoint'), None))), + ], + ), + 'statistics_msgs/msg/StatisticDataPoint': ( + [], + [ + ('data_type', (1, 'uint8')), + ('data', (1, 'float64')), + ], + ), + 'statistics_msgs/msg/StatisticDataType': ( + [ + ('STATISTICS_DATA_TYPE_UNINITIALIZED', 'uint8', 0), + ('STATISTICS_DATA_TYPE_AVERAGE', 'uint8', 1), + ('STATISTICS_DATA_TYPE_MINIMUM', 'uint8', 2), + ('STATISTICS_DATA_TYPE_MAXIMUM', 'uint8', 3), + ('STATISTICS_DATA_TYPE_STDDEV', 'uint8', 4), + ('STATISTICS_DATA_TYPE_SAMPLE_COUNT', 'uint8', 5), + ], + [ + ('structure_needs_at_least_one_member', (1, 'uint8')), + ], + ), + 'std_msgs/msg/Bool': ( + [], + [ + ('data', (1, 'bool')), + ], + ), + 'std_msgs/msg/Byte': ( + [], + [ + ('data', (1, 'uint8')), + ], + ), + 'std_msgs/msg/ByteMultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint8'), None))), + ], + ), + 'std_msgs/msg/Char': ( + [], + [ + ('data', (1, 'uint8')), + ], + ), + 'std_msgs/msg/ColorRGBA': ( + [], + [ + ('r', (1, 'float32')), + ('g', (1, 'float32')), + ('b', (1, 'float32')), + ('a', (1, 'float32')), + ], + ), + 'std_msgs/msg/Empty': ( + [], + [ + ('structure_needs_at_least_one_member', (1, 'uint8')), + ], + ), + 'std_msgs/msg/Float32': ( + [], + [ + ('data', (1, 'float32')), + ], + ), + 'std_msgs/msg/Float32MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'float32'), None))), + ], + ), + 'std_msgs/msg/Float64': ( + [], + [ + ('data', (1, 'float64')), + ], + ), + 'std_msgs/msg/Float64MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'float64'), None))), + ], + ), + 'std_msgs/msg/Header': ( + [], + [ + ('stamp', (2, 'builtin_interfaces/msg/Time')), + ('frame_id', (1, 'string')), + ], + ), + 'std_msgs/msg/Int16': ( + [], + [ + ('data', (1, 'int16')), + ], + ), + 'std_msgs/msg/Int16MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'int16'), None))), + ], + ), + 'std_msgs/msg/Int32': ( + [], + [ + ('data', (1, 'int32')), + ], + ), + 'std_msgs/msg/Int32MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'int32'), None))), + ], + ), + 'std_msgs/msg/Int64': ( + [], + [ + ('data', (1, 'int64')), + ], + ), + 'std_msgs/msg/Int64MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'int64'), None))), + ], + ), + 'std_msgs/msg/Int8': ( + [], + [ + ('data', (1, 'int8')), + ], + ), + 'std_msgs/msg/Int8MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'int8'), None))), + ], + ), + 'std_msgs/msg/MultiArrayDimension': ( + [], + [ + ('label', (1, 'string')), + ('size', (1, 'uint32')), + ('stride', (1, 'uint32')), + ], + ), + 'std_msgs/msg/MultiArrayLayout': ( + [], + [ + ('dim', (4, ((2, 'std_msgs/msg/MultiArrayDimension'), None))), + ('data_offset', (1, 'uint32')), + ], + ), + 'std_msgs/msg/String': ( + [], + [ + ('data', (1, 'string')), + ], + ), + 'std_msgs/msg/UInt16': ( + [], + [ + ('data', (1, 'uint16')), + ], + ), + 'std_msgs/msg/UInt16MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint16'), None))), + ], + ), + 'std_msgs/msg/UInt32': ( + [], + [ + ('data', (1, 'uint32')), + ], + ), + 'std_msgs/msg/UInt32MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint32'), None))), + ], + ), + 'std_msgs/msg/UInt64': ( + [], + [ + ('data', (1, 'uint64')), + ], + ), + 'std_msgs/msg/UInt64MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint64'), None))), + ], + ), + 'std_msgs/msg/UInt8': ( + [], + [ + ('data', (1, 'uint8')), + ], + ), + 'std_msgs/msg/UInt8MultiArray': ( + [], + [ + ('layout', (2, 'std_msgs/msg/MultiArrayLayout')), + ('data', (4, ((1, 'uint8'), None))), + ], + ), + 'stereo_msgs/msg/DisparityImage': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('image', (2, 'sensor_msgs/msg/Image')), + ('f', (1, 'float32')), + ('t', (1, 'float32')), + ('valid_window', (2, 'sensor_msgs/msg/RegionOfInterest')), + ('min_disparity', (1, 'float32')), + ('max_disparity', (1, 'float32')), + ('delta_d', (1, 'float32')), + ], + ), + 'tf2_msgs/msg/TF2Error': ( + [ + ('NO_ERROR', 'uint8', 0), + ('LOOKUP_ERROR', 'uint8', 1), + ('CONNECTIVITY_ERROR', 'uint8', 2), + ('EXTRAPOLATION_ERROR', 'uint8', 3), + ('INVALID_ARGUMENT_ERROR', 'uint8', 4), + ('TIMEOUT_ERROR', 'uint8', 5), + ('TRANSFORM_ERROR', 'uint8', 6), + ], + [ + ('error', (1, 'uint8')), + ('error_string', (1, 'string')), + ], + ), + 'tf2_msgs/msg/TFMessage': ( + [], + [ + ('transforms', (4, ((2, 'geometry_msgs/msg/TransformStamped'), None))), + ], + ), + 'trajectory_msgs/msg/JointTrajectory': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('joint_names', (4, ((1, 'string'), None))), + ('points', (4, ((2, 'trajectory_msgs/msg/JointTrajectoryPoint'), None))), + ], + ), + 'trajectory_msgs/msg/JointTrajectoryPoint': ( + [], + [ + ('positions', (4, ((1, 'float64'), None))), + ('velocities', (4, ((1, 'float64'), None))), + ('accelerations', (4, ((1, 'float64'), None))), + ('effort', (4, ((1, 'float64'), None))), + ('time_from_start', (2, 'builtin_interfaces/msg/Duration')), + ], + ), + 'trajectory_msgs/msg/MultiDOFJointTrajectory': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('joint_names', (4, ((1, 'string'), None))), + ('points', (4, ((2, 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint'), None))), + ], + ), + 'trajectory_msgs/msg/MultiDOFJointTrajectoryPoint': ( + [], + [ + ('transforms', (4, ((2, 'geometry_msgs/msg/Transform'), None))), + ('velocities', (4, ((2, 'geometry_msgs/msg/Twist'), None))), + ('accelerations', (4, ((2, 'geometry_msgs/msg/Twist'), None))), + ('time_from_start', (2, 'builtin_interfaces/msg/Duration')), + ], + ), + 'unique_identifier_msgs/msg/UUID': ( + [], + [ + ('uuid', (3, ((1, 'uint8'), 16))), + ], + ), + 'visualization_msgs/msg/ImageMarker': ( + [ + ('CIRCLE', 'int32', 0), + ('LINE_STRIP', 'int32', 1), + ('LINE_LIST', 'int32', 2), + ('POLYGON', 'int32', 3), + ('POINTS', 'int32', 4), + ('ADD', 'int32', 0), + ('REMOVE', 'int32', 1), + ], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('ns', (1, 'string')), + ('id', (1, 'int32')), + ('type', (1, 'int32')), + ('action', (1, 'int32')), + ('position', (2, 'geometry_msgs/msg/Point')), + ('scale', (1, 'float32')), + ('outline_color', (2, 'std_msgs/msg/ColorRGBA')), + ('filled', (1, 'uint8')), + ('fill_color', (2, 'std_msgs/msg/ColorRGBA')), + ('lifetime', (2, 'builtin_interfaces/msg/Duration')), + ('points', (4, ((2, 'geometry_msgs/msg/Point'), None))), + ('outline_colors', (4, ((2, 'std_msgs/msg/ColorRGBA'), None))), + ], + ), + 'visualization_msgs/msg/InteractiveMarker': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('name', (1, 'string')), + ('description', (1, 'string')), + ('scale', (1, 'float32')), + ('menu_entries', (4, ((2, 'visualization_msgs/msg/MenuEntry'), None))), + ('controls', (4, ((2, 'visualization_msgs/msg/InteractiveMarkerControl'), None))), + ], + ), + 'visualization_msgs/msg/InteractiveMarkerControl': ( + [ + ('INHERIT', 'uint8', 0), + ('FIXED', 'uint8', 1), + ('VIEW_FACING', 'uint8', 2), + ('NONE', 'uint8', 0), + ('MENU', 'uint8', 1), + ('BUTTON', 'uint8', 2), + ('MOVE_AXIS', 'uint8', 3), + ('MOVE_PLANE', 'uint8', 4), + ('ROTATE_AXIS', 'uint8', 5), + ('MOVE_ROTATE', 'uint8', 6), + ('MOVE_3D', 'uint8', 7), + ('ROTATE_3D', 'uint8', 8), + ('MOVE_ROTATE_3D', 'uint8', 9), + ], + [ + ('name', (1, 'string')), + ('orientation', (2, 'geometry_msgs/msg/Quaternion')), + ('orientation_mode', (1, 'uint8')), + ('interaction_mode', (1, 'uint8')), + ('always_visible', (1, 'bool')), + ('markers', (4, ((2, 'visualization_msgs/msg/Marker'), None))), + ('independent_marker_orientation', (1, 'bool')), + ('description', (1, 'string')), + ], + ), + 'visualization_msgs/msg/InteractiveMarkerFeedback': ( + [ + ('KEEP_ALIVE', 'uint8', 0), + ('POSE_UPDATE', 'uint8', 1), + ('MENU_SELECT', 'uint8', 2), + ('BUTTON_CLICK', 'uint8', 3), + ('MOUSE_DOWN', 'uint8', 4), + ('MOUSE_UP', 'uint8', 5), + ], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('client_id', (1, 'string')), + ('marker_name', (1, 'string')), + ('control_name', (1, 'string')), + ('event_type', (1, 'uint8')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('menu_entry_id', (1, 'uint32')), + ('mouse_point', (2, 'geometry_msgs/msg/Point')), + ('mouse_point_valid', (1, 'bool')), + ], + ), + 'visualization_msgs/msg/InteractiveMarkerInit': ( + [], + [ + ('server_id', (1, 'string')), + ('seq_num', (1, 'uint64')), + ('markers', (4, ((2, 'visualization_msgs/msg/InteractiveMarker'), None))), + ], + ), + 'visualization_msgs/msg/InteractiveMarkerPose': ( + [], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('name', (1, 'string')), + ], + ), + 'visualization_msgs/msg/InteractiveMarkerUpdate': ( + [ + ('KEEP_ALIVE', 'uint8', 0), + ('UPDATE', 'uint8', 1), + ], + [ + ('server_id', (1, 'string')), + ('seq_num', (1, 'uint64')), + ('type', (1, 'uint8')), + ('markers', (4, ((2, 'visualization_msgs/msg/InteractiveMarker'), None))), + ('poses', (4, ((2, 'visualization_msgs/msg/InteractiveMarkerPose'), None))), + ('erases', (4, ((1, 'string'), None))), + ], + ), + 'visualization_msgs/msg/Marker': ( + [ + ('ARROW', 'int32', 0), + ('CUBE', 'int32', 1), + ('SPHERE', 'int32', 2), + ('CYLINDER', 'int32', 3), + ('LINE_STRIP', 'int32', 4), + ('LINE_LIST', 'int32', 5), + ('CUBE_LIST', 'int32', 6), + ('SPHERE_LIST', 'int32', 7), + ('POINTS', 'int32', 8), + ('TEXT_VIEW_FACING', 'int32', 9), + ('MESH_RESOURCE', 'int32', 10), + ('TRIANGLE_LIST', 'int32', 11), + ('ADD', 'int32', 0), + ('MODIFY', 'int32', 0), + ('DELETE', 'int32', 2), + ('DELETEALL', 'int32', 3), + ], + [ + ('header', (2, 'std_msgs/msg/Header')), + ('ns', (1, 'string')), + ('id', (1, 'int32')), + ('type', (1, 'int32')), + ('action', (1, 'int32')), + ('pose', (2, 'geometry_msgs/msg/Pose')), + ('scale', (2, 'geometry_msgs/msg/Vector3')), + ('color', (2, 'std_msgs/msg/ColorRGBA')), + ('lifetime', (2, 'builtin_interfaces/msg/Duration')), + ('frame_locked', (1, 'bool')), + ('points', (4, ((2, 'geometry_msgs/msg/Point'), None))), + ('colors', (4, ((2, 'std_msgs/msg/ColorRGBA'), None))), + ('text', (1, 'string')), + ('mesh_resource', (1, 'string')), + ('mesh_use_embedded_materials', (1, 'bool')), + ], + ), + 'visualization_msgs/msg/MarkerArray': ( + [], + [ + ('markers', (4, ((2, 'visualization_msgs/msg/Marker'), None))), + ], + ), + 'visualization_msgs/msg/MenuEntry': ( + [ + ('FEEDBACK', 'uint8', 0), + ('ROSRUN', 'uint8', 1), + ('ROSLAUNCH', 'uint8', 2), + ], + [ + ('id', (1, 'uint32')), + ('parent_id', (1, 'uint32')), + ('title', (1, 'string')), + ('command', (1, 'string')), + ('command_type', (1, 'uint8')), + ], + ), } From c6dd2995f658919f5208be6a22adc383fe45bdb4 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 4 Oct 2021 22:56:58 +0200 Subject: [PATCH 049/114] Use repr on const field values --- src/rosbags/typesys/register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index bbe11d03..2b289932 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -88,7 +88,7 @@ def generate_python_code(typs: Typesdict) -> str: '', *[f' {fname}: {get_typehint(desc)}' for fname, desc in fields], *[ - f' {fname}: ClassVar[{get_typehint((1, ftype))}] = {fvalue}' + f' {fname}: ClassVar[{get_typehint((1, ftype))}] = {fvalue!r}' for fname, ftype, fvalue in consts ], f' __msgtype__: ClassVar[str] = {name!r}', From c55e81f375fd970b6a322b21a51700bc453639a7 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 21 Oct 2021 19:13:33 +0200 Subject: [PATCH 050/114] Report faulty index on connection or chunk error --- src/rosbags/rosbag1/reader.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 31e3e4a0..afe899e9 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -365,7 +365,7 @@ class Reader: self.current_chunk = (-1, BytesIO()) self.topics: dict[str, TopicInfo] = {} - def open(self): # pylint: disable=too-many-branches,too-many-locals + def open(self): # pylint: disable=too-many-branches,too-many-locals,too-many-statements """Open rosbag and read metadata.""" try: self.bio = self.path.open('rb') @@ -402,8 +402,12 @@ class Reader: raise ReaderError('Bag is not indexed, reindex before reading.') self.bio.seek(index_pos) - self.connections = dict(self.read_connection() for _ in range(conn_count)) - self.chunk_infos = [self.read_chunk_info() for _ in range(chunk_count)] + try: + self.connections = dict(self.read_connection() for _ in range(conn_count)) + self.chunk_infos = [self.read_chunk_info() for _ in range(chunk_count)] + except ReaderError as err: + raise ReaderError(f'Bag index looks damaged: {err.args}') from None + self.chunks = {} for chunk_info in self.chunk_infos: self.bio.seek(chunk_info.pos) From af848eb3d219414abf9129d341d355d10cdaa376 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 8 Nov 2021 15:11:39 +0100 Subject: [PATCH 051/114] Parse empty msg definitions --- src/rosbags/typesys/msg.py | 2 +- tests/test_parse.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 9eda4e46..d47626db 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -30,7 +30,7 @@ specification = msgdef (msgsep msgdef)* msgdef - = r'MSG:\s' scoped_name definition+ + = r'MSG:\s' scoped_name definition* msgsep = r'================================================================================' diff --git a/tests/test_parse.py b/tests/test_parse.py index 00783d91..59083b3e 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -118,10 +118,16 @@ module test_msgs { """ +def test_parse_empty_msg(): + """Test msg parser with empty message.""" + ret = get_types_from_msg('', 'std_msgs/msg/Empty') + assert ret == {'std_msgs/msg/Empty': ([], [])} + + def test_parse_msg(): """Test msg parser.""" with pytest.raises(TypesysError, match='Could not parse'): - get_types_from_msg('', 'test_msgs/msg/Foo') + get_types_from_msg('invalid', 'test_msgs/msg/Foo') ret = get_types_from_msg(MSG, 'test_msgs/msg/Foo') assert 'test_msgs/msg/Foo' in ret consts, fields = ret['test_msgs/msg/Foo'] From ae13edd2214fc8adc4aa255030a84be81703719f Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 8 Nov 2021 18:48:19 +0100 Subject: [PATCH 052/114] Make packages PEP561 compliant --- setup.cfg | 3 +++ src/rosbags/convert/py.typed | 0 src/rosbags/rosbag1/py.typed | 0 src/rosbags/rosbag2/py.typed | 0 src/rosbags/serde/py.typed | 0 src/rosbags/typesys/py.typed | 0 6 files changed, 3 insertions(+) create mode 100644 src/rosbags/convert/py.typed create mode 100644 src/rosbags/rosbag1/py.typed create mode 100644 src/rosbags/rosbag2/py.typed create mode 100644 src/rosbags/serde/py.typed create mode 100644 src/rosbags/typesys/py.typed diff --git a/setup.cfg b/setup.cfg index 43a56f13..b3f6caa2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -92,6 +92,9 @@ dev = [options.packages.find] where = src +[options.package_data] +* = py.typed + [sdist] formats = gztar, zip diff --git a/src/rosbags/convert/py.typed b/src/rosbags/convert/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/src/rosbags/rosbag1/py.typed b/src/rosbags/rosbag1/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/src/rosbags/rosbag2/py.typed b/src/rosbags/rosbag2/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/src/rosbags/serde/py.typed b/src/rosbags/serde/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/src/rosbags/typesys/py.typed b/src/rosbags/typesys/py.typed new file mode 100644 index 00000000..e69de29b From aaa996985699ab95888f99e6873a181893d7e539 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Tue, 9 Nov 2021 10:40:25 +0100 Subject: [PATCH 053/114] Parse msg bounded fields and default values --- docs/topics/typesys.rst | 4 ++ src/rosbags/typesys/msg.py | 89 ++++++++++++++++++++++++++++++++++---- tests/test_parse.py | 64 +++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 8 deletions(-) diff --git a/docs/topics/typesys.rst b/docs/topics/typesys.rst index acb98f8c..26c19274 100644 --- a/docs/topics/typesys.rst +++ b/docs/topics/typesys.rst @@ -9,6 +9,10 @@ Message instances ----------------- The type system generates a dataclass for each message type. These dataclasses give direct read write access to all mutable fields of a message. Fields should be mutated with care as no type checking is applied during runtime. +.. note:: + + Limitation: While the type system parses message definitions with array bounds and/or default values, neither bounds nor default values are enforced or assigned to message instances. + Extending the type system ------------------------- Adding custom message types consists of two steps. First, message definitions are converted into parse trees using :py:func:`get_types_from_idl() ` or :py:func:`get_types_from_msg() `, and second the types are registered in the type system via :py:func:`register_types() `. The following example shows how to add messages type definitions from ``.msg`` and ``.idl`` files: diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index d47626db..71e5f20a 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -48,24 +48,28 @@ const_dcl / type_spec identifier '=' integer_literal field_dcl - = type_spec identifier + = type_spec identifier default_value? type_spec = array_type_spec + / bounded_array_type_spec / simple_type_spec array_type_spec = simple_type_spec array_size +bounded_array_type_spec + = simple_type_spec array_bounds + simple_type_spec - = scoped_name + = 'string' '<=' integer_literal + / scoped_name array_size = '[' integer_literal? ']' -integer_literal - = r'[-+]?[1-9][0-9]+' - / r'[-+]?[0-9]' +array_bounds + = '[<=' integer_literal ']' scoped_name = identifier '/' scoped_name @@ -73,6 +77,50 @@ scoped_name identifier = r'[a-zA-Z_][a-zA-Z_0-9]*' + +default_value + = literal + +literal + = boolean_literal + / float_literal + / integer_literal + / string_literal + / array_literal + +boolean_literal + = 'true' + / 'false' + +integer_literal + = hexadecimal_literal + / octal_literal + / decimal_literal + +decimal_literal + = r'[-+]?[1-9][0-9]+' + / r'[-+]?[0-9]' + +octal_literal + = r'[-+]?0[0-7]+' + +hexadecimal_literal + = r'[-+]?0[xX][a-fA-F0-9]+' + +float_literal + = r'[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?' + / r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)' + +string_literal + = '"' r'(\\"|[^"])*' '"' + / '\'' r'(\\\'|[^'])*' '\'' + +array_literal + = '[' array_elements? ']' + +array_elements + = literal ',' array_elements + / literal """ @@ -207,15 +255,20 @@ class VisitorMSG(Visitor): return Nodetype.ARRAY, (children[0], length[0]) return Nodetype.SEQUENCE, (children[0], None) + def visit_bounded_array_type_spec(self, children: Any) -> Any: + """Process bounded array type specifier.""" + return Nodetype.SEQUENCE, (children[0], None) + def visit_simple_type_spec(self, children: Any) -> Any: """Process simple type specifier.""" + typespec = children[0][1] if ('LITERAL', '<=') in children else children[1] dct = { 'time': 'builtin_interfaces/msg/Time', 'duration': 'builtin_interfaces/msg/Duration', 'byte': 'uint8', 'char': 'uint8', } - return Nodetype.NAME, dct.get(children[1], children[1]) + return Nodetype.NAME, dct.get(typespec, typespec) def visit_scoped_name(self, children: Any) -> Any: """Process scoped name.""" @@ -228,10 +281,30 @@ class VisitorMSG(Visitor): """Process identifier.""" return (Nodetype.NAME, children) - def visit_integer_literal(self, children: Any) -> Any: - """Process integer literal.""" + def visit_boolean_literal(self, children: Any) -> Any: + """Process boolean literal.""" + return children[1] == 'TRUE' + + def visit_float_literal(self, children: Any) -> Any: + """Process float literal.""" + return float(children) + + def visit_decimal_literal(self, children: Any) -> Any: + """Process decimal integer literal.""" return int(children) + def visit_octal_literal(self, children: Any) -> Any: + """Process octal integer literal.""" + return int(children, 8) + + def visit_hexadecimal_literal(self, children: Any) -> Any: + """Process hexadecimal integer literal.""" + return int(children, 16) + + def visit_string_literal(self, children: Any) -> Any: + """Process integer literal.""" + return children[1] + def get_types_from_msg(text: str, name: str) -> Typesdict: """Get type from msg message definition. diff --git a/tests/test_parse.py b/tests/test_parse.py index 59083b3e..6112ef6f 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -29,6 +29,30 @@ float64[] seq2 float64[4] array """ +MSG_BOUNDS = """ +int32[] unbounded_integer_array +int32[5] five_integers_array +int32[<=5] up_to_five_integers_array + +string string_of_unbounded_size +string<=10 up_to_ten_characters_string + +string[<=5] up_to_five_unbounded_strings +string<=10[] unbounded_array_of_string_up_to_ten_characters_each +string<=10[<=5] up_to_five_strings_up_to_ten_characters_each +""" + +MSG_DEFAULTS = """ +bool b false +uint8 i 42 +uint8 o 0377 +uint8 h 0xff +float32 y -314.15e-2 +string name1 "John" +string name2 'Ringo' +int32[] samples [-200, -100, 0, 100, 200] +""" + MULTI_MSG = """ std_msgs/Header header byte b @@ -124,6 +148,46 @@ def test_parse_empty_msg(): assert ret == {'std_msgs/msg/Empty': ([], [])} +def test_parse_bounds_msg(): + """Test msg parser.""" + ret = get_types_from_msg(MSG_BOUNDS, 'test_msgs/msg/Foo') + assert ret == { + 'test_msgs/msg/Foo': ( + [], + [ + ('unbounded_integer_array', (4, ((1, 'int32'), None))), + ('five_integers_array', (3, ((1, 'int32'), 5))), + ('up_to_five_integers_array', (4, ((1, 'int32'), None))), + ('string_of_unbounded_size', (1, 'string')), + ('up_to_ten_characters_string', (1, 'string')), + ('up_to_five_unbounded_strings', (4, ((1, 'string'), None))), + ('unbounded_array_of_string_up_to_ten_characters_each', (4, ((1, 'string'), None))), + ('up_to_five_strings_up_to_ten_characters_each', (4, ((1, 'string'), None))), + ], + ), + } + + +def test_parse_defaults_msg(): + """Test msg parser.""" + ret = get_types_from_msg(MSG_DEFAULTS, 'test_msgs/msg/Foo') + assert ret == { + 'test_msgs/msg/Foo': ( + [], + [ + ('b', (1, 'bool')), + ('i', (1, 'uint8')), + ('o', (1, 'uint8')), + ('h', (1, 'uint8')), + ('y', (1, 'float32')), + ('name1', (1, 'string')), + ('name2', (1, 'string')), + ('samples', (4, ((1, 'int32'), None))), + ], + ), + } + + def test_parse_msg(): """Test msg parser.""" with pytest.raises(TypesysError, match='Could not parse'): From ac704bd890574f39fa87668ea985fa67783f7c13 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Tue, 9 Nov 2021 12:34:39 +0100 Subject: [PATCH 054/114] Release 0.9.7 --- CHANGES.rst | 9 +++++++++ setup.cfg | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index c0eece22..acb378a9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,14 +3,23 @@ Changes ======= +0.9.7 - 2021-11-09 +------------------ +- Fix parsing of const fields with string value +- Parse empty msg definitions +- Make packages PEP561 compliant +- Parse msg bounded fields and default values + 0.9.6 - 2021-10-04 ------------------ - Do not match msg separator as constant value + 0.9.5 - 2021-10-04 ------------------ - Add string constant support to msg parser + 0.9.4 - 2021-09-15 ------------------ - Make reader1 API match reader2 diff --git a/setup.cfg b/setup.cfg index b3f6caa2..89b22ffe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.6 +version = 0.9.7 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 52480e2badfd3bb579e16b868831aef712de5314 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 25 Nov 2021 14:26:17 +0100 Subject: [PATCH 055/114] Type generics and missing return types --- setup.cfg | 65 +++++++++++++++++++++++++-------- src/rosbags/convert/__main__.py | 2 +- src/rosbags/rosbag1/reader.py | 47 +++++++++++++----------- src/rosbags/rosbag1/writer.py | 34 +++++++++-------- src/rosbags/rosbag2/reader.py | 22 ++++++----- src/rosbags/rosbag2/writer.py | 13 ++++--- src/rosbags/serde/cdr.py | 8 ++-- src/rosbags/serde/messages.py | 8 ++-- src/rosbags/serde/ros1.py | 20 +++++++--- src/rosbags/serde/typing.py | 29 +++++++++------ src/rosbags/typesys/base.py | 2 +- src/rosbags/typesys/idl.py | 4 +- src/rosbags/typesys/peg.py | 4 +- src/rosbags/typesys/register.py | 11 ++++-- tests/cdr.py | 5 ++- tests/test_convert.py | 4 +- tests/test_parse.py | 26 ++++++------- tests/test_reader.py | 8 ++-- tests/test_reader1.py | 50 +++++++++++++++++-------- tests/test_roundtrip.py | 2 +- tests/test_roundtrip1.py | 2 +- tests/test_serde.py | 25 +++++++------ tests/test_writer.py | 4 +- tests/test_writer1.py | 14 +++---- tools/bench/bench.py | 14 +++---- tools/compare/compare.py | 15 +++----- 26 files changed, 263 insertions(+), 175 deletions(-) diff --git a/setup.cfg b/setup.cfg index 89b22ffe..06a203f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,7 @@ classifiers = Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Topic :: Scientific/Engineering Typing :: Typed project_urls = @@ -109,7 +110,7 @@ avoid-escape = False docstring_convention = google docstring_style = google extend-exclude = venv*,.venv* -extend-select = +extend-select = # docstrings D204, D400, @@ -119,8 +120,10 @@ extend-select = ignore = # do not require annotation of `self` ANN101, - # allow line break before binary operator - W503, + # handled by B001 + E722, + # allow line break after binary operator + W504, max-line-length = 100 strictness = long suppress-none-returning = True @@ -134,10 +137,14 @@ multi_line_output = 3 explicit_package_bases = True mypy_path = $MYPY_CONFIG_FILE_DIR/src namespace_packages = True +strict = True -[mypy-ruamel] +[mypy-lz4.frame] ignore_missing_imports = True +[mypy-ruamel.yaml] +implicit_reexport = True + [pydocstyle] convention = google add-select = D204,D400,D401,D404,D413 @@ -146,9 +153,37 @@ add-select = D204,D400,D401,D404,D413 max-line-length = 100 [pylint.'MESSAGES CONTROL'] +enable = all disable = duplicate-code, ungrouped-imports, + # isort (pylint FAQ) + wrong-import-order, + # mccabe (pylint FAQ) + too-many-branches, + # fixme + fixme, + # pep8-naming (pylint FAQ, keep: invalid-name) + bad-classmethod-argument, + bad-mcs-classmethod-argument, + no-self-argument + # pycodestyle (pylint FAQ) + bad-indentation, + bare-except, + line-too-long, + missing-final-newline, + multiple-statements, + trailing-whitespace, + unnecessary-semicolon, + unneeded-not, + # pydocstyle (pylint FAQ) + missing-class-docstring, + missing-function-docstring, + missing-module-docstring, + # pyflakes (pylint FAQ) + undefined-variable, + unused-import, + unused-variable, [yapf] based_on_style = google @@ -159,15 +194,15 @@ indent_dictionary_value = false [tool:pytest] addopts = - -v - --flake8 - --mypy - --pylint - --yapf - --cov=src - --cov-branch - --cov-report=html - --cov-report=term - --no-cov-on-fail - --junitxml=report.xml + -v + --flake8 + --mypy + --pylint + --yapf + --cov=src + --cov-branch + --cov-report=html + --cov-report=term + --no-cov-on-fail + --junitxml=report.xml junit_family=xunit2 diff --git a/src/rosbags/convert/__main__.py b/src/rosbags/convert/__main__.py index 2ab89904..60390544 100644 --- a/src/rosbags/convert/__main__.py +++ b/src/rosbags/convert/__main__.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: from typing import Callable -def pathtype(exists: bool = True) -> Callable: +def pathtype(exists: bool = True) -> Callable[[str], Path]: """Path argument for argparse. Args: diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index afe899e9..72f10021 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -10,14 +10,15 @@ import os import re import struct from bz2 import decompress as bz2_decompress +from collections import defaultdict from enum import Enum, IntEnum from functools import reduce from io import BytesIO from itertools import groupby from pathlib import Path -from typing import TYPE_CHECKING, NamedTuple +from typing import TYPE_CHECKING, Any, Dict, NamedTuple -from lz4.frame import decompress as lz4_decompress # type: ignore +from lz4.frame import decompress as lz4_decompress from rosbags.typesys.msg import normalize_msgtype @@ -59,7 +60,7 @@ class Connection(NamedTuple): md5sum: str callerid: Optional[str] latching: Optional[int] - indexes: list + indexes: list[IndexData] class ChunkInfo(NamedTuple): @@ -76,7 +77,7 @@ class Chunk(NamedTuple): datasize: int datapos: int - decompressor: Callable + decompressor: Callable[[bytes], bytes] class TopicInfo(NamedTuple): @@ -124,9 +125,9 @@ class IndexData(NamedTuple): return self.time != other[0] -deserialize_uint8 = struct.Struct(' int: @@ -139,11 +140,12 @@ def deserialize_time(val: bytes) -> int: Deserialized value. """ - sec, nsec = struct.unpack(' int: @@ -214,7 +216,9 @@ class Header(dict): """ try: - return self[name].decode() + value = self[name] + assert isinstance(value, bytes) + return value.decode() except (KeyError, ValueError) as err: raise ReaderError(f'Could not read string field {name!r}.') from err @@ -237,7 +241,7 @@ class Header(dict): raise ReaderError(f'Could not read time field {name!r}.') from err @classmethod - def read(cls: type, src: BinaryIO, expect: Optional[RecordType] = None) -> Header: + def read(cls: Type[Header], src: BinaryIO, expect: Optional[RecordType] = None) -> Header: """Read header from file handle. Args: @@ -362,10 +366,10 @@ class Reader: self.connections: dict[int, Connection] = {} self.chunk_infos: list[ChunkInfo] = [] self.chunks: dict[int, Chunk] = {} - self.current_chunk = (-1, BytesIO()) + self.current_chunk: tuple[int, BinaryIO] = (-1, BytesIO()) self.topics: dict[str, TopicInfo] = {} - def open(self): # pylint: disable=too-many-branches,too-many-locals,too-many-statements + def open(self) -> None: # pylint: disable=too-many-branches,too-many-locals,too-many-statements """Open rosbag and read metadata.""" try: self.bio = self.path.open('rb') @@ -409,24 +413,25 @@ class Reader: raise ReaderError(f'Bag index looks damaged: {err.args}') from None self.chunks = {} + indexes: dict[int, list[list[IndexData]]] = defaultdict(list) for chunk_info in self.chunk_infos: self.bio.seek(chunk_info.pos) self.chunks[chunk_info.pos] = self.read_chunk() for _ in range(len(chunk_info.connection_counts)): cid, index = self.read_index_data(chunk_info.pos) - self.connections[cid].indexes.append(index) + indexes[cid].append(index) - for connection in self.connections.values(): - connection.indexes[:] = list(heapq.merge(*connection.indexes, key=lambda x: x.time)) + for cid, connection in self.connections.items(): + connection.indexes.extend(heapq.merge(*indexes[cid], key=lambda x: x.time)) assert connection.indexes self.topics = {} - for topic, connections in groupby( + for topic, group in groupby( sorted(self.connections.values(), key=lambda x: x.topic), key=lambda x: x.topic, ): - connections = list(connections) + connections = list(group) count = reduce( lambda x, y: x + y, ( @@ -446,7 +451,7 @@ class Reader: self.close() raise - def close(self): + def close(self) -> None: """Close rosbag.""" assert self.bio self.bio.close() @@ -614,8 +619,8 @@ class Reader: chunk_header = self.chunks[entry.chunk_pos] self.bio.seek(chunk_header.datapos) - chunk = chunk_header.decompressor(read_bytes(self.bio, chunk_header.datasize)) - self.current_chunk = (entry.chunk_pos, BytesIO(chunk)) + rawbytes = chunk_header.decompressor(read_bytes(self.bio, chunk_header.datasize)) + self.current_chunk = (entry.chunk_pos, BytesIO(rawbytes)) chunk = self.current_chunk[1] chunk.seek(entry.offset) diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index 51a5e6bd..3f6d1719 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -11,9 +11,9 @@ from dataclasses import dataclass from enum import IntEnum, auto from io import BytesIO from pathlib import Path -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Dict -from lz4.frame import compress as lz4_compress # type: ignore +from lz4.frame import compress as lz4_compress from rosbags.typesys.msg import denormalize_msgtype, generate_msgdef @@ -21,7 +21,7 @@ from .reader import Connection, RecordType if TYPE_CHECKING: from types import TracebackType - from typing import Any, BinaryIO, Callable, Literal, Optional, Type, Union + from typing import BinaryIO, Callable, Literal, Optional, Type, Union class WriterError(Exception): @@ -57,10 +57,10 @@ def serialize_time(val: int) -> bytes: return struct.pack(' None: """Set field to uint32 value. Args: @@ -70,7 +70,7 @@ class Header(dict): """ self[name] = serialize_uint32(value) - def set_uint64(self, name: str, value: int): + def set_uint64(self, name: str, value: int) -> None: """Set field to uint64 value. Args: @@ -80,7 +80,7 @@ class Header(dict): """ self[name] = serialize_uint64(value) - def set_string(self, name: str, value: str): + def set_string(self, name: str, value: str) -> None: """Set field to string value. Args: @@ -90,7 +90,7 @@ class Header(dict): """ self[name] = value.encode() - def set_time(self, name: str, value: int): + def set_time(self, name: str, value: int) -> None: """Set field to time value. Args: @@ -163,7 +163,7 @@ class Writer: # pylint: disable=too-many-instance-attributes ] self.chunk_threshold = 1 * (1 << 20) - def set_compression(self, fmt: CompressionFormat): + def set_compression(self, fmt: CompressionFormat) -> None: """Enable compression on rosbag1. This function has to be called before opening. @@ -180,20 +180,21 @@ class Writer: # pylint: disable=too-many-instance-attributes self.compression_format = fmt.name.lower() - bz2: Callable[[bytes], bytes] = lambda x: bz2_compress(x, compresslevel=9) - lz4: Callable[[bytes], bytes] = lambda x: lz4_compress(x, compression_level=16) + bz2: Callable[[bytes], bytes] = lambda x: bz2_compress(x, 9) + lz4: Callable[[bytes], bytes] = lambda x: lz4_compress(x, 16) # type: ignore self.compressor = { 'bz2': bz2, 'lz4': lz4, }[self.compression_format] - def open(self): + def open(self) -> None: """Open rosbag1 for writing.""" try: self.bio = self.path.open('xb') except FileExistsError: raise WriterError(f'{self.path} exists already, not overwriting.') from None + assert self.bio self.bio.write(b'#ROSBAG V2.0\n') header = Header() header.set_uint64('index_pos', 0) @@ -263,7 +264,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.connections[connection.cid] = connection return connection - def write(self, connection: Connection, timestamp: int, data: bytes): + def write(self, connection: Connection, timestamp: int, data: bytes) -> None: """Write message to rosbag1. Args: @@ -301,7 +302,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.write_chunk(chunk) @staticmethod - def write_connection(connection: Connection, bio: BytesIO): + def write_connection(connection: Connection, bio: BinaryIO) -> None: """Write connection record.""" header = Header() header.set_uint32('conn', connection.cid) @@ -319,7 +320,7 @@ class Writer: # pylint: disable=too-many-instance-attributes header.set_string('latching', str(connection.latching)) header.write(bio) - def write_chunk(self, chunk: WriteChunk): + def write_chunk(self, chunk: WriteChunk) -> None: """Write open chunk to file.""" assert self.bio @@ -347,12 +348,13 @@ class Writer: # pylint: disable=too-many-instance-attributes chunk.data.close() self.chunks.append(WriteChunk(BytesIO(), -1, 2**64, 0, defaultdict(list))) - def close(self): + def close(self) -> None: """Close rosbag1 after writing. Closes open chunks and writes index. """ + assert self.bio for chunk in self.chunks: if chunk.pos == -1: self.write_chunk(chunk) diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index e5045e88..75fd616e 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -11,13 +11,14 @@ from tempfile import TemporaryDirectory from typing import TYPE_CHECKING import zstandard -from ruamel.yaml import YAML, YAMLError +from ruamel.yaml import YAML +from ruamel.yaml.error import YAMLError from .connection import Connection if TYPE_CHECKING: from types import TracebackType - from typing import Any, Dict, Generator, Iterable, Literal, Optional, Type, Union + from typing import Any, Generator, Iterable, Literal, Optional, Type, Union class ReaderError(Exception): @@ -25,7 +26,7 @@ class ReaderError(Exception): @contextmanager -def decompress(path: Path, do_decompress: bool): +def decompress(path: Path, do_decompress: bool) -> Generator[Path, None, None]: """Transparent rosbag2 database decompression context. This context manager will yield a path to the decompressed file contents. @@ -119,12 +120,12 @@ class Reader: except KeyError as exc: raise ReaderError(f'A metadata key is missing {exc!r}.') from None - def open(self): + def open(self) -> None: """Open rosbag2.""" # Future storage formats will require file handles. self.bio = True - def close(self): + def close(self) -> None: """Close rosbag2.""" # Future storage formats will require file handles. assert self.bio @@ -133,12 +134,14 @@ class Reader: @property def duration(self) -> int: """Duration in nanoseconds between earliest and latest messages.""" - return self.metadata['duration']['nanoseconds'] + 1 + nsecs: int = self.metadata['duration']['nanoseconds'] + return nsecs + 1 @property def start_time(self) -> int: """Timestamp in nanoseconds of the earliest message.""" - return self.metadata['starting_time']['nanoseconds_since_epoch'] + nsecs: int = self.metadata['starting_time']['nanoseconds_since_epoch'] + return nsecs @property def end_time(self) -> int: @@ -148,7 +151,8 @@ class Reader: @property def message_count(self) -> int: """Total message count.""" - return self.metadata['message_count'] + count: int = self.metadata['message_count'] + return count @property def compression_format(self) -> Optional[str]: @@ -233,7 +237,7 @@ class Reader: raise ReaderError(f'Cannot open database {path} or database missing tables.') cur.execute('SELECT name,id FROM topics') - connmap: Dict[int, Connection] = { + connmap: dict[int, Connection] = { row[1]: next((x for x in self.connections.values() if x.topic == row[0]), None) # type: ignore for row in cur diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 386aef23..a6b74daa 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -80,10 +80,10 @@ class Writer: # pylint: disable=too-many-instance-attributes self.compression_format = '' self.compressor: Optional[zstandard.ZstdCompressor] = None self.connections: dict[int, Connection] = {} - self.conn = None + self.conn: Optional[sqlite3.Connection] = None self.cursor: Optional[sqlite3.Cursor] = None - def set_compression(self, mode: CompressionMode, fmt: CompressionFormat): + def set_compression(self, mode: CompressionMode, fmt: CompressionFormat) -> None: """Enable compression on bag. This function has to be called before opening. @@ -104,7 +104,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.compression_format = fmt.name.lower() self.compressor = zstandard.ZstdCompressor() - def open(self): + def open(self) -> None: """Open rosbag2 for writing. Create base directory and open database connection. @@ -164,7 +164,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.cursor.execute('INSERT INTO topics VALUES(?, ?, ?, ?, ?)', meta) return connection - def write(self, connection: Connection, timestamp: int, data: bytes): + def write(self, connection: Connection, timestamp: int, data: bytes) -> None: """Write message to rosbag2. Args: @@ -191,12 +191,14 @@ class Writer: # pylint: disable=too-many-instance-attributes ) connection.count += 1 - def close(self): + def close(self) -> None: """Close rosbag2 after writing. Closes open database transactions and writes metadata.yaml. """ + assert self.cursor + assert self.conn self.cursor.close() self.cursor = None @@ -209,6 +211,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.conn.close() if self.compression_mode == 'file': + assert self.compressor src = self.dbpath self.dbpath = src.with_suffix(f'.db3.{self.compression_format}') with src.open('rb') as infile, self.dbpath.open('wb') as outfile: diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index 5b777878..0abafa52 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -19,10 +19,10 @@ from .typing import Field from .utils import SIZEMAP, Valtype, align, align_after, compile_lines if TYPE_CHECKING: - from typing import Callable + from .typing import CDRDeser, CDRSer, CDRSerSize -def generate_getsize_cdr(fields: list[Field]) -> tuple[Callable, int]: +def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: """Generate cdr size calculation function. Args: @@ -157,7 +157,7 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[Callable, int]: return compile_lines(lines).getsize_cdr, is_stat * size # type: ignore -def generate_serialize_cdr(fields: list[Field], endianess: str) -> Callable: +def generate_serialize_cdr(fields: list[Field], endianess: str) -> CDRSer: """Generate cdr serialization function. Args: @@ -296,7 +296,7 @@ def generate_serialize_cdr(fields: list[Field], endianess: str) -> Callable: return compile_lines(lines).serialize_cdr # type: ignore -def generate_deserialize_cdr(fields: list[Field], endianess: str) -> Callable: +def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: """Generate cdr deserialization function. Args: diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py index ed06449b..d83ce2c2 100644 --- a/src/rosbags/serde/messages.py +++ b/src/rosbags/serde/messages.py @@ -65,9 +65,9 @@ def get_msgdef(typename: str) -> Msgdef: generate_serialize_cdr(fields, 'be'), generate_deserialize_cdr(fields, 'le'), generate_deserialize_cdr(fields, 'be'), - generate_ros1_to_cdr(fields, typename, False), - generate_ros1_to_cdr(fields, typename, True), - generate_cdr_to_ros1(fields, typename, False), - generate_cdr_to_ros1(fields, typename, True), + generate_ros1_to_cdr(fields, typename, False), # type: ignore + generate_ros1_to_cdr(fields, typename, True), # type: ignore + generate_cdr_to_ros1(fields, typename, False), # type: ignore + generate_cdr_to_ros1(fields, typename, True), # type: ignore ) return MSGDEFCACHE[typename] diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index a12feef6..420177c3 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -18,10 +18,16 @@ from .typing import Field from .utils import SIZEMAP, Valtype, align, align_after, compile_lines if TYPE_CHECKING: - from typing import Callable # pylint: disable=ungrouped-imports + from typing import Union # pylint: disable=ungrouped-imports + + from .typing import Bitcvt, BitcvtSize -def generate_ros1_to_cdr(fields: list[Field], typename: str, copy: bool) -> Callable: +def generate_ros1_to_cdr( + fields: list[Field], + typename: str, + copy: bool, +) -> Union[Bitcvt, BitcvtSize]: """Generate ROS1 to CDR conversion function. Args: @@ -169,10 +175,14 @@ def generate_ros1_to_cdr(fields: list[Field], typename: str, copy: bool) -> Call aligned = anext lines.append(' return ipos, opos') - return getattr(compile_lines(lines), funcname) + return getattr(compile_lines(lines), funcname) # type: ignore -def generate_cdr_to_ros1(fields: list[Field], typename: str, copy: bool) -> Callable: +def generate_cdr_to_ros1( + fields: list[Field], + typename: str, + copy: bool, +) -> Union[Bitcvt, BitcvtSize]: """Generate CDR to ROS1 conversion function. Args: @@ -318,4 +328,4 @@ def generate_cdr_to_ros1(fields: list[Field], typename: str, copy: bool) -> Call aligned = anext lines.append(' return ipos, opos') - return getattr(compile_lines(lines), funcname) + return getattr(compile_lines(lines), funcname) # type: ignore diff --git a/src/rosbags/serde/typing.py b/src/rosbags/serde/typing.py index d4e8bd69..1079712e 100644 --- a/src/rosbags/serde/typing.py +++ b/src/rosbags/serde/typing.py @@ -7,7 +7,14 @@ from __future__ import annotations from typing import TYPE_CHECKING, NamedTuple if TYPE_CHECKING: - from typing import Any, Callable, List + from typing import Any, Callable, Tuple + + Bitcvt = Callable[[bytes, int, bytes, int], Tuple[int, int]] + BitcvtSize = Callable[[bytes, int, None, int], Tuple[int, int]] + + CDRDeser = Callable[[bytes, int, type], Tuple[Any, int]] + CDRSer = Callable[[bytes, int, type], int] + CDRSerSize = Callable[[int, type], int] class Descriptor(NamedTuple): @@ -28,15 +35,15 @@ class Msgdef(NamedTuple): """Metadata of a message.""" name: str - fields: List[Field] + fields: list[Field] cls: Any size_cdr: int - getsize_cdr: Callable - serialize_cdr_le: Callable - serialize_cdr_be: Callable - deserialize_cdr_le: Callable - deserialize_cdr_be: Callable - getsize_ros1_to_cdr: Callable - ros1_to_cdr: Callable - getsize_cdr_to_ros1: Callable - cdr_to_ros1: Callable + getsize_cdr: CDRSerSize + serialize_cdr_le: CDRSer + serialize_cdr_be: CDRSer + deserialize_cdr_le: CDRDeser + deserialize_cdr_be: CDRDeser + getsize_ros1_to_cdr: BitcvtSize + ros1_to_cdr: Bitcvt + getsize_cdr_to_ros1: BitcvtSize + cdr_to_ros1: Bitcvt diff --git a/src/rosbags/typesys/base.py b/src/rosbags/typesys/base.py index 02e0b3b9..cbed9279 100644 --- a/src/rosbags/typesys/base.py +++ b/src/rosbags/typesys/base.py @@ -67,6 +67,6 @@ def parse_message_definition(visitor: Visitor, text: str) -> Typesdict: pos = rule.skip_ws(text, 0) npos, trees = rule.parse(text, pos) assert npos == len(text), f'Could not parse: {text!r}' - return visitor.visit(trees) + return visitor.visit(trees) # type: ignore except Exception as err: # pylint: disable=broad-except raise TypesysError(f'Could not parse: {text!r}') from err diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index 6c930c8f..f8bee88f 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -253,10 +253,10 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods RULES = parse_grammar(GRAMMAR_IDL) - def __init__(self): + def __init__(self) -> None: """Initialize.""" super().__init__() - self.typedefs = {} + self.typedefs: dict[str, tuple[Nodetype, tuple[Any, Any]]] = {} def visit_specification(self, children: Any) -> Typesdict: """Process start symbol, return only children of modules.""" diff --git a/src/rosbags/typesys/peg.py b/src/rosbags/typesys/peg.py index 3298b2f9..833cfa09 100644 --- a/src/rosbags/typesys/peg.py +++ b/src/rosbags/typesys/peg.py @@ -50,7 +50,7 @@ class Rule: } return data - def parse(self, text: str, pos: int): + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" raise NotImplementedError # pragma: no cover @@ -192,7 +192,7 @@ class Visitor: # pylint: disable=too-few-public-methods RULES: dict[str, Rule] = {} - def __init__(self): + def __init__(self) -> None: """Initialize.""" def visit(self, tree: Any) -> Any: diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index 2b289932..6aa3bf9e 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -13,12 +13,14 @@ from . import types from .base import Nodetype, TypesysError if TYPE_CHECKING: + from typing import Any, Optional, Union + from .base import Typesdict INTLIKE = re.compile('^u?(bool|int|float)') -def get_typehint(desc: tuple) -> str: +def get_typehint(desc: tuple[int, Union[str, tuple[tuple[int, str], Optional[int]]]]) -> str: """Get python type hint for field. Args: @@ -29,18 +31,19 @@ def get_typehint(desc: tuple) -> str: """ if desc[0] == Nodetype.BASE: - if match := INTLIKE.match(desc[1]): + if match := INTLIKE.match(desc[1]): # type: ignore return match.group(1) return 'str' if desc[0] == Nodetype.NAME: + assert isinstance(desc[1], str) return desc[1].replace('/', '__') sub = desc[1][0] if INTLIKE.match(sub[1]): typ = 'bool8' if sub[1] == 'bool' else sub[1] return f'numpy.ndarray[Any, numpy.dtype[numpy.{typ}]]' - return f'list[{get_typehint(sub)}]' + return f'list[{get_typehint(sub)}]' # type: ignore def generate_python_code(typs: Typesdict) -> str: @@ -99,7 +102,7 @@ def generate_python_code(typs: Typesdict) -> str: '', ] - def get_ftype(ftype: tuple) -> tuple: + def get_ftype(ftype: tuple[int, Any]) -> tuple[int, Any]: if ftype[0] <= 2: return int(ftype[0]), ftype[1] return int(ftype[0]), ((int(ftype[1][0][0]), ftype[1][0][1]), ftype[1][1]) diff --git a/tests/cdr.py b/tests/cdr.py index c3cbb249..9db001cd 100644 --- a/tests/cdr.py +++ b/tests/cdr.py @@ -9,6 +9,7 @@ from struct import Struct, pack_into, unpack_from from typing import TYPE_CHECKING, Dict, List, Union, cast import numpy +from numpy.typing import NDArray from rosbags.serde.messages import SerdeError, get_msgdef from rosbags.serde.typing import Msgdef @@ -116,7 +117,7 @@ def deserialize_array(rawdata: bytes, bmap: BasetypeMap, pos: int, num: int, des size = SIZEMAP[desc.args] pos = (pos + size - 1) & -size - ndarr = numpy.frombuffer(rawdata, dtype=desc.args, count=num, offset=pos) + ndarr = numpy.frombuffer(rawdata, dtype=desc.args, count=num, offset=pos) # type: ignore if (bmap is BASETYPEMAP_LE) != (sys.byteorder == 'little'): ndarr = ndarr.byteswap() # no inplace on readonly array return ndarr, pos + num * SIZEMAP[desc.args] @@ -278,7 +279,7 @@ def serialize_array( size = SIZEMAP[desc.args] pos = (pos + size - 1) & -size size *= len(val) - val = cast(numpy.ndarray, val) + val = cast(NDArray[numpy.int_], val) if (bmap is BASETYPEMAP_LE) != (sys.byteorder == 'little'): val = val.byteswap() # no inplace on readonly array rawdata[pos:pos + size] = memoryview(val.tobytes()) diff --git a/tests/test_convert.py b/tests/test_convert.py index 057a6092..1b090223 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -15,7 +15,7 @@ from rosbags.rosbag1 import ReaderError from rosbags.rosbag2 import WriterError -def test_cliwrapper(tmp_path: Path): +def test_cliwrapper(tmp_path: Path) -> None: """Test cli wrapper.""" (tmp_path / 'subdir').mkdir() (tmp_path / 'ros1.bag').write_text('') @@ -62,7 +62,7 @@ def test_cliwrapper(tmp_path: Path): mock_print.assert_called_with('ERROR: exc') -def test_convert(tmp_path: Path): +def test_convert(tmp_path: Path) -> None: """Test conversion function.""" (tmp_path / 'subdir').mkdir() (tmp_path / 'foo.bag').write_text('') diff --git a/tests/test_parse.py b/tests/test_parse.py index 6112ef6f..edce9ab7 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -142,13 +142,13 @@ module test_msgs { """ -def test_parse_empty_msg(): +def test_parse_empty_msg() -> None: """Test msg parser with empty message.""" ret = get_types_from_msg('', 'std_msgs/msg/Empty') assert ret == {'std_msgs/msg/Empty': ([], [])} -def test_parse_bounds_msg(): +def test_parse_bounds_msg() -> None: """Test msg parser.""" ret = get_types_from_msg(MSG_BOUNDS, 'test_msgs/msg/Foo') assert ret == { @@ -168,7 +168,7 @@ def test_parse_bounds_msg(): } -def test_parse_defaults_msg(): +def test_parse_defaults_msg() -> None: """Test msg parser.""" ret = get_types_from_msg(MSG_DEFAULTS, 'test_msgs/msg/Foo') assert ret == { @@ -188,7 +188,7 @@ def test_parse_defaults_msg(): } -def test_parse_msg(): +def test_parse_msg() -> None: """Test msg parser.""" with pytest.raises(TypesysError, match='Could not parse'): get_types_from_msg('invalid', 'test_msgs/msg/Foo') @@ -208,7 +208,7 @@ def test_parse_msg(): assert fields[6][1][0] == Nodetype.ARRAY -def test_parse_multi_msg(): +def test_parse_multi_msg() -> None: """Test multi msg parser.""" ret = get_types_from_msg(MULTI_MSG, 'test_msgs/msg/Foo') assert len(ret) == 3 @@ -223,7 +223,7 @@ def test_parse_multi_msg(): assert consts == [('static', 'uint32', 42)] -def test_parse_cstring_confusion(): +def test_parse_cstring_confusion() -> None: """Test if msg separator is confused with const string.""" ret = get_types_from_msg(CSTRING_CONFUSION_MSG, 'test_msgs/msg/Foo') assert len(ret) == 2 @@ -235,7 +235,7 @@ def test_parse_cstring_confusion(): assert fields[1][1][1] == 'string' -def test_parse_relative_siblings_msg(): +def test_parse_relative_siblings_msg() -> None: """Test relative siblings with msg parser.""" ret = get_types_from_msg(RELSIBLING_MSG, 'test_msgs/msg/Foo') assert ret['test_msgs/msg/Foo'][1][0][1][1] == 'std_msgs/msg/Header' @@ -246,7 +246,7 @@ def test_parse_relative_siblings_msg(): assert ret['rel_msgs/msg/Foo'][1][1][1][1] == 'rel_msgs/msg/Other' -def test_parse_idl(): +def test_parse_idl() -> None: """Test idl parser.""" ret = get_types_from_idl(IDL_LANG) assert ret == {} @@ -267,21 +267,21 @@ def test_parse_idl(): assert fields[6][1][0] == Nodetype.ARRAY -def test_register_types(): +def test_register_types() -> None: """Test type registeration.""" assert 'foo' not in FIELDDEFS register_types({}) - register_types({'foo': [[], [('b', (1, 'bool'))]]}) + register_types({'foo': [[], [('b', (1, 'bool'))]]}) # type: ignore assert 'foo' in FIELDDEFS - register_types({'std_msgs/msg/Header': [[], []]}) + register_types({'std_msgs/msg/Header': [[], []]}) # type: ignore assert len(FIELDDEFS['std_msgs/msg/Header'][1]) == 2 with pytest.raises(TypesysError, match='different definition'): - register_types({'foo': [[], [('x', (1, 'bool'))]]}) + register_types({'foo': [[], [('x', (1, 'bool'))]]}) # type: ignore -def test_generate_msgdef(): +def test_generate_msgdef() -> None: """Test message definition generator.""" res = generate_msgdef('std_msgs/msg/Header') assert res == ('uint32 seq\ntime stamp\nstring frame_id\n', '2176decaecbce78abc3b96ef049fabed') diff --git a/tests/test_reader.py b/tests/test_reader.py index 2862954b..a941ead7 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -117,7 +117,7 @@ def bag(request: SubRequest, tmp_path: Path) -> Path: return tmp_path -def test_reader(bag: Path): +def test_reader(bag: Path) -> None: """Test reader and deserializer on simple bag.""" with Reader(bag) as reader: assert reader.duration == 43 @@ -151,7 +151,7 @@ def test_reader(bag: Path): next(gen) -def test_message_filters(bag: Path): +def test_message_filters(bag: Path) -> None: """Test reader filters messages.""" with Reader(bag) as reader: magn_connections = [x for x in reader.connections.values() if x.topic == '/magn'] @@ -188,14 +188,14 @@ def test_message_filters(bag: Path): next(gen) -def test_user_errors(bag: Path): +def test_user_errors(bag: Path) -> None: """Test user errors.""" reader = Reader(bag) with pytest.raises(ReaderError, match='Rosbag is not open'): next(reader.messages()) -def test_failure_cases(tmp_path: Path): +def test_failure_cases(tmp_path: Path) -> None: """Test bags with broken fs layout.""" with pytest.raises(ReaderError, match='not read metadata'): Reader(tmp_path) diff --git a/tests/test_reader1.py b/tests/test_reader1.py index 5acbcf5b..289027c8 100644 --- a/tests/test_reader1.py +++ b/tests/test_reader1.py @@ -2,8 +2,11 @@ # SPDX-License-Identifier: Apache-2.0 """Reader tests.""" +from __future__ import annotations + from collections import defaultdict from struct import pack +from typing import TYPE_CHECKING from unittest.mock import patch import pytest @@ -11,8 +14,12 @@ import pytest from rosbags.rosbag1 import Reader, ReaderError from rosbags.rosbag1.reader import IndexData +if TYPE_CHECKING: + from pathlib import Path + from typing import Any, Sequence, Union -def ser(data): + +def ser(data: Union[dict[str, Any], bytes]) -> bytes: """Serialize record header.""" if isinstance(data, dict): fields = [] @@ -23,7 +30,7 @@ def ser(data): return pack(' dict[str, bytes]: """Create empty rosbag header.""" return { 'op': b'\x03', @@ -32,7 +39,11 @@ def create_default_header(): } -def create_connection(cid=1, topic=0, typ=0): +def create_connection( + cid: int = 1, + topic: int = 0, + typ: int = 0, +) -> tuple[dict[str, bytes], dict[str, bytes]]: """Create connection record.""" return { 'op': b'\x07', @@ -45,7 +56,11 @@ def create_connection(cid=1, topic=0, typ=0): } -def create_message(cid=1, time=0, msg=0): +def create_message( + cid: int = 1, + time: int = 0, + msg: int = 0, +) -> tuple[dict[str, Union[bytes, int]], bytes]: """Create message record.""" return { 'op': b'\x02', @@ -54,7 +69,12 @@ def create_message(cid=1, time=0, msg=0): }, f'MSGCONTENT{msg}'.encode() -def write_bag(bag, header, chunks=None): # pylint: disable=too-many-locals,too-many-statements +def write_bag( # pylint: disable=too-many-locals,too-many-statements + + bag: Path, + header: dict[str, bytes], + chunks: Sequence[Any] = (), +) -> None: """Write bag file.""" magic = b'#ROSBAG V2.0\n' @@ -70,7 +90,7 @@ def write_bag(bag, header, chunks=None): # pylint: disable=too-many-locals,too- chunk_bytes = b'' start_time = 2**32 - 1 end_time = 0 - counts = defaultdict(int) + counts: dict[int, int] = defaultdict(int) index = {} offset = 0 @@ -95,8 +115,8 @@ def write_bag(bag, header, chunks=None): # pylint: disable=too-many-locals,too- 'count': 0, 'msgs': b'', } - index[conn]['count'] += 1 - index[conn]['msgs'] += pack(' None: """Test IndexData sort sorder.""" x42_1_0 = IndexData(42, 1, 0) x42_2_0 = IndexData(42, 2, 0) @@ -175,7 +195,7 @@ def test_indexdata(): assert not x42_1_0 > x43_3_0 -def test_reader(tmp_path): # pylint: disable=too-many-statements +def test_reader(tmp_path: Path) -> None: # pylint: disable=too-many-statements """Test reader and deserializer on simple bag.""" # empty bag bag = tmp_path / 'test.bag' @@ -268,7 +288,7 @@ def test_reader(tmp_path): # pylint: disable=too-many-statements assert msgs[0][2] == b'MSGCONTENT5' -def test_user_errors(tmp_path): +def test_user_errors(tmp_path: Path) -> None: """Test user errors.""" bag = tmp_path / 'test.bag' write_bag(bag, create_default_header(), chunks=[[ @@ -281,7 +301,7 @@ def test_user_errors(tmp_path): next(reader.messages()) -def test_failure_cases(tmp_path): # pylint: disable=too-many-statements +def test_failure_cases(tmp_path: Path) -> None: # pylint: disable=too-many-statements """Test failure cases.""" bag = tmp_path / 'test.bag' with pytest.raises(ReaderError, match='does not exist'): diff --git a/tests/test_roundtrip.py b/tests/test_roundtrip.py index 09f1d99b..f087b48c 100644 --- a/tests/test_roundtrip.py +++ b/tests/test_roundtrip.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: @pytest.mark.parametrize('mode', [*Writer.CompressionMode]) -def test_roundtrip(mode: Writer.CompressionMode, tmp_path: Path): +def test_roundtrip(mode: Writer.CompressionMode, tmp_path: Path) -> None: """Test full data roundtrip.""" class Foo: # pylint: disable=too-few-public-methods diff --git a/tests/test_roundtrip1.py b/tests/test_roundtrip1.py index a85288f8..5d677b05 100644 --- a/tests/test_roundtrip1.py +++ b/tests/test_roundtrip1.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: @pytest.mark.parametrize('fmt', [None, Writer.CompressionFormat.BZ2, Writer.CompressionFormat.LZ4]) -def test_roundtrip(tmp_path: Path, fmt: Optional[Writer.CompressionFormat]): +def test_roundtrip(tmp_path: Path, fmt: Optional[Writer.CompressionFormat]) -> None: """Test full data roundtrip.""" class Foo: # pylint: disable=too-few-public-methods diff --git a/tests/test_serde.py b/tests/test_serde.py index a8e95edf..ef429eba 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -18,7 +18,7 @@ from rosbags.typesys.types import builtin_interfaces__msg__Time, std_msgs__msg__ from .cdr import deserialize, serialize if TYPE_CHECKING: - from typing import Any, Tuple, Union + from typing import Any, Generator, Union MSG_POLY = ( ( @@ -169,7 +169,7 @@ test_msgs/msg/dynamic_s_64[] seq_msg_ds6 @pytest.fixture() -def _comparable(): +def _comparable() -> Generator[None, None, None]: """Make messages containing numpy arrays comparable. Notes: @@ -180,7 +180,7 @@ def _comparable(): def arreq(self: MagicMock, other: Union[MagicMock, Any]) -> bool: lhs = self._mock_wraps # pylint: disable=protected-access rhs = getattr(other, '_mock_wraps', other) - return (lhs == rhs).all() + return (lhs == rhs).all() # type: ignore class CNDArray(MagicMock): """Mock ndarray.""" @@ -194,14 +194,14 @@ def _comparable(): return CNDArray(wraps=self._mock_wraps.byteswap(*args)) def wrap_frombuffer(*args: Any, **kwargs: Any) -> CNDArray: - return CNDArray(wraps=frombuffer(*args, **kwargs)) + return CNDArray(wraps=frombuffer(*args, **kwargs)) # type: ignore with patch.object(numpy, 'frombuffer', side_effect=wrap_frombuffer): yield @pytest.mark.parametrize('message', MESSAGES) -def test_serde(message: Tuple[bytes, str, bool]): +def test_serde(message: tuple[bytes, str, bool]) -> None: """Test serialization deserialization roundtrip.""" rawdata, typ, is_little = message @@ -213,7 +213,7 @@ def test_serde(message: Tuple[bytes, str, bool]): @pytest.mark.usefixtures('_comparable') -def test_deserializer(): +def test_deserializer() -> None: """Test deserializer.""" msg = deserialize_cdr(*MSG_POLY[:2]) assert msg == deserialize(*MSG_POLY[:2]) @@ -233,7 +233,8 @@ def test_deserializer(): assert msg.header.frame_id == 'foo42' field = msg.magnetic_field assert (field.x, field.y, field.z) == (128., 128., 128.) - assert (numpy.diag(msg.magnetic_field_covariance.reshape(3, 3)) == [1., 1., 1.]).all() + diag = numpy.diag(msg.magnetic_field_covariance.reshape(3, 3)) # type: ignore + assert (diag == [1., 1., 1.]).all() msg_big = deserialize_cdr(*MSG_MAGN_BIG[:2]) assert msg_big == deserialize(*MSG_MAGN_BIG[:2]) @@ -241,7 +242,7 @@ def test_deserializer(): @pytest.mark.usefixtures('_comparable') -def test_serializer(): +def test_serializer() -> None: """Test serializer.""" class Foo: # pylint: disable=too-few-public-methods @@ -268,7 +269,7 @@ def test_serializer(): @pytest.mark.usefixtures('_comparable') -def test_serializer_errors(): +def test_serializer_errors() -> None: """Test seralizer with broken messages.""" class Foo: # pylint: disable=too-few-public-methods @@ -286,7 +287,7 @@ def test_serializer_errors(): @pytest.mark.usefixtures('_comparable') -def test_custom_type(): +def test_custom_type() -> None: """Test custom type.""" cname = 'test_msgs/msg/custom' register_types(dict(get_types_from_msg(STATIC_64_64, 'test_msgs/msg/static_64_64'))) @@ -362,7 +363,7 @@ def test_custom_type(): assert res == msg -def test_ros1_to_cdr(): +def test_ros1_to_cdr() -> None: """Test ROS1 to CDR conversion.""" register_types(dict(get_types_from_msg(STATIC_16_64, 'test_msgs/msg/static_16_64'))) msg_ros = (b'\x01\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02') @@ -385,7 +386,7 @@ def test_ros1_to_cdr(): assert ros1_to_cdr(msg_ros, 'test_msgs/msg/dynamic_s_64') == msg_cdr -def test_cdr_to_ros1(): +def test_cdr_to_ros1() -> None: """Test CDR to ROS1 conversion.""" register_types(dict(get_types_from_msg(STATIC_16_64, 'test_msgs/msg/static_16_64'))) msg_ros = (b'\x01\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02') diff --git a/tests/test_writer.py b/tests/test_writer.py index 5fac0e13..ebb2c354 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: from pathlib import Path -def test_writer(tmp_path: Path): +def test_writer(tmp_path: Path) -> None: """Test Writer.""" path = (tmp_path / 'rosbag2') with Writer(path) as bag: @@ -60,7 +60,7 @@ def test_writer(tmp_path: Path): assert size > (path / 'compress_message.db3').stat().st_size -def test_failure_cases(tmp_path: Path): +def test_failure_cases(tmp_path: Path) -> None: """Test writer failure cases.""" with pytest.raises(WriterError, match='exists'): Writer(tmp_path) diff --git a/tests/test_writer1.py b/tests/test_writer1.py index 384ebadd..b3c7f1e8 100644 --- a/tests/test_writer1.py +++ b/tests/test_writer1.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: from typing import Optional -def test_no_overwrite(tmp_path: Path): +def test_no_overwrite(tmp_path: Path) -> None: """Test writer does not touch existing files.""" path = tmp_path / 'test.bag' path.write_text('foo') @@ -30,7 +30,7 @@ def test_no_overwrite(tmp_path: Path): writer.open() -def test_empty(tmp_path: Path): +def test_empty(tmp_path: Path) -> None: """Test empty bag.""" path = tmp_path / 'test.bag' @@ -40,7 +40,7 @@ def test_empty(tmp_path: Path): assert len(data) == 13 + 4096 -def test_add_connection(tmp_path: Path): +def test_add_connection(tmp_path: Path) -> None: """Test adding of connections.""" path = tmp_path / 'test.bag' @@ -88,7 +88,7 @@ def test_add_connection(tmp_path: Path): assert (res1.cid, res2.cid, res3.cid) == (0, 1, 2) -def test_write_errors(tmp_path: Path): +def test_write_errors(tmp_path: Path) -> None: """Test write errors.""" path = tmp_path / 'test.bag' @@ -101,7 +101,7 @@ def test_write_errors(tmp_path: Path): path.unlink() -def test_write_simple(tmp_path: Path): +def test_write_simple(tmp_path: Path) -> None: """Test writing of messages.""" path = tmp_path / 'test.bag' @@ -179,7 +179,7 @@ def test_write_simple(tmp_path: Path): path.unlink() -def test_compression_errors(tmp_path: Path): +def test_compression_errors(tmp_path: Path) -> None: """Test compression modes.""" path = tmp_path / 'test.bag' with Writer(path) as writer, \ @@ -188,7 +188,7 @@ def test_compression_errors(tmp_path: Path): @pytest.mark.parametrize('fmt', [None, Writer.CompressionFormat.BZ2, Writer.CompressionFormat.LZ4]) -def test_compression_modes(tmp_path: Path, fmt: Optional[Writer.CompressionFormat]): +def test_compression_modes(tmp_path: Path, fmt: Optional[Writer.CompressionFormat]) -> None: """Test compression modes.""" path = tmp_path / 'test.bag' writer = Writer(path) diff --git a/tools/bench/bench.py b/tools/bench/bench.py index cb6d876a..b6330fcf 100644 --- a/tools/bench/bench.py +++ b/tools/bench/bench.py @@ -21,7 +21,7 @@ from rosbags.rosbag2 import Reader from rosbags.serde import deserialize_cdr if TYPE_CHECKING: - from typing import Any + from typing import Any, Generator class ReaderPy: # pylint: disable=too-few-public-methods @@ -35,7 +35,7 @@ class ReaderPy: # pylint: disable=too-few-public-methods self.reader.open(soptions, coptions) self.typemap = {x.name: x.type for x in self.reader.get_all_topics_and_types()} - def messages(self): + def messages(self) -> Generator[tuple[str, str, int, bytes], None, None]: """Expose rosbag2 like generator behavior.""" while self.reader.has_next(): topic, data, timestamp = self.reader.read_next() @@ -48,7 +48,7 @@ def deserialize_py(data: bytes, msgtype: str) -> Any: return deserialize_message(data, pytype) -def compare_msg(lite: Any, native: Any): +def compare_msg(lite: Any, native: Any) -> None: """Compare rosbag2 (lite) vs rosbag2_py (native) message content. Args: @@ -79,7 +79,7 @@ def compare_msg(lite: Any, native: Any): assert native_val == lite_val, f'{fieldname}: {native_val} != {lite_val}' -def compare(path: Path): +def compare(path: Path) -> None: """Compare raw and deserialized messages.""" with Reader(path) as reader: gens = (reader.messages(), ReaderPy(path).messages()) @@ -100,7 +100,7 @@ def compare(path: Path): assert len(list(gens[1])) == 0 -def read_deser_rosbag2_py(path: Path): +def read_deser_rosbag2_py(path: Path) -> None: """Read testbag with rosbag2_py.""" soptions = StorageOptions(str(path), 'sqlite3') coptions = ConverterOptions('', '') @@ -115,14 +115,14 @@ def read_deser_rosbag2_py(path: Path): deserialize_message(rawdata, pytype) -def read_deser_rosbag2(path: Path): +def read_deser_rosbag2(path: Path) -> None: """Read testbag with rosbag2lite.""" with Reader(path) as reader: for connection, _, data in reader.messages(): deserialize_cdr(data, connection.msgtype) -def main(): +def main() -> None: """Benchmark rosbag2 against rosbag2_py.""" path = Path(sys.argv[1]) try: diff --git a/tools/compare/compare.py b/tools/compare/compare.py index 9649ea48..bb9e34dc 100644 --- a/tools/compare/compare.py +++ b/tools/compare/compare.py @@ -25,7 +25,7 @@ rosgraph_msgs.msg.TopicStatistics = Mock() import rosbag.bag # type:ignore # noqa: E402 pylint: disable=wrong-import-position if TYPE_CHECKING: - from typing import Any, List, Union + from typing import Any, Generator, List, Union from rosbag.bag import _Connection_Info @@ -39,7 +39,7 @@ class Reader: # pylint: disable=too-few-public-methods self.reader.open(StorageOptions(path, 'sqlite3'), ConverterOptions('', '')) self.typemap = {x.name: x.type for x in self.reader.get_all_topics_and_types()} - def messages(self): + def messages(self) -> Generator[tuple[str, int, bytes], None, None]: """Expose rosbag2 like generator behavior.""" while self.reader.has_next(): topic, data, timestamp = self.reader.read_next() @@ -47,7 +47,7 @@ class Reader: # pylint: disable=too-few-public-methods yield topic, timestamp, deserialize_message(data, pytype) -def fixup_ros1(conns: List[_Connection_Info]): +def fixup_ros1(conns: List[_Connection_Info]) -> None: """Monkeypatch ROS2 fieldnames onto ROS1 objects. Args: @@ -69,16 +69,13 @@ def fixup_ros1(conns: List[_Connection_Info]): cls.p = property(lambda x: x.P, lambda x, y: setattr(x, 'P', y)) # noqa: B010 -def compare(ref: Any, msg: Any): +def compare(ref: Any, msg: Any) -> None: """Compare message to its reference. Args: ref: Reference ROS1 message. msg: Converted ROS2 message. - Return: - True if messages are identical. - """ if hasattr(msg, 'get_fields_and_field_types'): for name in msg.get_fields_and_field_types(): @@ -107,7 +104,7 @@ def compare(ref: Any, msg: Any): assert ref == msg -def main_bag1_bag1(path1: Path, path2: Path): +def main_bag1_bag1(path1: Path, path2: Path) -> None: """Compare rosbag1 to rosbag1 message by message. Args: @@ -132,7 +129,7 @@ def main_bag1_bag1(path1: Path, path2: Path): print('Bags are identical.') # noqa: T001 -def main_bag1_bag2(path1: Path, path2: Path): +def main_bag1_bag2(path1: Path, path2: Path) -> None: """Compare rosbag1 to rosbag2 message by message. Args: From 18983868c62e59b52d68175687798f8d6ecbebc3 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 25 Nov 2021 14:26:48 +0100 Subject: [PATCH 056/114] Support bool and float constants in msg files --- src/rosbags/typesys/msg.py | 14 +++++++++----- tests/test_parse.py | 9 ++++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 71e5f20a..5ee4df97 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -45,7 +45,9 @@ comment const_dcl = 'string' identifier r'=(?!={79}\n)' r'[^\n]+' + / type_spec identifier '=' float_literal / type_spec identifier '=' integer_literal + / type_spec identifier '=' boolean_literal field_dcl = type_spec identifier default_value? @@ -82,15 +84,17 @@ default_value = literal literal - = boolean_literal - / float_literal + = float_literal / integer_literal + / boolean_literal / string_literal / array_literal boolean_literal - = 'true' - / 'false' + = r'[tT][rR][uU][eE]' + / r'[fF][aA][lL][sS][eE]' + / '0' + / '1' integer_literal = hexadecimal_literal @@ -283,7 +287,7 @@ class VisitorMSG(Visitor): def visit_boolean_literal(self, children: Any) -> Any: """Process boolean literal.""" - return children[1] == 'TRUE' + return children.lower() in ['true', '1'] def visit_float_literal(self, children: Any) -> Any: """Process float literal.""" diff --git a/tests/test_parse.py b/tests/test_parse.py index edce9ab7..dc552975 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -17,7 +17,9 @@ from rosbags.typesys.types import FIELDDEFS MSG = """ # comment +bool b=true int32 global=42 +float32 f=1.33 string str= foo bar\t std_msgs/Header header @@ -195,7 +197,12 @@ def test_parse_msg() -> None: ret = get_types_from_msg(MSG, 'test_msgs/msg/Foo') assert 'test_msgs/msg/Foo' in ret consts, fields = ret['test_msgs/msg/Foo'] - assert consts == [('global', 'int32', 42), ('str', 'string', 'foo bar')] + assert consts == [ + ('b', 'bool', True), + ('global', 'int32', 42), + ('f', 'float32', 1.33), + ('str', 'string', 'foo bar'), + ] assert fields[0][0] == 'header' assert fields[0][1][1] == 'std_msgs/msg/Header' assert fields[1][0] == 'bool' From c9bd6ebbc02d14dff16883481aa101e9116bf898 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 25 Nov 2021 14:41:31 +0100 Subject: [PATCH 057/114] Release 0.9.8 --- CHANGES.rst | 6 ++++++ setup.cfg | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index acb378a9..7a4ae92b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,11 @@ Changes ======= +0.9.8 - 2021-11-25 +------------------ +- Support bool and float constants in msg files + + 0.9.7 - 2021-11-09 ------------------ - Fix parsing of const fields with string value @@ -10,6 +15,7 @@ Changes - Make packages PEP561 compliant - Parse msg bounded fields and default values + 0.9.6 - 2021-10-04 ------------------ - Do not match msg separator as constant value diff --git a/setup.cfg b/setup.cfg index 06a203f3..6ab57634 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.7 +version = 0.9.8 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 8bf8994fbf5497b4607e3b7f60942765093a94be Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 6 Jan 2022 12:50:40 +0100 Subject: [PATCH 058/114] Fix documentation code samples --- docs/topics/rosbag1.rst | 1 + docs/topics/rosbag2.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/topics/rosbag1.rst b/docs/topics/rosbag1.rst index bbae2eb4..0e9b7c94 100644 --- a/docs/topics/rosbag1.rst +++ b/docs/topics/rosbag1.rst @@ -22,6 +22,7 @@ Instances of the :py:class:`Writer ` class can create an # serialize and write message message = String('hello world') + timestamp = 42 writer.write(connection, timestamp, cdr_to_ros1(serialize_cdr(message, msgtype), msgtype)) Reading rosbag1 diff --git a/docs/topics/rosbag2.rst b/docs/topics/rosbag2.rst index 3e788bb3..1f3fbe57 100644 --- a/docs/topics/rosbag2.rst +++ b/docs/topics/rosbag2.rst @@ -37,6 +37,7 @@ Instances of the :py:class:`Writer ` class can create an connection = writer.add_connection(topic, msgtype, 'cdr', '') # serialize and write message + timestamp = 42 message = String('hello world') writer.write(connection, timestamp, serialize_cdr(message, msgtype)) From ac55fd2f4af68e9f3eb709b65b1b463064e1a49f Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 6 Jan 2022 20:35:58 +0100 Subject: [PATCH 059/114] Fix handling of padding after empty sequences --- src/rosbags/serde/cdr.py | 9 ++++--- src/rosbags/serde/ros1.py | 6 +++-- tests/test_serde.py | 50 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index 0abafa52..08052661 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -119,7 +119,8 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: else: anext = align(subdesc) if aligned < anext: - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' if len(message.{fieldname}):') + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') aligned = anext lines.append(f' pos += len(message.{fieldname}) * {SIZEMAP[subdesc.args]}') @@ -272,7 +273,8 @@ def generate_serialize_cdr(fields: list[Field], endianess: str) -> CDRSer: if (endianess == 'le') != (sys.byteorder == 'little'): lines.append(' val = val.byteswap()') if aligned < (anext := align(subdesc)): - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(' if size:') + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') lines.append(' rawdata[pos:pos + size] = val.view(numpy.uint8)') lines.append(' pos += size') aligned = anext @@ -417,7 +419,8 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: else: lines.append(f' length = size * {SIZEMAP[subdesc.args]}') if aligned < (anext := align(subdesc)): - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(' if size:') + lines.append(f' pos = (pos + {anext} - 1) & -{anext}') lines.append( f' val = numpy.frombuffer(rawdata, ' f'dtype=numpy.{subdesc.args}, count=size, offset=pos)', diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index 420177c3..387a1612 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -151,7 +151,8 @@ def generate_ros1_to_cdr( aligned = 1 else: if aligned < (anext := align(subdesc)): - lines.append(f' opos = (opos + {anext} - 1) & -{anext}') + lines.append(' if size:') + lines.append(f' opos = (opos + {anext} - 1) & -{anext}') lines.append(f' length = size * {SIZEMAP[subdesc.args]}') if copy: lines.append(' output[opos:opos + length] = input[ipos:ipos + length]') @@ -304,7 +305,8 @@ def generate_cdr_to_ros1( aligned = 1 else: if aligned < (anext := align(subdesc)): - lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') + lines.append(' if size:') + lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') lines.append(f' length = size * {SIZEMAP[subdesc.args]}') if copy: lines.append(' output[opos:opos + length] = input[ipos:ipos + length]') diff --git a/tests/test_serde.py b/tests/test_serde.py index ef429eba..cc146c10 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -167,6 +167,16 @@ test_msgs/msg/dynamic_64_s[] seq_msg_d6s test_msgs/msg/dynamic_s_64[] seq_msg_ds6 """ +SU64_B = """ +uint64[] su64 +bool b +""" + +SU64_U64 = """ +uint64[] su64 +uint64 u64 +""" + @pytest.fixture() def _comparable() -> Generator[None, None, None]: @@ -411,3 +421,43 @@ def test_cdr_to_ros1() -> None: header = std_msgs__msg__Header(stamp=builtin_interfaces__msg__Time(42, 666), frame_id='frame') msg_ros = cdr_to_ros1(serialize_cdr(header, 'std_msgs/msg/Header'), 'std_msgs/msg/Header') assert msg_ros == b'\x00\x00\x00\x00*\x00\x00\x00\x9a\x02\x00\x00\x05\x00\x00\x00frame' + + +@pytest.mark.usefixtures('_comparable') +def test_padding_empty_sequence() -> None: + """Test empty sequences do not add item padding.""" + # pylint: disable=protected-access + register_types(dict(get_types_from_msg(SU64_B, 'test_msgs/msg/su64_b'))) + + su64_b = get_msgdef('test_msgs/msg/su64_b').cls + msg = su64_b(numpy.array([], dtype=numpy.uint64), True) + + cdr = serialize_cdr(msg, msg.__msgtype__) + assert cdr[4:] == b'\x00\x00\x00\x00\x01' + + ros1 = cdr_to_ros1(cdr, msg.__msgtype__) + assert ros1 == cdr[4:] + + assert ros1_to_cdr(ros1, msg.__msgtype__) == cdr + + assert deserialize_cdr(cdr, msg.__msgtype__) == msg + + +@pytest.mark.usefixtures('_comparable') +def test_align_after_empty_sequence() -> None: + """Test alignment after empty sequences.""" + # pylint: disable=protected-access + register_types(dict(get_types_from_msg(SU64_U64, 'test_msgs/msg/su64_u64'))) + + su64_b = get_msgdef('test_msgs/msg/su64_u64').cls + msg = su64_b(numpy.array([], dtype=numpy.uint64), 42) + + cdr = serialize_cdr(msg, msg.__msgtype__) + assert cdr[4:] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x2a\x00\x00\x00\x00\x00\x00\x00' + + ros1 = cdr_to_ros1(cdr, msg.__msgtype__) + assert ros1 == b'\x00\x00\x00\x00\x2a\x00\x00\x00\x00\x00\x00\x00' + + assert ros1_to_cdr(ros1, msg.__msgtype__) == cdr + + assert deserialize_cdr(cdr, msg.__msgtype__) == msg From 2eb3b1c67146b30a902c235e495269182e7b9f2b Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 9 Jan 2022 20:31:51 +0100 Subject: [PATCH 060/114] Add conversion from rosbag2 to rosbag1 --- README.rst | 8 +- docs/topics/convert.rst | 11 ++- src/rosbags/convert/__main__.py | 11 ++- src/rosbags/convert/converter.py | 127 ++++++++++++++++++++------ src/rosbags/serde/serdes.py | 4 +- tests/test_convert.py | 150 +++++++++++++++++++++++++++++-- 6 files changed, 269 insertions(+), 42 deletions(-) diff --git a/README.rst b/README.rst index 3a0c76cc..d0248b6b 100644 --- a/README.rst +++ b/README.rst @@ -46,14 +46,20 @@ Read and deserialize rosbag2 messages: print(msg.header.frame_id) -Convert rosbag1 to rosbag2:: +Convert between rosbag versions:: # Convert "foo.bag", result will be "foo/" rosbags-convert foo.bag + # Convert "bar", result will be "bar.bag" + rosbags-convert bar + # Convert "foo.bag", save the result as "bar" rosbags-convert foo.bag --dst /path/to/bar + # Convert "bar", save the result as "foo.bag" + rosbags-convert bar --dst /path/to/foo.bag + Documentation ============= diff --git a/docs/topics/convert.rst b/docs/topics/convert.rst index c4d2014d..d305e675 100644 --- a/docs/topics/convert.rst +++ b/docs/topics/convert.rst @@ -1,7 +1,7 @@ Convert Rosbag1 to Rosbag2 ========================== -The :py:mod:`rosbags.convert` package includes a CLI tool to convert legacy rosbag1 files to rosbag2. +The :py:mod:`rosbags.convert` package includes a CLI tool to convert legacy rosbag1 files to rosbag2 and vice versa. Features -------- @@ -14,8 +14,9 @@ Features Limitations ----------- -- Refuses to convert unindexed rosbag files, please reindex files before conversion +- Refuses to convert unindexed rosbag1 files, please reindex files before conversion - Currently does not handle split bags +- Only ROS2 default message types are supported when converting rosbag2 to rosbag1 Usage ----- @@ -25,5 +26,11 @@ Usage # Convert "foo.bag", result will be "foo/" $ rosbags-convert foo.bag + # Convert "bar", result will be "bar.bag" + $ rosbags-convert bar + # Convert "foo.bag", save the result as "bar" $ rosbags-convert foo.bag --dst /path/to/bar + + # Convert "bar", save the result as "foo.bag" + $ rosbags-convert bar --dst /path/to/foo.bag diff --git a/src/rosbags/convert/__main__.py b/src/rosbags/convert/__main__.py index 60390544..544fa6d7 100644 --- a/src/rosbags/convert/__main__.py +++ b/src/rosbags/convert/__main__.py @@ -39,18 +39,23 @@ def pathtype(exists: bool = True) -> Callable[[str], Path]: def main() -> None: """Parse cli arguments and run conversion.""" - parser = argparse.ArgumentParser(description='Convert rosbag1 to rosbag2.') + parser = argparse.ArgumentParser(description='Convert between rosbag1 and rosbag2.') parser.add_argument( 'src', type=pathtype(), - help='source path to read rosbag1 from', + help='source path to read rosbag1 or rosbag2 from', ) parser.add_argument( '--dst', type=pathtype(exists=False), - help='destination path for rosbag2', + help='destination path for converted rosbag', ) + args = parser.parse_args() + if args.dst is not None and (args.src.suffix == '.bag') == (args.dst.suffix == '.bag'): + print('Source and destination rosbag versions must differ.') # noqa: T001 + sys.exit(1) + try: convert(args.src, args.dst) except ConverterError as err: diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index ef8565d4..f53ede7c 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -7,18 +7,24 @@ from __future__ import annotations from dataclasses import asdict from typing import TYPE_CHECKING -from rosbags.rosbag1 import Reader, ReaderError -from rosbags.rosbag2 import Writer, WriterError -from rosbags.rosbag2.connection import Connection as WConnection -from rosbags.serde import ros1_to_cdr +from rosbags.rosbag1 import Reader as Reader1 +from rosbags.rosbag1 import ReaderError as ReaderError1 +from rosbags.rosbag1 import Writer as Writer1 +from rosbags.rosbag1 import WriterError as WriterError1 +from rosbags.rosbag1.reader import Connection as Connection1 +from rosbags.rosbag2 import Reader as Reader2 +from rosbags.rosbag2 import ReaderError as ReaderError2 +from rosbags.rosbag2 import Writer as Writer2 +from rosbags.rosbag2 import WriterError as WriterError2 +from rosbags.rosbag2.connection import Connection as Connection2 +from rosbags.serde import cdr_to_ros1, ros1_to_cdr from rosbags.typesys import get_types_from_msg, register_types +from rosbags.typesys.msg import generate_msgdef if TYPE_CHECKING: from pathlib import Path from typing import Any, Optional - from rosbags.rosbag1.reader import Connection as RConnection - LATCH = """ - history: 3 depth: 0 @@ -42,7 +48,7 @@ class ConverterError(Exception): """Converter Error.""" -def convert_connection(rconn: RConnection) -> WConnection: +def upgrade_connection(rconn: Connection1) -> Connection2: """Convert rosbag1 connection to rosbag2 connection. Args: @@ -52,7 +58,7 @@ def convert_connection(rconn: RConnection) -> WConnection: Rosbag2 connection. """ - return WConnection( + return Connection2( -1, 0, rconn.topic, @@ -62,41 +68,108 @@ def convert_connection(rconn: RConnection) -> WConnection: ) -def convert(src: Path, dst: Optional[Path]) -> None: +def downgrade_connection(rconn: Connection2) -> Connection1: + """Convert rosbag2 connection to rosbag1 connection. + + Args: + rconn: Rosbag2 connection. + + Returns: + Rosbag1 connection. + + """ + msgdef, md5sum = generate_msgdef(rconn.msgtype) + return Connection1( + -1, + rconn.topic, + rconn.msgtype, + msgdef, + md5sum, + None, + int('durability: 1' in rconn.offered_qos_profiles), + [], + ) + + +def convert_1to2(src: Path, dst: Path) -> None: """Convert Rosbag1 to Rosbag2. Args: src: Rosbag1 path. dst: Rosbag2 path. + """ + with Reader1(src) as reader, Writer2(dst) as writer: + typs: dict[str, Any] = {} + connmap: dict[int, Connection2] = {} + + for rconn in reader.connections.values(): + candidate = upgrade_connection(rconn) + existing = next((x for x in writer.connections.values() if x == candidate), None) + wconn = existing if existing else writer.add_connection(**asdict(candidate)) + connmap[rconn.cid] = wconn + typs.update(get_types_from_msg(rconn.msgdef, rconn.msgtype)) + register_types(typs) + + for rconn, timestamp, data in reader.messages(): + data = ros1_to_cdr(data, rconn.msgtype) + writer.write(connmap[rconn.cid], timestamp, data) + + +def convert_2to1(src: Path, dst: Path) -> None: + """Convert Rosbag2 to Rosbag1. + + Args: + src: Rosbag2 path. + dst: Rosbag1 path. + + """ + with Reader2(src) as reader, Writer1(dst) as writer: + connmap: dict[int, Connection1] = {} + for rconn in reader.connections.values(): + candidate = downgrade_connection(rconn) + # yapf: disable + existing = next( + ( + x + for x in writer.connections.values() + if x.topic == candidate.topic + if x.md5sum == candidate.md5sum + if x.latching == candidate.latching + ), + None, + ) + # yapf: enable + connmap[rconn.id] = existing if existing else writer.add_connection(*candidate[1:-1]) + + for rconn, timestamp, data in reader.messages(): + data = cdr_to_ros1(data, rconn.msgtype) + writer.write(connmap[rconn.id], timestamp, data) + + +def convert(src: Path, dst: Optional[Path]) -> None: + """Convert between Rosbag1 and Rosbag2. + + Args: + src: Source rosbag. + dst: Destination rosbag. + Raises: ConverterError: An error occured during reading, writing, or converting. """ - dst = dst if dst else src.with_suffix('') + upgrade = src.suffix == '.bag' + dst = dst if dst else src.with_suffix('' if upgrade else '.bag') if dst.exists(): raise ConverterError(f'Output path {str(dst)!r} exists already.') + func = convert_1to2 if upgrade else convert_2to1 try: - with Reader(src) as reader, Writer(dst) as writer: - typs: dict[str, Any] = {} - connmap: dict[int, WConnection] = {} - - for rconn in reader.connections.values(): - candidate = convert_connection(rconn) - existing = next((x for x in writer.connections.values() if x == candidate), None) - wconn = existing if existing else writer.add_connection(**asdict(candidate)) - connmap[rconn.cid] = wconn - typs.update(get_types_from_msg(rconn.msgdef, rconn.msgtype)) - register_types(typs) - - for rconn, timestamp, data in reader.messages(): - data = ros1_to_cdr(data, rconn.msgtype) - writer.write(connmap[rconn.cid], timestamp, data) - except ReaderError as err: + func(src, dst) + except (ReaderError1, ReaderError2) as err: raise ConverterError(f'Reading source bag: {err}') from err - except WriterError as err: + except (WriterError1, WriterError2) as err: raise ConverterError(f'Writing destination bag: {err}') from err except Exception as err: # pylint: disable=broad-except raise ConverterError(f'Converting rosbag: {err!r}') from err diff --git a/src/rosbags/serde/serdes.py b/src/rosbags/serde/serdes.py index b4977fc1..920b399f 100644 --- a/src/rosbags/serde/serdes.py +++ b/src/rosbags/serde/serdes.py @@ -126,7 +126,7 @@ def cdr_to_ros1(raw: bytes, typename: str) -> memoryview: None, 0, ) - assert ipos + 4 == len(raw) + assert ipos + 4 + 3 >= len(raw) raw = memoryview(raw) size = opos @@ -138,6 +138,6 @@ def cdr_to_ros1(raw: bytes, typename: str) -> memoryview: rawdata, 0, ) - assert ipos + 4 == len(raw) + assert ipos + 4 + 3 >= len(raw) assert opos == size return rawdata.toreadonly() diff --git a/tests/test_convert.py b/tests/test_convert.py index 1b090223..c1790a7a 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -46,6 +46,15 @@ def test_cliwrapper(tmp_path: Path) -> None: main() assert not cvrt.called + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt', + str(tmp_path / 'ros1.bag'), + '--dst', + str(tmp_path / 'ros2.bag')]), \ + pytest.raises(SystemExit): + main() + assert not cvrt.called + with patch('rosbags.convert.__main__.convert') as cvrt, \ patch.object(sys, 'argv', ['cvt', str(tmp_path / 'ros1.bag'), @@ -61,17 +70,46 @@ def test_cliwrapper(tmp_path: Path) -> None: main() mock_print.assert_called_with('ERROR: exc') + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt', str(tmp_path / 'subdir')]): + main() + cvrt.assert_called_with(tmp_path / 'subdir', None) -def test_convert(tmp_path: Path) -> None: - """Test conversion function.""" + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt', + str(tmp_path / 'subdir'), + '--dst', + str(tmp_path / 'ros1.bag')]), \ + pytest.raises(SystemExit): + main() + assert not cvrt.called + + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt', + str(tmp_path / 'subdir'), + '--dst', + str(tmp_path / 'target.bag')]): + main() + cvrt.assert_called_with(tmp_path / 'subdir', tmp_path / 'target.bag') + + with patch.object(sys, 'argv', ['cvt', str(tmp_path / 'subdir')]), \ + patch('builtins.print') as mock_print, \ + patch('rosbags.convert.__main__.convert', side_effect=ConverterError('exc')), \ + pytest.raises(SystemExit): + main() + mock_print.assert_called_with('ERROR: exc') + + +def test_convert_1to2(tmp_path: Path) -> None: + """Test conversion from rosbag1 to rosbag2.""" (tmp_path / 'subdir').mkdir() (tmp_path / 'foo.bag').write_text('') with pytest.raises(ConverterError, match='exists already'): convert(Path('foo.bag'), tmp_path / 'subdir') - with patch('rosbags.convert.converter.Reader') as reader, \ - patch('rosbags.convert.converter.Writer') as writer, \ + with patch('rosbags.convert.converter.Reader1') as reader, \ + patch('rosbags.convert.converter.Writer2') as writer, \ patch('rosbags.convert.converter.get_types_from_msg', return_value={'typ': 'def'}), \ patch('rosbags.convert.converter.register_types') as register_types, \ patch('rosbags.convert.converter.ros1_to_cdr') as ros1_to_cdr: @@ -137,14 +175,112 @@ def test_convert(tmp_path: Path) -> None: register_types.assert_called_with({'typ': 'def'}) ros1_to_cdr.assert_has_calls([call(b'\x42', 'typ'), call(b'\x43', 'typ')]) + writer.return_value.__enter__.return_value.add_connection.side_effect = [ + wconnections[0], + wconnections[1], + ] ros1_to_cdr.side_effect = KeyError('exc') - with pytest.raises(ConverterError, match='Converting rosbag: '): + with pytest.raises(ConverterError, match='Converting rosbag: .*exc'): convert(Path('foo.bag'), None) writer.side_effect = WriterError('exc') - with pytest.raises(ConverterError, match='Writing destination bag: '): + with pytest.raises(ConverterError, match='Writing destination bag: exc'): convert(Path('foo.bag'), None) reader.side_effect = ReaderError('exc') - with pytest.raises(ConverterError, match='Reading source bag: '): + with pytest.raises(ConverterError, match='Reading source bag: exc'): convert(Path('foo.bag'), None) + + +def test_convert_2to1(tmp_path: Path) -> None: + """Test conversion from rosbag2 to rosbag1.""" + (tmp_path / 'subdir').mkdir() + (tmp_path / 'foo.bag').write_text('') + + with pytest.raises(ConverterError, match='exists already'): + convert(Path('subdir'), tmp_path / 'foo.bag') + + with patch('rosbags.convert.converter.Reader2') as reader, \ + patch('rosbags.convert.converter.Writer1') as writer, \ + patch('rosbags.convert.converter.cdr_to_ros1') as cdr_to_ros1: + + connections = [ + Mock(topic='/topic', msgtype='std_msgs/msg/Bool', offered_qos_profiles=''), + Mock(topic='/topic', msgtype='std_msgs/msg/Bool', offered_qos_profiles=LATCH), + ] + + wconnections = [ + Mock(topic='/topic', msgtype='typ'), + Mock(topic='/topic', msgtype='typ'), + ] + + reader.return_value.__enter__.return_value.connections = { + 1: connections[0], + 2: connections[1], + } + + reader.return_value.__enter__.return_value.messages.return_value = [ + (connections[0], 42, b'\x42'), + (connections[1], 43, b'\x43'), + ] + + writer.return_value.__enter__.return_value.add_connection.side_effect = [ + wconnections[0], + wconnections[1], + ] + + cdr_to_ros1.return_value = b'666' + + convert(Path('foo'), None) + + reader.assert_called_with(Path('foo')) + reader.return_value.__enter__.return_value.messages.assert_called_with() + + writer.assert_called_with(Path('foo.bag')) + writer.return_value.__enter__.return_value.add_connection.assert_has_calls( + [ + call( + '/topic', + 'std_msgs/msg/Bool', + 'bool data\n', + '8b94c1b53db61fb6aed406028ad6332a', + None, + 0, + ), + call( + '/topic', + 'std_msgs/msg/Bool', + 'bool data\n', + '8b94c1b53db61fb6aed406028ad6332a', + None, + 1, + ), + ], + ) + writer.return_value.__enter__.return_value.write.assert_has_calls( + [call(wconnections[0], 42, b'666'), + call(wconnections[1], 43, b'666')], + ) + + cdr_to_ros1.assert_has_calls( + [ + call(b'\x42', 'std_msgs/msg/Bool'), + call(b'\x43', 'std_msgs/msg/Bool'), + ], + ) + + writer.return_value.__enter__.return_value.add_connection.side_effect = [ + wconnections[0], + wconnections[1], + ] + cdr_to_ros1.side_effect = KeyError('exc') + with pytest.raises(ConverterError, match='Converting rosbag: .*exc'): + convert(Path('foo'), None) + + writer.side_effect = WriterError('exc') + with pytest.raises(ConverterError, match='Writing destination bag: exc'): + convert(Path('foo'), None) + + reader.side_effect = ReaderError('exc') + with pytest.raises(ConverterError, match='Reading source bag: exc'): + convert(Path('foo'), None) From 25520eab5760157429c08a3ec439e140fe5ae52f Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 10 Jan 2022 11:51:20 +0100 Subject: [PATCH 061/114] Release 0.9.9 --- CHANGES.rst | 21 ++++++++++++++++++--- setup.cfg | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7a4ae92b..f968582d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,17 @@ Changes ======= +0.9.9 - 2022-01-10 +------------------ +- Fix documentation code samples `#15`_ +- Fix handling of padding after empty sequences `#14`_ +- Support conversion from rosbag2 to rosbag1 `#11`_ + +.. _#11: https://gitlab.com/ternaris/rosbags/issues/11 +.. _#14: https://gitlab.com/ternaris/rosbags/issues/14 +.. _#15: https://gitlab.com/ternaris/rosbags/issues/15 + + 0.9.8 - 2021-11-25 ------------------ - Support bool and float constants in msg files @@ -10,11 +21,13 @@ Changes 0.9.7 - 2021-11-09 ------------------ -- Fix parsing of const fields with string value +- Fix parsing of const fields with string value `#9`_ - Parse empty msg definitions - Make packages PEP561 compliant -- Parse msg bounded fields and default values +- Parse msg bounded fields and default values `#12`_ +.. _#9: https://gitlab.com/ternaris/rosbags/issues/9 +.. _#12: https://gitlab.com/ternaris/rosbags/issues/12 0.9.6 - 2021-10-04 ------------------ @@ -29,8 +42,10 @@ Changes 0.9.4 - 2021-09-15 ------------------ - Make reader1 API match reader2 -- Fix connection mapping for reader2 messages +- Fix connection mapping for reader2 messages `#1`_, `#8`_ +.. _#1: https://gitlab.com/ternaris/rosbags/issues/1 +.. _#8: https://gitlab.com/ternaris/rosbags/issues/8 0.9.3 - 2021-08-06 ------------------ diff --git a/setup.cfg b/setup.cfg index 6ab57634..aee0ae1b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.8 +version = 0.9.9 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From af4830c647e6392ca28eb2321608e2fa09a677a4 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 31 Jan 2022 12:01:37 +0100 Subject: [PATCH 062/114] Adjust conversion documentation title --- docs/topics/convert.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/topics/convert.rst b/docs/topics/convert.rst index d305e675..f44fd06a 100644 --- a/docs/topics/convert.rst +++ b/docs/topics/convert.rst @@ -1,5 +1,5 @@ -Convert Rosbag1 to Rosbag2 -========================== +Convert rosbag versions +======================= The :py:mod:`rosbags.convert` package includes a CLI tool to convert legacy rosbag1 files to rosbag2 and vice versa. From f5d3494cbbfc0e341754ee6c853620173a350f53 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 31 Jan 2022 13:35:42 +0100 Subject: [PATCH 063/114] Add common usage examples to docs --- docs/examples/edit_rosbags_edit_timestamps.py | 42 ++++++++++++ docs/examples/edit_rosbags_remove_topic.py | 36 ++++++++++ docs/examples/edit_rosbas.rst | 16 +++++ docs/examples/register_types.rst | 22 +++++++ docs/examples/register_types_files.py | 24 +++++++ docs/examples/register_types_rosbag1.py | 29 ++++++++ docs/examples/register_types_string.py | 25 +++++++ docs/examples/save_images.rst | 16 +++++ docs/examples/save_images_rosbag1.py | 46 +++++++++++++ docs/examples/save_images_rosbag2.py | 43 ++++++++++++ docs/examples/use_with_native.py | 66 +++++++++++++++++++ docs/examples/use_with_native.rst | 10 +++ docs/index.rst | 7 ++ 13 files changed, 382 insertions(+) create mode 100644 docs/examples/edit_rosbags_edit_timestamps.py create mode 100644 docs/examples/edit_rosbags_remove_topic.py create mode 100644 docs/examples/edit_rosbas.rst create mode 100644 docs/examples/register_types.rst create mode 100644 docs/examples/register_types_files.py create mode 100644 docs/examples/register_types_rosbag1.py create mode 100644 docs/examples/register_types_string.py create mode 100644 docs/examples/save_images.rst create mode 100644 docs/examples/save_images_rosbag1.py create mode 100644 docs/examples/save_images_rosbag2.py create mode 100644 docs/examples/use_with_native.py create mode 100644 docs/examples/use_with_native.rst diff --git a/docs/examples/edit_rosbags_edit_timestamps.py b/docs/examples/edit_rosbags_edit_timestamps.py new file mode 100644 index 00000000..36e2cdc1 --- /dev/null +++ b/docs/examples/edit_rosbags_edit_timestamps.py @@ -0,0 +1,42 @@ +"""Example: Edit timestamps.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rosbags.rosbag2 import Reader, Writer +from rosbags.serde import deserialize_cdr, serialize_cdr + +if TYPE_CHECKING: + from pathlib import Path + + +def offset_timestamps(src: Path, dst: Path, offset: int) -> None: + """Offset timestamps. + + Args: + src: Source path. + dst: Destination path. + offset: Amount of nanoseconds to offset timestamps. + + """ + with Reader(src) as reader, Writer(dst) as writer: + conn_map = {} + for conn in reader.connections.values(): + conn_map[conn.id] = writer.add_connection( + conn.topic, + conn.msgtype, + conn.serialization_format, + conn.offered_qos_profiles, + ) + + for conn, timestamp, data in reader.messages(): + # Adjust header timestamps, too + msg = deserialize_cdr(data, conn.msgtype) + if head := getattr(msg, 'header', None): + headstamp = head.stamp.sec * 10**9 + head.stamp.nanosec + offset + head.stamp.sec = headstamp // 10**9 + head.stamp.nanosec = headstamp % 10**9 + data = serialize_cdr(msg, conn.msgtype) + + writer.write(conn_map[conn.id], timestamp + offset, data) diff --git a/docs/examples/edit_rosbags_remove_topic.py b/docs/examples/edit_rosbags_remove_topic.py new file mode 100644 index 00000000..0e416a84 --- /dev/null +++ b/docs/examples/edit_rosbags_remove_topic.py @@ -0,0 +1,36 @@ +"""Example: Remove topic.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rosbags.rosbag2 import Reader, Writer + +if TYPE_CHECKING: + from pathlib import Path + + +def remove_topic(src: Path, dst: Path, topic: str) -> None: + """Remove topic from rosbag2. + + Args: + src: Source path. + dst: Destination path. + topic: Name of topic to remove. + + """ + with Reader(src) as reader, Writer(dst) as writer: + conn_map = {} + for conn in reader.connections.values(): + if conn.topic == topic: + continue + conn_map[conn.id] = writer.add_connection( + conn.topic, + conn.msgtype, + conn.serialization_format, + conn.offered_qos_profiles, + ) + + rconns = [reader.connections[x] for x in conn_map] + for conn, timestamp, data in reader.messages(connections=rconns): + writer.write(conn_map[conn.id], timestamp, data) diff --git a/docs/examples/edit_rosbas.rst b/docs/examples/edit_rosbas.rst new file mode 100644 index 00000000..7e90cfb6 --- /dev/null +++ b/docs/examples/edit_rosbas.rst @@ -0,0 +1,16 @@ +Edit rosbags +============ + +Rosbags does not support opening files in read-write mode, but implicitly enforces copy-on-write semantics. Apart from the mapping of reader to writer connections the process is fairly straightforward. + + +Remove topic +------------ + +.. literalinclude:: ./edit_rosbags_remove_topic.py + + +Edit timestamps +--------------- + +.. literalinclude:: ./edit_rosbags_edit_timestamps.py diff --git a/docs/examples/register_types.rst b/docs/examples/register_types.rst new file mode 100644 index 00000000..c946b245 --- /dev/null +++ b/docs/examples/register_types.rst @@ -0,0 +1,22 @@ +Register custom message types +============================= + +Out of the box rosbags only supports the message types that ship with a default ROS2 distribution. If you want to (de)serialize custom messages you need to add them to the type system manually. + + +From rosbag1 +------------ + +.. literalinclude:: ./register_types_rosbag1.py + + +From definition string +---------------------- + +.. literalinclude:: ./register_types_string.py + + +From multiple files +------------------- + +.. literalinclude:: ./register_types_files.py diff --git a/docs/examples/register_types_files.py b/docs/examples/register_types_files.py new file mode 100644 index 00000000..3cf1fd57 --- /dev/null +++ b/docs/examples/register_types_files.py @@ -0,0 +1,24 @@ +"""Example: Register types from msg files.""" + +from pathlib import Path + +from rosbags.typesys import get_types_from_msg, register_types + +add_types = {} + +msgdef = Path('/path/to/custom_msgs/msg/Speed.msg').read_text(encoding='utf-8') +add_types.update(get_types_from_msg(msgdef, 'custom_msgs/msg/Speed.msg')) + +msgdef = Path('/path/to/custom_msgs/msg/Accel.msg').read_text(encoding='utf-8') +add_types.update(get_types_from_msg(msgdef, 'custom_msgs/msg/Accel.msg')) + +register_types(add_types) + +# Type import works only after the register_types call, +# the classname is derived from the msgtype names above + +# pylint: disable=no-name-in-module,wrong-import-position +from rosbags.typesys.types import custom_msgs__msg__Speed as Speed # type: ignore # noqa +from rosbags.typesys.types import custom_msgs__msg__Accel as Accel # type: ignore # noqa + +# pylint: enable=no-name-in-module,wrong-import-position diff --git a/docs/examples/register_types_rosbag1.py b/docs/examples/register_types_rosbag1.py new file mode 100644 index 00000000..0be64334 --- /dev/null +++ b/docs/examples/register_types_rosbag1.py @@ -0,0 +1,29 @@ +"""Example: Register rosbag1 types.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rosbags.rosbag1 import Reader +from rosbags.typesys import get_types_from_msg, register_types + +if TYPE_CHECKING: + from pathlib import Path + + +def process_bag(src: Path) -> None: + """Register contained messages types before processing bag. + + Args: + src: Bag to process. + + """ + with Reader(src) as reader: + typs = {} + for conn in reader.connections.values(): + typs.update(get_types_from_msg(conn.msgdef, conn.msgtype)) + register_types(typs) + + # Now all message types used in the bag are registered + # for conn, timestamp, data in reader.messages(): + # ... diff --git a/docs/examples/register_types_string.py b/docs/examples/register_types_string.py new file mode 100644 index 00000000..21f56934 --- /dev/null +++ b/docs/examples/register_types_string.py @@ -0,0 +1,25 @@ +"""Example: Register type from definition string.""" + +from rosbags.serde import serialize_cdr +from rosbags.typesys import get_types_from_msg, register_types + +# Your custom message definition +STRIDX_MSG = """ +string string +uint32 index +""" + +register_types(get_types_from_msg(STRIDX_MSG, 'custom_msgs/msg/StrIdx')) + +# Type import works only after the register_types call, +# the classname is derived from the msgtype name above + +# pylint: disable=no-name-in-module,wrong-import-position +from rosbags.typesys.types import custom_msgs__msg__StrIdx as StrIdx # type: ignore # noqa + +# pylint: enable=no-name-in-module,wrong-import-position + +message = StrIdx(string='foo', index=42) + +# Rawdata that can be passed to rosbag2.Writer.write +rawdata = serialize_cdr(message, message.__msgtype__) diff --git a/docs/examples/save_images.rst b/docs/examples/save_images.rst new file mode 100644 index 00000000..9296f634 --- /dev/null +++ b/docs/examples/save_images.rst @@ -0,0 +1,16 @@ +Save images as rosbag +===================== + +The following examples show how to create new ROS bags from images. + + +Save rosbag1 +------------ + +.. literalinclude:: ./save_images_rosbag1.py + + +Save rosbag2 +------------ + +.. literalinclude:: ./save_images_rosbag2.py diff --git a/docs/examples/save_images_rosbag1.py b/docs/examples/save_images_rosbag1.py new file mode 100644 index 00000000..60acc8e2 --- /dev/null +++ b/docs/examples/save_images_rosbag1.py @@ -0,0 +1,46 @@ +"""Example: Save images as rosbag1.""" + +import numpy + +from rosbags.rosbag1 import Writer +from rosbags.serde import cdr_to_ros1, serialize_cdr +from rosbags.typesys.types import builtin_interfaces__msg__Time as Time +from rosbags.typesys.types import sensor_msgs__msg__CompressedImage as CompressedImage +from rosbags.typesys.types import std_msgs__msg__Header as Header + +TOPIC = '/camera' +FRAMEID = 'map' + +# Contains filenames and their timestamps +IMAGES = [ + ('homer.jpg', 42), + ('marge.jpg', 43), +] + + +def save_images() -> None: + """Iterate over IMAGES and save to output bag.""" + with Writer('output.bag') as writer: + conn = writer.add_connection(TOPIC, CompressedImage.__msgtype__) + + for path, timestamp in IMAGES: + message = CompressedImage( + Header( + stamp=Time( + sec=int(timestamp // 10**9), + nanosec=int(timestamp % 10**9), + ), + frame_id=FRAMEID, + ), + format='jpeg', # could also be 'png' + data=numpy.fromfile(path, dtype=numpy.uint8), # type: ignore + ) + + writer.write( + conn, + timestamp, + cdr_to_ros1( + serialize_cdr(message, CompressedImage.__msgtype__), + CompressedImage.__msgtype__, + ), + ) diff --git a/docs/examples/save_images_rosbag2.py b/docs/examples/save_images_rosbag2.py new file mode 100644 index 00000000..673d4da5 --- /dev/null +++ b/docs/examples/save_images_rosbag2.py @@ -0,0 +1,43 @@ +"""Save multiple images in rosbag2.""" + +import numpy + +from rosbags.rosbag2 import Writer +from rosbags.serde import serialize_cdr +from rosbags.typesys.types import builtin_interfaces__msg__Time as Time +from rosbags.typesys.types import sensor_msgs__msg__CompressedImage as CompressedImage +from rosbags.typesys.types import std_msgs__msg__Header as Header + +TOPIC = '/camera' +FRAMEID = 'map' + +# Contains filenames and their timestamps +IMAGES = [ + ('homer.jpg', 42), + ('marge.jpg', 43), +] + + +def save_images() -> None: + """Iterate over IMAGES and save to output bag.""" + with Writer('output') as writer: + conn = writer.add_connection(TOPIC, CompressedImage.__msgtype__, 'cdr', '') + + for path, timestamp in IMAGES: + message = CompressedImage( + Header( + stamp=Time( + sec=int(timestamp // 10**9), + nanosec=int(timestamp % 10**9), + ), + frame_id=FRAMEID, + ), + format='jpeg', # could also be 'png' + data=numpy.fromfile(path, dtype=numpy.uint8), # type: ignore + ) + + writer.write( + conn, + timestamp, + serialize_cdr(message, message.__msgtype__), + ) diff --git a/docs/examples/use_with_native.py b/docs/examples/use_with_native.py new file mode 100644 index 00000000..f484bfe3 --- /dev/null +++ b/docs/examples/use_with_native.py @@ -0,0 +1,66 @@ +"""Example: Message instance conversion.""" + +from __future__ import annotations + +import importlib +from typing import TYPE_CHECKING + +import numpy + +if TYPE_CHECKING: + from typing import Any + +NATIVE_CLASSES: dict[str, Any] = {} + + +def to_native(msg: Any) -> Any: + """Convert rosbags message to native message. + + Args: + msg: Rosbags message. + + Returns: + Native message. + + """ + msgtype: str = msg.__msgtype__ + if msgtype not in NATIVE_CLASSES: + pkg, name = msgtype.rsplit('/', 1) + NATIVE_CLASSES[msgtype] = getattr(importlib.import_module(pkg.replace('/', '.')), name) + + fields = {} + for name, field in msg.__dataclass_fields__.items(): + if 'ClassVar' in field.type: + continue + value = getattr(msg, name) + if '__msg__' in field.type: + value = to_native(value) + elif isinstance(value, numpy.ndarray): + value = value.tolist() + fields[name] = value + + return NATIVE_CLASSES[msgtype](**fields) + + +if __name__ == '__main__': + from rosbags.typesys.types import ( + builtin_interfaces__msg__Time, + sensor_msgs__msg__Image, + std_msgs__msg__Header, + ) + + image = sensor_msgs__msg__Image( + std_msgs__msg__Header( + builtin_interfaces__msg__Time(42, 666), + '/frame', + ), + 4, + 4, + 'rgb8', + False, + 4 * 3, + numpy.zeros(4 * 4 * 3, dtype=numpy.uint8), + ) + + native_image = to_native(image) + # native_image can now be passed to the ROS stack diff --git a/docs/examples/use_with_native.rst b/docs/examples/use_with_native.rst new file mode 100644 index 00000000..a245e13e --- /dev/null +++ b/docs/examples/use_with_native.rst @@ -0,0 +1,10 @@ +Use with native stack +===================== + +Messages read with rosbags are simple dataclasses that mimic the native ROS2 interface. If you want to pass those messages to the native ROS2 stack, you need to convert them into native objects first. + + +Message instance conversion +--------------------------- + +.. literalinclude:: ./use_with_native.py diff --git a/docs/index.rst b/docs/index.rst index 904b22ff..d609e6df 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,6 +16,13 @@ topics/rosbag1 topics/convert +.. toctree:: + :caption: Usage examples + :maxdepth: 0 + :hidden: + :glob: + + examples/* .. toctree:: :caption: API From 946f2edb42024793b60e305596987a0aef4345b5 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 10 Apr 2022 23:32:25 +0200 Subject: [PATCH 064/114] Update copyright headers --- docs/conf.py | 4 ++-- src/rosbags/convert/__init__.py | 2 +- src/rosbags/convert/__main__.py | 2 +- src/rosbags/convert/converter.py | 2 +- src/rosbags/rosbag1/__init__.py | 2 +- src/rosbags/rosbag1/reader.py | 2 +- src/rosbags/rosbag1/writer.py | 2 +- src/rosbags/rosbag2/__init__.py | 2 +- src/rosbags/rosbag2/connection.py | 2 +- src/rosbags/rosbag2/reader.py | 2 +- src/rosbags/rosbag2/writer.py | 2 +- src/rosbags/serde/__init__.py | 2 +- src/rosbags/serde/cdr.py | 2 +- src/rosbags/serde/messages.py | 2 +- src/rosbags/serde/primitives.py | 2 +- src/rosbags/serde/ros1.py | 2 +- src/rosbags/serde/serdes.py | 2 +- src/rosbags/serde/typing.py | 2 +- src/rosbags/serde/utils.py | 2 +- src/rosbags/typesys/__init__.py | 2 +- src/rosbags/typesys/__main__.py | 2 +- src/rosbags/typesys/base.py | 2 +- src/rosbags/typesys/idl.py | 2 +- src/rosbags/typesys/msg.py | 2 +- src/rosbags/typesys/peg.py | 2 +- src/rosbags/typesys/register.py | 4 ++-- src/rosbags/typesys/types.py | 2 +- tests/__init__.py | 2 +- tests/cdr.py | 2 +- tests/test_convert.py | 2 +- tests/test_parse.py | 2 +- tests/test_reader.py | 2 +- tests/test_reader1.py | 2 +- tests/test_roundtrip.py | 2 +- tests/test_roundtrip1.py | 2 +- tests/test_serde.py | 2 +- tests/test_writer.py | 2 +- tests/test_writer1.py | 2 +- tools/bench/bench.py | 2 +- tools/compare/compare.py | 2 +- 40 files changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 39841a8a..e0b0441f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Sphinx config.""" @@ -18,7 +18,7 @@ import sphinx_rtd_theme # type: ignore # noqa typing.TYPE_CHECKING = True project = 'Rosbags' -copyright = '2020-2021, Ternaris' +copyright = '2020-2022, Ternaris' author = 'Ternaris' autoapi_python_use_implicit_namespaces = True diff --git a/src/rosbags/convert/__init__.py b/src/rosbags/convert/__init__.py index e4eddce0..0fe62a9d 100644 --- a/src/rosbags/convert/__init__.py +++ b/src/rosbags/convert/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags file format conversion. diff --git a/src/rosbags/convert/__main__.py b/src/rosbags/convert/__main__.py index 544fa6d7..b6f14159 100644 --- a/src/rosbags/convert/__main__.py +++ b/src/rosbags/convert/__main__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """CLI tool for rosbag conversion.""" diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index f53ede7c..c20f4735 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag1 to Rosbag2 Converter.""" diff --git a/src/rosbags/rosbag1/__init__.py b/src/rosbags/rosbag1/__init__.py index 192bcf62..4e668933 100644 --- a/src/rosbags/rosbag1/__init__.py +++ b/src/rosbags/rosbag1/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags support for rosbag1 files. diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 72f10021..83ad78a8 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag1 v2.0 reader.""" diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index 3f6d1719..9126a378 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag1 writer.""" diff --git a/src/rosbags/rosbag2/__init__.py b/src/rosbags/rosbag2/__init__.py index ca3cccbf..c11043cb 100644 --- a/src/rosbags/rosbag2/__init__.py +++ b/src/rosbags/rosbag2/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags support for rosbag2 files. diff --git a/src/rosbags/rosbag2/connection.py b/src/rosbags/rosbag2/connection.py index 76d080fc..73d5daa3 100644 --- a/src/rosbags/rosbag2/connection.py +++ b/src/rosbags/rosbag2/connection.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag2 connection.""" diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 75fd616e..ac719ec5 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag2 reader.""" diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index a6b74daa..915b6360 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag2 writer.""" diff --git a/src/rosbags/serde/__init__.py b/src/rosbags/serde/__init__.py index 707afd50..ef56d345 100644 --- a/src/rosbags/serde/__init__.py +++ b/src/rosbags/serde/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags message serialization and deserialization. diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index 08052661..7d7587d3 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Code generators for CDR. diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py index d83ce2c2..df9c8e0c 100644 --- a/src/rosbags/serde/messages.py +++ b/src/rosbags/serde/messages.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Runtime message loader and cache.""" diff --git a/src/rosbags/serde/primitives.py b/src/rosbags/serde/primitives.py index 21e04338..a5121657 100644 --- a/src/rosbags/serde/primitives.py +++ b/src/rosbags/serde/primitives.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Serialization primitives. diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index 387a1612..d51a83e0 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Code generators for ROS1. diff --git a/src/rosbags/serde/serdes.py b/src/rosbags/serde/serdes.py index 920b399f..57e92ceb 100644 --- a/src/rosbags/serde/serdes.py +++ b/src/rosbags/serde/serdes.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Serialization, deserializion and conversion functions.""" diff --git a/src/rosbags/serde/typing.py b/src/rosbags/serde/typing.py index 1079712e..413059a9 100644 --- a/src/rosbags/serde/typing.py +++ b/src/rosbags/serde/typing.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Python types used in this package.""" diff --git a/src/rosbags/serde/utils.py b/src/rosbags/serde/utils.py index 41cdf3fe..67407241 100644 --- a/src/rosbags/serde/utils.py +++ b/src/rosbags/serde/utils.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Helpers used by code generators.""" diff --git a/src/rosbags/typesys/__init__.py b/src/rosbags/typesys/__init__.py index 413df8b0..4e398555 100644 --- a/src/rosbags/typesys/__init__.py +++ b/src/rosbags/typesys/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags Type System. diff --git a/src/rosbags/typesys/__main__.py b/src/rosbags/typesys/__main__.py index 14178a7e..5f9f957f 100644 --- a/src/rosbags/typesys/__main__.py +++ b/src/rosbags/typesys/__main__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Tool to update builtin types shipped with rosbags.""" diff --git a/src/rosbags/typesys/base.py b/src/rosbags/typesys/base.py index cbed9279..33a56e1a 100644 --- a/src/rosbags/typesys/base.py +++ b/src/rosbags/typesys/base.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Types and helpers used by message definition converters.""" diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index f8bee88f..8dc9c6d4 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """IDL Parser. diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 5ee4df97..42b583a9 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """MSG Parser. diff --git a/src/rosbags/typesys/peg.py b/src/rosbags/typesys/peg.py index 833cfa09..27d7d5ff 100644 --- a/src/rosbags/typesys/peg.py +++ b/src/rosbags/typesys/peg.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """PEG Parser. diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index 6aa3bf9e..fbdd3e31 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Code generators and registration functions for the extensible type system.""" @@ -57,7 +57,7 @@ def generate_python_code(typs: Typesdict) -> str: """ lines = [ - '# Copyright 2020-2021 Ternaris.', + '# Copyright 2020-2022 Ternaris.', '# SPDX-License-Identifier: Apache-2.0', '#', '# THIS FILE IS GENERATED, DO NOT EDIT', diff --git a/src/rosbags/typesys/types.py b/src/rosbags/typesys/types.py index 63ece623..c393be6d 100644 --- a/src/rosbags/typesys/types.py +++ b/src/rosbags/typesys/types.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 # # THIS FILE IS GENERATED, DO NOT EDIT diff --git a/tests/__init__.py b/tests/__init__.py index 5a703b86..bec3ab4e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,3 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag tests.""" diff --git a/tests/cdr.py b/tests/cdr.py index 9db001cd..036576da 100644 --- a/tests/cdr.py +++ b/tests/cdr.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Reference CDR message serializer and deserializer.""" diff --git a/tests/test_convert.py b/tests/test_convert.py index c1790a7a..fa93351b 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag1to2 converter tests.""" diff --git a/tests/test_parse.py b/tests/test_parse.py index dc552975..e3453bd6 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Message definition parser tests.""" diff --git a/tests/test_reader.py b/tests/test_reader.py index a941ead7..4f15f2ff 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Reader tests.""" diff --git a/tests/test_reader1.py b/tests/test_reader1.py index 289027c8..550f417e 100644 --- a/tests/test_reader1.py +++ b/tests/test_reader1.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Reader tests.""" diff --git a/tests/test_roundtrip.py b/tests/test_roundtrip.py index f087b48c..bb80b4d0 100644 --- a/tests/test_roundtrip.py +++ b/tests/test_roundtrip.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Test full data roundtrip.""" diff --git a/tests/test_roundtrip1.py b/tests/test_roundtrip1.py index 5d677b05..95d3ace7 100644 --- a/tests/test_roundtrip1.py +++ b/tests/test_roundtrip1.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Test full data roundtrip.""" diff --git a/tests/test_serde.py b/tests/test_serde.py index cc146c10..d834f34a 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Serializer and deserializer tests.""" diff --git a/tests/test_writer.py b/tests/test_writer.py index ebb2c354..7dc7ee4d 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Writer tests.""" diff --git a/tests/test_writer1.py b/tests/test_writer1.py index b3c7f1e8..ab7d34aa 100644 --- a/tests/test_writer1.py +++ b/tests/test_writer1.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Writer tests.""" diff --git a/tools/bench/bench.py b/tools/bench/bench.py index b6330fcf..0a673541 100644 --- a/tools/bench/bench.py +++ b/tools/bench/bench.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Check and benchmark rosbag2 read implementations.""" diff --git a/tools/compare/compare.py b/tools/compare/compare.py index bb9e34dc..cda5ee7e 100644 --- a/tools/compare/compare.py +++ b/tools/compare/compare.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Ternaris. +# Copyright 2020-2022 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Tool checking if contents of two rosbags are equal.""" From 24bea31f380c35cd297d9f9400cd35e8f37454c3 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 10 Apr 2022 23:33:44 +0200 Subject: [PATCH 065/114] Update project settings --- docs/conf.py | 14 +++++++------- setup.cfg | 5 ++++- setup.py | 7 ------- 3 files changed, 11 insertions(+), 15 deletions(-) delete mode 100644 setup.py diff --git a/docs/conf.py b/docs/conf.py index e0b0441f..17b2ed43 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,13 +5,13 @@ import typing # https://github.com/sphinx-doc/sphinx/issues/9243 -# pylint: disable=unused-import -import sphinx.builders.html # noqa -import sphinx.builders.latex # noqa -import sphinx.builders.texinfo # noqa -import sphinx.builders.text # noqa -import sphinx.ext.autodoc # noqa -import sphinx_rtd_theme # type: ignore # noqa +import sphinx.builders.html as _1 +import sphinx.builders.latex as _2 +import sphinx.builders.texinfo as _3 +import sphinx.builders.text as _4 +import sphinx.ext.autodoc as _5 + +__all__ = ['_1', '_2', '_3', '_4', '_5'] # pylint: disable=invalid-name,redefined-builtin diff --git a/setup.cfg b/setup.cfg index aee0ae1b..8aa10ecb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -156,6 +156,8 @@ max-line-length = 100 enable = all disable = duplicate-code, + locally-disabled, + suppressed-message, ungrouped-imports, # isort (pylint FAQ) wrong-import-order, @@ -166,7 +168,7 @@ disable = # pep8-naming (pylint FAQ, keep: invalid-name) bad-classmethod-argument, bad-mcs-classmethod-argument, - no-self-argument + no-self-argument, # pycodestyle (pylint FAQ) bad-indentation, bare-except, @@ -203,6 +205,7 @@ addopts = --cov-branch --cov-report=html --cov-report=term + --cov-report=xml --no-cov-on-fail --junitxml=report.xml junit_family=xunit2 diff --git a/setup.py b/setup.py deleted file mode 100644 index 10c3180c..00000000 --- a/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright 2020-2021 Ternaris. -# SPDX-License-Identifier: Apache-2.0 -"""Minimal setuptools boilerplate.""" - -from setuptools import setup # type: ignore - -setup() From 7315a4ab4d5ad6b329f2b2f6f3ac4baf9bb2912d Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 10 Apr 2022 23:47:10 +0200 Subject: [PATCH 066/114] Update dependencies --- requirements-dev.txt | 1001 +++++++++++++++++++++--------------------- requirements.txt | 225 +++++----- 2 files changed, 613 insertions(+), 613 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5107a052..b82c85fc 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile +# This file is autogenerated by pip-compile with python 3.10 # To update, run: # -# pip-compile --generate-hashes +# pip-compile --extra=dev --generate-hashes --output-file=requirements-dev.txt setup.cfg # alabaster==0.7.12 \ --hash=sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359 \ @@ -12,14 +12,15 @@ astor==0.8.1 \ --hash=sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5 \ --hash=sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e # via flake8-simplify -astroid==2.8.3 \ - --hash=sha256:0e361da0744d5011d4f5d57e64473ba9b7ab4da1e2d45d6631ebd67dd28c3cce \ - --hash=sha256:f9d66e3a4a0e5b52819b2ff41ac2b179df9d180697db71c92beb33a60c661794 +astroid==2.11.2 \ + --hash=sha256:8d0a30fe6481ce919f56690076eafbb2fb649142a89dc874f1ec0e7a011492d0 \ + --hash=sha256:cc8cc0d2d916c42d0a7c476c57550a4557a083081976bf42a73414322a6411d9 # via pylint -attrs==21.2.0 \ - --hash=sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1 \ - --hash=sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb +attrs==21.4.0 \ + --hash=sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4 \ + --hash=sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd # via + # flake8-annotations # flake8-bugbear # pytest # pytest-mypy @@ -31,130 +32,74 @@ certifi==2021.10.8 \ --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 # via requests -charset-normalizer==2.0.7 \ - --hash=sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0 \ - --hash=sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b +charset-normalizer==2.0.12 \ + --hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 \ + --hash=sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df # via requests -coverage[toml]==6.0.2 \ - --hash=sha256:04560539c19ec26995ecfb3d9307ff154fbb9a172cb57e3b3cfc4ced673103d1 \ - --hash=sha256:1549e1d08ce38259de2bc3e9a0d5f3642ff4a8f500ffc1b2df73fd621a6cdfc0 \ - --hash=sha256:1db67c497688fd4ba85b373b37cc52c50d437fd7267520ecd77bddbd89ea22c9 \ - --hash=sha256:30922626ce6f7a5a30bdba984ad21021529d3d05a68b4f71ea3b16bda35b8895 \ - --hash=sha256:36e9040a43d2017f2787b28d365a4bb33fcd792c7ff46a047a04094dc0e2a30d \ - --hash=sha256:381d773d896cc7f8ba4ff3b92dee4ed740fb88dfe33b6e42efc5e8ab6dfa1cfe \ - --hash=sha256:3bbda1b550e70fa6ac40533d3f23acd4f4e9cb4e6e77251ce77fdf41b3309fb2 \ - --hash=sha256:3be1206dc09fb6298de3fce70593e27436862331a85daee36270b6d0e1c251c4 \ - --hash=sha256:424c44f65e8be58b54e2b0bd1515e434b940679624b1b72726147cfc6a9fc7ce \ - --hash=sha256:4b34ae4f51bbfa5f96b758b55a163d502be3dcb24f505d0227858c2b3f94f5b9 \ - --hash=sha256:4e28d2a195c533b58fc94a12826f4431726d8eb029ac21d874345f943530c122 \ - --hash=sha256:53a294dc53cfb39c74758edaa6305193fb4258a30b1f6af24b360a6c8bd0ffa7 \ - --hash=sha256:60e51a3dd55540bec686d7fff61b05048ca31e804c1f32cbb44533e6372d9cc3 \ - --hash=sha256:61b598cbdbaae22d9e34e3f675997194342f866bb1d781da5d0be54783dce1ff \ - --hash=sha256:6807947a09510dc31fa86f43595bf3a14017cd60bf633cc746d52141bfa6b149 \ - --hash=sha256:6a6a9409223a27d5ef3cca57dd7cd4dfcb64aadf2fad5c3b787830ac9223e01a \ - --hash=sha256:7092eab374346121805fb637572483270324407bf150c30a3b161fc0c4ca5164 \ - --hash=sha256:77b1da5767ed2f44611bc9bc019bc93c03fa495728ec389759b6e9e5039ac6b1 \ - --hash=sha256:8251b37be1f2cd9c0e5ccd9ae0380909c24d2a5ed2162a41fcdbafaf59a85ebd \ - --hash=sha256:9f1627e162e3864a596486774876415a7410021f4b67fd2d9efdf93ade681afc \ - --hash=sha256:a1b73c7c4d2a42b9d37dd43199c5711d91424ff3c6c22681bc132db4a4afec6f \ - --hash=sha256:a82d79586a0a4f5fd1cf153e647464ced402938fbccb3ffc358c7babd4da1dd9 \ - --hash=sha256:abbff240f77347d17306d3201e14431519bf64495648ca5a49571f988f88dee9 \ - --hash=sha256:ad9b8c1206ae41d46ec7380b78ba735ebb77758a650643e841dd3894966c31d0 \ - --hash=sha256:bbffde2a68398682623d9dd8c0ca3f46fda074709b26fcf08ae7a4c431a6ab2d \ - --hash=sha256:bcae10fccb27ca2a5f456bf64d84110a5a74144be3136a5e598f9d9fb48c0caa \ - --hash=sha256:c9cd3828bbe1a40070c11fe16a51df733fd2f0cb0d745fb83b7b5c1f05967df7 \ - --hash=sha256:cd1cf1deb3d5544bd942356364a2fdc8959bad2b6cf6eb17f47d301ea34ae822 \ - --hash=sha256:d036dc1ed8e1388e995833c62325df3f996675779541f682677efc6af71e96cc \ - --hash=sha256:db42baa892cba723326284490283a68d4de516bfb5aaba369b4e3b2787a778b7 \ - --hash=sha256:e4fb7ced4d9dec77d6cf533acfbf8e1415fe799430366affb18d69ee8a3c6330 \ - --hash=sha256:e7a0b42db2a47ecb488cde14e0f6c7679a2c5a9f44814393b162ff6397fcdfbb \ - --hash=sha256:f2f184bf38e74f152eed7f87e345b51f3ab0b703842f447c22efe35e59942c24 +coverage[toml]==6.3.2 \ + --hash=sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9 \ + --hash=sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d \ + --hash=sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf \ + --hash=sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7 \ + --hash=sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6 \ + --hash=sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4 \ + --hash=sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059 \ + --hash=sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39 \ + --hash=sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536 \ + --hash=sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac \ + --hash=sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c \ + --hash=sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903 \ + --hash=sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d \ + --hash=sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05 \ + --hash=sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684 \ + --hash=sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1 \ + --hash=sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f \ + --hash=sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7 \ + --hash=sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca \ + --hash=sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad \ + --hash=sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca \ + --hash=sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d \ + --hash=sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92 \ + --hash=sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4 \ + --hash=sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf \ + --hash=sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6 \ + --hash=sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1 \ + --hash=sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4 \ + --hash=sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359 \ + --hash=sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3 \ + --hash=sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620 \ + --hash=sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512 \ + --hash=sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69 \ + --hash=sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2 \ + --hash=sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518 \ + --hash=sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0 \ + --hash=sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa \ + --hash=sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4 \ + --hash=sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e \ + --hash=sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1 \ + --hash=sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2 # via pytest-cov darglint==1.8.1 \ --hash=sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da \ --hash=sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d - # via rosbags + # via rosbags (setup.cfg) +dill==0.3.4 \ + --hash=sha256:7e40e4a70304fd9ceab3535d36e58791d9c4a776b38ec7f7ec9afc8d3dca4d4f \ + --hash=sha256:9f9734205146b2b353ab3fec9af0070237b6ddae78452af83d2fca84d739e675 + # via pylint docutils==0.17.1 \ --hash=sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125 \ --hash=sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61 # via # sphinx # sphinx-rtd-theme -filelock==3.3.1 \ - --hash=sha256:2b5eb3589e7fdda14599e7eb1a50e09b4cc14f34ed98b8ba56d33bfaafcbef2f \ - --hash=sha256:34a9f35f95c441e7b38209775d6e0337f9a3759f3565f6c5798f19618527c76f +filelock==3.6.0 \ + --hash=sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85 \ + --hash=sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0 # via pytest-mypy -flake8-annotations==2.7.0 \ - --hash=sha256:3edfbbfb58e404868834fe6ec3eaf49c139f64f0701259f707d043185545151e \ - --hash=sha256:52e53c05b0c06cac1c2dec192ea2c36e85081238add3bd99421d56f574b9479b - # via rosbags -flake8-bugbear==21.9.2 \ - --hash=sha256:4f7eaa6f05b7d7ea4cbbde93f7bcdc5438e79320fa1ec420d860c181af38b769 \ - --hash=sha256:db9a09893a6c649a197f5350755100bb1dd84f110e60cf532fdfa07e41808ab2 - # via rosbags -flake8-commas==2.1.0 \ - --hash=sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263 \ - --hash=sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54 - # via rosbags -flake8-comprehensions==3.7.0 \ - --hash=sha256:6b3218b2dde8ac5959c6476cde8f41a79e823c22feb656be2710cd2a3232cef9 \ - --hash=sha256:a5d7aea6315bbbd6fbcb2b4e80bff6a54d1600155e26236e555d0c6fe1d62522 - # via rosbags -flake8-docstrings==1.6.0 \ - --hash=sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde \ - --hash=sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b - # via rosbags -flake8-fixme==1.1.1 \ - --hash=sha256:226a6f2ef916730899f29ac140bed5d4a17e5aba79f00a0e3ae1eff1997cb1ac \ - --hash=sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a - # via rosbags -flake8-isort==4.1.1 \ - --hash=sha256:c4e8b6dcb7be9b71a02e6e5d4196cefcef0f3447be51e82730fb336fff164949 \ - --hash=sha256:d814304ab70e6e58859bc5c3e221e2e6e71c958e7005239202fee19c24f82717 - # via rosbags -flake8-mutable==1.2.0 \ - --hash=sha256:38fd9dadcbcda6550a916197bc40ed76908119dabb37fbcca30873666c31d2d5 \ - --hash=sha256:ee9b77111b867d845177bbc289d87d541445ffcc6029a0c5c65865b42b18c6a6 - # via rosbags -flake8-plugin-utils==1.3.2 \ - --hash=sha256:1fe43e3e9acf3a7c0f6b88f5338cad37044d2f156c43cb6b080b5f9da8a76f06 \ - --hash=sha256:20fa2a8ca2decac50116edb42e6af0a1253ef639ad79941249b840531889c65a - # via - # flake8-pytest-style - # flake8-return -flake8-polyfill==1.0.2 \ - --hash=sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9 \ - --hash=sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda - # via pep8-naming -flake8-print==4.0.0 \ - --hash=sha256:5afac374b7dc49aac2c36d04b5eb1d746d72e6f5df75a6ecaecd99e9f79c6516 \ - --hash=sha256:6c0efce658513169f96d7a24cf136c434dc711eb00ebd0a985eb1120103fe584 - # via rosbags -flake8-pytest-style==1.5.0 \ - --hash=sha256:668ce8f55edf7db4ac386d2735c3b354b5cb47aa341a4655d91a5788dd03124b \ - --hash=sha256:ec287a7dc4fe95082af5e408c8b2f8f4b6bcb366d5a17ff6c34112eb03446580 - # via rosbags -flake8-quotes==3.3.1 \ - --hash=sha256:633adca6fb8a08131536af0d750b44d6985b9aba46f498871e21588c3e6f525a - # via rosbags -flake8-return==1.1.3 \ - --hash=sha256:13a31edeb0c6157dd4f77166cdaa6141703d2b8b24def5558ae659852a003cb4 \ - --hash=sha256:4a266191f7ed69aa26b835ec90c5a5522fa8f79f5cf6363a877ac499f8eb418b - # via rosbags -flake8-simplify==0.14.2 \ - --hash=sha256:4a8f103607195c3d0743a2fd8beeebe24926e19fb3e24521042cfc35771a8d4d \ - --hash=sha256:6584f3d49350659caaf2a42129f820dc64e60d4bdcb316f621b4e1cfae27e33a - # via rosbags -flake8-type-checking==1.0.3 \ - --hash=sha256:a236558b2b001b2f4909713342306c22d5c1fe8607053c3cf9d5abb13ba82ec7 \ - --hash=sha256:f76e71b0d9aae4dffe163dd62c2eacabbb3f94649cdd1f1bc6d16af34a2b7849 - # via rosbags -flake8-use-fstring==1.2.1 \ - --hash=sha256:d946ec744bf32e245d3aca1fbd95ddf9c43b27e69f8d5d26d83610ab5a5f4f2d - # via rosbags -flake8==3.9.2 \ - --hash=sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b \ - --hash=sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907 +flake8==4.0.1 \ + --hash=sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d \ + --hash=sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d # via # flake8-annotations # flake8-bugbear @@ -171,141 +116,204 @@ flake8==3.9.2 \ # flake8-use-fstring # pep8-naming # pytest-flake8 - # rosbags + # rosbags (setup.cfg) +flake8-annotations==2.8.0 \ + --hash=sha256:880f9bb0677b82655f9021112d64513e03caefd2e0d786ab4a59ddb5b262caa9 \ + --hash=sha256:a2765c6043098aab0a3f519b871b33586c7fba7037686404b920cf8100cc1cdc + # via rosbags (setup.cfg) +flake8-bugbear==22.3.23 \ + --hash=sha256:e0dc2a36474490d5b1a2d57f9e4ef570abc09f07cbb712b29802e28a2367ff19 \ + --hash=sha256:ec5ec92195720cee1589315416b844ffa5e82f73a78e65329e8055322df1e939 + # via rosbags (setup.cfg) +flake8-commas==2.1.0 \ + --hash=sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263 \ + --hash=sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54 + # via rosbags (setup.cfg) +flake8-comprehensions==3.8.0 \ + --hash=sha256:8e108707637b1d13734f38e03435984f6b7854fa6b5a4e34f93e69534be8e521 \ + --hash=sha256:9406314803abe1193c064544ab14fdc43c58424c0882f6ff8a581eb73fc9bb58 + # via rosbags (setup.cfg) +flake8-docstrings==1.6.0 \ + --hash=sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde \ + --hash=sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b + # via rosbags (setup.cfg) +flake8-fixme==1.1.1 \ + --hash=sha256:226a6f2ef916730899f29ac140bed5d4a17e5aba79f00a0e3ae1eff1997cb1ac \ + --hash=sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a + # via rosbags (setup.cfg) +flake8-isort==4.1.1 \ + --hash=sha256:c4e8b6dcb7be9b71a02e6e5d4196cefcef0f3447be51e82730fb336fff164949 \ + --hash=sha256:d814304ab70e6e58859bc5c3e221e2e6e71c958e7005239202fee19c24f82717 + # via rosbags (setup.cfg) +flake8-mutable==1.2.0 \ + --hash=sha256:38fd9dadcbcda6550a916197bc40ed76908119dabb37fbcca30873666c31d2d5 \ + --hash=sha256:ee9b77111b867d845177bbc289d87d541445ffcc6029a0c5c65865b42b18c6a6 + # via rosbags (setup.cfg) +flake8-plugin-utils==1.3.2 \ + --hash=sha256:1fe43e3e9acf3a7c0f6b88f5338cad37044d2f156c43cb6b080b5f9da8a76f06 \ + --hash=sha256:20fa2a8ca2decac50116edb42e6af0a1253ef639ad79941249b840531889c65a + # via + # flake8-pytest-style + # flake8-return +flake8-polyfill==1.0.2 \ + --hash=sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9 \ + --hash=sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda + # via pep8-naming +flake8-print==4.0.0 \ + --hash=sha256:5afac374b7dc49aac2c36d04b5eb1d746d72e6f5df75a6ecaecd99e9f79c6516 \ + --hash=sha256:6c0efce658513169f96d7a24cf136c434dc711eb00ebd0a985eb1120103fe584 + # via rosbags (setup.cfg) +flake8-pytest-style==1.6.0 \ + --hash=sha256:5fedb371a950e9fe0e0e6bfc854be7d99151271208f34cd2cc517681ece27780 \ + --hash=sha256:c1175713e9e11b78cd1a035ed0bca0d1e41d09c4af329a952750b61d4194ddac + # via rosbags (setup.cfg) +flake8-quotes==3.3.1 \ + --hash=sha256:633adca6fb8a08131536af0d750b44d6985b9aba46f498871e21588c3e6f525a + # via rosbags (setup.cfg) +flake8-return==1.1.3 \ + --hash=sha256:13a31edeb0c6157dd4f77166cdaa6141703d2b8b24def5558ae659852a003cb4 \ + --hash=sha256:4a266191f7ed69aa26b835ec90c5a5522fa8f79f5cf6363a877ac499f8eb418b + # via rosbags (setup.cfg) +flake8-simplify==0.19.2 \ + --hash=sha256:9c1e96ba738f9057a561697d67df7a9058898ac6313a67eda09942255cba6d38 \ + --hash=sha256:a30ef76bf1c0cb89a52a8a5cee37667688e61a66735e997c08d56cd002f9e3e9 + # via rosbags (setup.cfg) +flake8-type-checking==1.5.0 \ + --hash=sha256:b9873f011e18e20dee60e2b633f2c675f2aa1318499e5294b46c860fc75bdcd6 \ + --hash=sha256:d01234fc2d3ffc9661dd797fb1bed19e2d92a1fb4041a4e8dc0200ea14f357c0 + # via rosbags (setup.cfg) +flake8-use-fstring==1.3 \ + --hash=sha256:1bd4a409adbb93e64e711fdd26b88759c33792e3899f174edc68ddf7307e81b6 + # via rosbags (setup.cfg) idna==3.3 \ --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d # via requests -imagesize==1.2.0 \ - --hash=sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1 \ - --hash=sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1 +imagesize==1.3.0 \ + --hash=sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c \ + --hash=sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d + # via sphinx +importlib-metadata==4.11.3 \ + --hash=sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6 \ + --hash=sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539 # via sphinx iniconfig==1.1.1 \ --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 # via pytest -isort==5.9.3 \ - --hash=sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899 \ - --hash=sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2 +isort==5.10.1 \ + --hash=sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7 \ + --hash=sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951 # via # flake8-isort # pylint -jinja2==3.0.2 \ - --hash=sha256:827a0e32839ab1600d4eb1c4c33ec5a8edfbc5cb42dafa13b81f182f97784b45 \ - --hash=sha256:8569982d3f0889eed11dd620c706d39b60c36d6d25843961f33f77fb6bc6b20c +jinja2==3.1.1 \ + --hash=sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119 \ + --hash=sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9 # via sphinx -lazy-object-proxy==1.6.0 \ - --hash=sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653 \ - --hash=sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61 \ - --hash=sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2 \ - --hash=sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837 \ - --hash=sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3 \ - --hash=sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43 \ - --hash=sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726 \ - --hash=sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3 \ - --hash=sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587 \ - --hash=sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8 \ - --hash=sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a \ - --hash=sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd \ - --hash=sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f \ - --hash=sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad \ - --hash=sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4 \ - --hash=sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b \ - --hash=sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf \ - --hash=sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981 \ - --hash=sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741 \ - --hash=sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e \ - --hash=sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93 \ - --hash=sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b +lazy-object-proxy==1.7.1 \ + --hash=sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7 \ + --hash=sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a \ + --hash=sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c \ + --hash=sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc \ + --hash=sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f \ + --hash=sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09 \ + --hash=sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442 \ + --hash=sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e \ + --hash=sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029 \ + --hash=sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61 \ + --hash=sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb \ + --hash=sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0 \ + --hash=sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35 \ + --hash=sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42 \ + --hash=sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1 \ + --hash=sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad \ + --hash=sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443 \ + --hash=sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd \ + --hash=sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9 \ + --hash=sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148 \ + --hash=sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38 \ + --hash=sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55 \ + --hash=sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36 \ + --hash=sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a \ + --hash=sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b \ + --hash=sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44 \ + --hash=sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6 \ + --hash=sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69 \ + --hash=sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4 \ + --hash=sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84 \ + --hash=sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de \ + --hash=sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28 \ + --hash=sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c \ + --hash=sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1 \ + --hash=sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8 \ + --hash=sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b \ + --hash=sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb # via astroid -lz4==3.1.3 \ - --hash=sha256:081ef0a3b5941cb03127f314229a1c78bd70c9c220bb3f4dd80033e707feaa18 \ - --hash=sha256:0a3b7eeb879577f2c7c0872156c70f4ddfbb023c1198e33d54422e24fa5494ae \ - --hash=sha256:0acbc2b797fe3c51917011c8d7f6c99398ae33cc4a1ca23c3a246d60bbf56fc8 \ - --hash=sha256:0f889e854114f87b5f99e8c82c9bf85417468b291b99a2cb27bcdcc864841a33 \ - --hash=sha256:19d3b8dca0c18991ee243acf86932eb917f14e2e61dd34c7852a1088659d5499 \ - --hash=sha256:1f8320b7b047ec4ba9a7de3509a067ccaac84dab2cadf629d0518760594c3b6a \ - --hash=sha256:37c23ca41040751649e0266f9f267c0148db12968a0a031272ee2a99cef7c753 \ - --hash=sha256:38266a6fa124e3ec2ce3ed6fd34f8e86b13c588f12b005873421afb295caee2d \ - --hash=sha256:3c00b56fd9aef8d3f776653c92cec262d42b6ea144e9a41b58b8c22a85f90045 \ - --hash=sha256:408b2c1b65697d9bc6468c987977314acefc71573b996bd86190053ae7ffe8d1 \ - --hash=sha256:41a388a34eab3cca6180cdb179bd8fdfcf7fd1a569f0e9e6084ad0540a0d53a9 \ - --hash=sha256:4c3558f4b98adb7acee6f4b45edd848684daae6a92a5ff31f8071eb910779568 \ - --hash=sha256:502d6dc17aca64e4dc95d6e7920dca906b5eabc1e657213bd07066c97fbc8cd3 \ - --hash=sha256:511c755d89048a2583ab88088fe451f7e3f15cde30560c058d80c9ac097dab21 \ - --hash=sha256:57dbd50d6abeb85f8d273b9f24f0063c4b97aae07d267302101884611a2413da \ - --hash=sha256:57e5b0a818addacae254b9160a183122b6bc4737bc77c988b72e1c57bd22ed9e \ - --hash=sha256:5aa4dd12debc5cb90980e6bb26be8b1586e57b87aaf6c773b9b799bca16edd99 \ - --hash=sha256:71f6f4dc48669ba3807a5cb5876048dc9b6467c3db312acf2040a61ea9487161 \ - --hash=sha256:7cf0e6e1020dbf8ea72ff57ece3f321f603cfa54f14337b96f7b68a7c1a742b4 \ - --hash=sha256:81b54fc66555fc7653467bf5b789d0e480ab88d17c858405e326d9c898baff2e \ - --hash=sha256:869734b6f0e8a19af10a75c769db179dcd3d867e29c29b3808ef884e76799071 \ - --hash=sha256:af1bc2952214c5a3ec6706cb86bd3e321570c62136539d32e4a57da777b002f0 \ - --hash=sha256:b4b56ae630a41980b6cf17a043b57691ff1f1677425b67556453fd96257b2a9b \ - --hash=sha256:b91fbc9571d3f3fea587ce541f38a2e71ef192075b59c2846182cb98f99862a0 \ - --hash=sha256:c0a5f9b6962aaa4632e4385143a12f5b49ee8605a42589073e54c8f23ce111b2 \ - --hash=sha256:c25dffdb8ab9eb449aacf94ba45b3e6f573b38a1041be9370716cc68dea445a6 \ - --hash=sha256:c41759a97ccac751f69e48f12671c2c3e5e1ae3000d3ee5dfe750b31511d1576 \ - --hash=sha256:d50c9584fb355d5d51414b802f7012578240bcb259550b48de628e19cd5bff6c \ - --hash=sha256:d6afa929d2c8afafd8b89e898498484b145a94cf3c140bb68094a94590ab2c2a \ - --hash=sha256:de2aac0cfda79c5a3eda9dbed21d78dc05c4a9ac00061748c3b57ea0e4a0b6a8 \ - --hash=sha256:e3029738e64a0af1b04a32a39b32b0bba0e2088f61805e074c9a7e4bc212568f - # via rosbags -markupsafe==2.0.1 \ - --hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \ - --hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \ - --hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \ - --hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \ - --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \ - --hash=sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724 \ - --hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \ - --hash=sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646 \ - --hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \ - --hash=sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6 \ - --hash=sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6 \ - --hash=sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad \ - --hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \ - --hash=sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38 \ - --hash=sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac \ - --hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \ - --hash=sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6 \ - --hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \ - --hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \ - --hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \ - --hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \ - --hash=sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a \ - --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a \ - --hash=sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9 \ - --hash=sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864 \ - --hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \ - --hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \ - --hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \ - --hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \ - --hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \ - --hash=sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b \ - --hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \ - --hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \ - --hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \ - --hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \ - --hash=sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28 \ - --hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \ - --hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \ - --hash=sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d \ - --hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \ - --hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \ - --hash=sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145 \ - --hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \ - --hash=sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c \ - --hash=sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1 \ - --hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \ - --hash=sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53 \ - --hash=sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134 \ - --hash=sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85 \ - --hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \ - --hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \ - --hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \ - --hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \ - --hash=sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872 +lz4==4.0.0 \ + --hash=sha256:04067086a443eef46eb2dfc26e1e5a76165149ceb4a88f0b540b46ead95e39c8 \ + --hash=sha256:19e939cd1e5d1776ca8f431c18c10a59970cf98caceeaa6edc98fdcf58499f29 \ + --hash=sha256:1ef9b03386757546f962e0598ac1d863960018dd9c04ec207059d3eb52bba12b \ + --hash=sha256:2428f5525c214d8cca332a96a2561415fd1261be2372b68b32a1aa40b9c9000f \ + --hash=sha256:26e4e7f88757c31e5240016b863f37ad79fc2898be272a6d78f267963c1b094b \ + --hash=sha256:2e1fa0dba94a7dece5d0fe109e317242c28f09e1ad488b8db692fcafb094db79 \ + --hash=sha256:3221e9a46d343175cefe932b0e812f2ecd3de70c7d036b951488d664587bee4a \ + --hash=sha256:463814c29f1201ef876c031ad32a185adee807ae201c228b28d65f17b203ee11 \ + --hash=sha256:4bf2880cae9a5255f86698f60af635f184e49d5f6878a7e0520b6cfcdb0a0d50 \ + --hash=sha256:4d96e913877b687bb8e8c6f8a90ce1e2a9a5c5268d9bab489f49bd3ce9ae8afa \ + --hash=sha256:5721ec225a37794fbaabcfa5cf2289ebb4b9e770e73e4e779d514c1fc784df5b \ + --hash=sha256:57c5dfd3b7dae833b0d2b2c1aafd7f9d0dfcab40683d183d010c67c9fd1beca3 \ + --hash=sha256:6a4c004e664d8185e2bfeffb90e1bfe554a0cd1a764662648b528e37220822cb \ + --hash=sha256:6c9acd054426840de2e4bbf83321945c3a20e90402ad221f4302e983f8031e14 \ + --hash=sha256:79246da3207b9eb4e53b3bed86189a60631e74f9b3d2579918b032fb1c7b114b \ + --hash=sha256:7ec46449892159c869c88848ee2c9b3b5170d193ba79524732334e7bbc39639c \ + --hash=sha256:a4afc2c12c37896e5ac7c5343f255cc242962ed7af940f6ef5276cc5c22e81fd \ + --hash=sha256:afc6bebfcbc48873854c05366b35a69b9c4e440ce17571ade032941cb89585ac \ + --hash=sha256:b83fce61cec36cdc21d234524d60a96d70f1a928533228eae6f46a9de21dc218 \ + --hash=sha256:e05542c6cbdb1128c43cfefd7518e231b39329d212b19ab89fd3868596140bdf \ + --hash=sha256:f69405f196c6fb38b94ac6000baa59c0364a1ac264e64194bb2fc48513df79ad + # via rosbags (setup.cfg) +markupsafe==2.1.1 \ + --hash=sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003 \ + --hash=sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88 \ + --hash=sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5 \ + --hash=sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7 \ + --hash=sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a \ + --hash=sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603 \ + --hash=sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1 \ + --hash=sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135 \ + --hash=sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247 \ + --hash=sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6 \ + --hash=sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601 \ + --hash=sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77 \ + --hash=sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02 \ + --hash=sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e \ + --hash=sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63 \ + --hash=sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f \ + --hash=sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980 \ + --hash=sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b \ + --hash=sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812 \ + --hash=sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff \ + --hash=sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96 \ + --hash=sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1 \ + --hash=sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925 \ + --hash=sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a \ + --hash=sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6 \ + --hash=sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e \ + --hash=sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f \ + --hash=sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4 \ + --hash=sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f \ + --hash=sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3 \ + --hash=sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c \ + --hash=sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a \ + --hash=sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417 \ + --hash=sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a \ + --hash=sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a \ + --hash=sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37 \ + --hash=sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452 \ + --hash=sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933 \ + --hash=sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a \ + --hash=sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7 # via jinja2 mccabe==0.6.1 \ --hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \ @@ -313,95 +321,82 @@ mccabe==0.6.1 \ # via # flake8 # pylint +mypy==0.942 \ + --hash=sha256:0e2dd88410937423fba18e57147dd07cd8381291b93d5b1984626f173a26543e \ + --hash=sha256:10daab80bc40f84e3f087d896cdb53dc811a9f04eae4b3f95779c26edee89d16 \ + --hash=sha256:17e44649fec92e9f82102b48a3bf7b4a5510ad0cd22fa21a104826b5db4903e2 \ + --hash=sha256:1a0459c333f00e6a11cbf6b468b870c2b99a906cb72d6eadf3d1d95d38c9352c \ + --hash=sha256:246e1aa127d5b78488a4a0594bd95f6d6fb9d63cf08a66dafbff8595d8891f67 \ + --hash=sha256:2b184db8c618c43c3a31b32ff00cd28195d39e9c24e7c3b401f3db7f6e5767f5 \ + --hash=sha256:2bc249409a7168d37c658e062e1ab5173300984a2dada2589638568ddc1db02b \ + --hash=sha256:3841b5433ff936bff2f4dc8d54cf2cdbfea5d8e88cedfac45c161368e5770ba6 \ + --hash=sha256:4c3e497588afccfa4334a9986b56f703e75793133c4be3a02d06a3df16b67a58 \ + --hash=sha256:5bf44840fb43ac4074636fd47ee476d73f0039f4f54e86d7265077dc199be24d \ + --hash=sha256:64235137edc16bee6f095aba73be5334677d6f6bdb7fa03cfab90164fa294a17 \ + --hash=sha256:6776e5fa22381cc761df53e7496a805801c1a751b27b99a9ff2f0ca848c7eca0 \ + --hash=sha256:6ce34a118d1a898f47def970a2042b8af6bdcc01546454726c7dd2171aa6dfca \ + --hash=sha256:6f6ad963172152e112b87cc7ec103ba0f2db2f1cd8997237827c052a3903eaa6 \ + --hash=sha256:6f7106cbf9cc2f403693bf50ed7c9fa5bb3dfa9007b240db3c910929abe2a322 \ + --hash=sha256:7742d2c4e46bb5017b51c810283a6a389296cda03df805a4f7869a6f41246534 \ + --hash=sha256:9521c1265ccaaa1791d2c13582f06facf815f426cd8b07c3a485f486a8ffc1f3 \ + --hash=sha256:a1b383fe99678d7402754fe90448d4037f9512ce70c21f8aee3b8bf48ffc51db \ + --hash=sha256:b840cfe89c4ab6386c40300689cd8645fc8d2d5f20101c7f8bd23d15fca14904 \ + --hash=sha256:d8d3ba77e56b84cd47a8ee45b62c84b6d80d32383928fe2548c9a124ea0a725c \ + --hash=sha256:dcd955f36e0180258a96f880348fbca54ce092b40fbb4b37372ae3b25a0b0a46 \ + --hash=sha256:e865fec858d75b78b4d63266c9aff770ecb6a39dfb6d6b56c47f7f8aba6baba8 \ + --hash=sha256:edf7237137a1a9330046dbb14796963d734dd740a98d5e144a3eb1d267f5f9ee + # via pytest-mypy mypy-extensions==0.4.3 \ --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 # via mypy -mypy==0.910 \ - --hash=sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9 \ - --hash=sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a \ - --hash=sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9 \ - --hash=sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e \ - --hash=sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2 \ - --hash=sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212 \ - --hash=sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b \ - --hash=sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885 \ - --hash=sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150 \ - --hash=sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703 \ - --hash=sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072 \ - --hash=sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457 \ - --hash=sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e \ - --hash=sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0 \ - --hash=sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb \ - --hash=sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97 \ - --hash=sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8 \ - --hash=sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811 \ - --hash=sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6 \ - --hash=sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de \ - --hash=sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504 \ - --hash=sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921 \ - --hash=sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d - # via pytest-mypy -numpy==1.21.3 \ - --hash=sha256:043e83bfc274649c82a6f09836943e4a4aebe5e33656271c7dbf9621dd58b8ec \ - --hash=sha256:160ccc1bed3a8371bf0d760971f09bfe80a3e18646620e9ded0ad159d9749baa \ - --hash=sha256:188031f833bbb623637e66006cf75e933e00e7231f67e2b45cf8189612bb5dc3 \ - --hash=sha256:28f15209fb535dd4c504a7762d3bc440779b0e37d50ed810ced209e5cea60d96 \ - --hash=sha256:29fb3dcd0468b7715f8ce2c0c2d9bbbaf5ae686334951343a41bd8d155c6ea27 \ - --hash=sha256:2a6ee9620061b2a722749b391c0d80a0e2ae97290f1b32e28d5a362e21941ee4 \ - --hash=sha256:300321e3985c968e3ae7fbda187237b225f3ffe6528395a5b7a5407f73cf093e \ - --hash=sha256:32437f0b275c1d09d9c3add782516413e98cd7c09e6baf4715cbce781fc29912 \ - --hash=sha256:3c09418a14471c7ae69ba682e2428cae5b4420a766659605566c0fa6987f6b7e \ - --hash=sha256:49c6249260890e05b8111ebfc391ed58b3cb4b33e63197b2ec7f776e45330721 \ - --hash=sha256:4cc9b512e9fb590797474f58b7f6d1f1b654b3a94f4fa8558b48ca8b3cfc97cf \ - --hash=sha256:508b0b513fa1266875524ba8a9ecc27b02ad771fe1704a16314dc1a816a68737 \ - --hash=sha256:50cd26b0cf6664cb3b3dd161ba0a09c9c1343db064e7c69f9f8b551f5104d654 \ - --hash=sha256:5c4193f70f8069550a1788bd0cd3268ab7d3a2b70583dfe3b2e7f421e9aace06 \ - --hash=sha256:5dfe9d6a4c39b8b6edd7990091fea4f852888e41919d0e6722fe78dd421db0eb \ - --hash=sha256:63571bb7897a584ca3249c86dd01c10bcb5fe4296e3568b2e9c1a55356b6410e \ - --hash=sha256:75621882d2230ab77fb6a03d4cbccd2038511491076e7964ef87306623aa5272 \ - --hash=sha256:75eb7cadc8da49302f5b659d40ba4f6d94d5045fbd9569c9d058e77b0514c9e4 \ - --hash=sha256:88a5d6b268e9ad18f3533e184744acdaa2e913b13148160b1152300c949bbb5f \ - --hash=sha256:8a10968963640e75cc0193e1847616ab4c718e83b6938ae74dea44953950f6b7 \ - --hash=sha256:90bec6a86b348b4559b6482e2b684db4a9a7eed1fa054b86115a48d58fbbf62a \ - --hash=sha256:98339aa9911853f131de11010f6dd94c8cec254d3d1f7261528c3b3e3219f139 \ - --hash=sha256:a99a6b067e5190ac6d12005a4d85aa6227c5606fa93211f86b1dafb16233e57d \ - --hash=sha256:bffa2eee3b87376cc6b31eee36d05349571c236d1de1175b804b348dc0941e3f \ - --hash=sha256:c6c2d535a7beb1f8790aaa98fd089ceab2e3dd7ca48aca0af7dc60e6ef93ffe1 \ - --hash=sha256:cc14e7519fab2a4ed87d31f99c31a3796e4e1fe63a86ebdd1c5a1ea78ebd5896 \ - --hash=sha256:dd0482f3fc547f1b1b5d6a8b8e08f63fdc250c58ce688dedd8851e6e26cff0f3 \ - --hash=sha256:dde972a1e11bb7b702ed0e447953e7617723760f420decb97305e66fb4afc54f \ - --hash=sha256:e54af82d68ef8255535a6cdb353f55d6b8cf418a83e2be3569243787a4f4866f \ - --hash=sha256:e606e6316911471c8d9b4618e082635cfe98876007556e89ce03d52ff5e8fcf0 \ - --hash=sha256:f41b018f126aac18583956c54544db437f25c7ee4794bcb23eb38bef8e5e192a \ - --hash=sha256:f8f4625536926a155b80ad2bbff44f8cc59e9f2ad14cdda7acf4c135b4dc8ff2 \ - --hash=sha256:fe52dbe47d9deb69b05084abd4b0df7abb39a3c51957c09f635520abd49b29dd - # via rosbags -packaging==21.0 \ - --hash=sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7 \ - --hash=sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14 +numpy==1.22.3 \ + --hash=sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676 \ + --hash=sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4 \ + --hash=sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce \ + --hash=sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123 \ + --hash=sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1 \ + --hash=sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e \ + --hash=sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5 \ + --hash=sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d \ + --hash=sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a \ + --hash=sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab \ + --hash=sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75 \ + --hash=sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168 \ + --hash=sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4 \ + --hash=sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f \ + --hash=sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18 \ + --hash=sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62 \ + --hash=sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe \ + --hash=sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430 \ + --hash=sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802 \ + --hash=sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa + # via rosbags (setup.cfg) +packaging==21.3 \ + --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \ + --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522 # via # pytest # sphinx pep8-naming==0.12.1 \ --hash=sha256:4a8daeaeb33cfcde779309fc0c9c0a68a3bbe2ad8a8308b763c5068f86eb9f37 \ --hash=sha256:bb2455947757d162aa4cad55dba4ce029005cd1692f2899a21d51d8630ca7841 - # via rosbags -platformdirs==2.4.0 \ - --hash=sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2 \ - --hash=sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d + # via rosbags (setup.cfg) +platformdirs==2.5.1 \ + --hash=sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d \ + --hash=sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227 # via pylint pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 # via pytest -py==1.10.0 \ - --hash=sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3 \ - --hash=sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a +py==1.11.0 \ + --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \ + --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378 # via pytest -pycodestyle==2.7.0 \ - --hash=sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068 \ - --hash=sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef +pycodestyle==2.8.0 \ + --hash=sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20 \ + --hash=sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f # via # flake8 # flake8-print @@ -409,67 +404,75 @@ pydocstyle==6.1.1 \ --hash=sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc \ --hash=sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4 # via flake8-docstrings -pyflakes==2.3.1 \ - --hash=sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3 \ - --hash=sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db +pyflakes==2.4.0 \ + --hash=sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c \ + --hash=sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e # via flake8 -pygments==2.10.0 \ - --hash=sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380 \ - --hash=sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6 +pygments==2.11.2 \ + --hash=sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65 \ + --hash=sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a # via sphinx -pylint==2.11.1 \ - --hash=sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126 \ - --hash=sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436 +pylint==2.13.5 \ + --hash=sha256:c149694cfdeaee1aa2465e6eaab84c87a881a7d55e6e93e09466be7164764d1e \ + --hash=sha256:dab221658368c7a05242e673c275c488670144123f4bd262b2777249c1c0de9b # via pytest-pylint -pyparsing==2.4.7 \ - --hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1 \ - --hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b +pyparsing==3.0.8 \ + --hash=sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954 \ + --hash=sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06 # via packaging -pytest-cov==3.0.0 \ - --hash=sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6 \ - --hash=sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470 - # via rosbags -pytest-flake8==1.0.7 \ - --hash=sha256:c28cf23e7d359753c896745fd4ba859495d02e16c84bac36caa8b1eec58f5bc1 \ - --hash=sha256:f0259761a903563f33d6f099914afef339c085085e643bee8343eb323b32dd6b - # via rosbags -pytest-mypy==0.8.1 \ - --hash=sha256:1fa55723a4bf1d054fcba1c3bd694215a2a65cc95ab10164f5808afd893f3b11 \ - --hash=sha256:6e68e8eb7ceeb7d1c83a1590912f784879f037b51adfb9c17b95c6b2fc57466b - # via rosbags -pytest-pylint==0.18.0 \ - --hash=sha256:790c7a8019fab08e59bd3812db1657a01995a975af8b1c6ce95b9aa39d61da27 \ - --hash=sha256:b63aaf8b80ff33c8ceaa7f68323ed04102c7790093ccf6bdb261a4c2dc6fd564 - # via rosbags -pytest-yapf3==0.6.1 \ - --hash=sha256:63093e56f5c7baba4d221050c30e3d4e7132730741f95ec5de7a8c5fa3eb7821 \ - --hash=sha256:92b6b36d3b9435eedfb0072a8fb765496de1198c22d5cd3eb072359d35faba55 - # via rosbags -pytest==6.2.5 \ - --hash=sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89 \ - --hash=sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134 +pytest==7.1.1 \ + --hash=sha256:841132caef6b1ad17a9afde46dc4f6cfa59a05f9555aae5151f73bdf2820ca63 \ + --hash=sha256:92f723789a8fdd7180b6b06483874feca4c48a5c76968e03bb3e7f806a1869ea # via # pytest-cov # pytest-flake8 # pytest-mypy # pytest-pylint # pytest-yapf3 - # rosbags -pytz==2021.3 \ - --hash=sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c \ - --hash=sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326 + # rosbags (setup.cfg) +pytest-cov==3.0.0 \ + --hash=sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6 \ + --hash=sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470 + # via rosbags (setup.cfg) +pytest-flake8==1.1.1 \ + --hash=sha256:ba4f243de3cb4c2486ed9e70752c80dd4b636f7ccb27d4eba763c35ed0cd316e \ + --hash=sha256:e0661a786f8cbf976c185f706fdaf5d6df0b1667c3bcff8e823ba263618627e7 + # via rosbags (setup.cfg) +pytest-mypy==0.9.1 \ + --hash=sha256:9ffa3bf405c12c5c6be9e92e22bebb6ab2c91b9c32f45b0f0c93af473269ab5c \ + --hash=sha256:a2505fcf61f1c0c51f950d4623ea8ca2daf6fb2101a5603554bad2e130202083 + # via rosbags (setup.cfg) +pytest-pylint==0.18.0 \ + --hash=sha256:790c7a8019fab08e59bd3812db1657a01995a975af8b1c6ce95b9aa39d61da27 \ + --hash=sha256:b63aaf8b80ff33c8ceaa7f68323ed04102c7790093ccf6bdb261a4c2dc6fd564 + # via rosbags (setup.cfg) +pytest-yapf3==0.6.1 \ + --hash=sha256:63093e56f5c7baba4d221050c30e3d4e7132730741f95ec5de7a8c5fa3eb7821 \ + --hash=sha256:92b6b36d3b9435eedfb0072a8fb765496de1198c22d5cd3eb072359d35faba55 + # via rosbags (setup.cfg) +pytz==2022.1 \ + --hash=sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7 \ + --hash=sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c # via babel -requests==2.26.0 \ - --hash=sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24 \ - --hash=sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7 +requests==2.27.1 \ + --hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61 \ + --hash=sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d # via sphinx -ruamel.yaml.clib==0.2.6 \ +ruamel-yaml==0.17.21 \ + --hash=sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7 \ + --hash=sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af + # via rosbags (setup.cfg) +ruamel-yaml-clib==0.2.6 \ --hash=sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd \ + --hash=sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee \ --hash=sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0 \ + --hash=sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7 \ --hash=sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277 \ --hash=sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104 \ --hash=sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd \ + --hash=sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0 \ --hash=sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78 \ + --hash=sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de \ --hash=sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99 \ --hash=sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527 \ --hash=sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84 \ @@ -485,36 +488,32 @@ ruamel.yaml.clib==0.2.6 \ --hash=sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502 \ --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c - # via ruamel.yaml -ruamel.yaml==0.17.16 \ - --hash=sha256:1a771fc92d3823682b7f0893ad56cb5a5c87c48e62b5399d6f42c8759a583b33 \ - --hash=sha256:ea21da1198c4b41b8e7a259301cc9710d3b972bf8ba52f06218478e6802dd1f1 - # via rosbags + # via ruamel-yaml six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via flake8-print -snowballstemmer==2.1.0 \ - --hash=sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2 \ - --hash=sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914 +snowballstemmer==2.2.0 \ + --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \ + --hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a # via # pydocstyle # sphinx -sphinx-autodoc-typehints==1.12.0 \ - --hash=sha256:193617d9dbe0847281b1399d369e74e34cd959c82e02c7efde077fca908a9f52 \ - --hash=sha256:5e81776ec422dd168d688ab60f034fccfafbcd94329e9537712c93003bddc04a - # via rosbags +sphinx==4.5.0 \ + --hash=sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6 \ + --hash=sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226 + # via + # rosbags (setup.cfg) + # sphinx-autodoc-typehints + # sphinx-rtd-theme +sphinx-autodoc-typehints==1.17.0 \ + --hash=sha256:081daf53077b4ae1c28347d6d858e13e63aefe3b4aacef79fd717dd60687b470 \ + --hash=sha256:51c7b3f5cb9ccd15d0b52088c62df3094f1abd9612930340365c26def8629a14 + # via rosbags (setup.cfg) sphinx-rtd-theme==1.0.0 \ --hash=sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8 \ --hash=sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c - # via rosbags -sphinx==4.2.0 \ - --hash=sha256:94078db9184491e15bce0a56d9186e0aec95f16ac20b12d00e06d4e36f1058a6 \ - --hash=sha256:98a535c62a4fcfcc362528592f69b26f7caec587d32cd55688db580be0287ae0 - # via - # rosbags - # sphinx-autodoc-typehints - # sphinx-rtd-theme + # via rosbags (setup.cfg) sphinxcontrib-applehelp==1.0.2 \ --hash=sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a \ --hash=sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58 @@ -539,129 +538,149 @@ sphinxcontrib-serializinghtml==1.1.5 \ --hash=sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd \ --hash=sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952 # via sphinx -testfixtures==6.18.3 \ - --hash=sha256:2600100ae96ffd082334b378e355550fef8b4a529a6fa4c34f47130905c7426d \ - --hash=sha256:6ddb7f56a123e1a9339f130a200359092bd0a6455e31838d6c477e8729bb7763 +testfixtures==6.18.5 \ + --hash=sha256:02dae883f567f5b70fd3ad3c9eefb95912e78ac90be6c7444b5e2f46bf572c84 \ + --hash=sha256:7de200e24f50a4a5d6da7019fb1197aaf5abd475efb2ec2422fdcf2f2eb98c1d # via flake8-isort toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via pytest-pylint +tomli==2.0.1 \ + --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ + --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f # via + # coverage # mypy # pylint # pytest - # pytest-pylint -tomli==1.2.1 \ - --hash=sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f \ - --hash=sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442 - # via coverage -typing-extensions==3.10.0.2 \ - --hash=sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e \ - --hash=sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7 \ - --hash=sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34 - # via - # astroid - # mypy - # pylint -urllib3==1.26.7 \ - --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece \ - --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844 +typing-extensions==4.1.1 \ + --hash=sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42 \ + --hash=sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2 + # via mypy +urllib3==1.26.9 \ + --hash=sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14 \ + --hash=sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e # via requests -wrapt==1.13.2 \ - --hash=sha256:0473d1558b93e314e84313cc611f6c86be779369f9d3734302bf185a4d2625b1 \ - --hash=sha256:0582180566e7a13030f896c2f1ac6a56134ab5f3c3f4c5538086f758b1caf3f2 \ - --hash=sha256:15eee0e6fd07f48af2f66d0e6f2ff1916ffe9732d464d5e2390695296872cad9 \ - --hash=sha256:1c5c4cf188b5643a97e87e2110bbd4f5bc491d54a5b90633837b34d5df6a03fe \ - --hash=sha256:1eb657ed84f4d3e6ad648483c8a80a0cf0a78922ef94caa87d327e2e1ad49b48 \ - --hash=sha256:22142afab65daffc95863d78effcbd31c19a8003eca73de59f321ee77f73cadb \ - --hash=sha256:283e402e5357e104ac1e3fba5791220648e9af6fb14ad7d9cc059091af2b31d2 \ - --hash=sha256:3de7b4d3066cc610054e7aa2c005645e308df2f92be730aae3a47d42e910566a \ - --hash=sha256:3e0d16eedc242d01a6f8cf0623e9cdc3b869329da3f97a15961d8864111d8cf0 \ - --hash=sha256:3e33c138d1e3620b1e0cc6fd21e46c266393ed5dae0d595b7ed5a6b73ed57aa0 \ - --hash=sha256:3f87042623530bcffea038f824b63084180513c21e2e977291a9a7e65a66f13b \ - --hash=sha256:53c6706a1bcfb6436f1625511b95b812798a6d2ccc51359cd791e33722b5ea32 \ - --hash=sha256:593cb049ce1c391e0288523b30426c4430b26e74c7e6f6e2844bd99ac7ecc831 \ - --hash=sha256:6e6d1a8eeef415d7fb29fe017de0e48f45e45efd2d1bfda28fc50b7b330859ef \ - --hash=sha256:724ed2bc9c91a2b9026e5adce310fa60c6e7c8760b03391445730b9789b9d108 \ - --hash=sha256:728e2d9b7a99dd955d3426f237b940fc74017c4a39b125fec913f575619ddfe9 \ - --hash=sha256:7574de567dcd4858a2ffdf403088d6df8738b0e1eabea220553abf7c9048f59e \ - --hash=sha256:8164069f775c698d15582bf6320a4f308c50d048c1c10cf7d7a341feaccf5df7 \ - --hash=sha256:81a4cf257263b299263472d669692785f9c647e7dca01c18286b8f116dbf6b38 \ - --hash=sha256:82223f72eba6f63eafca87a0f614495ae5aa0126fe54947e2b8c023969e9f2d7 \ - --hash=sha256:8318088860968c07e741537030b1abdd8908ee2c71fbe4facdaade624a09e006 \ - --hash=sha256:83f2793ec6f3ef513ad8d5b9586f5ee6081cad132e6eae2ecb7eac1cc3decae0 \ - --hash=sha256:87ee3c73bdfb4367b26c57259995935501829f00c7b3eed373e2ad19ec21e4e4 \ - --hash=sha256:8860c8011a6961a651b1b9f46fdbc589ab63b0a50d645f7d92659618a3655867 \ - --hash=sha256:9adee1891253670575028279de8365c3a02d3489a74a66d774c321472939a0b1 \ - --hash=sha256:a0cdedf681db878416c05e1831ec69691b0e6577ac7dca9d4f815632e3549580 \ - --hash=sha256:a70d876c9aba12d3bd7f8f1b05b419322c6789beb717044eea2c8690d35cb91b \ - --hash=sha256:ada5e29e59e2feb710589ca1c79fd989b1dd94d27079dc1d199ec954a6ecc724 \ - --hash=sha256:af9480de8e63c5f959a092047aaf3d7077422ded84695b3398f5d49254af3e90 \ - --hash=sha256:b20703356cae1799080d0ad15085dc3213c1ac3f45e95afb9f12769b98231528 \ - --hash=sha256:bc85d17d90201afd88e3d25421da805e4e135012b5d1f149e4de2981394b2a52 \ - --hash=sha256:bff0a59387a0a2951cb869251257b6553663329a1b5525b5226cab8c88dcbe7e \ - --hash=sha256:c65e623ea7556e39c4f0818200a046cbba7575a6b570ff36122c276fdd30ab0a \ - --hash=sha256:c6ee5f8734820c21b9b8bf705e99faba87f21566d20626568eeb0d62cbeaf23c \ - --hash=sha256:c7ac2c7a8e34bd06710605b21dd1f3576764443d68e069d2afba9b116014d072 \ - --hash=sha256:ccb34ce599cab7f36a4c90318697ead18312c67a9a76327b3f4f902af8f68ea1 \ - --hash=sha256:d0d717e10f952df7ea41200c507cc7e24458f4c45b56c36ad418d2e79dacd1d4 \ - --hash=sha256:d90520616fce71c05dedeac3a0fe9991605f0acacd276e5f821842e454485a70 \ - --hash=sha256:dca56cc5963a5fd7c2aa8607017753f534ee514e09103a6c55d2db70b50e7447 \ - --hash=sha256:df3eae297a5f1594d1feb790338120f717dac1fa7d6feed7b411f87e0f2401c7 \ - --hash=sha256:e634136f700a21e1fcead0c137f433dde928979538c14907640607d43537d468 \ - --hash=sha256:fbad5ba74c46517e6488149514b2e2348d40df88cd6b52a83855b7a8bf04723f \ - --hash=sha256:fbe6aebc9559fed7ea27de51c2bf5c25ba2a4156cf0017556f72883f2496ee9a \ - --hash=sha256:fdede980273aeca591ad354608778365a3a310e0ecdd7a3587b38bc5be9b1808 +wrapt==1.14.0 \ + --hash=sha256:00108411e0f34c52ce16f81f1d308a571df7784932cc7491d1e94be2ee93374b \ + --hash=sha256:01f799def9b96a8ec1ef6b9c1bbaf2bbc859b87545efbecc4a78faea13d0e3a0 \ + --hash=sha256:09d16ae7a13cff43660155383a2372b4aa09109c7127aa3f24c3cf99b891c330 \ + --hash=sha256:14e7e2c5f5fca67e9a6d5f753d21f138398cad2b1159913ec9e9a67745f09ba3 \ + --hash=sha256:167e4793dc987f77fd476862d32fa404d42b71f6a85d3b38cbce711dba5e6b68 \ + --hash=sha256:1807054aa7b61ad8d8103b3b30c9764de2e9d0c0978e9d3fc337e4e74bf25faa \ + --hash=sha256:1f83e9c21cd5275991076b2ba1cd35418af3504667affb4745b48937e214bafe \ + --hash=sha256:21b1106bff6ece8cb203ef45b4f5778d7226c941c83aaaa1e1f0f4f32cc148cd \ + --hash=sha256:22626dca56fd7f55a0733e604f1027277eb0f4f3d95ff28f15d27ac25a45f71b \ + --hash=sha256:23f96134a3aa24cc50614920cc087e22f87439053d886e474638c68c8d15dc80 \ + --hash=sha256:2498762814dd7dd2a1d0248eda2afbc3dd9c11537bc8200a4b21789b6df6cd38 \ + --hash=sha256:28c659878f684365d53cf59dc9a1929ea2eecd7ac65da762be8b1ba193f7e84f \ + --hash=sha256:2eca15d6b947cfff51ed76b2d60fd172c6ecd418ddab1c5126032d27f74bc350 \ + --hash=sha256:354d9fc6b1e44750e2a67b4b108841f5f5ea08853453ecbf44c81fdc2e0d50bd \ + --hash=sha256:36a76a7527df8583112b24adc01748cd51a2d14e905b337a6fefa8b96fc708fb \ + --hash=sha256:3a0a4ca02752ced5f37498827e49c414d694ad7cf451ee850e3ff160f2bee9d3 \ + --hash=sha256:3a71dbd792cc7a3d772ef8cd08d3048593f13d6f40a11f3427c000cf0a5b36a0 \ + --hash=sha256:3a88254881e8a8c4784ecc9cb2249ff757fd94b911d5df9a5984961b96113fff \ + --hash=sha256:47045ed35481e857918ae78b54891fac0c1d197f22c95778e66302668309336c \ + --hash=sha256:4775a574e9d84e0212f5b18886cace049a42e13e12009bb0491562a48bb2b758 \ + --hash=sha256:493da1f8b1bb8a623c16552fb4a1e164c0200447eb83d3f68b44315ead3f9036 \ + --hash=sha256:4b847029e2d5e11fd536c9ac3136ddc3f54bc9488a75ef7d040a3900406a91eb \ + --hash=sha256:59d7d92cee84a547d91267f0fea381c363121d70fe90b12cd88241bd9b0e1763 \ + --hash=sha256:5a0898a640559dec00f3614ffb11d97a2666ee9a2a6bad1259c9facd01a1d4d9 \ + --hash=sha256:5a9a1889cc01ed2ed5f34574c90745fab1dd06ec2eee663e8ebeefe363e8efd7 \ + --hash=sha256:5b835b86bd5a1bdbe257d610eecab07bf685b1af2a7563093e0e69180c1d4af1 \ + --hash=sha256:5f24ca7953f2643d59a9c87d6e272d8adddd4a53bb62b9208f36db408d7aafc7 \ + --hash=sha256:61e1a064906ccba038aa3c4a5a82f6199749efbbb3cef0804ae5c37f550eded0 \ + --hash=sha256:65bf3eb34721bf18b5a021a1ad7aa05947a1767d1aa272b725728014475ea7d5 \ + --hash=sha256:6807bcee549a8cb2f38f73f469703a1d8d5d990815c3004f21ddb68a567385ce \ + --hash=sha256:68aeefac31c1f73949662ba8affaf9950b9938b712fb9d428fa2a07e40ee57f8 \ + --hash=sha256:6915682f9a9bc4cf2908e83caf5895a685da1fbd20b6d485dafb8e218a338279 \ + --hash=sha256:6d9810d4f697d58fd66039ab959e6d37e63ab377008ef1d63904df25956c7db0 \ + --hash=sha256:729d5e96566f44fccac6c4447ec2332636b4fe273f03da128fff8d5559782b06 \ + --hash=sha256:748df39ed634851350efa87690c2237a678ed794fe9ede3f0d79f071ee042561 \ + --hash=sha256:763a73ab377390e2af26042f685a26787c402390f682443727b847e9496e4a2a \ + --hash=sha256:8323a43bd9c91f62bb7d4be74cc9ff10090e7ef820e27bfe8815c57e68261311 \ + --hash=sha256:8529b07b49b2d89d6917cfa157d3ea1dfb4d319d51e23030664a827fe5fd2131 \ + --hash=sha256:87fa943e8bbe40c8c1ba4086971a6fefbf75e9991217c55ed1bcb2f1985bd3d4 \ + --hash=sha256:88236b90dda77f0394f878324cfbae05ae6fde8a84d548cfe73a75278d760291 \ + --hash=sha256:891c353e95bb11abb548ca95c8b98050f3620a7378332eb90d6acdef35b401d4 \ + --hash=sha256:89ba3d548ee1e6291a20f3c7380c92f71e358ce8b9e48161401e087e0bc740f8 \ + --hash=sha256:8c6be72eac3c14baa473620e04f74186c5d8f45d80f8f2b4eda6e1d18af808e8 \ + --hash=sha256:9a242871b3d8eecc56d350e5e03ea1854de47b17f040446da0e47dc3e0b9ad4d \ + --hash=sha256:9a3ff5fb015f6feb78340143584d9f8a0b91b6293d6b5cf4295b3e95d179b88c \ + --hash=sha256:9a5a544861b21e0e7575b6023adebe7a8c6321127bb1d238eb40d99803a0e8bd \ + --hash=sha256:9d57677238a0c5411c76097b8b93bdebb02eb845814c90f0b01727527a179e4d \ + --hash=sha256:9d8c68c4145041b4eeae96239802cfdfd9ef927754a5be3f50505f09f309d8c6 \ + --hash=sha256:9d9fcd06c952efa4b6b95f3d788a819b7f33d11bea377be6b8980c95e7d10775 \ + --hash=sha256:a0057b5435a65b933cbf5d859cd4956624df37b8bf0917c71756e4b3d9958b9e \ + --hash=sha256:a65bffd24409454b889af33b6c49d0d9bcd1a219b972fba975ac935f17bdf627 \ + --hash=sha256:b0ed6ad6c9640671689c2dbe6244680fe8b897c08fd1fab2228429b66c518e5e \ + --hash=sha256:b21650fa6907e523869e0396c5bd591cc326e5c1dd594dcdccac089561cacfb8 \ + --hash=sha256:b3f7e671fb19734c872566e57ce7fc235fa953d7c181bb4ef138e17d607dc8a1 \ + --hash=sha256:b77159d9862374da213f741af0c361720200ab7ad21b9f12556e0eb95912cd48 \ + --hash=sha256:bb36fbb48b22985d13a6b496ea5fb9bb2a076fea943831643836c9f6febbcfdc \ + --hash=sha256:d066ffc5ed0be00cd0352c95800a519cf9e4b5dd34a028d301bdc7177c72daf3 \ + --hash=sha256:d332eecf307fca852d02b63f35a7872de32d5ba8b4ec32da82f45df986b39ff6 \ + --hash=sha256:d808a5a5411982a09fef6b49aac62986274ab050e9d3e9817ad65b2791ed1425 \ + --hash=sha256:d9bdfa74d369256e4218000a629978590fd7cb6cf6893251dad13d051090436d \ + --hash=sha256:db6a0ddc1282ceb9032e41853e659c9b638789be38e5b8ad7498caac00231c23 \ + --hash=sha256:debaf04f813ada978d7d16c7dfa16f3c9c2ec9adf4656efdc4defdf841fc2f0c \ + --hash=sha256:f0408e2dbad9e82b4c960274214af533f856a199c9274bd4aff55d4634dedc33 \ + --hash=sha256:f2f3bc7cd9c9fcd39143f11342eb5963317bd54ecc98e3650ca22704b69d9653 # via astroid -yapf==0.31.0 \ - --hash=sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d \ - --hash=sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e +yapf==0.32.0 \ + --hash=sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32 \ + --hash=sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b # via # pytest-yapf3 - # rosbags -zstandard==0.16.0 \ - --hash=sha256:066488e721ec882485a500c216302b443f2eaef39356f7c65130e76c671e3ce2 \ - --hash=sha256:08a728715858f1477239887ba3c692bc462b2c86e7a8e467dc5affa7bba9093f \ - --hash=sha256:11216b47c62e9fc71a25f4b42f525a81da268071bdb434bc1e642ffc38a24a02 \ - --hash=sha256:127c4c93f578d9b509732c74ed9b44b23e94041ba11b13827be0a7d2e3869b39 \ - --hash=sha256:12dddee2574b00c262270cfb46bd0c048e92208b95fdd39ad2a9eac1cef30498 \ - --hash=sha256:1bdda52224043e13ed20f847e3b308de1c9372d1563824fad776b1cf1f847ef0 \ - --hash=sha256:2e31680d1bcf85e7a58a45df7365af894402ae77a9868c751dc991dd13099a5f \ - --hash=sha256:42992e89b250fe6878c175119af529775d4be7967cd9de86990145d615d6a444 \ - --hash=sha256:453e42af96923582ddbf3acf843f55d2dc534a3f7b345003852dd522aa51eae6 \ - --hash=sha256:4d8a296dab7f8f5d53acc693a6785751f43ca39b51c8eabc672f978306fb40e6 \ - --hash=sha256:5251ac352d8350869c404a0ca94457da018b726f692f6456ec82bbf907fbc956 \ - --hash=sha256:57a6cfc34d906d514358769ed6d510b312be1cf033aafb5db44865a6717579bd \ - --hash=sha256:6ed51162e270b9b8097dcae6f2c239ada05ec112194633193ec3241498988924 \ - --hash=sha256:74cbea966462afed5a89eb99e4577538d10d425e05bf6240a75c086d59ccaf89 \ - --hash=sha256:87bea44ad24c15cd872263c0d5f912186a4be3db361eab3b25f1a61dcb5ca014 \ - --hash=sha256:8a745862ed525eee4e28bdbd58bf3ea952bf9da3c31bb4e4ce11ef15aea5c625 \ - --hash=sha256:8b760fc8118b1a0aa1d8f4e2012622e8f5f178d4b8cb94f8c6d2948b6a49a485 \ - --hash=sha256:8c8c0e813b67de1c9d7f2760768c4ae53f011c75ace18d5cff4fb40d2173763f \ - --hash=sha256:8d5fe983e23b05f0e924fe8d0dd3935f0c9fd3266e4c6ff8621c12c350da299d \ - --hash=sha256:8f5785c0b9b71d49d789240ae16a636728596631cf100f32b963a6f9857af5a4 \ - --hash=sha256:91efd5ea5fb3c347e7ebb6d5622bfa37d72594a2dec37c5dde70b691edb6cc03 \ - --hash=sha256:92e6c1a656390176d51125847f2f422f9d8ed468c24b63958f6ee50d9aa98c83 \ - --hash=sha256:9bcbfe1ec89789239f63daeea8778488cb5ba9034a374d7753815935f83dad65 \ - --hash=sha256:a92aa26789f17ca3b1f45cc7e728597165e2b166b99d1204bb397a672edee761 \ - --hash=sha256:a9ec6de2c058e611e9dfe88d9809a5676bc1d2a53543c1273a90a60e41b8f43c \ - --hash=sha256:ac5d97f9dece91a1162f651da79b735c5cde4d5863477785962aad648b592446 \ - --hash=sha256:ae19628886d994ac1f3d2fc7f9ed5bb551d81000f7b4e0c57a0e88301aea2766 \ - --hash=sha256:b2ea1937eff0ed5621876dc377933fe76624abfb2ab5b418995f43af6bac50de \ - --hash=sha256:b46220bef7bf9271a2a05512e86acbabc86cca08bebde8447bdbb4acb3179447 \ - --hash=sha256:b61586b0ff55c4137e512f1e9df4e4d7a6e1e9df782b4b87652df27737c90cc1 \ - --hash=sha256:be68fbac1e88f0dbe033a2d2e3aaaf9c8307730b905f3cd3c698ca4b904f0702 \ - --hash=sha256:c75557d53bb2d064521ff20cce9b8a51ee8301e031b1d6bcedb6458dda3bc85d \ - --hash=sha256:c7e6b6ad58ae6f77872da9376ef0ecbf8c1ae7a0c8fc29a2473abc90f79a9a1b \ - --hash=sha256:c8828f4e78774a6c0b8d21e59677f8f48d2e17fe2ef72793c94c10abc032c41c \ - --hash=sha256:cae9bfcb9148152f8bfb9163b4b779326ca39fe9889e45e0572c56d25d5021be \ - --hash=sha256:ce61492764d0442ca1e81d38d7bf7847d7df5003bce28089bab64c0519749351 \ - --hash=sha256:d40447f4a44b442fa6715779ff49a1e319729d829198279927d18bca0d7ac32d \ - --hash=sha256:d9946cfe54bf3365f14a5aa233eb2425de3b77eac6a4c7d03dda7dbb6acd3267 \ - --hash=sha256:dd5a2287893e52204e4ce9d0e1bcea6240661dbb412efb53d5446b881d3c10a2 \ - --hash=sha256:e9456492eb13249841e53221e742bef93f4868122bfc26bafa12a07677619732 \ - --hash=sha256:eaae2d3e8fdf8bfe269628385087e4b648beef85bb0c187644e7df4fb0fe9046 \ - --hash=sha256:eba125d3899f2003debf97019cd6f46f841a405df067da23d11443ad17952a40 \ - --hash=sha256:ef759c1dfe78aa5a01747d3465d2585de14e08fc2b0195ce3f31f45477fc5a72 \ - --hash=sha256:ffe1d24c5e11e98e4c5f96f846cdd19619d8c7e5e8e5082bed62d39baa30cecb - # via rosbags + # rosbags (setup.cfg) +zipp==3.8.0 \ + --hash=sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad \ + --hash=sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099 + # via importlib-metadata +zstandard==0.17.0 \ + --hash=sha256:208fa6bead577b2607205640078ee452e81fe20fe96321623c632bad9ebd7148 \ + --hash=sha256:2a2ac752162ba5cbc869c60c4a4e54e890b2ee2ffb57d3ff159feab1ae4518db \ + --hash=sha256:37e50501baaa935f13a1820ab2114f74313b5cb4cfff8146acb8c5b18cdced2a \ + --hash=sha256:3cf96ace804945e53bc3e5294097e5fa32a2d43bc52416c632b414b870ee0a21 \ + --hash=sha256:42f3c02c7021073cafbc6cd152b288c56a25e585518861589bb08b063b6d2ad2 \ + --hash=sha256:4768449d8d1b0785309ace288e017cc5fa42e11a52bf08c90d9c3eb3a7a73cc6 \ + --hash=sha256:477f172807a9fa83467b30d7c58876af1410d20177c554c27525211edf535bae \ + --hash=sha256:49cd09ccbd1e3c0e2690dd62ebf95064d84aa42b9db381867e0b138631f969f2 \ + --hash=sha256:59eadb9f347d40e8f7ef77caffd0c04a31e82c1df82fe2d2a688032429d750ac \ + --hash=sha256:60943f71e3117583655a1eb76188a7cc78a25267ef09cc74be4d25a0b0c8b947 \ + --hash=sha256:787efc741e61e00ffe5e65dac99b0dc5c88b9421012a207a91b869a8b1164921 \ + --hash=sha256:7a3a1aa9528087f6f4c47f4ece2d5e6a160527821263fb8174ff36429233e093 \ + --hash=sha256:7d2e7abac41d2b4b18f03575aca860d2cb647c343e13c23d6c769106a3db2f6f \ + --hash=sha256:802109f67328c5b822d4fdac28e1cf65a24de2e2e99d76cdbeee9121cedb1b6c \ + --hash=sha256:8aedd38d357f6d5e2facd88ce62b4976afdc29db57216a23f14a0cd0ca05a8a3 \ + --hash=sha256:8fd386d0ec1f9343f1776391d9e60d4eedced0a0b0e625bb89b91f6d05f70e83 \ + --hash=sha256:90a9ba3a9c16b86afcb785b3c9418af39ccfb238fd5f6e429166e3ca8542b01f \ + --hash=sha256:91a228a077fc7cd8486c273788d4a006a37d060cb4293f471eb0325c3113af68 \ + --hash=sha256:9cf18c156b3a108197a8bf90b37d03c31c8ef35a7c18807b321d96b74e12c301 \ + --hash=sha256:9ec62a4c2dbb0a86ee5138c16ef133e59a23ac108f8d7ac97aeb61d410ce6857 \ + --hash=sha256:a1991cdf2e81e643b53fb8d272931d2bdf5f4e70d56a457e1ef95bde147ae627 \ + --hash=sha256:a628f20d019feb0f3a171c7a55cc4f75681f3b8c1bd7a5009165a487314887cd \ + --hash=sha256:a71809ec062c5b7acf286ba6d4484e6fe8130fc2b93c25e596bb34e7810c79b2 \ + --hash=sha256:a7756a9446f83c81101f6c0a48c3bfd8d387a249933c57b0d095ca8b20541337 \ + --hash=sha256:a827b9c464ee966524f8e82ec1aabb4a77ff9514cae041667fa81ae2ec8bd3e9 \ + --hash=sha256:b1ad6d2952b41d9a0ea702a474cc08c05210c6289e29dd496935c9ca3c7fb45c \ + --hash=sha256:b4e671c4c0804cdf752be26f260058bb858fbdaaef1340af170635913ecca01e \ + --hash=sha256:bd842ae3dbb7cba88beb022161c819fa80ca7d0c5a4ddd209e7daae85d904e49 \ + --hash=sha256:bdf691a205bc492956e6daef7a06fb38f8cbe8b2c1cb0386f35f4412c360c9e9 \ + --hash=sha256:c19d1e06569c277dcc872d80cbadf14a29e8199e013ff2a176d169f461439a40 \ + --hash=sha256:c81fd9386449df0ebf1ab3e01187bb30d61122c74df53ba4880a2454d866e55d \ + --hash=sha256:d0e9fec68e304fb35c559c44530213adbc7d5918bdab906a45a0f40cd56c4de2 \ + --hash=sha256:d1405caa964ba11b2396bd9fd19940440217345752e192c936d084ba5fe67dcb \ + --hash=sha256:d5373a56b90052f171c8634fedc53a6ac371e6c742606e9825772a394bdbd4b0 \ + --hash=sha256:d78aac2ffc4e88ab1cbcad844669924c24e24c7c255de9628a18f14d832007c5 \ + --hash=sha256:d916018289d2f9a882e90d2e3bd41652861ce11b5ecd8515fa07ad31d97d56e5 \ + --hash=sha256:db993a56e21d903893933887984ca9b0d274f2b1db7b3cf21ba129783953864f \ + --hash=sha256:de1aa618306a741e0497878b7f845fd6c397e52dd096fb76ed791e7268887176 \ + --hash=sha256:e37c4e21f696d6bcdbbc7caf98dffa505d04c0053909b9db0a6e8ca3b935eb07 \ + --hash=sha256:ef62eb3bcfd6d786f439828bb544ebd3936432db669403e0b8f48e424f1d55f1 \ + --hash=sha256:f0c87f097d6867833a839b086eb8d03676bb87c2efa067a131099f04aa790683 \ + --hash=sha256:f2e3ea5e4d5ecf3faefd4a5294acb6af1f0578b0cdd75d6b4529c45deaa54d6f \ + --hash=sha256:f502fe79757434292174b04db114f9e25c767b2d5ca9e759d118b22a66f445f8 \ + --hash=sha256:fa9194cb91441df7242aa3ddc4cb184be38876cb10dd973674887f334bafbfb6 + # via rosbags (setup.cfg) diff --git a/requirements.txt b/requirements.txt index 1cb14697..7b0f44ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,84 +1,69 @@ # -# This file is autogenerated by pip-compile +# This file is autogenerated by pip-compile with python 3.10 # To update, run: # -# pip-compile --generate-hashes +# pip-compile --generate-hashes setup.cfg # -lz4==3.1.3 \ - --hash=sha256:081ef0a3b5941cb03127f314229a1c78bd70c9c220bb3f4dd80033e707feaa18 \ - --hash=sha256:0a3b7eeb879577f2c7c0872156c70f4ddfbb023c1198e33d54422e24fa5494ae \ - --hash=sha256:0acbc2b797fe3c51917011c8d7f6c99398ae33cc4a1ca23c3a246d60bbf56fc8 \ - --hash=sha256:0f889e854114f87b5f99e8c82c9bf85417468b291b99a2cb27bcdcc864841a33 \ - --hash=sha256:19d3b8dca0c18991ee243acf86932eb917f14e2e61dd34c7852a1088659d5499 \ - --hash=sha256:1f8320b7b047ec4ba9a7de3509a067ccaac84dab2cadf629d0518760594c3b6a \ - --hash=sha256:37c23ca41040751649e0266f9f267c0148db12968a0a031272ee2a99cef7c753 \ - --hash=sha256:38266a6fa124e3ec2ce3ed6fd34f8e86b13c588f12b005873421afb295caee2d \ - --hash=sha256:3c00b56fd9aef8d3f776653c92cec262d42b6ea144e9a41b58b8c22a85f90045 \ - --hash=sha256:408b2c1b65697d9bc6468c987977314acefc71573b996bd86190053ae7ffe8d1 \ - --hash=sha256:41a388a34eab3cca6180cdb179bd8fdfcf7fd1a569f0e9e6084ad0540a0d53a9 \ - --hash=sha256:4c3558f4b98adb7acee6f4b45edd848684daae6a92a5ff31f8071eb910779568 \ - --hash=sha256:502d6dc17aca64e4dc95d6e7920dca906b5eabc1e657213bd07066c97fbc8cd3 \ - --hash=sha256:511c755d89048a2583ab88088fe451f7e3f15cde30560c058d80c9ac097dab21 \ - --hash=sha256:57dbd50d6abeb85f8d273b9f24f0063c4b97aae07d267302101884611a2413da \ - --hash=sha256:57e5b0a818addacae254b9160a183122b6bc4737bc77c988b72e1c57bd22ed9e \ - --hash=sha256:5aa4dd12debc5cb90980e6bb26be8b1586e57b87aaf6c773b9b799bca16edd99 \ - --hash=sha256:71f6f4dc48669ba3807a5cb5876048dc9b6467c3db312acf2040a61ea9487161 \ - --hash=sha256:7cf0e6e1020dbf8ea72ff57ece3f321f603cfa54f14337b96f7b68a7c1a742b4 \ - --hash=sha256:81b54fc66555fc7653467bf5b789d0e480ab88d17c858405e326d9c898baff2e \ - --hash=sha256:869734b6f0e8a19af10a75c769db179dcd3d867e29c29b3808ef884e76799071 \ - --hash=sha256:af1bc2952214c5a3ec6706cb86bd3e321570c62136539d32e4a57da777b002f0 \ - --hash=sha256:b4b56ae630a41980b6cf17a043b57691ff1f1677425b67556453fd96257b2a9b \ - --hash=sha256:b91fbc9571d3f3fea587ce541f38a2e71ef192075b59c2846182cb98f99862a0 \ - --hash=sha256:c0a5f9b6962aaa4632e4385143a12f5b49ee8605a42589073e54c8f23ce111b2 \ - --hash=sha256:c25dffdb8ab9eb449aacf94ba45b3e6f573b38a1041be9370716cc68dea445a6 \ - --hash=sha256:c41759a97ccac751f69e48f12671c2c3e5e1ae3000d3ee5dfe750b31511d1576 \ - --hash=sha256:d50c9584fb355d5d51414b802f7012578240bcb259550b48de628e19cd5bff6c \ - --hash=sha256:d6afa929d2c8afafd8b89e898498484b145a94cf3c140bb68094a94590ab2c2a \ - --hash=sha256:de2aac0cfda79c5a3eda9dbed21d78dc05c4a9ac00061748c3b57ea0e4a0b6a8 \ - --hash=sha256:e3029738e64a0af1b04a32a39b32b0bba0e2088f61805e074c9a7e4bc212568f - # via rosbags (setup.py) -numpy==1.21.3 \ - --hash=sha256:043e83bfc274649c82a6f09836943e4a4aebe5e33656271c7dbf9621dd58b8ec \ - --hash=sha256:160ccc1bed3a8371bf0d760971f09bfe80a3e18646620e9ded0ad159d9749baa \ - --hash=sha256:188031f833bbb623637e66006cf75e933e00e7231f67e2b45cf8189612bb5dc3 \ - --hash=sha256:28f15209fb535dd4c504a7762d3bc440779b0e37d50ed810ced209e5cea60d96 \ - --hash=sha256:29fb3dcd0468b7715f8ce2c0c2d9bbbaf5ae686334951343a41bd8d155c6ea27 \ - --hash=sha256:2a6ee9620061b2a722749b391c0d80a0e2ae97290f1b32e28d5a362e21941ee4 \ - --hash=sha256:300321e3985c968e3ae7fbda187237b225f3ffe6528395a5b7a5407f73cf093e \ - --hash=sha256:32437f0b275c1d09d9c3add782516413e98cd7c09e6baf4715cbce781fc29912 \ - --hash=sha256:3c09418a14471c7ae69ba682e2428cae5b4420a766659605566c0fa6987f6b7e \ - --hash=sha256:49c6249260890e05b8111ebfc391ed58b3cb4b33e63197b2ec7f776e45330721 \ - --hash=sha256:4cc9b512e9fb590797474f58b7f6d1f1b654b3a94f4fa8558b48ca8b3cfc97cf \ - --hash=sha256:508b0b513fa1266875524ba8a9ecc27b02ad771fe1704a16314dc1a816a68737 \ - --hash=sha256:50cd26b0cf6664cb3b3dd161ba0a09c9c1343db064e7c69f9f8b551f5104d654 \ - --hash=sha256:5c4193f70f8069550a1788bd0cd3268ab7d3a2b70583dfe3b2e7f421e9aace06 \ - --hash=sha256:5dfe9d6a4c39b8b6edd7990091fea4f852888e41919d0e6722fe78dd421db0eb \ - --hash=sha256:63571bb7897a584ca3249c86dd01c10bcb5fe4296e3568b2e9c1a55356b6410e \ - --hash=sha256:75621882d2230ab77fb6a03d4cbccd2038511491076e7964ef87306623aa5272 \ - --hash=sha256:75eb7cadc8da49302f5b659d40ba4f6d94d5045fbd9569c9d058e77b0514c9e4 \ - --hash=sha256:88a5d6b268e9ad18f3533e184744acdaa2e913b13148160b1152300c949bbb5f \ - --hash=sha256:8a10968963640e75cc0193e1847616ab4c718e83b6938ae74dea44953950f6b7 \ - --hash=sha256:90bec6a86b348b4559b6482e2b684db4a9a7eed1fa054b86115a48d58fbbf62a \ - --hash=sha256:98339aa9911853f131de11010f6dd94c8cec254d3d1f7261528c3b3e3219f139 \ - --hash=sha256:a99a6b067e5190ac6d12005a4d85aa6227c5606fa93211f86b1dafb16233e57d \ - --hash=sha256:bffa2eee3b87376cc6b31eee36d05349571c236d1de1175b804b348dc0941e3f \ - --hash=sha256:c6c2d535a7beb1f8790aaa98fd089ceab2e3dd7ca48aca0af7dc60e6ef93ffe1 \ - --hash=sha256:cc14e7519fab2a4ed87d31f99c31a3796e4e1fe63a86ebdd1c5a1ea78ebd5896 \ - --hash=sha256:dd0482f3fc547f1b1b5d6a8b8e08f63fdc250c58ce688dedd8851e6e26cff0f3 \ - --hash=sha256:dde972a1e11bb7b702ed0e447953e7617723760f420decb97305e66fb4afc54f \ - --hash=sha256:e54af82d68ef8255535a6cdb353f55d6b8cf418a83e2be3569243787a4f4866f \ - --hash=sha256:e606e6316911471c8d9b4618e082635cfe98876007556e89ce03d52ff5e8fcf0 \ - --hash=sha256:f41b018f126aac18583956c54544db437f25c7ee4794bcb23eb38bef8e5e192a \ - --hash=sha256:f8f4625536926a155b80ad2bbff44f8cc59e9f2ad14cdda7acf4c135b4dc8ff2 \ - --hash=sha256:fe52dbe47d9deb69b05084abd4b0df7abb39a3c51957c09f635520abd49b29dd - # via rosbags (setup.py) -ruamel.yaml.clib==0.2.6 \ +lz4==4.0.0 \ + --hash=sha256:04067086a443eef46eb2dfc26e1e5a76165149ceb4a88f0b540b46ead95e39c8 \ + --hash=sha256:19e939cd1e5d1776ca8f431c18c10a59970cf98caceeaa6edc98fdcf58499f29 \ + --hash=sha256:1ef9b03386757546f962e0598ac1d863960018dd9c04ec207059d3eb52bba12b \ + --hash=sha256:2428f5525c214d8cca332a96a2561415fd1261be2372b68b32a1aa40b9c9000f \ + --hash=sha256:26e4e7f88757c31e5240016b863f37ad79fc2898be272a6d78f267963c1b094b \ + --hash=sha256:2e1fa0dba94a7dece5d0fe109e317242c28f09e1ad488b8db692fcafb094db79 \ + --hash=sha256:3221e9a46d343175cefe932b0e812f2ecd3de70c7d036b951488d664587bee4a \ + --hash=sha256:463814c29f1201ef876c031ad32a185adee807ae201c228b28d65f17b203ee11 \ + --hash=sha256:4bf2880cae9a5255f86698f60af635f184e49d5f6878a7e0520b6cfcdb0a0d50 \ + --hash=sha256:4d96e913877b687bb8e8c6f8a90ce1e2a9a5c5268d9bab489f49bd3ce9ae8afa \ + --hash=sha256:5721ec225a37794fbaabcfa5cf2289ebb4b9e770e73e4e779d514c1fc784df5b \ + --hash=sha256:57c5dfd3b7dae833b0d2b2c1aafd7f9d0dfcab40683d183d010c67c9fd1beca3 \ + --hash=sha256:6a4c004e664d8185e2bfeffb90e1bfe554a0cd1a764662648b528e37220822cb \ + --hash=sha256:6c9acd054426840de2e4bbf83321945c3a20e90402ad221f4302e983f8031e14 \ + --hash=sha256:79246da3207b9eb4e53b3bed86189a60631e74f9b3d2579918b032fb1c7b114b \ + --hash=sha256:7ec46449892159c869c88848ee2c9b3b5170d193ba79524732334e7bbc39639c \ + --hash=sha256:a4afc2c12c37896e5ac7c5343f255cc242962ed7af940f6ef5276cc5c22e81fd \ + --hash=sha256:afc6bebfcbc48873854c05366b35a69b9c4e440ce17571ade032941cb89585ac \ + --hash=sha256:b83fce61cec36cdc21d234524d60a96d70f1a928533228eae6f46a9de21dc218 \ + --hash=sha256:e05542c6cbdb1128c43cfefd7518e231b39329d212b19ab89fd3868596140bdf \ + --hash=sha256:f69405f196c6fb38b94ac6000baa59c0364a1ac264e64194bb2fc48513df79ad + # via rosbags (setup.cfg) +numpy==1.22.3 \ + --hash=sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676 \ + --hash=sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4 \ + --hash=sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce \ + --hash=sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123 \ + --hash=sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1 \ + --hash=sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e \ + --hash=sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5 \ + --hash=sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d \ + --hash=sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a \ + --hash=sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab \ + --hash=sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75 \ + --hash=sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168 \ + --hash=sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4 \ + --hash=sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f \ + --hash=sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18 \ + --hash=sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62 \ + --hash=sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe \ + --hash=sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430 \ + --hash=sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802 \ + --hash=sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa + # via rosbags (setup.cfg) +ruamel-yaml==0.17.21 \ + --hash=sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7 \ + --hash=sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af + # via rosbags (setup.cfg) +ruamel-yaml-clib==0.2.6 \ --hash=sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd \ + --hash=sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee \ --hash=sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0 \ + --hash=sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7 \ --hash=sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277 \ --hash=sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104 \ --hash=sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd \ + --hash=sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0 \ --hash=sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78 \ + --hash=sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de \ --hash=sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99 \ --hash=sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527 \ --hash=sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84 \ @@ -94,54 +79,50 @@ ruamel.yaml.clib==0.2.6 \ --hash=sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502 \ --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c - # via ruamel.yaml -ruamel.yaml==0.17.16 \ - --hash=sha256:1a771fc92d3823682b7f0893ad56cb5a5c87c48e62b5399d6f42c8759a583b33 \ - --hash=sha256:ea21da1198c4b41b8e7a259301cc9710d3b972bf8ba52f06218478e6802dd1f1 - # via rosbags (setup.py) -zstandard==0.16.0 \ - --hash=sha256:066488e721ec882485a500c216302b443f2eaef39356f7c65130e76c671e3ce2 \ - --hash=sha256:08a728715858f1477239887ba3c692bc462b2c86e7a8e467dc5affa7bba9093f \ - --hash=sha256:11216b47c62e9fc71a25f4b42f525a81da268071bdb434bc1e642ffc38a24a02 \ - --hash=sha256:127c4c93f578d9b509732c74ed9b44b23e94041ba11b13827be0a7d2e3869b39 \ - --hash=sha256:12dddee2574b00c262270cfb46bd0c048e92208b95fdd39ad2a9eac1cef30498 \ - --hash=sha256:1bdda52224043e13ed20f847e3b308de1c9372d1563824fad776b1cf1f847ef0 \ - --hash=sha256:2e31680d1bcf85e7a58a45df7365af894402ae77a9868c751dc991dd13099a5f \ - --hash=sha256:42992e89b250fe6878c175119af529775d4be7967cd9de86990145d615d6a444 \ - --hash=sha256:453e42af96923582ddbf3acf843f55d2dc534a3f7b345003852dd522aa51eae6 \ - --hash=sha256:4d8a296dab7f8f5d53acc693a6785751f43ca39b51c8eabc672f978306fb40e6 \ - --hash=sha256:5251ac352d8350869c404a0ca94457da018b726f692f6456ec82bbf907fbc956 \ - --hash=sha256:57a6cfc34d906d514358769ed6d510b312be1cf033aafb5db44865a6717579bd \ - --hash=sha256:6ed51162e270b9b8097dcae6f2c239ada05ec112194633193ec3241498988924 \ - --hash=sha256:74cbea966462afed5a89eb99e4577538d10d425e05bf6240a75c086d59ccaf89 \ - --hash=sha256:87bea44ad24c15cd872263c0d5f912186a4be3db361eab3b25f1a61dcb5ca014 \ - --hash=sha256:8a745862ed525eee4e28bdbd58bf3ea952bf9da3c31bb4e4ce11ef15aea5c625 \ - --hash=sha256:8b760fc8118b1a0aa1d8f4e2012622e8f5f178d4b8cb94f8c6d2948b6a49a485 \ - --hash=sha256:8c8c0e813b67de1c9d7f2760768c4ae53f011c75ace18d5cff4fb40d2173763f \ - --hash=sha256:8d5fe983e23b05f0e924fe8d0dd3935f0c9fd3266e4c6ff8621c12c350da299d \ - --hash=sha256:8f5785c0b9b71d49d789240ae16a636728596631cf100f32b963a6f9857af5a4 \ - --hash=sha256:91efd5ea5fb3c347e7ebb6d5622bfa37d72594a2dec37c5dde70b691edb6cc03 \ - --hash=sha256:92e6c1a656390176d51125847f2f422f9d8ed468c24b63958f6ee50d9aa98c83 \ - --hash=sha256:9bcbfe1ec89789239f63daeea8778488cb5ba9034a374d7753815935f83dad65 \ - --hash=sha256:a92aa26789f17ca3b1f45cc7e728597165e2b166b99d1204bb397a672edee761 \ - --hash=sha256:a9ec6de2c058e611e9dfe88d9809a5676bc1d2a53543c1273a90a60e41b8f43c \ - --hash=sha256:ac5d97f9dece91a1162f651da79b735c5cde4d5863477785962aad648b592446 \ - --hash=sha256:ae19628886d994ac1f3d2fc7f9ed5bb551d81000f7b4e0c57a0e88301aea2766 \ - --hash=sha256:b2ea1937eff0ed5621876dc377933fe76624abfb2ab5b418995f43af6bac50de \ - --hash=sha256:b46220bef7bf9271a2a05512e86acbabc86cca08bebde8447bdbb4acb3179447 \ - --hash=sha256:b61586b0ff55c4137e512f1e9df4e4d7a6e1e9df782b4b87652df27737c90cc1 \ - --hash=sha256:be68fbac1e88f0dbe033a2d2e3aaaf9c8307730b905f3cd3c698ca4b904f0702 \ - --hash=sha256:c75557d53bb2d064521ff20cce9b8a51ee8301e031b1d6bcedb6458dda3bc85d \ - --hash=sha256:c7e6b6ad58ae6f77872da9376ef0ecbf8c1ae7a0c8fc29a2473abc90f79a9a1b \ - --hash=sha256:c8828f4e78774a6c0b8d21e59677f8f48d2e17fe2ef72793c94c10abc032c41c \ - --hash=sha256:cae9bfcb9148152f8bfb9163b4b779326ca39fe9889e45e0572c56d25d5021be \ - --hash=sha256:ce61492764d0442ca1e81d38d7bf7847d7df5003bce28089bab64c0519749351 \ - --hash=sha256:d40447f4a44b442fa6715779ff49a1e319729d829198279927d18bca0d7ac32d \ - --hash=sha256:d9946cfe54bf3365f14a5aa233eb2425de3b77eac6a4c7d03dda7dbb6acd3267 \ - --hash=sha256:dd5a2287893e52204e4ce9d0e1bcea6240661dbb412efb53d5446b881d3c10a2 \ - --hash=sha256:e9456492eb13249841e53221e742bef93f4868122bfc26bafa12a07677619732 \ - --hash=sha256:eaae2d3e8fdf8bfe269628385087e4b648beef85bb0c187644e7df4fb0fe9046 \ - --hash=sha256:eba125d3899f2003debf97019cd6f46f841a405df067da23d11443ad17952a40 \ - --hash=sha256:ef759c1dfe78aa5a01747d3465d2585de14e08fc2b0195ce3f31f45477fc5a72 \ - --hash=sha256:ffe1d24c5e11e98e4c5f96f846cdd19619d8c7e5e8e5082bed62d39baa30cecb - # via rosbags (setup.py) + # via ruamel-yaml +zstandard==0.17.0 \ + --hash=sha256:208fa6bead577b2607205640078ee452e81fe20fe96321623c632bad9ebd7148 \ + --hash=sha256:2a2ac752162ba5cbc869c60c4a4e54e890b2ee2ffb57d3ff159feab1ae4518db \ + --hash=sha256:37e50501baaa935f13a1820ab2114f74313b5cb4cfff8146acb8c5b18cdced2a \ + --hash=sha256:3cf96ace804945e53bc3e5294097e5fa32a2d43bc52416c632b414b870ee0a21 \ + --hash=sha256:42f3c02c7021073cafbc6cd152b288c56a25e585518861589bb08b063b6d2ad2 \ + --hash=sha256:4768449d8d1b0785309ace288e017cc5fa42e11a52bf08c90d9c3eb3a7a73cc6 \ + --hash=sha256:477f172807a9fa83467b30d7c58876af1410d20177c554c27525211edf535bae \ + --hash=sha256:49cd09ccbd1e3c0e2690dd62ebf95064d84aa42b9db381867e0b138631f969f2 \ + --hash=sha256:59eadb9f347d40e8f7ef77caffd0c04a31e82c1df82fe2d2a688032429d750ac \ + --hash=sha256:60943f71e3117583655a1eb76188a7cc78a25267ef09cc74be4d25a0b0c8b947 \ + --hash=sha256:787efc741e61e00ffe5e65dac99b0dc5c88b9421012a207a91b869a8b1164921 \ + --hash=sha256:7a3a1aa9528087f6f4c47f4ece2d5e6a160527821263fb8174ff36429233e093 \ + --hash=sha256:7d2e7abac41d2b4b18f03575aca860d2cb647c343e13c23d6c769106a3db2f6f \ + --hash=sha256:802109f67328c5b822d4fdac28e1cf65a24de2e2e99d76cdbeee9121cedb1b6c \ + --hash=sha256:8aedd38d357f6d5e2facd88ce62b4976afdc29db57216a23f14a0cd0ca05a8a3 \ + --hash=sha256:8fd386d0ec1f9343f1776391d9e60d4eedced0a0b0e625bb89b91f6d05f70e83 \ + --hash=sha256:90a9ba3a9c16b86afcb785b3c9418af39ccfb238fd5f6e429166e3ca8542b01f \ + --hash=sha256:91a228a077fc7cd8486c273788d4a006a37d060cb4293f471eb0325c3113af68 \ + --hash=sha256:9cf18c156b3a108197a8bf90b37d03c31c8ef35a7c18807b321d96b74e12c301 \ + --hash=sha256:9ec62a4c2dbb0a86ee5138c16ef133e59a23ac108f8d7ac97aeb61d410ce6857 \ + --hash=sha256:a1991cdf2e81e643b53fb8d272931d2bdf5f4e70d56a457e1ef95bde147ae627 \ + --hash=sha256:a628f20d019feb0f3a171c7a55cc4f75681f3b8c1bd7a5009165a487314887cd \ + --hash=sha256:a71809ec062c5b7acf286ba6d4484e6fe8130fc2b93c25e596bb34e7810c79b2 \ + --hash=sha256:a7756a9446f83c81101f6c0a48c3bfd8d387a249933c57b0d095ca8b20541337 \ + --hash=sha256:a827b9c464ee966524f8e82ec1aabb4a77ff9514cae041667fa81ae2ec8bd3e9 \ + --hash=sha256:b1ad6d2952b41d9a0ea702a474cc08c05210c6289e29dd496935c9ca3c7fb45c \ + --hash=sha256:b4e671c4c0804cdf752be26f260058bb858fbdaaef1340af170635913ecca01e \ + --hash=sha256:bd842ae3dbb7cba88beb022161c819fa80ca7d0c5a4ddd209e7daae85d904e49 \ + --hash=sha256:bdf691a205bc492956e6daef7a06fb38f8cbe8b2c1cb0386f35f4412c360c9e9 \ + --hash=sha256:c19d1e06569c277dcc872d80cbadf14a29e8199e013ff2a176d169f461439a40 \ + --hash=sha256:c81fd9386449df0ebf1ab3e01187bb30d61122c74df53ba4880a2454d866e55d \ + --hash=sha256:d0e9fec68e304fb35c559c44530213adbc7d5918bdab906a45a0f40cd56c4de2 \ + --hash=sha256:d1405caa964ba11b2396bd9fd19940440217345752e192c936d084ba5fe67dcb \ + --hash=sha256:d5373a56b90052f171c8634fedc53a6ac371e6c742606e9825772a394bdbd4b0 \ + --hash=sha256:d78aac2ffc4e88ab1cbcad844669924c24e24c7c255de9628a18f14d832007c5 \ + --hash=sha256:d916018289d2f9a882e90d2e3bd41652861ce11b5ecd8515fa07ad31d97d56e5 \ + --hash=sha256:db993a56e21d903893933887984ca9b0d274f2b1db7b3cf21ba129783953864f \ + --hash=sha256:de1aa618306a741e0497878b7f845fd6c397e52dd096fb76ed791e7268887176 \ + --hash=sha256:e37c4e21f696d6bcdbbc7caf98dffa505d04c0053909b9db0a6e8ca3b935eb07 \ + --hash=sha256:ef62eb3bcfd6d786f439828bb544ebd3936432db669403e0b8f48e424f1d55f1 \ + --hash=sha256:f0c87f097d6867833a839b086eb8d03676bb87c2efa067a131099f04aa790683 \ + --hash=sha256:f2e3ea5e4d5ecf3faefd4a5294acb6af1f0578b0cdd75d6b4529c45deaa54d6f \ + --hash=sha256:f502fe79757434292174b04db114f9e25c767b2d5ca9e759d118b22a66f445f8 \ + --hash=sha256:fa9194cb91441df7242aa3ddc4cb184be38876cb10dd973674887f334bafbfb6 + # via rosbags (setup.cfg) From 19f0678645293197b91e09ce265f5c7736916b6f Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 11 Apr 2022 00:07:53 +0200 Subject: [PATCH 067/114] Update lint --- docs/examples/save_images_rosbag1.py | 2 +- docs/examples/save_images_rosbag2.py | 2 +- docs/examples/use_with_native.py | 2 +- src/rosbags/convert/converter.py | 2 +- src/rosbags/rosbag1/reader.py | 24 ++++---- src/rosbags/rosbag1/writer.py | 5 +- src/rosbags/rosbag2/reader.py | 57 ++++++++++++++---- src/rosbags/rosbag2/writer.py | 6 +- src/rosbags/serde/cdr.py | 90 ++++++++++++++-------------- src/rosbags/serde/messages.py | 8 ++- src/rosbags/serde/ros1.py | 46 +++++++------- src/rosbags/serde/serdes.py | 4 +- src/rosbags/serde/typing.py | 4 +- src/rosbags/typesys/base.py | 2 +- src/rosbags/typesys/register.py | 11 ++-- tests/cdr.py | 8 +-- tests/test_roundtrip.py | 2 +- tests/test_roundtrip1.py | 2 +- tests/test_serde.py | 37 +++++++----- tests/test_writer1.py | 6 +- tools/bench/bench.py | 19 ++++-- tools/compare/compare.py | 25 +++++--- 22 files changed, 215 insertions(+), 149 deletions(-) diff --git a/docs/examples/save_images_rosbag1.py b/docs/examples/save_images_rosbag1.py index 60acc8e2..3390407c 100644 --- a/docs/examples/save_images_rosbag1.py +++ b/docs/examples/save_images_rosbag1.py @@ -33,7 +33,7 @@ def save_images() -> None: frame_id=FRAMEID, ), format='jpeg', # could also be 'png' - data=numpy.fromfile(path, dtype=numpy.uint8), # type: ignore + data=numpy.fromfile(path, dtype=numpy.uint8), ) writer.write( diff --git a/docs/examples/save_images_rosbag2.py b/docs/examples/save_images_rosbag2.py index 673d4da5..f8a97959 100644 --- a/docs/examples/save_images_rosbag2.py +++ b/docs/examples/save_images_rosbag2.py @@ -33,7 +33,7 @@ def save_images() -> None: frame_id=FRAMEID, ), format='jpeg', # could also be 'png' - data=numpy.fromfile(path, dtype=numpy.uint8), # type: ignore + data=numpy.fromfile(path, dtype=numpy.uint8), ) writer.write( diff --git a/docs/examples/use_with_native.py b/docs/examples/use_with_native.py index f484bfe3..93715b80 100644 --- a/docs/examples/use_with_native.py +++ b/docs/examples/use_with_native.py @@ -13,7 +13,7 @@ if TYPE_CHECKING: NATIVE_CLASSES: dict[str, Any] = {} -def to_native(msg: Any) -> Any: +def to_native(msg: Any) -> Any: # noqa: ANN401 """Convert rosbags message to native message. Args: diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index c20f4735..5b6614c5 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -171,5 +171,5 @@ def convert(src: Path, dst: Optional[Path]) -> None: raise ConverterError(f'Reading source bag: {err}') from err except (WriterError1, WriterError2) as err: raise ConverterError(f'Writing destination bag: {err}') from err - except Exception as err: # pylint: disable=broad-except + except Exception as err: raise ConverterError(f'Converting rosbag: {err!r}') from err diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 83ad78a8..14742118 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -106,9 +106,9 @@ class IndexData(NamedTuple): def __eq__(self, other: object) -> bool: """Compare by time only.""" - if not isinstance(other, IndexData): # pragma: no cover - return NotImplemented - return self.time == other[0] + if isinstance(other, IndexData): + return self.time == other[0] + return NotImplemented # pragma: no cover def __ge__(self, other: tuple[int, ...]) -> bool: """Compare by time only.""" @@ -120,9 +120,9 @@ class IndexData(NamedTuple): def __ne__(self, other: object) -> bool: """Compare by time only.""" - if not isinstance(other, IndexData): # pragma: no cover - return NotImplemented - return self.time != other[0] + if isinstance(other, IndexData): + return self.time != other[0] + return NotImplemented # pragma: no cover deserialize_uint8: Callable[[bytes], tuple[int]] = struct.Struct(' None: # pylint: disable=too-many-branches,too-many-locals,too-many-statements + def open(self) -> None: # pylint: disable=too-many-branches,too-many-locals """Open rosbag and read metadata.""" try: self.bio = self.path.open('rb') @@ -394,13 +394,11 @@ class Reader: conn_count = header.get_uint32('conn_count') chunk_count = header.get_uint32('chunk_count') try: - encryptor = header.get_string('encryptor') - if encryptor: - raise ValueError - except ValueError: - raise ReaderError(f'Bag encryption {encryptor!r} is not supported.') from None + encryptor: Optional[str] = header.get_string('encryptor') except ReaderError: - pass + encryptor = None + if encryptor: + raise ReaderError(f'Bag encryption {encryptor!r} is not supported.') from None if index_pos == 0: raise ReaderError('Bag is not indexed, reindex before reading.') diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index 9126a378..cbe4bc35 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -31,6 +31,7 @@ class WriterError(Exception): @dataclass class WriteChunk: """In progress chunk.""" + data: BytesIO pos: int start: int @@ -126,7 +127,7 @@ class Header(Dict[str, Any]): return size + 4 -class Writer: # pylint: disable=too-many-instance-attributes +class Writer: """Rosbag1 writer. This class implements writing of rosbag1 files in version 2.0. It should be @@ -212,7 +213,7 @@ class Writer: # pylint: disable=too-many-instance-attributes md5sum: Optional[str] = None, callerid: Optional[str] = None, latching: Optional[int] = None, - **_kw: Any, + **_kw: Any, # noqa: ANN401 ) -> Connection: """Add a connection. diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index ac719ec5..b3145aa3 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -18,7 +18,44 @@ from .connection import Connection if TYPE_CHECKING: from types import TracebackType - from typing import Any, Generator, Iterable, Literal, Optional, Type, Union + from typing import Any, Generator, Iterable, Literal, Optional, Type, TypedDict, Union + + class StartingTime(TypedDict): + """Bag starting time.""" + + nanoseconds_since_epoch: int + + class Duration(TypedDict): + """Bag starting time.""" + + nanoseconds: int + + class TopicMetadata(TypedDict): + """Topic metadata.""" + + name: str + type: str + serialization_format: str + offered_qos_profiles: str + + class TopicWithMessageCount(TypedDict): + """Topic with message count.""" + + message_count: int + topic_metadata: TopicMetadata + + class Metadata(TypedDict): + """Rosbag2 metadata file.""" + + version: int + storage_identifier: str + relative_file_paths: list[str] + starting_time: StartingTime + duration: Duration + message_count: int + compression_format: str + compression_mode: str + topics_with_message_count: list[TopicWithMessageCount] class ReaderError(Exception): @@ -72,13 +109,14 @@ class Reader: Raises: ReaderError: Bag not readable or bag metadata. + """ path = Path(path) - self.path = Path + yamlpath = path / 'metadata.yaml' + self.path = path self.bio = False try: yaml = YAML(typ='safe') - yamlpath = path / 'metadata.yaml' dct = yaml.load(yamlpath.read_text()) except OSError as err: raise ReaderError(f'Could not read metadata at {yamlpath}: {err}.') from None @@ -86,7 +124,7 @@ class Reader: raise ReaderError(f'Could not load YAML from {yamlpath}: {exc}') from None try: - self.metadata = dct['rosbag2_bagfile_information'] + self.metadata: Metadata = dct['rosbag2_bagfile_information'] if (ver := self.metadata['version']) > 4: raise ReaderError(f'Rosbag2 version {ver} not supported; please report issue.') if storageid := self.metadata['storage_identifier'] != 'sqlite3': @@ -95,8 +133,7 @@ class Reader: ) self.paths = [path / Path(x).name for x in self.metadata['relative_file_paths']] - missing = [x for x in self.paths if not x.exists()] - if missing: + if missing := [x for x in self.paths if not x.exists()]: raise ReaderError(f'Some database files are missing: {[str(x) for x in missing]!r}') self.connections = { @@ -110,7 +147,7 @@ class Reader: ) for idx, x in enumerate(self.metadata['topics_with_message_count']) } noncdr = { - y for x in self.connections.values() if (y := x.serialization_format) != 'cdr' + fmt for x in self.connections.values() if (fmt := x.serialization_format) != 'cdr' } if noncdr: raise ReaderError(f'Serialization format {noncdr!r} is not supported.') @@ -140,8 +177,7 @@ class Reader: @property def start_time(self) -> int: """Timestamp in nanoseconds of the earliest message.""" - nsecs: int = self.metadata['starting_time']['nanoseconds_since_epoch'] - return nsecs + return self.metadata['starting_time']['nanoseconds_since_epoch'] @property def end_time(self) -> int: @@ -151,8 +187,7 @@ class Reader: @property def message_count(self) -> int: """Total message count.""" - count: int = self.metadata['message_count'] - return count + return self.metadata['message_count'] @property def compression_format(self) -> Optional[str]: diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 915b6360..027647ac 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -18,6 +18,8 @@ if TYPE_CHECKING: from types import TracebackType from typing import Any, Literal, Optional, Type, Union + from .reader import Metadata + class WriterError(Exception): """Writer Error.""" @@ -125,7 +127,7 @@ class Writer: # pylint: disable=too-many-instance-attributes msgtype: str, serialization_format: str = 'cdr', offered_qos_profiles: str = '', - **_kw: Any, + **_kw: Any, # noqa: ANN401 ) -> Connection: """Add a connection. @@ -218,7 +220,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.compressor.copy_stream(infile, outfile) src.unlink() - metadata = { + metadata: dict[str, Metadata] = { 'rosbag2_bagfile_information': { 'version': 4, 'storage_identifier': 'sqlite3', diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index 7d7587d3..8dc87c96 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -86,22 +86,22 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: else: assert subdesc.valtype == Valtype.MESSAGE - anext = align(subdesc) + anext_before = align(subdesc) anext_after = align_after(subdesc) if subdesc.args.size_cdr: for _ in range(length): - if anext > anext_after: - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') - size = (size + anext - 1) & -anext + if anext_before > anext_after: + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') + size = (size + anext_before - 1) & -anext_before lines.append(f' pos += {subdesc.args.size_cdr}') size += subdesc.args.size_cdr else: lines.append(f' func = get_msgdef("{subdesc.args.name}").getsize_cdr') lines.append(f' val = message.{fieldname}') for idx in range(length): - if anext > anext_after: - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + if anext_before > anext_after: + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(f' pos = func(pos, val[{idx}])') is_stat = False aligned = align_after(subdesc) @@ -117,45 +117,45 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: lines.append(' pos += 4 + len(val.encode()) + 1') aligned = 1 else: - anext = align(subdesc) - if aligned < anext: + anext_before = align(subdesc) + if aligned < anext_before: lines.append(f' if len(message.{fieldname}):') - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') - aligned = anext + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') + aligned = anext_before lines.append(f' pos += len(message.{fieldname}) * {SIZEMAP[subdesc.args]}') else: assert subdesc.valtype == Valtype.MESSAGE - anext = align(subdesc) + anext_before = align(subdesc) anext_after = align_after(subdesc) lines.append(f' val = message.{fieldname}') if subdesc.args.size_cdr: - if aligned < anext <= anext_after: - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + if aligned < anext_before <= anext_after: + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(' for _ in val:') - if anext > anext_after: - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + if anext_before > anext_after: + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(f' pos += {subdesc.args.size_cdr}') else: lines.append(f' func = get_msgdef("{subdesc.args.name}").getsize_cdr') - if aligned < anext <= anext_after: - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + if aligned < anext_before <= anext_after: + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(' for item in val:') - if anext > anext_after: - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + if anext_before > anext_after: + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(' pos = func(pos, item)') aligned = align_after(subdesc) aligned = min([aligned, 4]) is_stat = False - if fnext and aligned < (anext := align(fnext.descriptor)): - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') - aligned = anext + if fnext and aligned < (anext_before := align(fnext.descriptor)): + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') + aligned = anext_before is_stat = False lines.append(' return pos') - return compile_lines(lines).getsize_cdr, is_stat * size # type: ignore + return compile_lines(lines).getsize_cdr, is_stat * size def generate_serialize_cdr(fields: list[Field], endianess: str) -> CDRSer: @@ -240,14 +240,14 @@ def generate_serialize_cdr(fields: list[Field], endianess: str) -> CDRSer: else: assert subdesc.valtype == Valtype.MESSAGE - anext = align(subdesc) + anext_before = align(subdesc) anext_after = align_after(subdesc) lines.append( f' func = get_msgdef("{subdesc.args.name}").serialize_cdr_{endianess}', ) for idx in range(length): - if anext > anext_after: - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + if anext_before > anext_after: + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(f' pos = func(rawdata, pos, val[{idx}])') aligned = align_after(subdesc) else: @@ -272,28 +272,28 @@ def generate_serialize_cdr(fields: list[Field], endianess: str) -> CDRSer: lines.append(f' size = len(val) * {SIZEMAP[subdesc.args]}') if (endianess == 'le') != (sys.byteorder == 'little'): lines.append(' val = val.byteswap()') - if aligned < (anext := align(subdesc)): + if aligned < (anext_before := align(subdesc)): lines.append(' if size:') - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(' rawdata[pos:pos + size] = val.view(numpy.uint8)') lines.append(' pos += size') - aligned = anext + aligned = anext_before if subdesc.valtype == Valtype.MESSAGE: - anext = align(subdesc) + anext_before = align(subdesc) lines.append( f' func = get_msgdef("{subdesc.args.name}").serialize_cdr_{endianess}', ) lines.append(' for item in val:') - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(' pos = func(rawdata, pos, item)') aligned = align_after(subdesc) aligned = min([4, aligned]) - if fnext and aligned < (anext := align(fnext.descriptor)): - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') - aligned = anext + if fnext and aligned < (anext_before := align(fnext.descriptor)): + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') + aligned = anext_before lines.append(' return pos') return compile_lines(lines).serialize_cdr # type: ignore @@ -384,13 +384,13 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: lines.append(f' pos += {size}') else: assert subdesc.valtype == Valtype.MESSAGE - anext = align(subdesc) + anext_before = align(subdesc) anext_after = align_after(subdesc) lines.append(f' msgdef = get_msgdef("{subdesc.args.name}")') lines.append(' value = []') for _ in range(length): - if anext > anext_after: - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + if anext_before > anext_after: + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls)') lines.append(' value.append(obj)') lines.append(' values.append(value)') @@ -418,9 +418,9 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: aligned = 1 else: lines.append(f' length = size * {SIZEMAP[subdesc.args]}') - if aligned < (anext := align(subdesc)): + if aligned < (anext_before := align(subdesc)): lines.append(' if size:') - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append( f' val = numpy.frombuffer(rawdata, ' f'dtype=numpy.{subdesc.args}, count=size, offset=pos)', @@ -429,14 +429,14 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: lines.append(' val = val.byteswap()') lines.append(' values.append(val)') lines.append(' pos += length') - aligned = anext + aligned = anext_before if subdesc.valtype == Valtype.MESSAGE: - anext = align(subdesc) + anext_before = align(subdesc) lines.append(f' msgdef = get_msgdef("{subdesc.args.name}")') lines.append(' value = []') lines.append(' for _ in range(size):') - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls)') lines.append(' value.append(obj)') lines.append(' values.append(value)') @@ -444,9 +444,9 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: aligned = min([4, aligned]) - if fnext and aligned < (anext := align(fnext.descriptor)): - lines.append(f' pos = (pos + {anext} - 1) & -{anext}') - aligned = anext + if fnext and aligned < (anext_before := align(fnext.descriptor)): + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') + aligned = anext_before lines.append(' return cls(*values), pos') return compile_lines(lines).deserialize_cdr # type: ignore diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py index df9c8e0c..bad20c4e 100644 --- a/src/rosbags/serde/messages.py +++ b/src/rosbags/serde/messages.py @@ -14,7 +14,7 @@ from .typing import Descriptor, Field, Msgdef from .utils import Valtype if TYPE_CHECKING: - from typing import Any + from rosbags.typesys.base import Fielddesc MSGDEFCACHE: dict[str, Msgdef] = {} @@ -38,14 +38,18 @@ def get_msgdef(typename: str) -> Msgdef: if typename not in MSGDEFCACHE: entries = types.FIELDDEFS[typename][1] - def fixup(entry: Any) -> Descriptor: + def fixup(entry: Fielddesc) -> Descriptor: if entry[0] == Valtype.BASE: + assert isinstance(entry[1], str) return Descriptor(Valtype.BASE, entry[1]) if entry[0] == Valtype.MESSAGE: + assert isinstance(entry[1], str) return Descriptor(Valtype.MESSAGE, get_msgdef(entry[1])) if entry[0] == Valtype.ARRAY: + assert not isinstance(entry[1][0], str) return Descriptor(Valtype.ARRAY, (fixup(entry[1][0]), entry[1][1])) if entry[0] == Valtype.SEQUENCE: + assert not isinstance(entry[1][0], str) return Descriptor(Valtype.SEQUENCE, (fixup(entry[1][0]), entry[1][1])) raise SerdeError( # pragma: no cover f'Unknown field type {entry[0]!r} encountered.', diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index d51a83e0..402afb8c 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -18,7 +18,7 @@ from .typing import Field from .utils import SIZEMAP, Valtype, align, align_after, compile_lines if TYPE_CHECKING: - from typing import Union # pylint: disable=ungrouped-imports + from typing import Union from .typing import Bitcvt, BitcvtSize @@ -114,13 +114,13 @@ def generate_ros1_to_cdr( aligned = SIZEMAP[subdesc.args] if subdesc.valtype == Valtype.MESSAGE: - anext = align(subdesc) + anext_before = align(subdesc) anext_after = align_after(subdesc) lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') for _ in range(length): - if anext > anext_after: - lines.append(f' opos = (opos + {anext} - 1) & -{anext}') + if anext_before > anext_after: + lines.append(f' opos = (opos + {anext_before} - 1) & -{anext_before}') lines.append(' ipos, opos = func(input, ipos, output, opos)') aligned = anext_after else: @@ -150,30 +150,30 @@ def generate_ros1_to_cdr( lines.append(' opos += length') aligned = 1 else: - if aligned < (anext := align(subdesc)): + if aligned < (anext_before := align(subdesc)): lines.append(' if size:') - lines.append(f' opos = (opos + {anext} - 1) & -{anext}') + lines.append(f' opos = (opos + {anext_before} - 1) & -{anext_before}') lines.append(f' length = size * {SIZEMAP[subdesc.args]}') if copy: lines.append(' output[opos:opos + length] = input[ipos:ipos + length]') lines.append(' ipos += length') lines.append(' opos += length') - aligned = anext + aligned = anext_before else: assert subdesc.valtype == Valtype.MESSAGE - anext = align(subdesc) + anext_before = align(subdesc) lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') lines.append(' for _ in range(size):') - lines.append(f' opos = (opos + {anext} - 1) & -{anext}') + lines.append(f' opos = (opos + {anext_before} - 1) & -{anext_before}') lines.append(' ipos, opos = func(input, ipos, output, opos)') aligned = align_after(subdesc) aligned = min([aligned, 4]) - if fnext and aligned < (anext := align(fnext.descriptor)): - lines.append(f' opos = (opos + {anext} - 1) & -{anext}') - aligned = anext + if fnext and aligned < (anext_before := align(fnext.descriptor)): + lines.append(f' opos = (opos + {anext_before} - 1) & -{anext_before}') + aligned = anext_before lines.append(' return ipos, opos') return getattr(compile_lines(lines), funcname) # type: ignore @@ -270,13 +270,13 @@ def generate_cdr_to_ros1( aligned = SIZEMAP[subdesc.args] if subdesc.valtype == Valtype.MESSAGE: - anext = align(subdesc) + anext_before = align(subdesc) anext_after = align_after(subdesc) lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') for _ in range(length): - if anext > anext_after: - lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') + if anext_before > anext_after: + lines.append(f' ipos = (ipos + {anext_before} - 1) & -{anext_before}') lines.append(' ipos, opos = func(input, ipos, output, opos)') aligned = anext_after else: @@ -304,30 +304,30 @@ def generate_cdr_to_ros1( lines.append(' opos += length') aligned = 1 else: - if aligned < (anext := align(subdesc)): + if aligned < (anext_before := align(subdesc)): lines.append(' if size:') - lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') + lines.append(f' ipos = (ipos + {anext_before} - 1) & -{anext_before}') lines.append(f' length = size * {SIZEMAP[subdesc.args]}') if copy: lines.append(' output[opos:opos + length] = input[ipos:ipos + length]') lines.append(' ipos += length') lines.append(' opos += length') - aligned = anext + aligned = anext_before else: assert subdesc.valtype == Valtype.MESSAGE - anext = align(subdesc) + anext_before = align(subdesc) lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') lines.append(' for _ in range(size):') - lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') + lines.append(f' ipos = (ipos + {anext_before} - 1) & -{anext_before}') lines.append(' ipos, opos = func(input, ipos, output, opos)') aligned = align_after(subdesc) aligned = min([aligned, 4]) - if fnext and aligned < (anext := align(fnext.descriptor)): - lines.append(f' ipos = (ipos + {anext} - 1) & -{anext}') - aligned = anext + if fnext and aligned < (anext_before := align(fnext.descriptor)): + lines.append(f' ipos = (ipos + {anext_before} - 1) & -{anext_before}') + aligned = anext_before lines.append(' return ipos, opos') return getattr(compile_lines(lines), funcname) # type: ignore diff --git a/src/rosbags/serde/serdes.py b/src/rosbags/serde/serdes.py index 57e92ceb..96d4aabe 100644 --- a/src/rosbags/serde/serdes.py +++ b/src/rosbags/serde/serdes.py @@ -14,7 +14,7 @@ if TYPE_CHECKING: from typing import Any -def deserialize_cdr(rawdata: bytes, typename: str) -> Any: +def deserialize_cdr(rawdata: bytes, typename: str) -> Any: # noqa: ANN401 """Deserialize raw data into a message object. Args: @@ -35,7 +35,7 @@ def deserialize_cdr(rawdata: bytes, typename: str) -> Any: def serialize_cdr( - message: Any, + message: object, typename: str, little_endian: bool = sys.byteorder == 'little', ) -> memoryview: diff --git a/src/rosbags/serde/typing.py b/src/rosbags/serde/typing.py index 413059a9..de4e27b5 100644 --- a/src/rosbags/serde/typing.py +++ b/src/rosbags/serde/typing.py @@ -13,8 +13,8 @@ if TYPE_CHECKING: BitcvtSize = Callable[[bytes, int, None, int], Tuple[int, int]] CDRDeser = Callable[[bytes, int, type], Tuple[Any, int]] - CDRSer = Callable[[bytes, int, type], int] - CDRSerSize = Callable[[int, type], int] + CDRSer = Callable[[bytes, int, object], int] + CDRSerSize = Callable[[int, object], int] class Descriptor(NamedTuple): diff --git a/src/rosbags/typesys/base.py b/src/rosbags/typesys/base.py index 33a56e1a..79814ab0 100644 --- a/src/rosbags/typesys/base.py +++ b/src/rosbags/typesys/base.py @@ -68,5 +68,5 @@ def parse_message_definition(visitor: Visitor, text: str) -> Typesdict: npos, trees = rule.parse(text, pos) assert npos == len(text), f'Could not parse: {text!r}' return visitor.visit(trees) # type: ignore - except Exception as err: # pylint: disable=broad-except + except Exception as err: raise TypesysError(f'Could not parse: {text!r}') from err diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index fbdd3e31..ebbb53bc 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -31,9 +31,8 @@ def get_typehint(desc: tuple[int, Union[str, tuple[tuple[int, str], Optional[int """ if desc[0] == Nodetype.BASE: - if match := INTLIKE.match(desc[1]): # type: ignore - return match.group(1) - return 'str' + assert isinstance(desc[1], str) + return match.group(1) if (match := INTLIKE.match(desc[1])) else 'str' if desc[0] == Nodetype.NAME: assert isinstance(desc[1], str) @@ -43,7 +42,8 @@ def get_typehint(desc: tuple[int, Union[str, tuple[tuple[int, str], Optional[int if INTLIKE.match(sub[1]): typ = 'bool8' if sub[1] == 'bool' else sub[1] return f'numpy.ndarray[Any, numpy.dtype[numpy.{typ}]]' - return f'list[{get_typehint(sub)}]' # type: ignore + assert isinstance(sub, tuple) + return f'list[{get_typehint(sub)}]' def generate_python_code(typs: Typesdict) -> str: @@ -142,6 +142,7 @@ def register_types(typs: Typesdict) -> None: Raises: TypesysError: Type already present with different definition. + """ code = generate_python_code(typs) name = 'rosbags.usertypes' @@ -150,7 +151,7 @@ def register_types(typs: Typesdict) -> None: module = module_from_spec(spec) sys.modules[name] = module exec(code, module.__dict__) # pylint: disable=exec-used - fielddefs: Typesdict = module.FIELDDEFS # type: ignore + fielddefs: Typesdict = module.FIELDDEFS for name, (_, fields) in fielddefs.items(): if name == 'std_msgs/msg/Header': diff --git a/tests/cdr.py b/tests/cdr.py index 036576da..562329f3 100644 --- a/tests/cdr.py +++ b/tests/cdr.py @@ -117,7 +117,7 @@ def deserialize_array(rawdata: bytes, bmap: BasetypeMap, pos: int, num: int, des size = SIZEMAP[desc.args] pos = (pos + size - 1) & -size - ndarr = numpy.frombuffer(rawdata, dtype=desc.args, count=num, offset=pos) # type: ignore + ndarr = numpy.frombuffer(rawdata, dtype=desc.args, count=num, offset=pos) if (bmap is BASETYPEMAP_LE) != (sys.byteorder == 'little'): ndarr = ndarr.byteswap() # no inplace on readonly array return ndarr, pos + num * SIZEMAP[desc.args] @@ -297,7 +297,7 @@ def serialize_message( rawdata: memoryview, bmap: BasetypeMap, pos: int, - message: Any, + message: object, msgdef: Msgdef, ) -> int: """Serialize a message. @@ -369,7 +369,7 @@ def get_array_size(desc: Descriptor, val: Array, size: int) -> int: raise SerdeError(f'Nested arrays {desc!r} are not supported.') # pragma: no cover -def get_size(message: Any, msgdef: Msgdef, size: int = 0) -> int: +def get_size(message: object, msgdef: Msgdef, size: int = 0) -> int: """Calculate size of serialzied message. Args: @@ -413,7 +413,7 @@ def get_size(message: Any, msgdef: Msgdef, size: int = 0) -> int: def serialize( - message: Any, + message: object, typename: str, little_endian: bool = sys.byteorder == 'little', ) -> memoryview: diff --git a/tests/test_roundtrip.py b/tests/test_roundtrip.py index bb80b4d0..6743deed 100644 --- a/tests/test_roundtrip.py +++ b/tests/test_roundtrip.py @@ -38,6 +38,6 @@ def test_roundtrip(mode: Writer.CompressionMode, tmp_path: Path) -> None: rconnection, _, raw = next(gen) assert rconnection == wconnection msg = deserialize_cdr(raw, rconnection.msgtype) - assert msg.data == Foo.data + assert getattr(msg, 'data', None) == Foo.data with pytest.raises(StopIteration): next(gen) diff --git a/tests/test_roundtrip1.py b/tests/test_roundtrip1.py index 95d3ace7..60758b91 100644 --- a/tests/test_roundtrip1.py +++ b/tests/test_roundtrip1.py @@ -39,6 +39,6 @@ def test_roundtrip(tmp_path: Path, fmt: Optional[Writer.CompressionFormat]) -> N gen = rbag.messages() connection, _, raw = next(gen) msg = deserialize_cdr(ros1_to_cdr(raw, connection.msgtype), connection.msgtype) - assert msg.data == Foo.data + assert getattr(msg, 'data', None) == Foo.data with pytest.raises(StopIteration): next(gen) diff --git a/tests/test_serde.py b/tests/test_serde.py index d834f34a..8359f52f 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -13,7 +13,10 @@ import pytest from rosbags.serde import SerdeError, cdr_to_ros1, deserialize_cdr, ros1_to_cdr, serialize_cdr from rosbags.serde.messages import get_msgdef from rosbags.typesys import get_types_from_msg, register_types -from rosbags.typesys.types import builtin_interfaces__msg__Time, std_msgs__msg__Header +from rosbags.typesys.types import builtin_interfaces__msg__Time as Time +from rosbags.typesys.types import geometry_msgs__msg__Polygon as Polygon +from rosbags.typesys.types import sensor_msgs__msg__MagneticField as MagneticField +from rosbags.typesys.types import std_msgs__msg__Header as Header from .cdr import deserialize, serialize @@ -184,6 +187,7 @@ def _comparable() -> Generator[None, None, None]: Notes: This solution is necessary as numpy.ndarray is not directly patchable. + """ frombuffer = numpy.frombuffer @@ -195,16 +199,16 @@ def _comparable() -> Generator[None, None, None]: class CNDArray(MagicMock): """Mock ndarray.""" - def __init__(self, *args: Any, **kwargs: Any): + def __init__(self, *args: Any, **kwargs: Any): # noqa: ANN401 super().__init__(*args, **kwargs) self.__eq__ = arreq # type: ignore - def byteswap(self, *args: Any) -> 'CNDArray': + def byteswap(self, *args: Any) -> CNDArray: # noqa: ANN401 """Wrap return value also in mock.""" return CNDArray(wraps=self._mock_wraps.byteswap(*args)) - def wrap_frombuffer(*args: Any, **kwargs: Any) -> CNDArray: - return CNDArray(wraps=frombuffer(*args, **kwargs)) # type: ignore + def wrap_frombuffer(*args: Any, **kwargs: Any) -> CNDArray: # noqa: ANN401 + return CNDArray(wraps=frombuffer(*args, **kwargs)) with patch.object(numpy, 'frombuffer', side_effect=wrap_frombuffer): yield @@ -217,7 +221,7 @@ def test_serde(message: tuple[bytes, str, bool]) -> None: serdeser = serialize_cdr(deserialize_cdr(rawdata, typ), typ, is_little) assert serdeser == serialize(deserialize(rawdata, typ), typ, is_little) - assert serdeser == rawdata[0:len(serdeser)] + assert serdeser == rawdata[:len(serdeser)] assert len(rawdata) - len(serdeser) < 4 assert all(x == 0 for x in rawdata[len(serdeser):]) @@ -227,6 +231,7 @@ def test_deserializer() -> None: """Test deserializer.""" msg = deserialize_cdr(*MSG_POLY[:2]) assert msg == deserialize(*MSG_POLY[:2]) + assert isinstance(msg, Polygon) assert len(msg.points) == 2 assert msg.points[0].x == 1 assert msg.points[0].y == 2 @@ -237,6 +242,7 @@ def test_deserializer() -> None: msg = deserialize_cdr(*MSG_MAGN[:2]) assert msg == deserialize(*MSG_MAGN[:2]) + assert isinstance(msg, MagneticField) assert 'MagneticField' in repr(msg) assert msg.header.stamp.sec == 708 assert msg.header.stamp.nanosec == 256 @@ -248,6 +254,7 @@ def test_deserializer() -> None: msg_big = deserialize_cdr(*MSG_MAGN_BIG[:2]) assert msg_big == deserialize(*MSG_MAGN_BIG[:2]) + assert isinstance(msg_big, MagneticField) assert msg.magnetic_field == msg_big.magnetic_field @@ -285,7 +292,7 @@ def test_serializer_errors() -> None: class Foo: # pylint: disable=too-few-public-methods """Dummy class.""" - coef = numpy.array([1, 2, 3, 4]) + coef: numpy.ndarray[Any, numpy.dtype[numpy.int_]] = numpy.array([1, 2, 3, 4]) msg = Foo() ret = serialize_cdr(msg, 'shape_msgs/msg/Plane', True) @@ -376,7 +383,8 @@ def test_custom_type() -> None: def test_ros1_to_cdr() -> None: """Test ROS1 to CDR conversion.""" register_types(dict(get_types_from_msg(STATIC_16_64, 'test_msgs/msg/static_16_64'))) - msg_ros = (b'\x01\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02') + msg_ros = (b'\x01\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x02') msg_cdr = ( b'\x00\x01\x00\x00' b'\x01\x00' @@ -386,7 +394,8 @@ def test_ros1_to_cdr() -> None: assert ros1_to_cdr(msg_ros, 'test_msgs/msg/static_16_64') == msg_cdr register_types(dict(get_types_from_msg(DYNAMIC_S_64, 'test_msgs/msg/dynamic_s_64'))) - msg_ros = (b'\x01\x00\x00\x00X' b'\x00\x00\x00\x00\x00\x00\x00\x02') + msg_ros = (b'\x01\x00\x00\x00X' + b'\x00\x00\x00\x00\x00\x00\x00\x02') msg_cdr = ( b'\x00\x01\x00\x00' b'\x02\x00\x00\x00X\x00' @@ -399,7 +408,8 @@ def test_ros1_to_cdr() -> None: def test_cdr_to_ros1() -> None: """Test CDR to ROS1 conversion.""" register_types(dict(get_types_from_msg(STATIC_16_64, 'test_msgs/msg/static_16_64'))) - msg_ros = (b'\x01\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02') + msg_ros = (b'\x01\x00' + b'\x00\x00\x00\x00\x00\x00\x00\x02') msg_cdr = ( b'\x00\x01\x00\x00' b'\x01\x00' @@ -409,7 +419,8 @@ def test_cdr_to_ros1() -> None: assert cdr_to_ros1(msg_cdr, 'test_msgs/msg/static_16_64') == msg_ros register_types(dict(get_types_from_msg(DYNAMIC_S_64, 'test_msgs/msg/dynamic_s_64'))) - msg_ros = (b'\x01\x00\x00\x00X' b'\x00\x00\x00\x00\x00\x00\x00\x02') + msg_ros = (b'\x01\x00\x00\x00X' + b'\x00\x00\x00\x00\x00\x00\x00\x02') msg_cdr = ( b'\x00\x01\x00\x00' b'\x02\x00\x00\x00X\x00' @@ -418,7 +429,7 @@ def test_cdr_to_ros1() -> None: ) assert cdr_to_ros1(msg_cdr, 'test_msgs/msg/dynamic_s_64') == msg_ros - header = std_msgs__msg__Header(stamp=builtin_interfaces__msg__Time(42, 666), frame_id='frame') + header = Header(stamp=Time(42, 666), frame_id='frame') msg_ros = cdr_to_ros1(serialize_cdr(header, 'std_msgs/msg/Header'), 'std_msgs/msg/Header') assert msg_ros == b'\x00\x00\x00\x00*\x00\x00\x00\x9a\x02\x00\x00\x05\x00\x00\x00frame' @@ -426,7 +437,6 @@ def test_cdr_to_ros1() -> None: @pytest.mark.usefixtures('_comparable') def test_padding_empty_sequence() -> None: """Test empty sequences do not add item padding.""" - # pylint: disable=protected-access register_types(dict(get_types_from_msg(SU64_B, 'test_msgs/msg/su64_b'))) su64_b = get_msgdef('test_msgs/msg/su64_b').cls @@ -446,7 +456,6 @@ def test_padding_empty_sequence() -> None: @pytest.mark.usefixtures('_comparable') def test_align_after_empty_sequence() -> None: """Test alignment after empty sequences.""" - # pylint: disable=protected-access register_types(dict(get_types_from_msg(SU64_U64, 'test_msgs/msg/su64_u64'))) su64_b = get_msgdef('test_msgs/msg/su64_u64').cls diff --git a/tests/test_writer1.py b/tests/test_writer1.py index ab7d34aa..28ba6cee 100644 --- a/tests/test_writer1.py +++ b/tests/test_writer1.py @@ -49,7 +49,7 @@ def test_add_connection(tmp_path: Path) -> None: with Writer(path) as writer: res = writer.add_connection('/foo', 'test_msgs/msg/Test', 'MESSAGE_DEFINITION', 'HASH') - assert res.cid == 0 + assert res.cid == 0 data = path.read_bytes() assert data.count(b'MESSAGE_DEFINITION') == 2 assert data.count(b'HASH') == 2 @@ -57,7 +57,7 @@ def test_add_connection(tmp_path: Path) -> None: with Writer(path) as writer: res = writer.add_connection('/foo', 'std_msgs/msg/Int8') - assert res.cid == 0 + assert res.cid == 0 data = path.read_bytes() assert data.count(b'int8 data') == 2 assert data.count(b'27ffa0c9c4b8fb8492252bcad9e5c57b') == 2 @@ -85,7 +85,7 @@ def test_add_connection(tmp_path: Path) -> None: 'HASH', latching=1, ) - assert (res1.cid, res2.cid, res3.cid) == (0, 1, 2) + assert (res1.cid, res2.cid, res3.cid) == (0, 1, 2) def test_write_errors(tmp_path: Path) -> None: diff --git a/tools/bench/bench.py b/tools/bench/bench.py index 0a673541..b8e70b17 100644 --- a/tools/bench/bench.py +++ b/tools/bench/bench.py @@ -21,7 +21,14 @@ from rosbags.rosbag2 import Reader from rosbags.serde import deserialize_cdr if TYPE_CHECKING: - from typing import Any, Generator + from typing import Generator, Protocol + + class NativeMSG(Protocol): # pylint: disable=too-few-public-methods + """Minimal native ROS message interface used for benchmark.""" + + def get_fields_and_field_types(self) -> dict[str, str]: + """Introspect message type.""" + raise NotImplementedError class ReaderPy: # pylint: disable=too-few-public-methods @@ -42,13 +49,13 @@ class ReaderPy: # pylint: disable=too-few-public-methods yield topic, self.typemap[topic], timestamp, data -def deserialize_py(data: bytes, msgtype: str) -> Any: +def deserialize_py(data: bytes, msgtype: str) -> NativeMSG: """Deserialization helper for rosidl_runtime_py + rclpy.""" pytype = get_message(msgtype) - return deserialize_message(data, pytype) + return deserialize_message(data, pytype) # type: ignore -def compare_msg(lite: Any, native: Any) -> None: +def compare_msg(lite: object, native: NativeMSG) -> None: """Compare rosbag2 (lite) vs rosbag2_py (native) message content. Args: @@ -96,8 +103,8 @@ def compare(path: Path) -> None: msg = deserialize_cdr(data, connection.msgtype) compare_msg(msg, msg_py) - assert len(list(gens[0])) == 0 - assert len(list(gens[1])) == 0 + assert not list(gens[0]) + assert not list(gens[1]) def read_deser_rosbag2_py(path: Path) -> None: diff --git a/tools/compare/compare.py b/tools/compare/compare.py index cda5ee7e..f928737f 100644 --- a/tools/compare/compare.py +++ b/tools/compare/compare.py @@ -14,6 +14,7 @@ from typing import TYPE_CHECKING from unittest.mock import Mock import genpy # type: ignore +import numpy import rosgraph_msgs.msg # type: ignore from rclpy.serialization import deserialize_message # type: ignore from rosbag2_py import ConverterOptions, SequentialReader, StorageOptions # type: ignore @@ -25,9 +26,15 @@ rosgraph_msgs.msg.TopicStatistics = Mock() import rosbag.bag # type:ignore # noqa: E402 pylint: disable=wrong-import-position if TYPE_CHECKING: - from typing import Any, Generator, List, Union + from typing import Generator, List, Protocol, Union, runtime_checkable - from rosbag.bag import _Connection_Info + @runtime_checkable + class NativeMSG(Protocol): # pylint: disable=too-few-public-methods + """Minimal native ROS message interface used for benchmark.""" + + def get_fields_and_field_types(self) -> dict[str, str]: + """Introspect message type.""" + raise NotImplementedError class Reader: # pylint: disable=too-few-public-methods @@ -47,7 +54,7 @@ class Reader: # pylint: disable=too-few-public-methods yield topic, timestamp, deserialize_message(data, pytype) -def fixup_ros1(conns: List[_Connection_Info]) -> None: +def fixup_ros1(conns: List[rosbag.bag._Connection_Info]) -> None: """Monkeypatch ROS2 fieldnames onto ROS1 objects. Args: @@ -61,7 +68,6 @@ def fixup_ros1(conns: List[_Connection_Info]) -> None: if conn := next((x for x in conns if x.datatype == 'sensor_msgs/CameraInfo'), None): print('Patching CameraInfo') # noqa: T001 - # pylint: disable=assignment-from-no-return,too-many-function-args cls = rosbag.bag._get_message_type(conn) # pylint: disable=protected-access cls.d = property(lambda x: x.D, lambda x, y: setattr(x, 'D', y)) # noqa: B010 cls.k = property(lambda x: x.K, lambda x, y: setattr(x, 'K', y)) # noqa: B010 @@ -69,7 +75,7 @@ def fixup_ros1(conns: List[_Connection_Info]) -> None: cls.p = property(lambda x: x.P, lambda x, y: setattr(x, 'P', y)) # noqa: B010 -def compare(ref: Any, msg: Any) -> None: +def compare(ref: object, msg: object) -> None: """Compare message to its reference. Args: @@ -77,7 +83,7 @@ def compare(ref: Any, msg: Any) -> None: msg: Converted ROS2 message. """ - if hasattr(msg, 'get_fields_and_field_types'): + if isinstance(msg, NativeMSG): for name in msg.get_fields_and_field_types(): refval = getattr(ref, name) msgval = getattr(msg, name) @@ -87,9 +93,11 @@ def compare(ref: Any, msg: Any) -> None: if isinstance(ref, bytes): assert msg.tobytes() == ref else: + assert isinstance(msg, numpy.ndarray) assert (msg == ref).all() elif isinstance(msg, list): + assert isinstance(ref, (list, numpy.ndarray)) assert len(msg) == len(ref) for refitem, msgitem in zip(ref, msg): compare(refitem, msgitem) @@ -97,8 +105,9 @@ def compare(ref: Any, msg: Any) -> None: elif isinstance(msg, str): assert msg == ref - elif isinstance(msg, float) and math.isnan(ref): - assert math.isnan(msg) + elif isinstance(msg, float) and math.isnan(msg): + assert isinstance(ref, float) + assert math.isnan(ref) else: assert ref == msg From e88241074e334a5ccdfedacb233ee95dda9de4b9 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 11 Apr 2022 10:46:12 +0200 Subject: [PATCH 068/114] Type message definition parsers --- src/rosbags/typesys/idl.py | 279 +++++++++++++++++++++++++++---------- src/rosbags/typesys/msg.py | 115 +++++++++------ src/rosbags/typesys/peg.py | 70 ++++++---- tests/test_parse.py | 11 ++ 4 files changed, 335 insertions(+), 140 deletions(-) diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index 8dc9c6d4..28cfcd85 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -14,12 +14,17 @@ from __future__ import annotations from typing import TYPE_CHECKING from .base import Nodetype, parse_message_definition -from .peg import Rule, Visitor, parse_grammar +from .peg import Visitor, parse_grammar if TYPE_CHECKING: - from typing import Any + from typing import Any, Generator, Optional, Tuple, Union - from .base import Typesdict + from .base import Fielddefs, Fielddesc, Typesdict + + StringNode = Tuple[Nodetype, str] + ConstValue = Union[str, bool, int, float] + LiteralMatch = Tuple[str, str] + LiteralNode = Tuple[Nodetype, ConstValue] GRAMMAR_IDL = r""" specification @@ -256,47 +261,79 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods def __init__(self) -> None: """Initialize.""" super().__init__() - self.typedefs: dict[str, tuple[Nodetype, tuple[Any, Any]]] = {} + self.typedefs: dict[str, Fielddesc] = {} - def visit_specification(self, children: Any) -> Typesdict: + # yapf: disable + def visit_specification( + self, + children: tuple[ + Optional[ + tuple[ + tuple[ + Nodetype, + list[tuple[Nodetype, tuple[str, str, ConstValue]]], + list[tuple[Nodetype, str, Fielddefs]], + ], + LiteralMatch, + ], + ], + ], + ) -> Typesdict: """Process start symbol, return only children of modules.""" - children = [x[0] for x in children if x is not None] - structs = {} - consts: dict[str, list[tuple[str, str, Any]]] = {} + structs: dict[str, Fielddefs] = {} + consts: dict[str, list[tuple[str, str, ConstValue]]] = {} for item in children: - if item[0] != Nodetype.MODULE: + if item is None or item[0][0] != Nodetype.MODULE: continue - for subitem in item[1]: - if subitem[0] == Nodetype.STRUCT: - structs[subitem[1]] = subitem[2] - elif subitem[0] == Nodetype.CONST and '_Constants/' in subitem[1][1]: - structname, varname = subitem[1][1].split('_Constants/') + for csubitem in item[0][1]: + assert csubitem[0] == Nodetype.CONST + if '_Constants/' in csubitem[1][1]: + structname, varname = csubitem[1][1].split('_Constants/') if structname not in consts: consts[structname] = [] - consts[structname].append((varname, subitem[1][0], subitem[1][2])) - return {k: (consts.get(k, []), v) for k, v in structs.items()} + consts[structname].append((varname, csubitem[1][0], csubitem[1][2])) - def visit_comment(self, children: Any) -> Any: + for ssubitem in item[0][2]: + assert ssubitem[0] == Nodetype.STRUCT + structs[ssubitem[1]] = ssubitem[2] + if ssubitem[1] not in consts: + consts[ssubitem[1]] = [] + return {k: (consts[k], v) for k, v in structs.items()} + # yapf: enable + + def visit_comment(self, _: str) -> None: """Process comment, suppress output.""" - def visit_macro(self, children: Any) -> Any: + def visit_macro(self, _: Union[LiteralMatch, tuple[LiteralMatch, str]]) -> None: """Process macro, suppress output.""" - def visit_include(self, children: Any) -> Any: + def visit_include( + self, + _: tuple[LiteralMatch, tuple[LiteralMatch, str, LiteralMatch]], + ) -> None: """Process include, suppress output.""" - def visit_module_dcl(self, children: Any) -> Any: + # yapf: disable + def visit_module_dcl( + self, + children: tuple[tuple[()], LiteralMatch, StringNode, LiteralMatch, Any, LiteralMatch], + ) -> tuple[ + Nodetype, + list[tuple[Nodetype, tuple[str, str, ConstValue]]], + list[tuple[Nodetype, str, Fielddefs]], + ]: """Process module declaration.""" assert len(children) == 6 assert children[2][0] == Nodetype.NAME name = children[2][1] - children = children[4] + definitions = children[4] consts = [] structs = [] - for item in children: - if not item or item[0] is None: + for item in definitions: + if item is None or item[0] is None: continue + assert item[1] == ('LITERAL', ';') item = item[0] if item[0] == Nodetype.CONST: consts.append(item) @@ -304,58 +341,98 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods structs.append(item) else: assert item[0] == Nodetype.MODULE - consts += [x for x in item[1] if x[0] == Nodetype.CONST] - structs += [x for x in item[1] if x[0] == Nodetype.STRUCT] + consts += item[1] + structs += item[2] - consts = [(x[0], (x[1][0], f'{name}/{x[1][1]}', x[1][2])) for x in consts] - structs = [(x[0], f'{name}/{x[1]}', *x[2:]) for x in structs] + consts = [(ityp, (typ, f'{name}/{subname}', val)) for ityp, (typ, subname, val) in consts] + structs = [(typ, f'{name}/{subname}', *rest) for typ, subname, *rest in structs] - return (Nodetype.MODULE, consts + structs) + return (Nodetype.MODULE, consts, structs) + # yapf: enable - def visit_const_dcl(self, children: Any) -> Any: + def visit_const_dcl( + self, + children: tuple[LiteralMatch, StringNode, StringNode, LiteralMatch, LiteralNode], + ) -> tuple[Nodetype, tuple[str, str, ConstValue]]: """Process const declaration.""" return (Nodetype.CONST, (children[1][1], children[2][1], children[4][1])) - def visit_type_dcl(self, children: Any) -> Any: + def visit_type_dcl( + self, + children: Optional[tuple[Nodetype, str, Fielddefs]], + ) -> Optional[tuple[Nodetype, str, Fielddefs]]: """Process type, pass structs, suppress otherwise.""" - if children[0] == Nodetype.STRUCT: - return children - return None + return children if children and children[0] == Nodetype.STRUCT else None - def visit_type_declarator(self, children: Any) -> Any: + def visit_typedef_dcl( + self, + children: tuple[LiteralMatch, tuple[StringNode, tuple[Any, ...]]], + ) -> None: """Process type declarator, register type mapping in instance typedef dictionary.""" assert len(children) == 2 - base, declarators = children - if base[1] in self.typedefs: - base = self.typedefs[base[1]] - declarators = [children[1][0], *[x[1:][0] for x in children[1][1]]] - for declarator in declarators: + dclchildren = children[1] + assert len(dclchildren) == 2 + base: Fielddesc + value: Fielddesc + base = typedef if (typedef := self.typedefs.get(dclchildren[0][1])) else dclchildren[0] + flat = [dclchildren[1][0], *[x[1:][0] for x in dclchildren[1][1]]] + for declarator in flat: if declarator[0] == Nodetype.ADECLARATOR: - value = (Nodetype.ARRAY, (base, declarator[2][1])) + typ, name = base + assert isinstance(typ, Nodetype) + assert isinstance(name, str) + assert isinstance(declarator[2][1], int) + value = (Nodetype.ARRAY, ((typ, name), declarator[2][1])) else: value = base self.typedefs[declarator[1][1]] = value - def visit_sequence_type(self, children: Any) -> Any: + def visit_sequence_type( + self, + children: Union[tuple[LiteralMatch, LiteralMatch, StringNode, LiteralMatch], + tuple[LiteralMatch, LiteralMatch, StringNode, LiteralMatch, LiteralNode, + LiteralMatch]], + ) -> tuple[Nodetype, tuple[StringNode, None]]: """Process sequence type specification.""" - assert len(children) in [4, 6] + assert len(children) in {4, 6} if len(children) == 6: - assert children[4][0] == Nodetype.LITERAL_NUMBER + idx = len(children) - 2 + assert children[idx][0] == Nodetype.LITERAL_NUMBER return (Nodetype.SEQUENCE, (children[2], None)) - def create_struct_field(self, parts: Any) -> Any: + # yapf: disable + def create_struct_field( + self, + parts: tuple[ + tuple[()], + Fielddesc, + tuple[ + tuple[Nodetype, StringNode], + tuple[ + tuple[str, tuple[Nodetype, StringNode]], + ..., + ], + ], + LiteralMatch, + ], + ) -> Generator[tuple[str, Fielddesc], None, None]: """Create struct field and expand typedefs.""" typename, params = parts[1:3] - params = [params[0], *[x[1:][0] for x in params[1]]] + flat = [params[0], *[x[1:][0] for x in params[1]]] - def resolve_name(name: Any) -> Any: + def resolve_name(name: Fielddesc) -> Fielddesc: while name[0] == Nodetype.NAME and name[1] in self.typedefs: + assert isinstance(name[1], str) name = self.typedefs[name[1]] return name - yield from ((x[1][1], resolve_name(typename)) for x in params if x) + yield from ((x[1][1], resolve_name(typename)) for x in flat if x) + # yapf: enable - def visit_struct_dcl(self, children: Any) -> Any: + def visit_struct_dcl( + self, + children: tuple[tuple[()], LiteralMatch, StringNode, LiteralMatch, Any, LiteralMatch], + ) -> tuple[Nodetype, str, Any]: """Process struct declaration.""" assert len(children) == 6 assert children[2][0] == Nodetype.NAME @@ -363,27 +440,51 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods fields = [y for x in children[4] for y in self.create_struct_field(x)] return (Nodetype.STRUCT, children[2][1], fields) - def visit_simple_declarator(self, children: Any) -> Any: + def visit_simple_declarator(self, children: StringNode) -> tuple[Nodetype, StringNode]: """Process simple declarator.""" assert len(children) == 2 return (Nodetype.SDECLARATOR, children) - def visit_array_declarator(self, children: Any) -> Any: + def visit_array_declarator( + self, + children: tuple[StringNode, tuple[tuple[LiteralMatch, LiteralNode, LiteralMatch]]], + ) -> tuple[Nodetype, StringNode, LiteralNode]: """Process array declarator.""" assert len(children) == 2 return (Nodetype.ADECLARATOR, children[0], children[1][0][1]) - def visit_annotation(self, children: Any) -> Any: + # yapf: disable + def visit_annotation( + self, + children: tuple[ + LiteralMatch, + StringNode, + tuple[ + tuple[ + LiteralMatch, + tuple[ + tuple[StringNode, LiteralMatch, LiteralNode], + tuple[ + tuple[LiteralMatch, tuple[StringNode, LiteralMatch, LiteralNode]], + ..., + ], + ], + LiteralMatch, + ], + ], + ], + ) -> tuple[Nodetype, str, list[tuple[StringNode, LiteralNode]]]: """Process annotation.""" assert len(children) == 3 assert children[1][0] == Nodetype.NAME params = children[2][0][1] - params = [ - [z for z in y if z[0] != Rule.LIT] for y in [params[0], *[x[1:][0] for x in params[1]]] - ] - return (Nodetype.ANNOTATION, children[1][1], params) + flat = [params[0], *[x[1:][0] for x in params[1]]] + assert all(len(x) == 3 for x in flat) + retparams = [(x[0], x[2]) for x in flat] + return (Nodetype.ANNOTATION, children[1][1], retparams) + # yapf: enable - def visit_base_type_spec(self, children: Any) -> Any: + def visit_base_type_spec(self, children: str) -> StringNode: """Process base type specifier.""" oname = children name = { @@ -394,26 +495,40 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods }.get(oname, oname) return (Nodetype.BASE, name) - def visit_string_type(self, children: Any) -> Any: + def visit_string_type( + self, + children: Union[StringNode, tuple[LiteralMatch, LiteralMatch, LiteralNode, LiteralMatch]], + ) -> Union[StringNode, tuple[Nodetype, str, LiteralNode]]: """Prrocess string type specifier.""" - assert len(children) in [2, 4] - if len(children) == 4: - return (Nodetype.BASE, 'string', children[2]) - return (Nodetype.BASE, 'string') + if len(children) == 2: + return (Nodetype.BASE, 'string') - def visit_scoped_name(self, children: Any) -> Any: + assert len(children) == 4 + assert isinstance(children[0], tuple) + return (Nodetype.BASE, 'string', children[2]) + + def visit_scoped_name( + self, + children: Union[StringNode, tuple[StringNode, LiteralMatch, StringNode]], + ) -> StringNode: """Process scoped name.""" if len(children) == 2: + assert isinstance(children[1], str) return (Nodetype.NAME, children[1]) assert len(children) == 3 + assert isinstance(children[0], tuple) assert children[1][1] == '::' return (Nodetype.NAME, f'{children[0][1]}/{children[2][1]}') - def visit_identifier(self, children: Any) -> Any: + def visit_identifier(self, children: str) -> StringNode: """Process identifier.""" return (Nodetype.NAME, children) - def visit_expression(self, children: Any) -> Any: + def visit_expression( + self, + children: Union[LiteralNode, tuple[LiteralMatch, LiteralNode], + tuple[LiteralNode, LiteralMatch, LiteralNode]], + ) -> Union[LiteralNode, tuple[Nodetype, str, int], tuple[Nodetype, str, int, int]]: """Process expression, literals are assumed to be integers only.""" if children[0] in [ Nodetype.LITERAL_STRING, @@ -422,46 +537,56 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods Nodetype.LITERAL_CHAR, Nodetype.NAME, ]: - return children + assert isinstance(children[1], (str, bool, int, float)) + return (children[0], children[1]) - assert len(children) in [2, 3] + assert isinstance(children[0], tuple) if len(children) == 3: assert isinstance(children[0][1], int) + assert isinstance(children[1][1], str) assert isinstance(children[2][1], int) - return (Nodetype.EXPRESSION_BINARY, children[1], children[0][1], children[2]) + return (Nodetype.EXPRESSION_BINARY, children[1][1], children[0][1], children[2][1]) assert len(children) == 2 - assert isinstance(children[1][1], int), children - return (Nodetype.EXPRESSION_UNARY, children[0][1], children[1]) + assert isinstance(children[0][1], str) + assert isinstance(children[1], tuple) + assert isinstance(children[1][1], int) + return (Nodetype.EXPRESSION_UNARY, children[0][1], children[1][1]) - def visit_boolean_literal(self, children: Any) -> Any: + def visit_boolean_literal(self, children: str) -> LiteralNode: """Process boolean literal.""" return (Nodetype.LITERAL_BOOLEAN, children[1] == 'TRUE') - def visit_float_literal(self, children: Any) -> Any: + def visit_float_literal(self, children: str) -> LiteralNode: """Process float literal.""" return (Nodetype.LITERAL_NUMBER, float(children)) - def visit_decimal_literal(self, children: Any) -> Any: + def visit_decimal_literal(self, children: str) -> LiteralNode: """Process decimal integer literal.""" return (Nodetype.LITERAL_NUMBER, int(children)) - def visit_octal_literal(self, children: Any) -> Any: + def visit_octal_literal(self, children: str) -> LiteralNode: """Process octal integer literal.""" return (Nodetype.LITERAL_NUMBER, int(children, 8)) - def visit_hexadecimal_literal(self, children: Any) -> Any: + def visit_hexadecimal_literal(self, children: str) -> LiteralNode: """Process hexadecimal integer literal.""" return (Nodetype.LITERAL_NUMBER, int(children, 16)) - def visit_character_literal(self, children: Any) -> Any: + def visit_character_literal( + self, + children: tuple[LiteralMatch, str, LiteralMatch], + ) -> StringNode: """Process char literal.""" return (Nodetype.LITERAL_CHAR, children[1]) - def visit_string_literals(self, children: Any) -> Any: + def visit_string_literals( + self, + children: tuple[tuple[LiteralMatch, str, LiteralMatch], ...], + ) -> StringNode: """Process string literal.""" return ( Nodetype.LITERAL_STRING, - ''.join(y for x in children for y in x if y and y[0] != Rule.LIT), + ''.join(x[1] for x in children), ) diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 42b583a9..3b9a110f 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -21,9 +21,16 @@ from .peg import Rule, Visitor, parse_grammar from .types import FIELDDEFS if TYPE_CHECKING: - from typing import Any + from typing import Optional, Tuple, TypeVar, Union - from .base import Fielddesc, Typesdict + from .base import Constdefs, Fielddefs, Fielddesc, Typesdict + + T = TypeVar('T') + + StringNode = Tuple[Nodetype, str] + ConstValue = Union[str, bool, int, float] + Msgdesc = Tuple[Tuple[StringNode, Tuple[str, str, int], str], ...] + LiteralMatch = Tuple[str, str] GRAMMAR_MSG = r""" specification @@ -44,7 +51,7 @@ comment = r'#[^\n]*' const_dcl - = 'string' identifier r'=(?!={79}\n)' r'[^\n]+' + = 'string' identifier '=' r'(?!={79}\n)[^\n]+' / type_spec identifier '=' float_literal / type_spec identifier '=' integer_literal / type_spec identifier '=' boolean_literal @@ -158,10 +165,7 @@ def normalize_fieldtype(typename: str, field: Fielddesc, names: list[str]) -> Fi """ dct = {Path(name).name: name for name in names} ftype, args = field - if ftype == Nodetype.NAME: - name = args - else: - name = args[0][1] + name = args if ftype == Nodetype.NAME else args[0][1] assert isinstance(name, str) if name in VisitorMSG.BASETYPES: @@ -220,52 +224,82 @@ class VisitorMSG(Visitor): 'string', } - def visit_comment(self, children: Any) -> Any: + def visit_comment(self, _: str) -> None: """Process comment, suppress output.""" - def visit_const_dcl(self, children: Any) -> Any: + def visit_const_dcl( + self, + children: tuple[StringNode, StringNode, LiteralMatch, ConstValue], + ) -> tuple[StringNode, tuple[str, str, ConstValue]]: """Process const declaration, suppress output.""" - typ = children[0][1] - if typ == 'string': + value: Union[str, bool, int, float] + if (typ := children[0][1]) == 'string': + assert isinstance(children[3], str) value = children[3].strip() else: value = children[3] - return Nodetype.CONST, (typ, children[1][1], value) + return (Nodetype.CONST, ''), (typ, children[1][1], value) - def visit_specification(self, children: Any) -> Typesdict: + def visit_specification( + self, + children: tuple[tuple[str, Msgdesc], tuple[tuple[str, tuple[str, Msgdesc]], ...]], + ) -> Typesdict: """Process start symbol.""" typelist = [children[0], *[x[1] for x in children[1]]] typedict = dict(typelist) names = list(typedict.keys()) - for name, fields in typedict.items(): - consts = [(x[1][1], x[1][0], x[1][2]) for x in fields if x[0] == Nodetype.CONST] - fields = [x for x in fields if x[0] != Nodetype.CONST] - fields = [(field[1][1], normalize_fieldtype(name, field[0], names)) for field in fields] - typedict[name] = consts, fields - return typedict + res: Typesdict = {} + for name, items in typedict.items(): + consts: Constdefs = [ + (x[1][1], x[1][0], x[1][2]) for x in items if x[0] == (Nodetype.CONST, '') + ] + fields: Fielddefs = [ + (field[1][1], normalize_fieldtype(name, field[0], names)) + for field in items + if field[0] != (Nodetype.CONST, '') + ] + res[name] = consts, fields + return res - def visit_msgdef(self, children: Any) -> Any: + def visit_msgdef( + self, + children: tuple[str, StringNode, tuple[Optional[T]]], + ) -> tuple[str, tuple[T, ...]]: """Process single message definition.""" assert len(children) == 3 - return normalize_msgtype(children[1][1]), [x for x in children[2] if x is not None] + return normalize_msgtype(children[1][1]), tuple(x for x in children[2] if x is not None) - def visit_msgsep(self, children: Any) -> Any: + def visit_msgsep(self, _: str) -> None: """Process message separator, suppress output.""" - def visit_array_type_spec(self, children: Any) -> Any: + def visit_array_type_spec( + self, + children: tuple[StringNode, tuple[LiteralMatch, tuple[int, ...], LiteralMatch]], + ) -> tuple[Nodetype, tuple[StringNode, Optional[int]]]: """Process array type specifier.""" - length = children[1][1] - if length: + if length := children[1][1]: return Nodetype.ARRAY, (children[0], length[0]) return Nodetype.SEQUENCE, (children[0], None) - def visit_bounded_array_type_spec(self, children: Any) -> Any: + def visit_bounded_array_type_spec( + self, + children: list[StringNode], + ) -> tuple[Nodetype, tuple[StringNode, None]]: """Process bounded array type specifier.""" return Nodetype.SEQUENCE, (children[0], None) - def visit_simple_type_spec(self, children: Any) -> Any: + def visit_simple_type_spec( + self, + children: Union[StringNode, tuple[LiteralMatch, LiteralMatch, int]], + ) -> StringNode: """Process simple type specifier.""" - typespec = children[0][1] if ('LITERAL', '<=') in children else children[1] + if len(children) > 2: + assert (Rule.LIT, '<=') in children + assert isinstance(children[0], tuple) + typespec = children[0][1] + else: + assert isinstance(children[1], str) + typespec = children[1] dct = { 'time': 'builtin_interfaces/msg/Time', 'duration': 'builtin_interfaces/msg/Duration', @@ -274,38 +308,41 @@ class VisitorMSG(Visitor): } return Nodetype.NAME, dct.get(typespec, typespec) - def visit_scoped_name(self, children: Any) -> Any: + def visit_scoped_name( + self, + children: Union[StringNode, tuple[StringNode, LiteralMatch, StringNode]], + ) -> StringNode: """Process scoped name.""" if len(children) == 2: - return children + return children # type: ignore assert len(children) == 3 - return (Nodetype.NAME, '/'.join(x[1] for x in children if x[0] != Rule.LIT)) + return (Nodetype.NAME, '/'.join(x[1] for x in children if x[0] != Rule.LIT)) # type: ignore - def visit_identifier(self, children: Any) -> Any: + def visit_identifier(self, children: str) -> StringNode: """Process identifier.""" return (Nodetype.NAME, children) - def visit_boolean_literal(self, children: Any) -> Any: + def visit_boolean_literal(self, children: str) -> bool: """Process boolean literal.""" - return children.lower() in ['true', '1'] + return children.lower() in {'true', '1'} - def visit_float_literal(self, children: Any) -> Any: + def visit_float_literal(self, children: str) -> float: """Process float literal.""" return float(children) - def visit_decimal_literal(self, children: Any) -> Any: + def visit_decimal_literal(self, children: str) -> int: """Process decimal integer literal.""" return int(children) - def visit_octal_literal(self, children: Any) -> Any: + def visit_octal_literal(self, children: str) -> int: """Process octal integer literal.""" return int(children, 8) - def visit_hexadecimal_literal(self, children: Any) -> Any: + def visit_hexadecimal_literal(self, children: str) -> int: """Process hexadecimal integer literal.""" return int(children, 16) - def visit_string_literal(self, children: Any) -> Any: + def visit_string_literal(self, children: str) -> str: """Process integer literal.""" return children[1] diff --git a/src/rosbags/typesys/peg.py b/src/rosbags/typesys/peg.py index 27d7d5ff..bb2b9dba 100644 --- a/src/rosbags/typesys/peg.py +++ b/src/rosbags/typesys/peg.py @@ -14,7 +14,10 @@ import re from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Any, Optional + from typing import Any, Optional, Pattern, TypeVar, Union + + Tree = Any + T = TypeVar('T') class Rule: @@ -23,7 +26,12 @@ class Rule: LIT = 'LITERAL' WS = re.compile(r'\s+', re.M | re.S) - def __init__(self, value: Any, rules: dict[str, Rule], name: Optional[str] = None): + def __init__( + self, + value: Union[str, Pattern[str], Rule, list[Rule]], + rules: dict[str, Rule], + name: Optional[str] = None, + ): """Initialize. Args: @@ -41,14 +49,9 @@ class Rule: match = self.WS.match(text, pos) return match.span()[1] if match else pos - def make_node(self, data: Any) -> Any: + def make_node(self, data: T) -> Union[T, dict[str, Union[str, T]]]: """Make node for parse tree.""" - if self.name: - return { - 'node': self.name, - 'data': data, - } - return data + return {'node': self.name, 'data': data} if self.name else data def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" @@ -58,7 +61,7 @@ class Rule: class RuleLiteral(Rule): """Rule to match string literal.""" - def __init__(self, value: Any, rules: dict[str, Rule], name: Optional[str] = None): + def __init__(self, value: str, rules: dict[str, Rule], name: Optional[str] = None): """Initialize. Args: @@ -73,6 +76,7 @@ class RuleLiteral(Rule): def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" value = self.value + assert isinstance(value, str) if text[pos:pos + len(value)] == value: npos = pos + len(value) npos = self.skip_ws(text, npos) @@ -83,7 +87,9 @@ class RuleLiteral(Rule): class RuleRegex(Rule): """Rule to match regular expression.""" - def __init__(self, value: Any, rules: dict[str, Rule], name: Optional[str] = None): + value: Pattern[str] + + def __init__(self, value: str, rules: dict[str, Rule], name: Optional[str] = None): """Initialize. Args: @@ -99,7 +105,7 @@ class RuleRegex(Rule): """Apply rule at position.""" match = self.value.match(text, pos) if not match: - return -1, [] + return -1, () npos = self.skip_ws(text, match.span()[1]) return npos, self.make_node(match.group()) @@ -107,6 +113,8 @@ class RuleRegex(Rule): class RuleToken(Rule): """Rule to match token.""" + value: str + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" token = self.rules[self.value] @@ -119,18 +127,22 @@ class RuleToken(Rule): class RuleOneof(Rule): """Rule to match first matching subrule.""" + value: list[Rule] + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" for value in self.value: npos, data = value.parse(text, pos) if npos != -1: return npos, self.make_node(data) - return -1, [] + return -1, () class RuleSequence(Rule): """Rule to match a sequence of subrules.""" + value: list[Rule] + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" data = [] @@ -138,14 +150,16 @@ class RuleSequence(Rule): for value in self.value: npos, node = value.parse(text, npos) if npos == -1: - return -1, [] + return -1, () data.append(node) - return npos, self.make_node(data) + return npos, self.make_node(tuple(data)) class RuleZeroPlus(Rule): """Rule to match zero or more occurences of subrule.""" + value: Rule + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" data: list[Any] = [] @@ -153,7 +167,7 @@ class RuleZeroPlus(Rule): while True: npos, node = self.value.parse(text, lpos) if npos == -1: - return lpos, self.make_node(data) + return lpos, self.make_node(tuple(data)) data.append(node) lpos = npos @@ -161,17 +175,19 @@ class RuleZeroPlus(Rule): class RuleOnePlus(Rule): """Rule to match one or more occurences of subrule.""" + value: Rule + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" npos, node = self.value.parse(text, pos) if npos == -1: - return -1, [] + return -1, () data = [node] lpos = npos while True: npos, node = self.value.parse(text, lpos) if npos == -1: - return lpos, self.make_node(data) + return lpos, self.make_node(tuple(data)) data.append(node) lpos = npos @@ -179,12 +195,14 @@ class RuleOnePlus(Rule): class RuleZeroOne(Rule): """Rule to match zero or one occurence of subrule.""" + value: Rule + def parse(self, text: str, pos: int) -> tuple[int, Any]: """Apply rule at position.""" npos, node = self.value.parse(text, pos) if npos == -1: - return pos, self.make_node([]) - return npos, self.make_node([node]) + return pos, self.make_node(()) + return npos, self.make_node((node,)) class Visitor: # pylint: disable=too-few-public-methods @@ -195,14 +213,17 @@ class Visitor: # pylint: disable=too-few-public-methods def __init__(self) -> None: """Initialize.""" - def visit(self, tree: Any) -> Any: + def visit(self, tree: Tree) -> Tree: """Visit all nodes in parse tree.""" - if isinstance(tree, list): - return [self.visit(x) for x in tree] + if isinstance(tree, tuple): + return tuple(self.visit(x) for x in tree) - if not isinstance(tree, dict): + if isinstance(tree, str): return tree + assert isinstance(tree, dict), tree + assert list(tree.keys()) == ['node', 'data'], tree.keys() + tree['data'] = self.visit(tree['data']) func = getattr(self, f'visit_{tree["node"]}', lambda x: x) return func(tree['data']) @@ -242,6 +263,7 @@ def parse_grammar(grammar: str) -> dict[str, Rule]: while items: tok = items.pop(0) if tok in ['*', '+', '?']: + assert isinstance(stack[-1], Rule) stack[-1] = { '*': RuleZeroPlus, '+': RuleOnePlus, diff --git a/tests/test_parse.py b/tests/test_parse.py index e3453bd6..662aecfd 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -140,6 +140,10 @@ module test_msgs { d4 array; }; }; + + struct Bar { + int i; + }; }; """ @@ -273,6 +277,13 @@ def test_parse_idl() -> None: assert fields[5][1][0] == Nodetype.SEQUENCE assert fields[6][1][0] == Nodetype.ARRAY + assert 'test_msgs/Bar' in ret + consts, fields = ret['test_msgs/Bar'] + assert consts == [] + assert len(fields) == 1 + assert fields[0][0] == 'i' + assert fields[0][1][1] == 'int' + def test_register_types() -> None: """Test type registeration.""" From dd357fa3e4aa673e5af89696216f8b0df421f20d Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 11 Apr 2022 12:47:01 +0200 Subject: [PATCH 069/114] Allow alternative type stores --- src/rosbags/serde/cdr.py | 57 ++++++++++++++++++--------------- src/rosbags/serde/messages.py | 24 ++++++++------ src/rosbags/serde/ros1.py | 28 ++++++++-------- src/rosbags/serde/serdes.py | 37 +++++++++++++++------ src/rosbags/serde/typing.py | 12 ++++--- src/rosbags/typesys/register.py | 19 +++++++---- tests/cdr.py | 5 +-- tests/test_serde.py | 22 ++++++------- 8 files changed, 121 insertions(+), 83 deletions(-) diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index 8dc87c96..894cc5fc 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -44,7 +44,7 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: lines = [ 'import sys', 'from rosbags.serde.messages import get_msgdef', - 'def getsize_cdr(pos, message):', + 'def getsize_cdr(pos, message, typestore):', ] for fcurr, fnext in zip(icurr, inext): fieldname, desc = fcurr @@ -54,8 +54,8 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: lines.append(f' pos += {desc.args.size_cdr}') size += desc.args.size_cdr else: - lines.append(f' func = get_msgdef("{desc.args.name}").getsize_cdr') - lines.append(f' pos = func(pos, message.{fieldname})') + lines.append(f' func = get_msgdef("{desc.args.name}", typestore).getsize_cdr') + lines.append(f' pos = func(pos, message.{fieldname}, typestore)') is_stat = False aligned = align_after(desc) @@ -97,12 +97,14 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: lines.append(f' pos += {subdesc.args.size_cdr}') size += subdesc.args.size_cdr else: - lines.append(f' func = get_msgdef("{subdesc.args.name}").getsize_cdr') + lines.append( + f' func = get_msgdef("{subdesc.args.name}", typestore).getsize_cdr', + ) lines.append(f' val = message.{fieldname}') for idx in range(length): if anext_before > anext_after: lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') - lines.append(f' pos = func(pos, val[{idx}])') + lines.append(f' pos = func(pos, val[{idx}], typestore)') is_stat = False aligned = align_after(subdesc) else: @@ -138,13 +140,15 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: lines.append(f' pos += {subdesc.args.size_cdr}') else: - lines.append(f' func = get_msgdef("{subdesc.args.name}").getsize_cdr') + lines.append( + f' func = get_msgdef("{subdesc.args.name}", typestore).getsize_cdr', + ) if aligned < anext_before <= anext_after: lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(' for item in val:') if anext_before > anext_after: lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') - lines.append(' pos = func(pos, item)') + lines.append(' pos = func(pos, item, typestore)') aligned = align_after(subdesc) aligned = min([aligned, 4]) @@ -190,15 +194,16 @@ def generate_serialize_cdr(fields: list[Field], endianess: str) -> CDRSer: f'from rosbags.serde.primitives import pack_uint64_{endianess}', f'from rosbags.serde.primitives import pack_float32_{endianess}', f'from rosbags.serde.primitives import pack_float64_{endianess}', - 'def serialize_cdr(rawdata, pos, message):', + 'def serialize_cdr(rawdata, pos, message, typestore):', ] for fcurr, fnext in zip(icurr, inext): fieldname, desc = fcurr lines.append(f' val = message.{fieldname}') if desc.valtype == Valtype.MESSAGE: - lines.append(f' func = get_msgdef("{desc.args.name}").serialize_cdr_{endianess}') - lines.append(' pos = func(rawdata, pos, val)') + name = desc.args.name + lines.append(f' func = get_msgdef("{name}", typestore).serialize_cdr_{endianess}') + lines.append(' pos = func(rawdata, pos, val, typestore)') aligned = align_after(desc) elif desc.valtype == Valtype.BASE: @@ -242,13 +247,12 @@ def generate_serialize_cdr(fields: list[Field], endianess: str) -> CDRSer: assert subdesc.valtype == Valtype.MESSAGE anext_before = align(subdesc) anext_after = align_after(subdesc) - lines.append( - f' func = get_msgdef("{subdesc.args.name}").serialize_cdr_{endianess}', - ) + name = subdesc.args.name + lines.append(f' func = get_msgdef("{name}", typestore).serialize_cdr_{endianess}') for idx in range(length): if anext_before > anext_after: lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') - lines.append(f' pos = func(rawdata, pos, val[{idx}])') + lines.append(f' pos = func(rawdata, pos, val[{idx}], typestore)') aligned = align_after(subdesc) else: assert desc.valtype == Valtype.SEQUENCE @@ -281,12 +285,11 @@ def generate_serialize_cdr(fields: list[Field], endianess: str) -> CDRSer: if subdesc.valtype == Valtype.MESSAGE: anext_before = align(subdesc) - lines.append( - f' func = get_msgdef("{subdesc.args.name}").serialize_cdr_{endianess}', - ) + name = subdesc.args.name + lines.append(f' func = get_msgdef("{name}", typestore).serialize_cdr_{endianess}') lines.append(' for item in val:') lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') - lines.append(' pos = func(rawdata, pos, item)') + lines.append(' pos = func(rawdata, pos, item, typestore)') aligned = align_after(subdesc) aligned = min([4, aligned]) @@ -330,7 +333,7 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: f'from rosbags.serde.primitives import unpack_uint64_{endianess}', f'from rosbags.serde.primitives import unpack_float32_{endianess}', f'from rosbags.serde.primitives import unpack_float64_{endianess}', - 'def deserialize_cdr(rawdata, pos, cls):', + 'def deserialize_cdr(rawdata, pos, cls, typestore):', ] funcname = f'deserialize_cdr_{endianess}' @@ -339,8 +342,8 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: desc = fcurr[1] if desc.valtype == Valtype.MESSAGE: - lines.append(f' msgdef = get_msgdef("{desc.args.name}")') - lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls)') + lines.append(f' msgdef = get_msgdef("{desc.args.name}", typestore)') + lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls, typestore)') lines.append(' values.append(obj)') aligned = align_after(desc) @@ -386,12 +389,14 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: assert subdesc.valtype == Valtype.MESSAGE anext_before = align(subdesc) anext_after = align_after(subdesc) - lines.append(f' msgdef = get_msgdef("{subdesc.args.name}")') + lines.append(f' msgdef = get_msgdef("{subdesc.args.name}", typestore)') lines.append(' value = []') for _ in range(length): if anext_before > anext_after: lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') - lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls)') + lines.append( + f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls, typestore)', + ) lines.append(' value.append(obj)') lines.append(' values.append(value)') aligned = align_after(subdesc) @@ -433,11 +438,13 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: if subdesc.valtype == Valtype.MESSAGE: anext_before = align(subdesc) - lines.append(f' msgdef = get_msgdef("{subdesc.args.name}")') + lines.append(f' msgdef = get_msgdef("{subdesc.args.name}", typestore)') lines.append(' value = []') lines.append(' for _ in range(size):') lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') - lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls)') + lines.append( + f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls, typestore)', + ) lines.append(' value.append(obj)') lines.append(' values.append(value)') aligned = align_after(subdesc) diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py index bad20c4e..c4f52097 100644 --- a/src/rosbags/serde/messages.py +++ b/src/rosbags/serde/messages.py @@ -6,8 +6,6 @@ from __future__ import annotations from typing import TYPE_CHECKING -from rosbags.typesys import types - from .cdr import generate_deserialize_cdr, generate_getsize_cdr, generate_serialize_cdr from .ros1 import generate_cdr_to_ros1, generate_ros1_to_cdr from .typing import Descriptor, Field, Msgdef @@ -15,28 +13,34 @@ from .utils import Valtype if TYPE_CHECKING: from rosbags.typesys.base import Fielddesc + from rosbags.typesys.register import Typestore -MSGDEFCACHE: dict[str, Msgdef] = {} +MSGDEFCACHE: dict[Typestore, dict[str, Msgdef]] = {} class SerdeError(Exception): """Serialization and Deserialization Error.""" -def get_msgdef(typename: str) -> Msgdef: +def get_msgdef(typename: str, typestore: Typestore) -> Msgdef: """Retrieve message definition for typename. Message definitions are cached globally and generated as needed. Args: typename: Msgdef type name to load. + typestore: Type store. Returns: Message definition. """ - if typename not in MSGDEFCACHE: - entries = types.FIELDDEFS[typename][1] + if typestore not in MSGDEFCACHE: + MSGDEFCACHE[typestore] = {} + cache = MSGDEFCACHE[typestore] + + if typename not in cache: + entries = typestore.FIELDDEFS[typename][1] def fixup(entry: Fielddesc) -> Descriptor: if entry[0] == Valtype.BASE: @@ -44,7 +48,7 @@ def get_msgdef(typename: str) -> Msgdef: return Descriptor(Valtype.BASE, entry[1]) if entry[0] == Valtype.MESSAGE: assert isinstance(entry[1], str) - return Descriptor(Valtype.MESSAGE, get_msgdef(entry[1])) + return Descriptor(Valtype.MESSAGE, get_msgdef(entry[1], typestore)) if entry[0] == Valtype.ARRAY: assert not isinstance(entry[1][0], str) return Descriptor(Valtype.ARRAY, (fixup(entry[1][0]), entry[1][1])) @@ -59,10 +63,10 @@ def get_msgdef(typename: str) -> Msgdef: getsize_cdr, size_cdr = generate_getsize_cdr(fields) - MSGDEFCACHE[typename] = Msgdef( + cache[typename] = Msgdef( typename, fields, - getattr(types, typename.replace('/', '__')), + getattr(typestore, typename.replace('/', '__')), size_cdr, getsize_cdr, generate_serialize_cdr(fields, 'le'), @@ -74,4 +78,4 @@ def get_msgdef(typename: str) -> Msgdef: generate_cdr_to_ros1(fields, typename, False), # type: ignore generate_cdr_to_ros1(fields, typename, True), # type: ignore ) - return MSGDEFCACHE[typename] + return cache[typename] diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index 402afb8c..38322e4c 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -52,7 +52,7 @@ def generate_ros1_to_cdr( 'from rosbags.serde.messages import SerdeError, get_msgdef', 'from rosbags.serde.primitives import pack_int32_le', 'from rosbags.serde.primitives import unpack_int32_le', - f'def {funcname}(input, ipos, output, opos):', + f'def {funcname}(input, ipos, output, opos, typestore):', ] if typename == 'std_msgs/msg/Header': @@ -62,8 +62,8 @@ def generate_ros1_to_cdr( _, desc = fcurr if desc.valtype == Valtype.MESSAGE: - lines.append(f' func = get_msgdef("{desc.args.name}").{funcname}') - lines.append(' ipos, opos = func(input, ipos, output, opos)') + lines.append(f' func = get_msgdef("{desc.args.name}", typestore).{funcname}') + lines.append(' ipos, opos = func(input, ipos, output, opos, typestore)') aligned = align_after(desc) elif desc.valtype == Valtype.BASE: @@ -117,11 +117,11 @@ def generate_ros1_to_cdr( anext_before = align(subdesc) anext_after = align_after(subdesc) - lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') + lines.append(f' func = get_msgdef("{subdesc.args.name}", typestore).{funcname}') for _ in range(length): if anext_before > anext_after: lines.append(f' opos = (opos + {anext_before} - 1) & -{anext_before}') - lines.append(' ipos, opos = func(input, ipos, output, opos)') + lines.append(' ipos, opos = func(input, ipos, output, opos, typestore)') aligned = anext_after else: assert desc.valtype == Valtype.SEQUENCE @@ -163,10 +163,10 @@ def generate_ros1_to_cdr( else: assert subdesc.valtype == Valtype.MESSAGE anext_before = align(subdesc) - lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') + lines.append(f' func = get_msgdef("{subdesc.args.name}", typestore).{funcname}') lines.append(' for _ in range(size):') lines.append(f' opos = (opos + {anext_before} - 1) & -{anext_before}') - lines.append(' ipos, opos = func(input, ipos, output, opos)') + lines.append(' ipos, opos = func(input, ipos, output, opos, typestore)') aligned = align_after(subdesc) aligned = min([aligned, 4]) @@ -208,7 +208,7 @@ def generate_cdr_to_ros1( 'from rosbags.serde.messages import SerdeError, get_msgdef', 'from rosbags.serde.primitives import pack_int32_le', 'from rosbags.serde.primitives import unpack_int32_le', - f'def {funcname}(input, ipos, output, opos):', + f'def {funcname}(input, ipos, output, opos, typestore):', ] if typename == 'std_msgs/msg/Header': @@ -218,8 +218,8 @@ def generate_cdr_to_ros1( _, desc = fcurr if desc.valtype == Valtype.MESSAGE: - lines.append(f' func = get_msgdef("{desc.args.name}").{funcname}') - lines.append(' ipos, opos = func(input, ipos, output, opos)') + lines.append(f' func = get_msgdef("{desc.args.name}", typestore).{funcname}') + lines.append(' ipos, opos = func(input, ipos, output, opos, typestore)') aligned = align_after(desc) elif desc.valtype == Valtype.BASE: @@ -273,11 +273,11 @@ def generate_cdr_to_ros1( anext_before = align(subdesc) anext_after = align_after(subdesc) - lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') + lines.append(f' func = get_msgdef("{subdesc.args.name}", typestore).{funcname}') for _ in range(length): if anext_before > anext_after: lines.append(f' ipos = (ipos + {anext_before} - 1) & -{anext_before}') - lines.append(' ipos, opos = func(input, ipos, output, opos)') + lines.append(' ipos, opos = func(input, ipos, output, opos, typestore)') aligned = anext_after else: assert desc.valtype == Valtype.SEQUENCE @@ -317,10 +317,10 @@ def generate_cdr_to_ros1( else: assert subdesc.valtype == Valtype.MESSAGE anext_before = align(subdesc) - lines.append(f' func = get_msgdef("{subdesc.args.name}").{funcname}') + lines.append(f' func = get_msgdef("{subdesc.args.name}", typestore).{funcname}') lines.append(' for _ in range(size):') lines.append(f' ipos = (ipos + {anext_before} - 1) & -{anext_before}') - lines.append(' ipos, opos = func(input, ipos, output, opos)') + lines.append(' ipos, opos = func(input, ipos, output, opos, typestore)') aligned = align_after(subdesc) aligned = min([aligned, 4]) diff --git a/src/rosbags/serde/serdes.py b/src/rosbags/serde/serdes.py index 96d4aabe..6d56a7b0 100644 --- a/src/rosbags/serde/serdes.py +++ b/src/rosbags/serde/serdes.py @@ -8,18 +8,27 @@ import sys from struct import pack_into from typing import TYPE_CHECKING +from rosbags.typesys import types + from .messages import get_msgdef if TYPE_CHECKING: from typing import Any + from rosbags.typesys.register import Typestore -def deserialize_cdr(rawdata: bytes, typename: str) -> Any: # noqa: ANN401 + +def deserialize_cdr( + rawdata: bytes, + typename: str, + typestore: Typestore = types, +) -> Any: # noqa: ANN401 """Deserialize raw data into a message object. Args: rawdata: Serialized data. typename: Message type name. + typestore: Type store. Returns: Deserialized message object. @@ -27,9 +36,9 @@ def deserialize_cdr(rawdata: bytes, typename: str) -> Any: # noqa: ANN401 """ little_endian = bool(rawdata[1]) - msgdef = get_msgdef(typename) + msgdef = get_msgdef(typename, typestore) func = msgdef.deserialize_cdr_le if little_endian else msgdef.deserialize_cdr_be - message, pos = func(rawdata[4:], 0, msgdef.cls) + message, pos = func(rawdata[4:], 0, msgdef.cls, typestore) assert pos + 4 + 3 >= len(rawdata) return message @@ -38,6 +47,7 @@ def serialize_cdr( message: object, typename: str, little_endian: bool = sys.byteorder == 'little', + typestore: Typestore = types, ) -> memoryview: """Serialize message object to bytes. @@ -45,24 +55,25 @@ def serialize_cdr( message: Message object. typename: Message type name. little_endian: Should use little endianess. + typestore: Type store. Returns: Serialized bytes. """ - msgdef = get_msgdef(typename) - size = 4 + msgdef.getsize_cdr(0, message) + msgdef = get_msgdef(typename, typestore) + size = 4 + msgdef.getsize_cdr(0, message, typestore) rawdata = memoryview(bytearray(size)) pack_into('BB', rawdata, 0, 0, little_endian) func = msgdef.serialize_cdr_le if little_endian else msgdef.serialize_cdr_be - pos = func(rawdata[4:], 0, message) + pos = func(rawdata[4:], 0, message, typestore) assert pos + 4 == size return rawdata.toreadonly() -def ros1_to_cdr(raw: bytes, typename: str) -> memoryview: +def ros1_to_cdr(raw: bytes, typename: str, typestore: Typestore = types) -> memoryview: """Convert serialized ROS1 message directly to CDR. This should be reasonably fast as conversions happen on a byte-level @@ -71,18 +82,20 @@ def ros1_to_cdr(raw: bytes, typename: str) -> memoryview: Args: raw: ROS1 serialized message. typename: Message type name. + typestore: Type store. Returns: CDR serialized message. """ - msgdef = get_msgdef(typename) + msgdef = get_msgdef(typename, typestore) ipos, opos = msgdef.getsize_ros1_to_cdr( raw, 0, None, 0, + typestore, ) assert ipos == len(raw) @@ -96,13 +109,14 @@ def ros1_to_cdr(raw: bytes, typename: str) -> memoryview: 0, rawdata[4:], 0, + typestore, ) assert ipos == len(raw) assert opos + 4 == size return rawdata.toreadonly() -def cdr_to_ros1(raw: bytes, typename: str) -> memoryview: +def cdr_to_ros1(raw: bytes, typename: str, typestore: Typestore = types) -> memoryview: """Convert serialized CDR message directly to ROS1. This should be reasonably fast as conversions happen on a byte-level @@ -111,6 +125,7 @@ def cdr_to_ros1(raw: bytes, typename: str) -> memoryview: Args: raw: CDR serialized message. typename: Message type name. + typestore: Type store. Returns: ROS1 serialized message. @@ -118,13 +133,14 @@ def cdr_to_ros1(raw: bytes, typename: str) -> memoryview: """ assert raw[1] == 1, 'Message byte order is not little endian' - msgdef = get_msgdef(typename) + msgdef = get_msgdef(typename, typestore) ipos, opos = msgdef.getsize_cdr_to_ros1( raw[4:], 0, None, 0, + typestore, ) assert ipos + 4 + 3 >= len(raw) @@ -137,6 +153,7 @@ def cdr_to_ros1(raw: bytes, typename: str) -> memoryview: 0, rawdata, 0, + typestore, ) assert ipos + 4 + 3 >= len(raw) assert opos == size diff --git a/src/rosbags/serde/typing.py b/src/rosbags/serde/typing.py index de4e27b5..5275cc2b 100644 --- a/src/rosbags/serde/typing.py +++ b/src/rosbags/serde/typing.py @@ -9,12 +9,14 @@ from typing import TYPE_CHECKING, NamedTuple if TYPE_CHECKING: from typing import Any, Callable, Tuple - Bitcvt = Callable[[bytes, int, bytes, int], Tuple[int, int]] - BitcvtSize = Callable[[bytes, int, None, int], Tuple[int, int]] + from rosbags.typesys.register import Typestore - CDRDeser = Callable[[bytes, int, type], Tuple[Any, int]] - CDRSer = Callable[[bytes, int, object], int] - CDRSerSize = Callable[[int, object], int] + Bitcvt = Callable[[bytes, int, bytes, int, Typestore], Tuple[int, int]] + BitcvtSize = Callable[[bytes, int, None, int, Typestore], Tuple[int, int]] + + CDRDeser = Callable[[bytes, int, type, Typestore], Tuple[Any, int]] + CDRSer = Callable[[bytes, int, object, Typestore], int] + CDRSerSize = Callable[[int, object, Typestore], int] class Descriptor(NamedTuple): diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index ebbb53bc..21b0714d 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -13,10 +13,16 @@ from . import types from .base import Nodetype, TypesysError if TYPE_CHECKING: - from typing import Any, Optional, Union + from typing import Any, Optional, Protocol, Union from .base import Typesdict + class Typestore(Protocol): # pylint: disable=too-few-public-methods + """Type storage.""" + + FIELDDEFS: Typesdict + + INTLIKE = re.compile('^u?(bool|int|float)') @@ -134,11 +140,12 @@ def generate_python_code(typs: Typesdict) -> str: return '\n'.join(lines) -def register_types(typs: Typesdict) -> None: +def register_types(typs: Typesdict, typestore: Typestore = types) -> None: """Register types in type system. Args: typs: Dictionary mapping message typenames to parsetrees. + typestore: Type store. Raises: TypesysError: Type already present with different definition. @@ -156,14 +163,14 @@ def register_types(typs: Typesdict) -> None: for name, (_, fields) in fielddefs.items(): if name == 'std_msgs/msg/Header': continue - if have := types.FIELDDEFS.get(name): + if have := typestore.FIELDDEFS.get(name): _, have_fields = have have_fields = [(x[0].lower(), x[1]) for x in have_fields] fields = [(x[0].lower(), x[1]) for x in fields] if have_fields != fields: raise TypesysError(f'Type {name!r} is already present with different definition.') - for name in fielddefs.keys() - types.FIELDDEFS.keys(): + for name in fielddefs.keys() - typestore.FIELDDEFS.keys(): pyname = name.replace('/', '__') - setattr(types, pyname, getattr(module, pyname)) - types.FIELDDEFS[name] = fielddefs[name] + setattr(typestore, pyname, getattr(module, pyname)) + typestore.FIELDDEFS[name] = fielddefs[name] diff --git a/tests/cdr.py b/tests/cdr.py index 562329f3..c7dbd76a 100644 --- a/tests/cdr.py +++ b/tests/cdr.py @@ -14,6 +14,7 @@ from numpy.typing import NDArray from rosbags.serde.messages import SerdeError, get_msgdef from rosbags.serde.typing import Msgdef from rosbags.serde.utils import SIZEMAP, Valtype +from rosbags.typesys import types if TYPE_CHECKING: from typing import Any, Tuple @@ -187,7 +188,7 @@ def deserialize(rawdata: bytes, typename: str) -> Msgdef: """ _, little_endian = unpack_from('BB', rawdata, 0) - msgdef = get_msgdef(typename) + msgdef = get_msgdef(typename, types) obj, _ = deserialize_message( rawdata[4:], BASETYPEMAP_LE if little_endian else BASETYPEMAP_BE, @@ -428,7 +429,7 @@ def serialize( Serialized bytes. """ - msgdef = get_msgdef(typename) + msgdef = get_msgdef(typename, types) size = 4 + get_size(message, msgdef) rawdata = memoryview(bytearray(size)) diff --git a/tests/test_serde.py b/tests/test_serde.py index 8359f52f..276bcd6c 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -12,7 +12,7 @@ import pytest from rosbags.serde import SerdeError, cdr_to_ros1, deserialize_cdr, ros1_to_cdr, serialize_cdr from rosbags.serde.messages import get_msgdef -from rosbags.typesys import get_types_from_msg, register_types +from rosbags.typesys import get_types_from_msg, register_types, types from rosbags.typesys.types import builtin_interfaces__msg__Time as Time from rosbags.typesys.types import geometry_msgs__msg__Polygon as Polygon from rosbags.typesys.types import sensor_msgs__msg__MagneticField as MagneticField @@ -316,14 +316,14 @@ def test_custom_type() -> None: register_types(dict(get_types_from_msg(DYNAMIC_S_64, 'test_msgs/msg/dynamic_s_64'))) register_types(dict(get_types_from_msg(CUSTOM, cname))) - static_64_64 = get_msgdef('test_msgs/msg/static_64_64').cls - static_64_16 = get_msgdef('test_msgs/msg/static_64_16').cls - static_16_64 = get_msgdef('test_msgs/msg/static_16_64').cls - dynamic_64_64 = get_msgdef('test_msgs/msg/dynamic_64_64').cls - dynamic_64_b_64 = get_msgdef('test_msgs/msg/dynamic_64_b_64').cls - dynamic_64_s = get_msgdef('test_msgs/msg/dynamic_64_s').cls - dynamic_s_64 = get_msgdef('test_msgs/msg/dynamic_s_64').cls - custom = get_msgdef('test_msgs/msg/custom').cls + static_64_64 = get_msgdef('test_msgs/msg/static_64_64', types).cls + static_64_16 = get_msgdef('test_msgs/msg/static_64_16', types).cls + static_16_64 = get_msgdef('test_msgs/msg/static_16_64', types).cls + dynamic_64_64 = get_msgdef('test_msgs/msg/dynamic_64_64', types).cls + dynamic_64_b_64 = get_msgdef('test_msgs/msg/dynamic_64_b_64', types).cls + dynamic_64_s = get_msgdef('test_msgs/msg/dynamic_64_s', types).cls + dynamic_s_64 = get_msgdef('test_msgs/msg/dynamic_s_64', types).cls + custom = get_msgdef('test_msgs/msg/custom', types).cls msg = custom( 'str', @@ -439,7 +439,7 @@ def test_padding_empty_sequence() -> None: """Test empty sequences do not add item padding.""" register_types(dict(get_types_from_msg(SU64_B, 'test_msgs/msg/su64_b'))) - su64_b = get_msgdef('test_msgs/msg/su64_b').cls + su64_b = get_msgdef('test_msgs/msg/su64_b', types).cls msg = su64_b(numpy.array([], dtype=numpy.uint64), True) cdr = serialize_cdr(msg, msg.__msgtype__) @@ -458,7 +458,7 @@ def test_align_after_empty_sequence() -> None: """Test alignment after empty sequences.""" register_types(dict(get_types_from_msg(SU64_U64, 'test_msgs/msg/su64_u64'))) - su64_b = get_msgdef('test_msgs/msg/su64_u64').cls + su64_b = get_msgdef('test_msgs/msg/su64_u64', types).cls msg = su64_b(numpy.array([], dtype=numpy.uint64), 42) cdr = serialize_cdr(msg, msg.__msgtype__) From b924fd4642dbad6ef17daca33c36ccd7196e7556 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Mon, 11 Apr 2022 14:10:38 +0200 Subject: [PATCH 070/114] Add documentation for included types --- docs/api/rosbags.rst | 1 + docs/api/rosbags.typesys.types.rst | 6 + docs/conf.py | 1 + docs/topics/typesys-types.rst | 194 +++++++++++++++++++++++++++++ docs/topics/typesys.rst | 5 + src/rosbags/typesys/__main__.py | 16 +++ 6 files changed, 223 insertions(+) create mode 100644 docs/api/rosbags.typesys.types.rst create mode 100644 docs/topics/typesys-types.rst diff --git a/docs/api/rosbags.rst b/docs/api/rosbags.rst index 19c8a4f3..e8761f70 100644 --- a/docs/api/rosbags.rst +++ b/docs/api/rosbags.rst @@ -9,3 +9,4 @@ Rosbags namespace rosbags.rosbag2 rosbags.serde rosbags.typesys + rosbags.typesys.types diff --git a/docs/api/rosbags.typesys.types.rst b/docs/api/rosbags.typesys.types.rst new file mode 100644 index 00000000..46b5d243 --- /dev/null +++ b/docs/api/rosbags.typesys.types.rst @@ -0,0 +1,6 @@ +rosbags.typesys.types +===================== + +.. automodule:: rosbags.typesys.types + :members: + :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py index 17b2ed43..ace01c87 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,6 +22,7 @@ copyright = '2020-2022, Ternaris' author = 'Ternaris' autoapi_python_use_implicit_namespaces = True +autodoc_typehints = 'description' extensions = [ 'sphinx.ext.autodoc', diff --git a/docs/topics/typesys-types.rst b/docs/topics/typesys-types.rst new file mode 100644 index 00000000..6c8cfb60 --- /dev/null +++ b/docs/topics/typesys-types.rst @@ -0,0 +1,194 @@ +builtin_interfaces +****************** +- :py:class:`Duration ` +- :py:class:`Time ` + +diagnostic_msgs +*************** +- :py:class:`DiagnosticArray ` +- :py:class:`DiagnosticStatus ` +- :py:class:`KeyValue ` + +geometry_msgs +************* +- :py:class:`Accel ` +- :py:class:`AccelStamped ` +- :py:class:`AccelWithCovariance ` +- :py:class:`AccelWithCovarianceStamped ` +- :py:class:`Inertia ` +- :py:class:`InertiaStamped ` +- :py:class:`Point ` +- :py:class:`Point32 ` +- :py:class:`PointStamped ` +- :py:class:`Polygon ` +- :py:class:`PolygonStamped ` +- :py:class:`Pose ` +- :py:class:`Pose2D ` +- :py:class:`PoseArray ` +- :py:class:`PoseStamped ` +- :py:class:`PoseWithCovariance ` +- :py:class:`PoseWithCovarianceStamped ` +- :py:class:`Quaternion ` +- :py:class:`QuaternionStamped ` +- :py:class:`Transform ` +- :py:class:`TransformStamped ` +- :py:class:`Twist ` +- :py:class:`TwistStamped ` +- :py:class:`TwistWithCovariance ` +- :py:class:`TwistWithCovarianceStamped ` +- :py:class:`Vector3 ` +- :py:class:`Vector3Stamped ` +- :py:class:`Wrench ` +- :py:class:`WrenchStamped ` + +libstatistics_collector +*********************** +- :py:class:`DummyMessage ` + +lifecycle_msgs +************** +- :py:class:`State ` +- :py:class:`Transition ` +- :py:class:`TransitionDescription ` +- :py:class:`TransitionEvent ` + +nav_msgs +******** +- :py:class:`GridCells ` +- :py:class:`MapMetaData ` +- :py:class:`OccupancyGrid ` +- :py:class:`Odometry ` +- :py:class:`Path ` + +rcl_interfaces +************** +- :py:class:`FloatingPointRange ` +- :py:class:`IntegerRange ` +- :py:class:`ListParametersResult ` +- :py:class:`Log ` +- :py:class:`Parameter ` +- :py:class:`ParameterDescriptor ` +- :py:class:`ParameterEvent ` +- :py:class:`ParameterEventDescriptors ` +- :py:class:`ParameterType ` +- :py:class:`ParameterValue ` +- :py:class:`SetParametersResult ` + +rmw_dds_common +************** +- :py:class:`Gid ` +- :py:class:`NodeEntitiesInfo ` +- :py:class:`ParticipantEntitiesInfo ` + +rosgraph_msgs +************* +- :py:class:`Clock ` + +sensor_msgs +*********** +- :py:class:`BatteryState ` +- :py:class:`CameraInfo ` +- :py:class:`ChannelFloat32 ` +- :py:class:`CompressedImage ` +- :py:class:`FluidPressure ` +- :py:class:`Illuminance ` +- :py:class:`Image ` +- :py:class:`Imu ` +- :py:class:`JointState ` +- :py:class:`Joy ` +- :py:class:`JoyFeedback ` +- :py:class:`JoyFeedbackArray ` +- :py:class:`LaserEcho ` +- :py:class:`LaserScan ` +- :py:class:`MagneticField ` +- :py:class:`MultiDOFJointState ` +- :py:class:`MultiEchoLaserScan ` +- :py:class:`NavSatFix ` +- :py:class:`NavSatStatus ` +- :py:class:`PointCloud ` +- :py:class:`PointCloud2 ` +- :py:class:`PointField ` +- :py:class:`Range ` +- :py:class:`RegionOfInterest ` +- :py:class:`RelativeHumidity ` +- :py:class:`Temperature ` +- :py:class:`TimeReference ` + +shape_msgs +********** +- :py:class:`Mesh ` +- :py:class:`MeshTriangle ` +- :py:class:`Plane ` +- :py:class:`SolidPrimitive ` + +statistics_msgs +*************** +- :py:class:`MetricsMessage ` +- :py:class:`StatisticDataPoint ` +- :py:class:`StatisticDataType ` + +std_msgs +******** +- :py:class:`Bool ` +- :py:class:`Byte ` +- :py:class:`ByteMultiArray ` +- :py:class:`Char ` +- :py:class:`ColorRGBA ` +- :py:class:`Empty ` +- :py:class:`Float32 ` +- :py:class:`Float32MultiArray ` +- :py:class:`Float64 ` +- :py:class:`Float64MultiArray ` +- :py:class:`Header ` +- :py:class:`Int16 ` +- :py:class:`Int16MultiArray ` +- :py:class:`Int32 ` +- :py:class:`Int32MultiArray ` +- :py:class:`Int64 ` +- :py:class:`Int64MultiArray ` +- :py:class:`Int8 ` +- :py:class:`Int8MultiArray ` +- :py:class:`MultiArrayDimension ` +- :py:class:`MultiArrayLayout ` +- :py:class:`String ` +- :py:class:`UInt16 ` +- :py:class:`UInt16MultiArray ` +- :py:class:`UInt32 ` +- :py:class:`UInt32MultiArray ` +- :py:class:`UInt64 ` +- :py:class:`UInt64MultiArray ` +- :py:class:`UInt8 ` +- :py:class:`UInt8MultiArray ` + +stereo_msgs +*********** +- :py:class:`DisparityImage ` + +tf2_msgs +******** +- :py:class:`TF2Error ` +- :py:class:`TFMessage ` + +trajectory_msgs +*************** +- :py:class:`JointTrajectory ` +- :py:class:`JointTrajectoryPoint ` +- :py:class:`MultiDOFJointTrajectory ` +- :py:class:`MultiDOFJointTrajectoryPoint ` + +unique_identifier_msgs +********************** +- :py:class:`UUID ` + +visualization_msgs +****************** +- :py:class:`ImageMarker ` +- :py:class:`InteractiveMarker ` +- :py:class:`InteractiveMarkerControl ` +- :py:class:`InteractiveMarkerFeedback ` +- :py:class:`InteractiveMarkerInit ` +- :py:class:`InteractiveMarkerPose ` +- :py:class:`InteractiveMarkerUpdate ` +- :py:class:`Marker ` +- :py:class:`MarkerArray ` +- :py:class:`MenuEntry ` diff --git a/docs/topics/typesys.rst b/docs/topics/typesys.rst index 26c19274..b846adc4 100644 --- a/docs/topics/typesys.rst +++ b/docs/topics/typesys.rst @@ -13,6 +13,11 @@ The type system generates a dataclass for each message type. These dataclasses g Limitation: While the type system parses message definitions with array bounds and/or default values, neither bounds nor default values are enforced or assigned to message instances. +Included message types +---------------------- + +.. include:: ./typesys-types.rst + Extending the type system ------------------------- Adding custom message types consists of two steps. First, message definitions are converted into parse trees using :py:func:`get_types_from_idl() ` or :py:func:`get_types_from_msg() `, and second the types are registered in the type system via :py:func:`register_types() `. The following example shows how to add messages type definitions from ``.msg`` and ``.idl`` files: diff --git a/src/rosbags/typesys/__main__.py b/src/rosbags/typesys/__main__.py index 5f9f957f..f7dd37ff 100644 --- a/src/rosbags/typesys/__main__.py +++ b/src/rosbags/typesys/__main__.py @@ -4,6 +4,7 @@ from __future__ import annotations +from itertools import groupby from os import walk from pathlib import Path from typing import TYPE_CHECKING @@ -16,6 +17,19 @@ if TYPE_CHECKING: from .base import Typesdict +def generate_docs(typs: Typesdict) -> str: + """Generate types documentation.""" + res = [] + for namespace, msgs in groupby([x.split('/msg/') for x in typs], key=lambda x: x[0]): + res.append(namespace) + res.append('*' * len(namespace)) + + for _, msg in msgs: + res.append(f'- :py:class:`{msg} `') + res.append('') + return '\n'.join(res) + + def main() -> None: # pragma: no cover """Update builtin types. @@ -24,6 +38,7 @@ def main() -> None: # pragma: no cover """ typs: Typesdict = {} selfdir = Path(__file__).parent + projectdir = selfdir.parent.parent.parent for root, dirnames, files in walk(selfdir.parents[2] / 'tools' / 'messages'): if '.rosbags_ignore' in files: dirnames.clear() @@ -40,6 +55,7 @@ def main() -> None: # pragma: no cover typs = dict(sorted(typs.items())) register_types(typs) (selfdir / 'types.py').write_text(generate_python_code(typs)) + (projectdir / 'docs' / 'topics' / 'typesys-types.rst').write_text(generate_docs(typs)) if __name__ == '__main__': From 42243eac2d45a11c1b2c9004e21491e49823c2fc Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Tue, 12 Apr 2022 17:03:04 +0200 Subject: [PATCH 071/114] Move indices to dedicated attribute --- src/rosbags/convert/converter.py | 3 +-- src/rosbags/rosbag1/reader.py | 16 +++++++++------- src/rosbags/rosbag1/writer.py | 1 - 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index 5b6614c5..65c3a5f9 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -87,7 +87,6 @@ def downgrade_connection(rconn: Connection2) -> Connection1: md5sum, None, int('durability: 1' in rconn.offered_qos_profiles), - [], ) @@ -140,7 +139,7 @@ def convert_2to1(src: Path, dst: Path) -> None: None, ) # yapf: enable - connmap[rconn.id] = existing if existing else writer.add_connection(*candidate[1:-1]) + connmap[rconn.id] = existing if existing else writer.add_connection(*candidate[1:]) for rconn, timestamp, data in reader.messages(): data = cdr_to_ros1(data, rconn.msgtype) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 14742118..c7733c66 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -60,7 +60,6 @@ class Connection(NamedTuple): md5sum: str callerid: Optional[str] latching: Optional[int] - indexes: list[IndexData] class ChunkInfo(NamedTuple): @@ -348,6 +347,8 @@ class Reader: """ + # pylint: disable=too-many-instance-attributes + def __init__(self, path: Union[str, Path]): """Initialize. @@ -364,12 +365,13 @@ class Reader: self.bio: Optional[BinaryIO] = None self.connections: dict[int, Connection] = {} + self.indexes: dict[int, list[IndexData]] self.chunk_infos: list[ChunkInfo] = [] self.chunks: dict[int, Chunk] = {} self.current_chunk: tuple[int, BinaryIO] = (-1, BytesIO()) self.topics: dict[str, TopicInfo] = {} - def open(self) -> None: # pylint: disable=too-many-branches,too-many-locals + def open(self) -> None: # pylint: disable=too-many-locals """Open rosbag and read metadata.""" try: self.bio = self.path.open('rb') @@ -420,9 +422,10 @@ class Reader: cid, index = self.read_index_data(chunk_info.pos) indexes[cid].append(index) - for cid, connection in self.connections.items(): - connection.indexes.extend(heapq.merge(*indexes[cid], key=lambda x: x.time)) - assert connection.indexes + self.indexes = { + cid: list(heapq.merge(*x, key=lambda x: x.time)) for cid, x in indexes.items() + } + assert all(self.indexes[x] for x in self.connections) self.topics = {} for topic, group in groupby( @@ -498,7 +501,6 @@ class Reader: md5sum, callerid, latching, - [], ) def read_chunk_info(self) -> ChunkInfo: @@ -605,7 +607,7 @@ class Reader: if not connections: connections = self.connections.values() - indexes = [x.indexes for x in connections] + indexes = [self.indexes[x.cid] for x in connections] for entry in heapq.merge(*indexes): if start and entry.time < start: continue diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index cbe4bc35..2d5076ea 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -251,7 +251,6 @@ class Writer: md5sum, callerid, latching, - [], ) if any(x[1:] == connection[1:] for x in self.connections.values()): From dee7e9c2fcd11dc597cdce4af4ebf900600d5f3d Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Tue, 12 Apr 2022 17:48:11 +0200 Subject: [PATCH 072/114] Rename rosbag1 connection cid to id --- src/rosbags/convert/converter.py | 4 ++-- src/rosbags/rosbag1/reader.py | 6 +++--- src/rosbags/rosbag1/writer.py | 8 ++++---- tests/test_writer1.py | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index 65c3a5f9..03af6168 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -106,13 +106,13 @@ def convert_1to2(src: Path, dst: Path) -> None: candidate = upgrade_connection(rconn) existing = next((x for x in writer.connections.values() if x == candidate), None) wconn = existing if existing else writer.add_connection(**asdict(candidate)) - connmap[rconn.cid] = wconn + connmap[rconn.id] = wconn typs.update(get_types_from_msg(rconn.msgdef, rconn.msgtype)) register_types(typs) for rconn, timestamp, data in reader.messages(): data = ros1_to_cdr(data, rconn.msgtype) - writer.write(connmap[rconn.cid], timestamp, data) + writer.write(connmap[rconn.id], timestamp, data) def convert_2to1(src: Path, dst: Path) -> None: diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index c7733c66..8798c529 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -53,7 +53,7 @@ class RecordType(IntEnum): class Connection(NamedTuple): """Connection information.""" - cid: int + id: int topic: str msgtype: str msgdef: str @@ -436,7 +436,7 @@ class Reader: count = reduce( lambda x, y: x + y, ( - y.connection_counts.get(x.cid, 0) + y.connection_counts.get(x.id, 0) for x in connections for y in self.chunk_infos ), @@ -607,7 +607,7 @@ class Reader: if not connections: connections = self.connections.values() - indexes = [self.indexes[x.cid] for x in connections] + indexes = [self.indexes[x.id] for x in connections] for entry in heapq.merge(*indexes): if start and entry.time < start: continue diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index 2d5076ea..8063757f 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -261,7 +261,7 @@ class Writer: bio = self.chunks[-1].data self.write_connection(connection, bio) - self.connections[connection.cid] = connection + self.connections[connection.id] = connection return connection def write(self, connection: Connection, timestamp: int, data: bytes) -> None: @@ -283,7 +283,7 @@ class Writer: raise WriterError(f'There is no connection {connection!r}.') from None chunk = self.chunks[-1] - chunk.connections[connection.cid].append((timestamp, chunk.data.tell())) + chunk.connections[connection.id].append((timestamp, chunk.data.tell())) if timestamp < chunk.start: chunk.start = timestamp @@ -292,7 +292,7 @@ class Writer: chunk.end = timestamp header = Header() - header.set_uint32('conn', connection.cid) + header.set_uint32('conn', connection.id) header.set_time('time', timestamp) header.write(chunk.data, RecordType.MSGDATA) @@ -305,7 +305,7 @@ class Writer: def write_connection(connection: Connection, bio: BinaryIO) -> None: """Write connection record.""" header = Header() - header.set_uint32('conn', connection.cid) + header.set_uint32('conn', connection.id) header.set_string('topic', connection.topic) header.write(bio, RecordType.CONNECTION) diff --git a/tests/test_writer1.py b/tests/test_writer1.py index 28ba6cee..dd188525 100644 --- a/tests/test_writer1.py +++ b/tests/test_writer1.py @@ -49,7 +49,7 @@ def test_add_connection(tmp_path: Path) -> None: with Writer(path) as writer: res = writer.add_connection('/foo', 'test_msgs/msg/Test', 'MESSAGE_DEFINITION', 'HASH') - assert res.cid == 0 + assert res.id == 0 data = path.read_bytes() assert data.count(b'MESSAGE_DEFINITION') == 2 assert data.count(b'HASH') == 2 @@ -57,7 +57,7 @@ def test_add_connection(tmp_path: Path) -> None: with Writer(path) as writer: res = writer.add_connection('/foo', 'std_msgs/msg/Int8') - assert res.cid == 0 + assert res.id == 0 data = path.read_bytes() assert data.count(b'int8 data') == 2 assert data.count(b'27ffa0c9c4b8fb8492252bcad9e5c57b') == 2 @@ -85,7 +85,7 @@ def test_add_connection(tmp_path: Path) -> None: 'HASH', latching=1, ) - assert (res1.cid, res2.cid, res3.cid) == (0, 1, 2) + assert (res1.id, res2.id, res3.id) == (0, 1, 2) def test_write_errors(tmp_path: Path) -> None: From 16d1758327469047e8559361465148fbfc3d3081 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 13 Apr 2022 09:40:22 +0200 Subject: [PATCH 073/114] Unify rosbag1 and rosbag2 connection class --- docs/examples/edit_rosbags_edit_timestamps.py | 8 +- docs/examples/edit_rosbags_remove_topic.py | 8 +- src/rosbags/convert/converter.py | 89 ++++++--- src/rosbags/interfaces/__init__.py | 36 ++++ src/rosbags/interfaces/py.typed | 0 src/rosbags/rosbag1/reader.py | 27 ++- src/rosbags/rosbag1/writer.py | 19 +- src/rosbags/rosbag2/connection.py | 18 -- src/rosbags/rosbag2/reader.py | 15 +- src/rosbags/rosbag2/writer.py | 32 +-- tests/test_convert.py | 185 +++++++++++++----- tests/test_roundtrip.py | 4 +- tests/test_writer.py | 10 +- 13 files changed, 301 insertions(+), 150 deletions(-) create mode 100644 src/rosbags/interfaces/__init__.py create mode 100644 src/rosbags/interfaces/py.typed delete mode 100644 src/rosbags/rosbag2/connection.py diff --git a/docs/examples/edit_rosbags_edit_timestamps.py b/docs/examples/edit_rosbags_edit_timestamps.py index 36e2cdc1..6de70aae 100644 --- a/docs/examples/edit_rosbags_edit_timestamps.py +++ b/docs/examples/edit_rosbags_edit_timestamps.py @@ -2,8 +2,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast +from rosbags.interfaces import ConnectionExtRosbag2 from rosbags.rosbag2 import Reader, Writer from rosbags.serde import deserialize_cdr, serialize_cdr @@ -23,11 +24,12 @@ def offset_timestamps(src: Path, dst: Path, offset: int) -> None: with Reader(src) as reader, Writer(dst) as writer: conn_map = {} for conn in reader.connections.values(): + ext = cast(ConnectionExtRosbag2, conn.ext) conn_map[conn.id] = writer.add_connection( conn.topic, conn.msgtype, - conn.serialization_format, - conn.offered_qos_profiles, + ext.serialization_format, + ext.offered_qos_profiles, ) for conn, timestamp, data in reader.messages(): diff --git a/docs/examples/edit_rosbags_remove_topic.py b/docs/examples/edit_rosbags_remove_topic.py index 0e416a84..aa00dabd 100644 --- a/docs/examples/edit_rosbags_remove_topic.py +++ b/docs/examples/edit_rosbags_remove_topic.py @@ -2,8 +2,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast +from rosbags.interfaces import ConnectionExtRosbag2 from rosbags.rosbag2 import Reader, Writer if TYPE_CHECKING: @@ -24,11 +25,12 @@ def remove_topic(src: Path, dst: Path, topic: str) -> None: for conn in reader.connections.values(): if conn.topic == topic: continue + ext = cast(ConnectionExtRosbag2, conn.ext) conn_map[conn.id] = writer.add_connection( conn.topic, conn.msgtype, - conn.serialization_format, - conn.offered_qos_profiles, + ext.serialization_format, + ext.offered_qos_profiles, ) rconns = [reader.connections[x] for x in conn_map] diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index 03af6168..ce47674c 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -4,19 +4,17 @@ from __future__ import annotations -from dataclasses import asdict from typing import TYPE_CHECKING +from rosbags.interfaces import Connection, ConnectionExtRosbag1, ConnectionExtRosbag2 from rosbags.rosbag1 import Reader as Reader1 from rosbags.rosbag1 import ReaderError as ReaderError1 from rosbags.rosbag1 import Writer as Writer1 from rosbags.rosbag1 import WriterError as WriterError1 -from rosbags.rosbag1.reader import Connection as Connection1 from rosbags.rosbag2 import Reader as Reader2 from rosbags.rosbag2 import ReaderError as ReaderError2 from rosbags.rosbag2 import Writer as Writer2 from rosbags.rosbag2 import WriterError as WriterError2 -from rosbags.rosbag2.connection import Connection as Connection2 from rosbags.serde import cdr_to_ros1, ros1_to_cdr from rosbags.typesys import get_types_from_msg, register_types from rosbags.typesys.msg import generate_msgdef @@ -48,7 +46,7 @@ class ConverterError(Exception): """Converter Error.""" -def upgrade_connection(rconn: Connection1) -> Connection2: +def upgrade_connection(rconn: Connection) -> Connection: """Convert rosbag1 connection to rosbag2 connection. Args: @@ -58,17 +56,22 @@ def upgrade_connection(rconn: Connection1) -> Connection2: Rosbag2 connection. """ - return Connection2( - -1, - 0, + assert isinstance(rconn.ext, ConnectionExtRosbag1) + return Connection( + rconn.id, rconn.topic, rconn.msgtype, - 'cdr', - LATCH if rconn.latching else '', + '', + '', + 0, + ConnectionExtRosbag2( + 'cdr', + LATCH if rconn.ext.latching else '', + ), ) -def downgrade_connection(rconn: Connection2) -> Connection1: +def downgrade_connection(rconn: Connection) -> Connection: """Convert rosbag2 connection to rosbag1 connection. Args: @@ -78,15 +81,19 @@ def downgrade_connection(rconn: Connection2) -> Connection1: Rosbag1 connection. """ + assert isinstance(rconn.ext, ConnectionExtRosbag2) msgdef, md5sum = generate_msgdef(rconn.msgtype) - return Connection1( - -1, + return Connection( + rconn.id, rconn.topic, rconn.msgtype, msgdef, md5sum, - None, - int('durability: 1' in rconn.offered_qos_profiles), + -1, + ConnectionExtRosbag1( + None, + int('durability: 1' in rconn.ext.offered_qos_profiles), + ), ) @@ -100,13 +107,26 @@ def convert_1to2(src: Path, dst: Path) -> None: """ with Reader1(src) as reader, Writer2(dst) as writer: typs: dict[str, Any] = {} - connmap: dict[int, Connection2] = {} + connmap: dict[int, Connection] = {} for rconn in reader.connections.values(): candidate = upgrade_connection(rconn) - existing = next((x for x in writer.connections.values() if x == candidate), None) - wconn = existing if existing else writer.add_connection(**asdict(candidate)) - connmap[rconn.id] = wconn + assert isinstance(candidate.ext, ConnectionExtRosbag2) + for conn in writer.connections.values(): + assert isinstance(conn.ext, ConnectionExtRosbag2) + if ( + conn.topic == candidate.topic and conn.msgtype == candidate.msgtype and + conn.ext == candidate.ext + ): + break + else: + conn = writer.add_connection( + candidate.topic, + candidate.msgtype, + candidate.ext.serialization_format, + candidate.ext.offered_qos_profiles, + ) + connmap[rconn.id] = conn typs.update(get_types_from_msg(rconn.msgdef, rconn.msgtype)) register_types(typs) @@ -124,22 +144,27 @@ def convert_2to1(src: Path, dst: Path) -> None: """ with Reader2(src) as reader, Writer1(dst) as writer: - connmap: dict[int, Connection1] = {} + connmap: dict[int, Connection] = {} for rconn in reader.connections.values(): candidate = downgrade_connection(rconn) - # yapf: disable - existing = next( - ( - x - for x in writer.connections.values() - if x.topic == candidate.topic - if x.md5sum == candidate.md5sum - if x.latching == candidate.latching - ), - None, - ) - # yapf: enable - connmap[rconn.id] = existing if existing else writer.add_connection(*candidate[1:]) + assert isinstance(candidate.ext, ConnectionExtRosbag1) + for conn in writer.connections.values(): + assert isinstance(conn.ext, ConnectionExtRosbag1) + if ( + conn.topic == candidate.topic and conn.md5sum == candidate.md5sum and + conn.ext.latching == candidate.ext.latching + ): + break + else: + conn = writer.add_connection( + candidate.topic, + candidate.msgtype, + candidate.msgdef, + candidate.md5sum, + candidate.ext.callerid, + candidate.ext.latching, + ) + connmap[rconn.id] = conn for rconn, timestamp, data in reader.messages(): data = cdr_to_ros1(data, rconn.msgtype) diff --git a/src/rosbags/interfaces/__init__.py b/src/rosbags/interfaces/__init__.py new file mode 100644 index 00000000..df5b221c --- /dev/null +++ b/src/rosbags/interfaces/__init__.py @@ -0,0 +1,36 @@ +# Copyright 2020-2022 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Shared interfaces.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, NamedTuple + +if TYPE_CHECKING: + from typing import Optional, Union + + +class ConnectionExtRosbag1(NamedTuple): + """Rosbag1 specific connection extensions.""" + + callerid: Optional[str] + latching: Optional[int] + + +class ConnectionExtRosbag2(NamedTuple): + """Rosbag2 specific connection extensions.""" + + serialization_format: str + offered_qos_profiles: str + + +class Connection(NamedTuple): + """Connection information.""" + + id: int + topic: str + msgtype: str + msgdef: str + md5sum: str + msgcount: int + ext: Union[ConnectionExtRosbag1, ConnectionExtRosbag2] diff --git a/src/rosbags/interfaces/py.typed b/src/rosbags/interfaces/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 8798c529..99ec9014 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -20,6 +20,7 @@ from typing import TYPE_CHECKING, Any, Dict, NamedTuple from lz4.frame import decompress as lz4_decompress +from rosbags.interfaces import Connection, ConnectionExtRosbag1 from rosbags.typesys.msg import normalize_msgtype if TYPE_CHECKING: @@ -50,18 +51,6 @@ class RecordType(IntEnum): CONNECTION = 7 -class Connection(NamedTuple): - """Connection information.""" - - id: int - topic: str - msgtype: str - msgdef: str - md5sum: str - callerid: Optional[str] - latching: Optional[int] - - class ChunkInfo(NamedTuple): """Chunk information.""" @@ -427,6 +416,13 @@ class Reader: } assert all(self.indexes[x] for x in self.connections) + for cid, connection in self.connections.items(): + self.connections[cid] = Connection( + *connection[0:5], + len(self.indexes[cid]), + connection[6], + ) + self.topics = {} for topic, group in groupby( sorted(self.connections.values(), key=lambda x: x.topic), @@ -499,8 +495,11 @@ class Reader: normalize_msgtype(typ), msgdef, md5sum, - callerid, - latching, + 0, + ConnectionExtRosbag1( + callerid, + latching, + ), ) def read_chunk_info(self) -> ChunkInfo: diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index 8063757f..f71e0366 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -15,9 +15,10 @@ from typing import TYPE_CHECKING, Any, Dict from lz4.frame import compress as lz4_compress +from rosbags.interfaces import Connection, ConnectionExtRosbag1 from rosbags.typesys.msg import denormalize_msgtype, generate_msgdef -from .reader import Connection, RecordType +from .reader import RecordType if TYPE_CHECKING: from types import TracebackType @@ -249,8 +250,11 @@ class Writer: denormalize_msgtype(msgtype), msgdef, md5sum, - callerid, - latching, + -1, + ConnectionExtRosbag1( + callerid, + latching, + ), ) if any(x[1:] == connection[1:] for x in self.connections.values()): @@ -314,10 +318,11 @@ class Writer: header.set_string('type', connection.msgtype) header.set_string('md5sum', connection.md5sum) header.set_string('message_definition', connection.msgdef) - if connection.callerid is not None: - header.set_string('callerid', connection.callerid) - if connection.latching is not None: - header.set_string('latching', str(connection.latching)) + assert isinstance(connection.ext, ConnectionExtRosbag1) + if connection.ext.callerid is not None: + header.set_string('callerid', connection.ext.callerid) + if connection.ext.latching is not None: + header.set_string('latching', str(connection.ext.latching)) header.write(bio) def write_chunk(self, chunk: WriteChunk) -> None: diff --git a/src/rosbags/rosbag2/connection.py b/src/rosbags/rosbag2/connection.py deleted file mode 100644 index 73d5daa3..00000000 --- a/src/rosbags/rosbag2/connection.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2020-2022 Ternaris. -# SPDX-License-Identifier: Apache-2.0 -"""Rosbag2 connection.""" - -from __future__ import annotations - -from dataclasses import dataclass, field - - -@dataclass -class Connection: - """Connection metadata.""" - id: int = field(compare=False) # pylint: disable=invalid-name - count: int = field(compare=False) - topic: str - msgtype: str - serialization_format: str - offered_qos_profiles: str diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index b3145aa3..72ea005d 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -14,7 +14,7 @@ import zstandard from ruamel.yaml import YAML from ruamel.yaml.error import YAMLError -from .connection import Connection +from rosbags.interfaces import Connection, ConnectionExtRosbag2 if TYPE_CHECKING: from types import TracebackType @@ -139,15 +139,20 @@ class Reader: self.connections = { idx + 1: Connection( id=idx + 1, - count=x['message_count'], topic=x['topic_metadata']['name'], msgtype=x['topic_metadata']['type'], - serialization_format=x['topic_metadata']['serialization_format'], - offered_qos_profiles=x['topic_metadata'].get('offered_qos_profiles', ''), + msgdef='', + md5sum='', + msgcount=x['message_count'], + ext=ConnectionExtRosbag2( + serialization_format=x['topic_metadata']['serialization_format'], + offered_qos_profiles=x['topic_metadata'].get('offered_qos_profiles', ''), + ), ) for idx, x in enumerate(self.metadata['topics_with_message_count']) } noncdr = { - fmt for x in self.connections.values() if (fmt := x.serialization_format) != 'cdr' + fmt for x in self.connections.values() if isinstance(x.ext, ConnectionExtRosbag2) + if (fmt := x.ext.serialization_format) != 'cdr' } if noncdr: raise ReaderError(f'Serialization format {noncdr!r} is not supported.') diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 027647ac..f7263148 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -12,7 +12,7 @@ from typing import TYPE_CHECKING import zstandard from ruamel.yaml import YAML -from .connection import Connection +from rosbags.interfaces import Connection, ConnectionExtRosbag2 if TYPE_CHECKING: from types import TracebackType @@ -82,6 +82,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.compression_format = '' self.compressor: Optional[zstandard.ZstdCompressor] = None self.connections: dict[int, Connection] = {} + self.counts: dict[int, int] = {} self.conn: Optional[sqlite3.Connection] = None self.cursor: Optional[sqlite3.Cursor] = None @@ -152,16 +153,25 @@ class Writer: # pylint: disable=too-many-instance-attributes connection = Connection( id=len(self.connections.values()) + 1, - count=0, topic=topic, msgtype=msgtype, - serialization_format=serialization_format, - offered_qos_profiles=offered_qos_profiles, + msgdef='', + md5sum='', + msgcount=0, + ext=ConnectionExtRosbag2( + serialization_format=serialization_format, + offered_qos_profiles=offered_qos_profiles, + ), ) - if connection in self.connections.values(): - raise WriterError(f'Connection can only be added once: {connection!r}.') + for conn in self.connections.values(): + if ( + conn.topic == connection.topic and conn.msgtype == connection.msgtype and + conn.ext == connection.ext + ): + raise WriterError(f'Connection can only be added once: {connection!r}.') self.connections[connection.id] = connection + self.counts[connection.id] = 0 meta = (connection.id, topic, msgtype, serialization_format, offered_qos_profiles) self.cursor.execute('INSERT INTO topics VALUES(?, ?, ?, ?, ?)', meta) return connection @@ -191,7 +201,7 @@ class Writer: # pylint: disable=too-many-instance-attributes 'INSERT INTO messages (topic_id, timestamp, data) VALUES(?, ?, ?)', (connection.id, timestamp, data), ) - connection.count += 1 + self.counts[connection.id] += 1 def close(self) -> None: """Close rosbag2 after writing. @@ -237,11 +247,11 @@ class Writer: # pylint: disable=too-many-instance-attributes 'topic_metadata': { 'name': x.topic, 'type': x.msgtype, - 'serialization_format': x.serialization_format, - 'offered_qos_profiles': x.offered_qos_profiles, + 'serialization_format': x.ext.serialization_format, + 'offered_qos_profiles': x.ext.offered_qos_profiles, }, - 'message_count': x.count, - } for x in self.connections.values() + 'message_count': self.counts[x.id], + } for x in self.connections.values() if isinstance(x.ext, ConnectionExtRosbag2) ], 'compression_format': self.compression_format, 'compression_mode': self.compression_mode, diff --git a/tests/test_convert.py b/tests/test_convert.py index fa93351b..6d030d8f 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -2,18 +2,25 @@ # SPDX-License-Identifier: Apache-2.0 """Rosbag1to2 converter tests.""" +from __future__ import annotations + import sys from pathlib import Path -from unittest.mock import Mock, call, patch +from typing import TYPE_CHECKING +from unittest.mock import call, patch import pytest from rosbags.convert import ConverterError, convert from rosbags.convert.__main__ import main from rosbags.convert.converter import LATCH +from rosbags.interfaces import Connection, ConnectionExtRosbag1, ConnectionExtRosbag2 from rosbags.rosbag1 import ReaderError from rosbags.rosbag2 import WriterError +if TYPE_CHECKING: + from typing import Any + def test_cliwrapper(tmp_path: Path) -> None: """Test cli wrapper.""" @@ -114,71 +121,83 @@ def test_convert_1to2(tmp_path: Path) -> None: patch('rosbags.convert.converter.register_types') as register_types, \ patch('rosbags.convert.converter.ros1_to_cdr') as ros1_to_cdr: + readerinst = reader.return_value.__enter__.return_value + writerinst = writer.return_value.__enter__.return_value + connections = [ - Mock(topic='/topic', msgtype='typ', latching=False), - Mock(topic='/topic', msgtype='typ', latching=True), + Connection(1, '/topic', 'typ', 'def', '', -1, ConnectionExtRosbag1(None, False)), + Connection(2, '/topic', 'typ', 'def', '', -1, ConnectionExtRosbag1(None, True)), + Connection(3, '/other', 'typ', 'def', '', -1, ConnectionExtRosbag1(None, False)), + Connection(4, '/other', 'typ', 'def', '', -1, ConnectionExtRosbag1('caller', False)), ] wconnections = [ - Mock(topic='/topic', msgtype='typ'), - Mock(topic='/topic', msgtype='typ'), + Connection(1, '/topic', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', '')), + Connection(2, '/topic', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', LATCH)), + Connection(3, '/other', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', '')), ] - reader.return_value.__enter__.return_value.connections = { + readerinst.connections = { 1: connections[0], 2: connections[1], + 3: connections[2], + 4: connections[3], } - reader.return_value.__enter__.return_value.messages.return_value = [ + readerinst.messages.return_value = [ (connections[0], 42, b'\x42'), (connections[1], 43, b'\x43'), + (connections[2], 44, b'\x44'), + (connections[3], 45, b'\x45'), ] - writer.return_value.__enter__.return_value.add_connection.side_effect = [ - wconnections[0], - wconnections[1], - ] + writerinst.connections = {} + + def add_connection(*_: Any) -> Connection: # noqa: ANN401 + """Mock for Writer.add_connection.""" + writerinst.connections = { + conn.id: conn + for _, conn in zip(range(len(writerinst.connections) + 1), wconnections) + } + return wconnections[len(writerinst.connections) - 1] + + writerinst.add_connection.side_effect = add_connection ros1_to_cdr.return_value = b'666' convert(Path('foo.bag'), None) reader.assert_called_with(Path('foo.bag')) - reader.return_value.__enter__.return_value.messages.assert_called_with() + readerinst.messages.assert_called_with() writer.assert_called_with(Path('foo')) - writer.return_value.__enter__.return_value.add_connection.assert_has_calls( + writerinst.add_connection.assert_has_calls( [ - call( - id=-1, - count=0, - topic='/topic', - msgtype='typ', - serialization_format='cdr', - offered_qos_profiles='', - ), - call( - id=-1, - count=0, - topic='/topic', - msgtype='typ', - serialization_format='cdr', - offered_qos_profiles=LATCH, - ), + call('/topic', 'typ', 'cdr', ''), + call('/topic', 'typ', 'cdr', LATCH), + call('/other', 'typ', 'cdr', ''), ], ) - writer.return_value.__enter__.return_value.write.assert_has_calls( - [call(wconnections[0], 42, b'666'), - call(wconnections[1], 43, b'666')], + writerinst.write.assert_has_calls( + [ + call(wconnections[0], 42, b'666'), + call(wconnections[1], 43, b'666'), + call(wconnections[2], 44, b'666'), + call(wconnections[2], 45, b'666'), + ], ) register_types.assert_called_with({'typ': 'def'}) - ros1_to_cdr.assert_has_calls([call(b'\x42', 'typ'), call(b'\x43', 'typ')]) + ros1_to_cdr.assert_has_calls( + [ + call(b'\x42', 'typ'), + call(b'\x43', 'typ'), + call(b'\x44', 'typ'), + call(b'\x45', 'typ'), + ], + ) - writer.return_value.__enter__.return_value.add_connection.side_effect = [ - wconnections[0], - wconnections[1], - ] + writerinst.connections.clear() ros1_to_cdr.side_effect = KeyError('exc') with pytest.raises(ConverterError, match='Converting rosbag: .*exc'): convert(Path('foo.bag'), None) @@ -204,30 +223,79 @@ def test_convert_2to1(tmp_path: Path) -> None: patch('rosbags.convert.converter.Writer1') as writer, \ patch('rosbags.convert.converter.cdr_to_ros1') as cdr_to_ros1: + readerinst = reader.return_value.__enter__.return_value + writerinst = writer.return_value.__enter__.return_value + connections = [ - Mock(topic='/topic', msgtype='std_msgs/msg/Bool', offered_qos_profiles=''), - Mock(topic='/topic', msgtype='std_msgs/msg/Bool', offered_qos_profiles=LATCH), + Connection(1, '/topic', 'std_msgs/msg/Bool', '', '', -1, ConnectionExtRosbag2('', '')), + Connection( + 2, + '/topic', + 'std_msgs/msg/Bool', + '', + '', + -1, + ConnectionExtRosbag2('', LATCH), + ), + Connection(3, '/other', 'std_msgs/msg/Bool', '', '', -1, ConnectionExtRosbag2('', '')), + Connection(4, '/other', 'std_msgs/msg/Bool', '', '', -1, ConnectionExtRosbag2('', '0')), ] wconnections = [ - Mock(topic='/topic', msgtype='typ'), - Mock(topic='/topic', msgtype='typ'), + Connection( + 1, + '/topic', + 'std_msgs/msg/Bool', + '', + '8b94c1b53db61fb6aed406028ad6332a', + -1, + ConnectionExtRosbag1(None, False), + ), + Connection( + 2, + '/topic', + 'std_msgs/msg/Bool', + '', + '8b94c1b53db61fb6aed406028ad6332a', + -1, + ConnectionExtRosbag1(None, True), + ), + Connection( + 3, + '/other', + 'std_msgs/msg/Bool', + '', + '8b94c1b53db61fb6aed406028ad6332a', + -1, + ConnectionExtRosbag1(None, False), + ), ] - reader.return_value.__enter__.return_value.connections = { + readerinst.connections = { 1: connections[0], 2: connections[1], + 3: connections[2], + 4: connections[3], } - reader.return_value.__enter__.return_value.messages.return_value = [ + readerinst.messages.return_value = [ (connections[0], 42, b'\x42'), (connections[1], 43, b'\x43'), + (connections[2], 44, b'\x44'), + (connections[3], 45, b'\x45'), ] - writer.return_value.__enter__.return_value.add_connection.side_effect = [ - wconnections[0], - wconnections[1], - ] + writerinst.connections = {} + + def add_connection(*_: Any) -> Connection: # noqa: ANN401 + """Mock for Writer.add_connection.""" + writerinst.connections = { + conn.id: conn + for _, conn in zip(range(len(writerinst.connections) + 1), wconnections) + } + return wconnections[len(writerinst.connections) - 1] + + writerinst.add_connection.side_effect = add_connection cdr_to_ros1.return_value = b'666' @@ -255,24 +323,35 @@ def test_convert_2to1(tmp_path: Path) -> None: None, 1, ), + call( + '/other', + 'std_msgs/msg/Bool', + 'bool data\n', + '8b94c1b53db61fb6aed406028ad6332a', + None, + 0, + ), ], ) writer.return_value.__enter__.return_value.write.assert_has_calls( - [call(wconnections[0], 42, b'666'), - call(wconnections[1], 43, b'666')], + [ + call(wconnections[0], 42, b'666'), + call(wconnections[1], 43, b'666'), + call(wconnections[2], 44, b'666'), + call(wconnections[2], 45, b'666'), + ], ) cdr_to_ros1.assert_has_calls( [ call(b'\x42', 'std_msgs/msg/Bool'), call(b'\x43', 'std_msgs/msg/Bool'), + call(b'\x44', 'std_msgs/msg/Bool'), + call(b'\x45', 'std_msgs/msg/Bool'), ], ) - writer.return_value.__enter__.return_value.add_connection.side_effect = [ - wconnections[0], - wconnections[1], - ] + writerinst.connections.clear() cdr_to_ros1.side_effect = KeyError('exc') with pytest.raises(ConverterError, match='Converting rosbag: .*exc'): convert(Path('foo'), None) diff --git a/tests/test_roundtrip.py b/tests/test_roundtrip.py index 6743deed..d3ce5d44 100644 --- a/tests/test_roundtrip.py +++ b/tests/test_roundtrip.py @@ -36,7 +36,9 @@ def test_roundtrip(mode: Writer.CompressionMode, tmp_path: Path) -> None: with rbag: gen = rbag.messages() rconnection, _, raw = next(gen) - assert rconnection == wconnection + assert rconnection.topic == wconnection.topic + assert rconnection.msgtype == wconnection.msgtype + assert rconnection.ext == wconnection.ext msg = deserialize_cdr(raw, rconnection.msgtype) assert getattr(msg, 'data', None) == Foo.data with pytest.raises(StopIteration): diff --git a/tests/test_writer.py b/tests/test_writer.py index 7dc7ee4d..5d2206a8 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -8,8 +8,8 @@ from typing import TYPE_CHECKING import pytest +from rosbags.interfaces import Connection, ConnectionExtRosbag2 from rosbags.rosbag2 import Writer, WriterError -from rosbags.rosbag2.connection import Connection if TYPE_CHECKING: from pathlib import Path @@ -81,7 +81,11 @@ def test_failure_cases(tmp_path: Path) -> None: bag = Writer(tmp_path / 'write') with pytest.raises(WriterError, match='was not opened'): - bag.write(Connection(1, 0, '/tf', 'tf_msgs/msg/tf2', 'cdr', ''), 0, b'') + bag.write( + Connection(1, '/tf', 'tf_msgs/msg/tf2', '', '', 0, ConnectionExtRosbag2('cdr', '')), + 0, + b'', + ) bag = Writer(tmp_path / 'topic') bag.open() @@ -91,6 +95,6 @@ def test_failure_cases(tmp_path: Path) -> None: bag = Writer(tmp_path / 'notopic') bag.open() - connection = Connection(1, 0, '/tf', 'tf_msgs/msg/tf2', 'cdr', '') + connection = Connection(1, '/tf', 'tf_msgs/msg/tf2', '', '', 0, ConnectionExtRosbag2('cdr', '')) with pytest.raises(WriterError, match='unknown connection'): bag.write(connection, 42, b'\x00') From d32742b90431f5060fb3a56f998c549c4d5aa3a9 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 13 Apr 2022 10:42:47 +0200 Subject: [PATCH 074/114] Unify rosbag1 and rosbag2 topic information --- src/rosbags/interfaces/__init__.py | 9 +++++ src/rosbags/rosbag1/reader.py | 58 ++++++++++++------------------ src/rosbags/rosbag2/reader.py | 15 ++++---- 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/rosbags/interfaces/__init__.py b/src/rosbags/interfaces/__init__.py index df5b221c..c60e1ea0 100644 --- a/src/rosbags/interfaces/__init__.py +++ b/src/rosbags/interfaces/__init__.py @@ -34,3 +34,12 @@ class Connection(NamedTuple): md5sum: str msgcount: int ext: Union[ConnectionExtRosbag1, ConnectionExtRosbag2] + + +class TopicInfo(NamedTuple): + """Topic information.""" + + msgtype: Optional[str] + msgdef: Optional[str] + msgcount: int + connections: list[Connection] diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 99ec9014..4034ff1b 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -20,7 +20,7 @@ from typing import TYPE_CHECKING, Any, Dict, NamedTuple from lz4.frame import decompress as lz4_decompress -from rosbags.interfaces import Connection, ConnectionExtRosbag1 +from rosbags.interfaces import Connection, ConnectionExtRosbag1, TopicInfo from rosbags.typesys.msg import normalize_msgtype if TYPE_CHECKING: @@ -68,15 +68,6 @@ class Chunk(NamedTuple): decompressor: Callable[[bytes], bytes] -class TopicInfo(NamedTuple): - """Topic information.""" - - conncount: int - msgcount: int - msgdef: str - msgtype: str - - class IndexData(NamedTuple): """Index data.""" @@ -336,8 +327,6 @@ class Reader: """ - # pylint: disable=too-many-instance-attributes - def __init__(self, path: Union[str, Path]): """Initialize. @@ -358,7 +347,6 @@ class Reader: self.chunk_infos: list[ChunkInfo] = [] self.chunks: dict[int, Chunk] = {} self.current_chunk: tuple[int, BinaryIO] = (-1, BytesIO()) - self.topics: dict[str, TopicInfo] = {} def open(self) -> None: # pylint: disable=too-many-locals """Open rosbag and read metadata.""" @@ -422,28 +410,6 @@ class Reader: len(self.indexes[cid]), connection[6], ) - - self.topics = {} - for topic, group in groupby( - sorted(self.connections.values(), key=lambda x: x.topic), - key=lambda x: x.topic, - ): - connections = list(group) - count = reduce( - lambda x, y: x + y, - ( - y.connection_counts.get(x.id, 0) - for x in connections - for y in self.chunk_infos - ), - ) - - self.topics[topic] = TopicInfo( - len(connections), - count, - connections[0].msgdef, - connections[0].msgtype, - ) except ReaderError: self.close() raise @@ -474,6 +440,28 @@ class Reader: """Total message count.""" return reduce(lambda x, y: x + y, (x.msgcount for x in self.topics.values()), 0) + @property + def topics(self) -> dict[str, TopicInfo]: + """Topic information.""" + topics = {} + for topic, group in groupby( + sorted(self.connections.values(), key=lambda x: x.topic), + key=lambda x: x.topic, + ): + connections = list(group) + msgcount = reduce( + lambda x, y: x + y, + (y.connection_counts.get(x.id, 0) for x in connections for y in self.chunk_infos), + ) + + topics[topic] = TopicInfo( + msgtypes.pop() if len(msgtypes := {x.msgtype for x in connections}) == 1 else None, + msgdefs.pop() if len(msgdefs := {x.msgdef for x in connections}) == 1 else None, + msgcount, + connections, + ) + return topics + def read_connection(self) -> tuple[int, Connection]: """Read connection record from current position.""" assert self.bio diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 72ea005d..c9ec5082 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -14,7 +14,7 @@ import zstandard from ruamel.yaml import YAML from ruamel.yaml.error import YAMLError -from rosbags.interfaces import Connection, ConnectionExtRosbag2 +from rosbags.interfaces import Connection, ConnectionExtRosbag2, TopicInfo if TYPE_CHECKING: from types import TracebackType @@ -206,13 +206,12 @@ class Reader: return mode if mode != 'none' else None @property - def topics(self) -> dict[str, Connection]: - """Topic information. - - For the moment this a dictionary mapping topic names to connections. - - """ - return {x.topic: x for x in self.connections.values()} + def topics(self) -> dict[str, TopicInfo]: + """Topic information.""" + return { + x.topic: TopicInfo(x.msgtype, x.msgdef, x.msgcount, [x]) + for x in self.connections.values() + } def messages( # pylint: disable=too-many-locals self, From 657032ce9f1093499f4ff5b34b93abb890b6d635 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 13 Apr 2022 11:09:30 +0200 Subject: [PATCH 075/114] Add owner field to connection instances --- src/rosbags/convert/converter.py | 2 + src/rosbags/interfaces/__init__.py | 1 + src/rosbags/rosbag1/reader.py | 3 +- src/rosbags/rosbag1/writer.py | 1 + src/rosbags/rosbag2/reader.py | 1 + src/rosbags/rosbag2/writer.py | 1 + tests/test_convert.py | 60 +++++++++++++++++++++++++----- tests/test_writer.py | 22 ++++++++++- 8 files changed, 78 insertions(+), 13 deletions(-) diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index ce47674c..093d2f03 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -68,6 +68,7 @@ def upgrade_connection(rconn: Connection) -> Connection: 'cdr', LATCH if rconn.ext.latching else '', ), + None, ) @@ -94,6 +95,7 @@ def downgrade_connection(rconn: Connection) -> Connection: None, int('durability: 1' in rconn.ext.offered_qos_profiles), ), + None, ) diff --git a/src/rosbags/interfaces/__init__.py b/src/rosbags/interfaces/__init__.py index c60e1ea0..76988fc1 100644 --- a/src/rosbags/interfaces/__init__.py +++ b/src/rosbags/interfaces/__init__.py @@ -34,6 +34,7 @@ class Connection(NamedTuple): md5sum: str msgcount: int ext: Union[ConnectionExtRosbag1, ConnectionExtRosbag2] + owner: object class TopicInfo(NamedTuple): diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 4034ff1b..1f3058aa 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -408,7 +408,7 @@ class Reader: self.connections[cid] = Connection( *connection[0:5], len(self.indexes[cid]), - connection[6], + *connection[6:], ) except ReaderError: self.close() @@ -488,6 +488,7 @@ class Reader: callerid, latching, ), + self, ) def read_chunk_info(self) -> ChunkInfo: diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index f71e0366..8150b36d 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -255,6 +255,7 @@ class Writer: callerid, latching, ), + self, ) if any(x[1:] == connection[1:] for x in self.connections.values()): diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index c9ec5082..6ac3e90a 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -148,6 +148,7 @@ class Reader: serialization_format=x['topic_metadata']['serialization_format'], offered_qos_profiles=x['topic_metadata'].get('offered_qos_profiles', ''), ), + owner=self, ) for idx, x in enumerate(self.metadata['topics_with_message_count']) } noncdr = { diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index f7263148..5c5becc0 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -162,6 +162,7 @@ class Writer: # pylint: disable=too-many-instance-attributes serialization_format=serialization_format, offered_qos_profiles=offered_qos_profiles, ), + owner=self, ) for conn in self.connections.values(): if ( diff --git a/tests/test_convert.py b/tests/test_convert.py index 6d030d8f..d4c69293 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -125,16 +125,25 @@ def test_convert_1to2(tmp_path: Path) -> None: writerinst = writer.return_value.__enter__.return_value connections = [ - Connection(1, '/topic', 'typ', 'def', '', -1, ConnectionExtRosbag1(None, False)), - Connection(2, '/topic', 'typ', 'def', '', -1, ConnectionExtRosbag1(None, True)), - Connection(3, '/other', 'typ', 'def', '', -1, ConnectionExtRosbag1(None, False)), - Connection(4, '/other', 'typ', 'def', '', -1, ConnectionExtRosbag1('caller', False)), + Connection(1, '/topic', 'typ', 'def', '', -1, ConnectionExtRosbag1(None, False), None), + Connection(2, '/topic', 'typ', 'def', '', -1, ConnectionExtRosbag1(None, True), None), + Connection(3, '/other', 'typ', 'def', '', -1, ConnectionExtRosbag1(None, False), None), + Connection( + 4, + '/other', + 'typ', + 'def', + '', + -1, + ConnectionExtRosbag1('caller', False), + None, + ), ] wconnections = [ - Connection(1, '/topic', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', '')), - Connection(2, '/topic', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', LATCH)), - Connection(3, '/other', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', '')), + Connection(1, '/topic', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', ''), None), + Connection(2, '/topic', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', LATCH), None), + Connection(3, '/other', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', ''), None), ] readerinst.connections = { @@ -227,7 +236,16 @@ def test_convert_2to1(tmp_path: Path) -> None: writerinst = writer.return_value.__enter__.return_value connections = [ - Connection(1, '/topic', 'std_msgs/msg/Bool', '', '', -1, ConnectionExtRosbag2('', '')), + Connection( + 1, + '/topic', + 'std_msgs/msg/Bool', + '', + '', + -1, + ConnectionExtRosbag2('', ''), + None, + ), Connection( 2, '/topic', @@ -236,9 +254,28 @@ def test_convert_2to1(tmp_path: Path) -> None: '', -1, ConnectionExtRosbag2('', LATCH), + None, + ), + Connection( + 3, + '/other', + 'std_msgs/msg/Bool', + '', + '', + -1, + ConnectionExtRosbag2('', ''), + None, + ), + Connection( + 4, + '/other', + 'std_msgs/msg/Bool', + '', + '', + -1, + ConnectionExtRosbag2('', '0'), + None, ), - Connection(3, '/other', 'std_msgs/msg/Bool', '', '', -1, ConnectionExtRosbag2('', '')), - Connection(4, '/other', 'std_msgs/msg/Bool', '', '', -1, ConnectionExtRosbag2('', '0')), ] wconnections = [ @@ -250,6 +287,7 @@ def test_convert_2to1(tmp_path: Path) -> None: '8b94c1b53db61fb6aed406028ad6332a', -1, ConnectionExtRosbag1(None, False), + None, ), Connection( 2, @@ -259,6 +297,7 @@ def test_convert_2to1(tmp_path: Path) -> None: '8b94c1b53db61fb6aed406028ad6332a', -1, ConnectionExtRosbag1(None, True), + None, ), Connection( 3, @@ -268,6 +307,7 @@ def test_convert_2to1(tmp_path: Path) -> None: '8b94c1b53db61fb6aed406028ad6332a', -1, ConnectionExtRosbag1(None, False), + None, ), ] diff --git a/tests/test_writer.py b/tests/test_writer.py index 5d2206a8..b4efa1e8 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -82,7 +82,16 @@ def test_failure_cases(tmp_path: Path) -> None: bag = Writer(tmp_path / 'write') with pytest.raises(WriterError, match='was not opened'): bag.write( - Connection(1, '/tf', 'tf_msgs/msg/tf2', '', '', 0, ConnectionExtRosbag2('cdr', '')), + Connection( + 1, + '/tf', + 'tf_msgs/msg/tf2', + '', + '', + 0, + ConnectionExtRosbag2('cdr', ''), + None, + ), 0, b'', ) @@ -95,6 +104,15 @@ def test_failure_cases(tmp_path: Path) -> None: bag = Writer(tmp_path / 'notopic') bag.open() - connection = Connection(1, '/tf', 'tf_msgs/msg/tf2', '', '', 0, ConnectionExtRosbag2('cdr', '')) + connection = Connection( + 1, + '/tf', + 'tf_msgs/msg/tf2', + '', + '', + 0, + ConnectionExtRosbag2('cdr', ''), + None, + ) with pytest.raises(WriterError, match='unknown connection'): bag.write(connection, 42, b'\x00') From f7d69e35d5833eda608058e75f253ff321631cd4 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 13 Apr 2022 13:12:18 +0200 Subject: [PATCH 076/114] Add all-in-one reader --- docs/api/rosbags.highlevel.rst | 6 + docs/api/rosbags.rst | 1 + src/rosbags/highlevel/__init__.py | 10 ++ src/rosbags/highlevel/anyreader.py | 258 +++++++++++++++++++++++++++++ src/rosbags/highlevel/py.typed | 0 tests/test_highlevel.py | 202 ++++++++++++++++++++++ 6 files changed, 477 insertions(+) create mode 100644 docs/api/rosbags.highlevel.rst create mode 100644 src/rosbags/highlevel/__init__.py create mode 100644 src/rosbags/highlevel/anyreader.py create mode 100644 src/rosbags/highlevel/py.typed create mode 100644 tests/test_highlevel.py diff --git a/docs/api/rosbags.highlevel.rst b/docs/api/rosbags.highlevel.rst new file mode 100644 index 00000000..66c7ba3f --- /dev/null +++ b/docs/api/rosbags.highlevel.rst @@ -0,0 +1,6 @@ +rosbags.highlevel +================= + +.. automodule:: rosbags.highlevel + :members: + :show-inheritance: diff --git a/docs/api/rosbags.rst b/docs/api/rosbags.rst index e8761f70..cedf169a 100644 --- a/docs/api/rosbags.rst +++ b/docs/api/rosbags.rst @@ -5,6 +5,7 @@ Rosbags namespace :maxdepth: 4 rosbags.convert + rosbags.highlevel rosbags.rosbag1 rosbags.rosbag2 rosbags.serde diff --git a/src/rosbags/highlevel/__init__.py b/src/rosbags/highlevel/__init__.py new file mode 100644 index 00000000..3ce7382d --- /dev/null +++ b/src/rosbags/highlevel/__init__.py @@ -0,0 +1,10 @@ +# Copyright 2020-2022 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Highlevel interfaces for rosbags.""" + +from .anyreader import AnyReader, AnyReaderError + +__all__ = [ + 'AnyReader', + 'AnyReaderError', +] diff --git a/src/rosbags/highlevel/anyreader.py b/src/rosbags/highlevel/anyreader.py new file mode 100644 index 00000000..1a635a17 --- /dev/null +++ b/src/rosbags/highlevel/anyreader.py @@ -0,0 +1,258 @@ +# Copyright 2020-2022 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Tools for reading all rosbag versions with unified api.""" + +from __future__ import annotations + +from contextlib import suppress +from dataclasses import dataclass +from heapq import merge +from itertools import groupby +from typing import TYPE_CHECKING + +from rosbags.interfaces import TopicInfo +from rosbags.rosbag1 import Reader as Reader1 +from rosbags.rosbag1 import ReaderError as ReaderError1 +from rosbags.rosbag2 import Reader as Reader2 +from rosbags.rosbag2 import ReaderError as ReaderError2 +from rosbags.serde import deserialize_cdr, ros1_to_cdr +from rosbags.typesys import get_types_from_msg, register_types, types + +if TYPE_CHECKING: + import sys + from pathlib import Path + from types import TracebackType + from typing import Any, Generator, Iterable, Literal, Optional, Sequence, Type, Union + + from rosbags.interfaces import Connection + from rosbags.typesys.base import Typesdict + from rosbags.typesys.register import Typestore + + if sys.version_info < (3, 10): + from typing_extensions import TypeGuard + else: + from typing import TypeGuard + + +class AnyReaderError(Exception): + """Reader error.""" + + +ReaderErrors = (ReaderError1, ReaderError2) + + +def is_reader1(val: Union[Sequence[Reader1], Sequence[Reader2]]) -> TypeGuard[Sequence[Reader1]]: + """Determine wether all items are Reader1 instances.""" + return all(isinstance(x, Reader1) for x in val) + + +@dataclass +class SimpleTypeStore: + """Simple type store implementation.""" + + FIELDDEFS: Typesdict # pylint: disable=invalid-name + + def __hash__(self) -> int: + """Create hash.""" + return id(self) + + +class AnyReader: + """Unified rosbag1 and rosbag2 reader.""" + + readers: Union[Sequence[Reader1], Sequence[Reader2]] + typestore: Typestore + + def __init__(self, paths: Sequence[Path]): + """Initialize RosbagReader. + + Opens one or multiple rosbag1 recordings or a single rosbag2 recording. + + Args: + paths: Paths to multiple rosbag1 files or single rosbag2 directory. + + Raises: + AnyReaderError: If paths do not exist or multiple rosbag2 files are given. + + """ + if not paths: + raise AnyReaderError('Must call with at least one path.') + + if len(paths) > 1 and any((x / 'metadata.yaml').exists() for x in paths): + raise AnyReaderError('Opening of multiple rosbag2 recordings is not supported.') + + if missing := [x for x in paths if not x.exists()]: + raise AnyReaderError(f'The following paths are missing: {missing!r}') + + self.paths = paths + self.is2 = (paths[0] / 'metadata.yaml').exists() + self.isopen = False + self.connections: list[Connection] = [] + + try: + if self.is2: + self.readers = [Reader2(x) for x in paths] + else: + self.readers = [Reader1(x) for x in paths] + except ReaderErrors as err: + raise AnyReaderError(*err.args) from err + + self.typestore = SimpleTypeStore({}) + + def _deser_ros1(self, rawdata: bytes, typ: str) -> object: + """Deserialize ROS1 message.""" + return deserialize_cdr(ros1_to_cdr(rawdata, typ, self.typestore), typ, self.typestore) + + def _deser_ros2(self, rawdata: bytes, typ: str) -> object: + """Deserialize CDR message.""" + return deserialize_cdr(rawdata, typ, self.typestore) + + def deserialize(self, rawdata: bytes, typ: str) -> object: + """Deserialize message with appropriate helper.""" + return self._deser_ros2(rawdata, typ) if self.is2 else self._deser_ros1(rawdata, typ) + + def open(self) -> None: + """Open rosbags.""" + assert not self.isopen + rollback = [] + try: + for reader in self.readers: + reader.open() + rollback.append(reader) + except ReaderErrors as err: + for reader in rollback: + with suppress(*ReaderErrors): + reader.close() + raise AnyReaderError(*err.args) from err + + if self.is2: + for key, value in types.FIELDDEFS.items(): + self.typestore.FIELDDEFS[key] = value + attr = key.replace('/', '__') + setattr(self.typestore, attr, getattr(types, attr)) + else: + for key in [ + 'builtin_interfaces/msg/Time', + 'builtin_interfaces/msg/Duration', + 'std_msgs/msg/Header', + ]: + self.typestore.FIELDDEFS[key] = types.FIELDDEFS[key] + attr = key.replace('/', '__') + setattr(self.typestore, attr, getattr(types, attr)) + + typs: dict[str, Any] = {} + for reader in self.readers: + assert isinstance(reader, Reader1) + for connection in reader.connections.values(): + typs.update(get_types_from_msg(connection.msgdef, connection.msgtype)) + register_types(typs, self.typestore) + + self.connections = [y for x in self.readers for y in x.connections.values()] + self.isopen = True + + def close(self) -> None: + """Close rosbag.""" + assert self.isopen + for reader in self.readers: + with suppress(*ReaderErrors): + reader.close() + self.isopen = False + + def __enter__(self) -> AnyReader: + """Open rosbags when entering contextmanager.""" + self.open() + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> Literal[False]: + """Close rosbags when exiting contextmanager.""" + self.close() + return False + + @property + def duration(self) -> int: + """Duration in nanoseconds between earliest and latest messages.""" + return self.end_time - self.start_time + + @property + def start_time(self) -> int: + """Timestamp in nanoseconds of the earliest message.""" + return min(x.start_time for x in self.readers) + + @property + def end_time(self) -> int: + """Timestamp in nanoseconds after the latest message.""" + return max(x.end_time for x in self.readers) + + @property + def message_count(self) -> int: + """Total message count.""" + return sum(x.message_count for x in self.readers) + + @property + def topics(self) -> dict[str, TopicInfo]: + """Topics stored in the rosbags.""" + assert self.isopen + + if self.is2: + assert isinstance(self.readers[0], Reader2) + return self.readers[0].topics + + assert is_reader1(self.readers) + + def summarize(names_infos: Iterable[tuple[str, TopicInfo]]) -> TopicInfo: + """Summarize topic infos.""" + infos = [x[1] for x in names_infos] + return TopicInfo( + msgtypes.pop() if len(msgtypes := {x.msgtype for x in infos}) == 1 else None, + msgdefs.pop() if len(msgdefs := {x.msgdef for x in infos}) == 1 else None, + sum(x.msgcount for x in infos), + sum((x.connections for x in infos), []), + ) + + return { + name: summarize(infos) for name, infos in groupby( + sorted( + (x for reader in self.readers for x in reader.topics.items()), + key=lambda x: x[0], + ), + key=lambda x: x[0], + ) + } + + def messages( + self, + connections: Iterable[Any] = (), + start: Optional[int] = None, + stop: Optional[int] = None, + ) -> Generator[tuple[Any, int, bytes], None, None]: + """Read messages from bags. + + Args: + connections: Iterable with connections to filter for. An empty + iterable disables filtering on connections. + start: Yield only messages at or after this timestamp (ns). + stop: Yield only messages before this timestamp (ns). + + Yields: + Tuples of connection, timestamp (ns), and rawdata. + + """ + assert self.isopen + + def get_owner(connection: Connection) -> Union[Reader1, Reader2]: + assert isinstance(connection.owner, (Reader1, Reader2)) + return connection.owner + + if connections: + generators = [ + reader.messages(connections=list(conns), start=start, stop=stop) for reader, conns + in groupby(sorted(connections, key=lambda x: id(get_owner(x))), key=get_owner) + ] + else: + generators = [reader.messages(start=start, stop=stop) for reader in self.readers] + yield from merge(*generators, key=lambda x: x[1]) diff --git a/src/rosbags/highlevel/py.typed b/src/rosbags/highlevel/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_highlevel.py b/tests/test_highlevel.py new file mode 100644 index 00000000..17fcaa7c --- /dev/null +++ b/tests/test_highlevel.py @@ -0,0 +1,202 @@ +# Copyright 2020-2022 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Reader tests.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from rosbags.highlevel import AnyReader, AnyReaderError +from rosbags.rosbag1 import Writer as Writer1 +from rosbags.rosbag2 import Writer as Writer2 + +if TYPE_CHECKING: + from pathlib import Path + from typing import Sequence + +HEADER = b'\x00\x01\x00\x00' + + +@pytest.fixture() +def bags1(tmp_path: Path) -> list[Path]: + """Test data fixture.""" + paths = [ + tmp_path / 'ros1_1.bag', + tmp_path / 'ros1_2.bag', + tmp_path / 'ros1_3.bag', + tmp_path / 'bad.bag', + ] + with (Writer1(paths[0])) as writer: + topic1 = writer.add_connection('/topic1', 'std_msgs/msg/Int8') + topic2 = writer.add_connection('/topic2', 'std_msgs/msg/Int16') + writer.write(topic1, 1, b'\x01') + writer.write(topic2, 2, b'\x02\x00') + writer.write(topic1, 9, b'\x09') + with (Writer1(paths[1])) as writer: + topic1 = writer.add_connection('/topic1', 'std_msgs/msg/Int8') + writer.write(topic1, 5, b'\x05') + with (Writer1(paths[2])) as writer: + topic2 = writer.add_connection('/topic2', 'std_msgs/msg/Int16') + writer.write(topic2, 15, b'\x15\x00') + + paths[3].touch() + + return paths + + +@pytest.fixture() +def bags2(tmp_path: Path) -> list[Path]: + """Test data fixture.""" + paths = [ + tmp_path / 'ros2_1', + tmp_path / 'bad', + ] + with (Writer2(paths[0])) as writer: + topic1 = writer.add_connection('/topic1', 'std_msgs/msg/Int8') + topic2 = writer.add_connection('/topic2', 'std_msgs/msg/Int16') + writer.write(topic1, 1, HEADER + b'\x01') + writer.write(topic2, 2, HEADER + b'\x02\x00') + writer.write(topic1, 9, HEADER + b'\x09') + writer.write(topic1, 5, HEADER + b'\x05') + writer.write(topic2, 15, HEADER + b'\x15\x00') + + paths[1].mkdir() + (paths[1] / 'metadata.yaml').write_text(':') + + return paths + + +def test_anyreader1(bags1: Sequence[Path]) -> None: # pylint: disable=redefined-outer-name + """Test AnyReader on rosbag1.""" + # pylint: disable=too-many-statements + with pytest.raises(AnyReaderError, match='at least one'): + AnyReader([]) + + with pytest.raises(AnyReaderError, match='missing'): + AnyReader([bags1[0] / 'badname']) + + reader = AnyReader(bags1) + with pytest.raises(AssertionError): + assert reader.topics + + with pytest.raises(AssertionError): + next(reader.messages()) + + reader = AnyReader(bags1) + with pytest.raises(AnyReaderError, match='seems to be empty'): + reader.open() + assert all(not x.bio for x in reader.readers) + + with AnyReader(bags1[:3]) as reader: + assert reader.duration == 15 + assert reader.start_time == 1 + assert reader.end_time == 16 + assert reader.message_count == 5 + assert list(reader.topics.keys()) == ['/topic1', '/topic2'] + assert len(reader.topics['/topic1'].connections) == 2 + assert reader.topics['/topic1'].msgcount == 3 + assert len(reader.topics['/topic2'].connections) == 2 + assert reader.topics['/topic2'].msgcount == 2 + + gen = reader.messages() + + nxt = next(gen) + assert nxt[0].topic == '/topic1' + assert nxt[1:] == (1, b'\x01') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 1 # type: ignore + nxt = next(gen) + assert nxt[0].topic == '/topic2' + assert nxt[1:] == (2, b'\x02\x00') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 2 # type: ignore + nxt = next(gen) + assert nxt[0].topic == '/topic1' + assert nxt[1:] == (5, b'\x05') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 5 # type: ignore + nxt = next(gen) + assert nxt[0].topic == '/topic1' + assert nxt[1:] == (9, b'\x09') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 9 # type: ignore + nxt = next(gen) + assert nxt[0].topic == '/topic2' + assert nxt[1:] == (15, b'\x15\x00') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 21 # type: ignore + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(connections=reader.topics['/topic1'].connections) + nxt = next(gen) + assert nxt[0].topic == '/topic1' + nxt = next(gen) + assert nxt[0].topic == '/topic1' + nxt = next(gen) + assert nxt[0].topic == '/topic1' + with pytest.raises(StopIteration): + next(gen) + + +def test_anyreader2(bags2: list[Path]) -> None: # pylint: disable=redefined-outer-name + """Test AnyReader on rosbag2.""" + # pylint: disable=too-many-statements + with pytest.raises(AnyReaderError, match='multiple rosbag2'): + AnyReader(bags2) + + with pytest.raises(AnyReaderError, match='YAML'): + AnyReader([bags2[1]]) + + with AnyReader([bags2[0]]) as reader: + assert reader.duration == 15 + assert reader.start_time == 1 + assert reader.end_time == 16 + assert reader.message_count == 5 + assert list(reader.topics.keys()) == ['/topic1', '/topic2'] + assert len(reader.topics['/topic1'].connections) == 1 + assert reader.topics['/topic1'].msgcount == 3 + assert len(reader.topics['/topic2'].connections) == 1 + assert reader.topics['/topic2'].msgcount == 2 + + gen = reader.messages() + + nxt = next(gen) + assert nxt[0].topic == '/topic1' + assert nxt[1:] == (1, HEADER + b'\x01') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 1 # type: ignore + nxt = next(gen) + assert nxt[0].topic == '/topic2' + assert nxt[1:] == (2, HEADER + b'\x02\x00') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 2 # type: ignore + nxt = next(gen) + assert nxt[0].topic == '/topic1' + assert nxt[1:] == (5, HEADER + b'\x05') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 5 # type: ignore + nxt = next(gen) + assert nxt[0].topic == '/topic1' + assert nxt[1:] == (9, HEADER + b'\x09') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 9 # type: ignore + nxt = next(gen) + assert nxt[0].topic == '/topic2' + assert nxt[1:] == (15, HEADER + b'\x15\x00') + msg = reader.deserialize(nxt[2], nxt[0].msgtype) + assert msg.data == 21 # type: ignore + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(connections=reader.topics['/topic1'].connections) + nxt = next(gen) + assert nxt[0].topic == '/topic1' + nxt = next(gen) + assert nxt[0].topic == '/topic1' + nxt = next(gen) + assert nxt[0].topic == '/topic1' + with pytest.raises(StopIteration): + next(gen) From 34ffe9669257bed40bfc60c3e338719efeb6d294 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 21 Apr 2022 15:15:10 +0200 Subject: [PATCH 077/114] Convert connections attribute to list --- docs/examples/edit_rosbags_edit_timestamps.py | 2 +- docs/examples/edit_rosbags_remove_topic.py | 2 +- docs/examples/register_types_rosbag1.py | 2 +- docs/topics/rosbag1.rst | 6 +-- docs/topics/rosbag2.rst | 6 +-- src/rosbags/convert/converter.py | 8 ++-- src/rosbags/highlevel/anyreader.py | 4 +- src/rosbags/rosbag1/reader.py | 31 +++++++------- src/rosbags/rosbag1/writer.py | 10 ++--- src/rosbags/rosbag2/reader.py | 15 +++---- src/rosbags/rosbag2/writer.py | 12 +++--- tests/test_convert.py | 42 +++++++++---------- tests/test_reader.py | 4 +- tests/test_reader1.py | 2 +- 14 files changed, 72 insertions(+), 74 deletions(-) diff --git a/docs/examples/edit_rosbags_edit_timestamps.py b/docs/examples/edit_rosbags_edit_timestamps.py index 6de70aae..04af43db 100644 --- a/docs/examples/edit_rosbags_edit_timestamps.py +++ b/docs/examples/edit_rosbags_edit_timestamps.py @@ -23,7 +23,7 @@ def offset_timestamps(src: Path, dst: Path, offset: int) -> None: """ with Reader(src) as reader, Writer(dst) as writer: conn_map = {} - for conn in reader.connections.values(): + for conn in reader.connections: ext = cast(ConnectionExtRosbag2, conn.ext) conn_map[conn.id] = writer.add_connection( conn.topic, diff --git a/docs/examples/edit_rosbags_remove_topic.py b/docs/examples/edit_rosbags_remove_topic.py index aa00dabd..4824343c 100644 --- a/docs/examples/edit_rosbags_remove_topic.py +++ b/docs/examples/edit_rosbags_remove_topic.py @@ -22,7 +22,7 @@ def remove_topic(src: Path, dst: Path, topic: str) -> None: """ with Reader(src) as reader, Writer(dst) as writer: conn_map = {} - for conn in reader.connections.values(): + for conn in reader.connections: if conn.topic == topic: continue ext = cast(ConnectionExtRosbag2, conn.ext) diff --git a/docs/examples/register_types_rosbag1.py b/docs/examples/register_types_rosbag1.py index 0be64334..c183a014 100644 --- a/docs/examples/register_types_rosbag1.py +++ b/docs/examples/register_types_rosbag1.py @@ -20,7 +20,7 @@ def process_bag(src: Path) -> None: """ with Reader(src) as reader: typs = {} - for conn in reader.connections.values(): + for conn in reader.connections: typs.update(get_types_from_msg(conn.msgdef, conn.msgtype)) register_types(typs) diff --git a/docs/topics/rosbag1.rst b/docs/topics/rosbag1.rst index 0e9b7c94..73b0aa62 100644 --- a/docs/topics/rosbag1.rst +++ b/docs/topics/rosbag1.rst @@ -37,8 +37,8 @@ Instances of the :py:class:`Reader ` class are typically # create reader instance with Reader('/home/ros/rosbag_2020_03_24.bag') as reader: - # topic and msgtype information is available on .connections dictionary - for connection in reader.connections.values(): + # topic and msgtype information is available on .connections list + for connection in reader.connections: print(connection.topic, connection.msgtype) # iterate over messages @@ -48,7 +48,7 @@ Instances of the :py:class:`Reader ` class are typically print(msg.header.frame_id) # messages() accepts connection filters - connections = [x for x in reader.connections.values() if x.topic == '/imu_raw/Imu'] + connections = [x for x in reader.connections if x.topic == '/imu_raw/Imu'] for connection, timestamp, rawdata in reader.messages(connections=connections): msg = deserialize_cdr(ros1_to_cdr(rawdata, connection.msgtype), connection.msgtype) print(msg.header.frame_id) diff --git a/docs/topics/rosbag2.rst b/docs/topics/rosbag2.rst index 1f3fbe57..dbee4d06 100644 --- a/docs/topics/rosbag2.rst +++ b/docs/topics/rosbag2.rst @@ -52,8 +52,8 @@ Instances of the :py:class:`Reader ` class are used to r # create reader instance and open for reading with Reader('/home/ros/rosbag_2020_03_24') as reader: - # topic and msgtype information is available on .connections dict - for connection in reader.connections.values(): + # topic and msgtype information is available on .connections list + for connection in reader.connections: print(connection.topic, connection.msgtype) # iterate over messages @@ -63,7 +63,7 @@ Instances of the :py:class:`Reader ` class are used to r print(msg.header.frame_id) # messages() accepts connection filters - connections = [x for x in reader.connections.values() if x.topic == '/imu_raw/Imu'] + connections = [x for x in reader.connections if x.topic == '/imu_raw/Imu'] for connection, timestamp, rawdata in reader.messages(connections=connections): msg = deserialize_cdr(rawdata, connection.msgtype) print(msg.header.frame_id) diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index 093d2f03..339ad0ce 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -111,10 +111,10 @@ def convert_1to2(src: Path, dst: Path) -> None: typs: dict[str, Any] = {} connmap: dict[int, Connection] = {} - for rconn in reader.connections.values(): + for rconn in reader.connections: candidate = upgrade_connection(rconn) assert isinstance(candidate.ext, ConnectionExtRosbag2) - for conn in writer.connections.values(): + for conn in writer.connections: assert isinstance(conn.ext, ConnectionExtRosbag2) if ( conn.topic == candidate.topic and conn.msgtype == candidate.msgtype and @@ -147,10 +147,10 @@ def convert_2to1(src: Path, dst: Path) -> None: """ with Reader2(src) as reader, Writer1(dst) as writer: connmap: dict[int, Connection] = {} - for rconn in reader.connections.values(): + for rconn in reader.connections: candidate = downgrade_connection(rconn) assert isinstance(candidate.ext, ConnectionExtRosbag1) - for conn in writer.connections.values(): + for conn in writer.connections: assert isinstance(conn.ext, ConnectionExtRosbag1) if ( conn.topic == candidate.topic and conn.md5sum == candidate.md5sum and diff --git a/src/rosbags/highlevel/anyreader.py b/src/rosbags/highlevel/anyreader.py index 1a635a17..ba265b56 100644 --- a/src/rosbags/highlevel/anyreader.py +++ b/src/rosbags/highlevel/anyreader.py @@ -143,11 +143,11 @@ class AnyReader: typs: dict[str, Any] = {} for reader in self.readers: assert isinstance(reader, Reader1) - for connection in reader.connections.values(): + for connection in reader.connections: typs.update(get_types_from_msg(connection.msgdef, connection.msgtype)) register_types(typs, self.typestore) - self.connections = [y for x in self.readers for y in x.connections.values()] + self.connections = [y for x in self.readers for y in x.connections] self.isopen = True def close(self) -> None: diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 1f3058aa..483e731f 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -342,7 +342,7 @@ class Reader: raise ReaderError(f'File {str(self.path)!r} does not exist.') self.bio: Optional[BinaryIO] = None - self.connections: dict[int, Connection] = {} + self.connections: list[Connection] = [] self.indexes: dict[int, list[IndexData]] self.chunk_infos: list[ChunkInfo] = [] self.chunks: dict[int, Chunk] = {} @@ -384,7 +384,7 @@ class Reader: self.bio.seek(index_pos) try: - self.connections = dict(self.read_connection() for _ in range(conn_count)) + self.connections = [self.read_connection() for _ in range(conn_count)] self.chunk_infos = [self.read_chunk_info() for _ in range(chunk_count)] except ReaderError as err: raise ReaderError(f'Bag index looks damaged: {err.args}') from None @@ -402,14 +402,15 @@ class Reader: self.indexes = { cid: list(heapq.merge(*x, key=lambda x: x.time)) for cid, x in indexes.items() } - assert all(self.indexes[x] for x in self.connections) + assert all(self.indexes[x.id] for x in self.connections) - for cid, connection in self.connections.items(): - self.connections[cid] = Connection( - *connection[0:5], - len(self.indexes[cid]), - *connection[6:], - ) + self.connections = [ + Connection( + *x[0:5], + len(self.indexes[x.id]), + *x[6:], + ) for x in self.connections + ] except ReaderError: self.close() raise @@ -445,7 +446,7 @@ class Reader: """Topic information.""" topics = {} for topic, group in groupby( - sorted(self.connections.values(), key=lambda x: x.topic), + sorted(self.connections, key=lambda x: x.topic), key=lambda x: x.topic, ): connections = list(group) @@ -462,7 +463,7 @@ class Reader: ) return topics - def read_connection(self) -> tuple[int, Connection]: + def read_connection(self) -> Connection: """Read connection record from current position.""" assert self.bio header = Header.read(self.bio, RecordType.CONNECTION) @@ -477,7 +478,7 @@ class Reader: callerid = header.get_string('callerid') if 'callerid' in header else None latching = int(header.get_string('latching')) if 'latching' in header else None - return conn, Connection( + return Connection( conn, topic, normalize_msgtype(typ), @@ -593,7 +594,9 @@ class Reader: raise ReaderError('Rosbag is not open.') if not connections: - connections = self.connections.values() + connections = self.connections + + connmap = {x.id: x for x in self.connections} indexes = [self.indexes[x.id] for x in connections] for entry in heapq.merge(*indexes): @@ -624,7 +627,7 @@ class Reader: raise ReaderError('Expected to find message data.') data = read_bytes(chunk, read_uint32(chunk)) - connection = self.connections[header.get_uint32('conn')] + connection = connmap[header.get_uint32('conn')] assert entry.time == header.get_time('time') yield connection, entry.time, data diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index 8150b36d..d1ba14ed 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -159,7 +159,7 @@ class Writer: self.bio: Optional[BinaryIO] = None self.compressor: Callable[[bytes], bytes] = lambda x: x self.compression_format = 'none' - self.connections: dict[int, Connection] = {} + self.connections: list[Connection] = [] self.chunks: list[WriteChunk] = [ WriteChunk(BytesIO(), -1, 2**64, 0, defaultdict(list)), ] @@ -258,7 +258,7 @@ class Writer: self, ) - if any(x[1:] == connection[1:] for x in self.connections.values()): + if any(x[1:] == connection[1:] for x in self.connections): raise WriterError( f'Connections can only be added once with same arguments: {connection!r}.', ) @@ -266,7 +266,7 @@ class Writer: bio = self.chunks[-1].data self.write_connection(connection, bio) - self.connections[connection.id] = connection + self.connections.append(connection) return connection def write(self, connection: Connection, timestamp: int, data: bytes) -> None: @@ -284,7 +284,7 @@ class Writer: if not self.bio: raise WriterError('Bag was not opened.') - if connection not in self.connections.values(): + if connection not in self.connections: raise WriterError(f'There is no connection {connection!r}.') from None chunk = self.chunks[-1] @@ -367,7 +367,7 @@ class Writer: index_pos = self.bio.tell() - for connection in self.connections.values(): + for connection in self.connections: self.write_connection(connection, self.bio) for chunk in self.chunks: diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 6ac3e90a..0f261a99 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -136,8 +136,8 @@ class Reader: if missing := [x for x in self.paths if not x.exists()]: raise ReaderError(f'Some database files are missing: {[str(x) for x in missing]!r}') - self.connections = { - idx + 1: Connection( + self.connections = [ + Connection( id=idx + 1, topic=x['topic_metadata']['name'], msgtype=x['topic_metadata']['type'], @@ -150,9 +150,9 @@ class Reader: ), owner=self, ) for idx, x in enumerate(self.metadata['topics_with_message_count']) - } + ] noncdr = { - fmt for x in self.connections.values() if isinstance(x.ext, ConnectionExtRosbag2) + fmt for x in self.connections if isinstance(x.ext, ConnectionExtRosbag2) if (fmt := x.ext.serialization_format) != 'cdr' } if noncdr: @@ -209,10 +209,7 @@ class Reader: @property def topics(self) -> dict[str, TopicInfo]: """Topic information.""" - return { - x.topic: TopicInfo(x.msgtype, x.msgdef, x.msgcount, [x]) - for x in self.connections.values() - } + return {x.topic: TopicInfo(x.msgtype, x.msgdef, x.msgcount, [x]) for x in self.connections} def messages( # pylint: disable=too-many-locals self, @@ -278,7 +275,7 @@ class Reader: cur.execute('SELECT name,id FROM topics') connmap: dict[int, Connection] = { - row[1]: next((x for x in self.connections.values() if x.topic == row[0]), + row[1]: next((x for x in self.connections if x.topic == row[0]), None) # type: ignore for row in cur } diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 5c5becc0..6efaf197 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -81,7 +81,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.compression_mode = '' self.compression_format = '' self.compressor: Optional[zstandard.ZstdCompressor] = None - self.connections: dict[int, Connection] = {} + self.connections: list[Connection] = [] self.counts: dict[int, int] = {} self.conn: Optional[sqlite3.Connection] = None self.cursor: Optional[sqlite3.Cursor] = None @@ -152,7 +152,7 @@ class Writer: # pylint: disable=too-many-instance-attributes raise WriterError('Bag was not opened.') connection = Connection( - id=len(self.connections.values()) + 1, + id=len(self.connections) + 1, topic=topic, msgtype=msgtype, msgdef='', @@ -164,14 +164,14 @@ class Writer: # pylint: disable=too-many-instance-attributes ), owner=self, ) - for conn in self.connections.values(): + for conn in self.connections: if ( conn.topic == connection.topic and conn.msgtype == connection.msgtype and conn.ext == connection.ext ): raise WriterError(f'Connection can only be added once: {connection!r}.') - self.connections[connection.id] = connection + self.connections.append(connection) self.counts[connection.id] = 0 meta = (connection.id, topic, msgtype, serialization_format, offered_qos_profiles) self.cursor.execute('INSERT INTO topics VALUES(?, ?, ?, ?, ?)', meta) @@ -191,7 +191,7 @@ class Writer: # pylint: disable=too-many-instance-attributes """ if not self.cursor: raise WriterError('Bag was not opened.') - if connection not in self.connections.values(): + if connection not in self.connections: raise WriterError(f'Tried to write to unknown connection {connection!r}.') if self.compression_mode == 'message': @@ -252,7 +252,7 @@ class Writer: # pylint: disable=too-many-instance-attributes 'offered_qos_profiles': x.ext.offered_qos_profiles, }, 'message_count': self.counts[x.id], - } for x in self.connections.values() if isinstance(x.ext, ConnectionExtRosbag2) + } for x in self.connections if isinstance(x.ext, ConnectionExtRosbag2) ], 'compression_format': self.compression_format, 'compression_mode': self.compression_mode, diff --git a/tests/test_convert.py b/tests/test_convert.py index d4c69293..8efa2192 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -146,12 +146,12 @@ def test_convert_1to2(tmp_path: Path) -> None: Connection(3, '/other', 'typ', '', '', -1, ConnectionExtRosbag2('cdr', ''), None), ] - readerinst.connections = { - 1: connections[0], - 2: connections[1], - 3: connections[2], - 4: connections[3], - } + readerinst.connections = [ + connections[0], + connections[1], + connections[2], + connections[3], + ] readerinst.messages.return_value = [ (connections[0], 42, b'\x42'), @@ -160,14 +160,13 @@ def test_convert_1to2(tmp_path: Path) -> None: (connections[3], 45, b'\x45'), ] - writerinst.connections = {} + writerinst.connections = [] def add_connection(*_: Any) -> Connection: # noqa: ANN401 """Mock for Writer.add_connection.""" - writerinst.connections = { - conn.id: conn - for _, conn in zip(range(len(writerinst.connections) + 1), wconnections) - } + writerinst.connections = [ + conn for _, conn in zip(range(len(writerinst.connections) + 1), wconnections) + ] return wconnections[len(writerinst.connections) - 1] writerinst.add_connection.side_effect = add_connection @@ -311,12 +310,12 @@ def test_convert_2to1(tmp_path: Path) -> None: ), ] - readerinst.connections = { - 1: connections[0], - 2: connections[1], - 3: connections[2], - 4: connections[3], - } + readerinst.connections = [ + connections[0], + connections[1], + connections[2], + connections[3], + ] readerinst.messages.return_value = [ (connections[0], 42, b'\x42'), @@ -325,14 +324,13 @@ def test_convert_2to1(tmp_path: Path) -> None: (connections[3], 45, b'\x45'), ] - writerinst.connections = {} + writerinst.connections = [] def add_connection(*_: Any) -> Connection: # noqa: ANN401 """Mock for Writer.add_connection.""" - writerinst.connections = { - conn.id: conn - for _, conn in zip(range(len(writerinst.connections) + 1), wconnections) - } + writerinst.connections = [ + conn for _, conn in zip(range(len(writerinst.connections) + 1), wconnections) + ] return wconnections[len(writerinst.connections) - 1] writerinst.add_connection.side_effect = add_connection diff --git a/tests/test_reader.py b/tests/test_reader.py index 4f15f2ff..84ecddfd 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -126,7 +126,7 @@ def test_reader(bag: Path) -> None: assert reader.message_count == 4 if reader.compression_mode: assert reader.compression_format == 'zstd' - assert [*reader.connections.keys()] == [1, 2, 3] + assert [x.id for x in reader.connections] == [1, 2, 3] assert [*reader.topics.keys()] == ['/poly', '/magn', '/joint'] gen = reader.messages() @@ -154,7 +154,7 @@ def test_reader(bag: Path) -> None: def test_message_filters(bag: Path) -> None: """Test reader filters messages.""" with Reader(bag) as reader: - magn_connections = [x for x in reader.connections.values() if x.topic == '/magn'] + magn_connections = [x for x in reader.connections if x.topic == '/magn'] gen = reader.messages(connections=magn_connections) connection, _, _ = next(gen) assert connection.topic == '/magn' diff --git a/tests/test_reader1.py b/tests/test_reader1.py index 550f417e..2a9cbe1d 100644 --- a/tests/test_reader1.py +++ b/tests/test_reader1.py @@ -274,7 +274,7 @@ def test_reader(tmp_path: Path) -> None: # pylint: disable=too-many-statements assert msgs[0][2] == b'MSGCONTENT5' assert msgs[1][2] == b'MSGCONTENT10' - connections = [x for x in reader.connections.values() if x.topic == '/topic0'] + connections = [x for x in reader.connections if x.topic == '/topic0'] msgs = list(reader.messages(connections)) assert len(msgs) == 1 assert msgs[0][2] == b'MSGCONTENT10' From d196e8b74e4608cd07d0c6db23f723d948dd9eef Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 21 Apr 2022 15:58:09 +0200 Subject: [PATCH 078/114] Add support for rosbag2 version 5 metadata --- src/rosbags/rosbag2/reader.py | 15 ++++++++++++++- src/rosbags/rosbag2/writer.py | 14 +++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 0f261a99..dfc19a9b 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -44,6 +44,14 @@ if TYPE_CHECKING: message_count: int topic_metadata: TopicMetadata + class FileInformation(TypedDict): + """Per file metadata.""" + + path: str + starting_time: StartingTime + duration: Duration + message_count: int + class Metadata(TypedDict): """Rosbag2 metadata file.""" @@ -56,6 +64,7 @@ if TYPE_CHECKING: compression_format: str compression_mode: str topics_with_message_count: list[TopicWithMessageCount] + files: list[FileInformation] class ReaderError(Exception): @@ -99,6 +108,8 @@ class Reader: - Version 2: Changed field sizes in C++ implementation. - Version 3: Added compression. - Version 4: Added QoS metadata to topics, changed relative file paths + - Version 5: Added per file metadata + """ def __init__(self, path: Union[Path, str]): @@ -125,7 +136,7 @@ class Reader: try: self.metadata: Metadata = dct['rosbag2_bagfile_information'] - if (ver := self.metadata['version']) > 4: + if (ver := self.metadata['version']) > 5: raise ReaderError(f'Rosbag2 version {ver} not supported; please report issue.') if storageid := self.metadata['storage_identifier'] != 'sqlite3': raise ReaderError( @@ -160,6 +171,8 @@ class Reader: if self.compression_mode and (cfmt := self.compression_format) != 'zstd': raise ReaderError(f'Compression format {cfmt!r} is not supported.') + + self.files: list[FileInformation] = self.metadata.get('files', [])[:] except KeyError as exc: raise ReaderError(f'A metadata key is missing {exc!r}.') from None diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 6efaf197..6454562b 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -233,7 +233,7 @@ class Writer: # pylint: disable=too-many-instance-attributes metadata: dict[str, Metadata] = { 'rosbag2_bagfile_information': { - 'version': 4, + 'version': 5, 'storage_identifier': 'sqlite3', 'relative_file_paths': [self.dbpath.name], 'duration': { @@ -256,6 +256,18 @@ class Writer: # pylint: disable=too-many-instance-attributes ], 'compression_format': self.compression_format, 'compression_mode': self.compression_mode, + 'files': [ + { + 'path': self.dbpath.name, + 'starting_time': { + 'nanoseconds_since_epoch': start, + }, + 'duration': { + 'nanoseconds': duration, + }, + 'message_count': count, + }, + ], }, } with self.metapath.open('w') as metafile: From 926813bf173c8b26da5e2a12e60eb4099f6be092 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sat, 23 Apr 2022 11:21:47 +0200 Subject: [PATCH 079/114] Speed up index construction --- src/rosbags/rosbag1/reader.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 483e731f..657fa18e 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -4,7 +4,6 @@ from __future__ import annotations -import bisect import heapq import os import re @@ -348,7 +347,7 @@ class Reader: self.chunks: dict[int, Chunk] = {} self.current_chunk: tuple[int, BinaryIO] = (-1, BytesIO()) - def open(self) -> None: # pylint: disable=too-many-locals + def open(self) -> None: """Open rosbag and read metadata.""" try: self.bio = self.path.open('rb') @@ -390,18 +389,15 @@ class Reader: raise ReaderError(f'Bag index looks damaged: {err.args}') from None self.chunks = {} - indexes: dict[int, list[list[IndexData]]] = defaultdict(list) + indexes: dict[int, list[IndexData]] = defaultdict(list) for chunk_info in self.chunk_infos: self.bio.seek(chunk_info.pos) self.chunks[chunk_info.pos] = self.read_chunk() for _ in range(len(chunk_info.connection_counts)): - cid, index = self.read_index_data(chunk_info.pos) - indexes[cid].append(index) + self.read_index_data(chunk_info.pos, indexes) - self.indexes = { - cid: list(heapq.merge(*x, key=lambda x: x.time)) for cid, x in indexes.items() - } + self.indexes = {cid: sorted(x) for cid, x in indexes.items()} assert all(self.indexes[x.id] for x in self.connections) self.connections = [ @@ -538,14 +534,12 @@ class Reader: decompressor, ) - def read_index_data(self, pos: int) -> tuple[int, list[IndexData]]: + def read_index_data(self, pos: int, indexes: dict[int, list[IndexData]]) -> None: """Read index data from position. Args: pos: Seek position. - - Returns: - Connection id and list of index data. + indexes: Accumulated index data. Raises: ReaderError: Record unreadable. @@ -562,12 +556,11 @@ class Reader: self.bio.seek(4, os.SEEK_CUR) - index: list[IndexData] = [] + index = indexes[conn] for _ in range(count): time = deserialize_time(self.bio.read(8)) offset = read_uint32(self.bio) - bisect.insort(index, IndexData(time, pos, offset)) - return conn, index + index.append(IndexData(time, pos, offset)) def messages( self, From c2e676a01fb0d3f57e09adb83f27869075500cf0 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 24 Apr 2022 10:11:31 +0200 Subject: [PATCH 080/114] Speed up reading of index data records --- src/rosbags/rosbag1/reader.py | 78 +++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 657fa18e..0ee6c784 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -24,7 +24,20 @@ from rosbags.typesys.msg import normalize_msgtype if TYPE_CHECKING: from types import TracebackType - from typing import BinaryIO, Callable, Generator, Iterable, Literal, Optional, Type, Union + from typing import ( + BinaryIO, + Callable, + Generator, + Iterable, + Literal, + Optional, + Tuple, + Type, + Union, + ) + + Unpack = Callable[[bytes], Tuple[int]] + UnpackFrom = Callable[[bytes, int], Tuple[int]] class ReaderError(Exception): @@ -103,9 +116,9 @@ class IndexData(NamedTuple): return NotImplemented # pragma: no cover -deserialize_uint8: Callable[[bytes], tuple[int]] = struct.Struct(' int: @@ -158,7 +171,7 @@ class Header(Dict[str, Any]): """ try: - return deserialize_uint32(self[name])[0] + return deserialize_uint32(self[name], 0)[0] except (KeyError, struct.error) as err: raise ReaderError(f'Could not read uint32 field {name!r}.') from err @@ -243,7 +256,7 @@ class Header(Dict[str, Any]): length = len(binary) while pos < length: try: - size = deserialize_uint32(binary[pos:pos + 4])[0] + size = deserialize_uint32(binary, pos)[0] except struct.error as err: raise ReaderError('Header field size could not be read.') from err pos += 4 @@ -280,7 +293,7 @@ def read_uint32(src: BinaryIO) -> int: """ try: - return deserialize_uint32(src.read(4))[0] + return deserialize_uint32(src.read(4), 0)[0] except struct.error as err: raise ReaderError('Could not read uint32.') from err @@ -326,6 +339,8 @@ class Reader: """ + # pylint: disable=too-many-instance-attributes + def __init__(self, path: Union[str, Path]): """Initialize. @@ -343,6 +358,7 @@ class Reader: self.bio: Optional[BinaryIO] = None self.connections: list[Connection] = [] self.indexes: dict[int, list[IndexData]] + self.index_data_header_offsets: Optional[tuple[int, int]] = None self.chunk_infos: list[ChunkInfo] = [] self.chunks: dict[int, Chunk] = {} self.current_chunk: tuple[int, BinaryIO] = (-1, BytesIO()) @@ -537,6 +553,9 @@ class Reader: def read_index_data(self, pos: int, indexes: dict[int, list[IndexData]]) -> None: """Read index data from position. + The implementation purposely avoids the generic Header class and + its costly string processing. + Args: pos: Seek position. indexes: Accumulated index data. @@ -546,20 +565,45 @@ class Reader: """ assert self.bio - header = Header.read(self.bio, RecordType.IDXDATA) - ver = header.get_uint32('ver') - if ver != 1: - raise ReaderError(f'IDXDATA version {ver} is not supported.') - conn = header.get_uint32('conn') - count = header.get_uint32('count') + buf = self.bio.read(55) + if not self.index_data_header_offsets: + size, = deserialize_uint32(buf, 0) + assert size == 47 + idx = 4 + connpos = -1 + countpos = -1 + while idx < size: + char = buf[idx + 6] + if char == 61: # ord(b'=') + assert buf[idx + 7] == 4 + idx += 8 + elif char == 114: # ord(b'r') + if (ver := buf[idx + 8]) != 1: + raise ReaderError(f'IDXDATA version {ver} is not supported.') + idx += 12 + elif char == 110: # ord(b'n') + connpos = idx + 9 + idx += 13 + else: + assert char == 117 # ord(b'u') + countpos = idx + 10 + idx += 14 + self.index_data_header_offsets = (connpos, countpos) + connpos, countpos = self.index_data_header_offsets - self.bio.seek(4, os.SEEK_CUR) + conn, = deserialize_uint32(buf, connpos) + count, = deserialize_uint32(buf, countpos) + size, = deserialize_uint32(buf, 51) + assert size == count * 12 index = indexes[conn] - for _ in range(count): - time = deserialize_time(self.bio.read(8)) - offset = read_uint32(self.bio) + buf = self.bio.read(size) + idx = 0 + while idx < size: + time = deserialize_uint32(buf, idx)[0] * 10**9 + deserialize_uint32(buf, idx + 4)[0] + offset, = deserialize_uint32(buf, idx + 8) + idx += 12 index.append(IndexData(time, pos, offset)) def messages( From 9f3da0c2be974d9060c5337408f9d909fe0eb736 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 4 May 2022 17:40:50 +0200 Subject: [PATCH 081/114] Fix serialization of empty message sequences --- src/rosbags/serde/cdr.py | 6 ++++-- tests/test_serde.py | 23 +++++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index 894cc5fc..0ef393af 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -133,7 +133,8 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: lines.append(f' val = message.{fieldname}') if subdesc.args.size_cdr: if aligned < anext_before <= anext_after: - lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') + lines.append(' if len(val):') + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(' for _ in val:') if anext_before > anext_after: lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') @@ -144,7 +145,8 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: f' func = get_msgdef("{subdesc.args.name}", typestore).getsize_cdr', ) if aligned < anext_before <= anext_after: - lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') + lines.append(' if len(val):') + lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') lines.append(' for item in val:') if anext_before > anext_after: lines.append(f' pos = (pos + {anext_before} - 1) & -{anext_before}') diff --git a/tests/test_serde.py b/tests/test_serde.py index 276bcd6c..8142ac79 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -180,6 +180,11 @@ uint64[] su64 uint64 u64 """ +SMSG_U64 = """ +su64_u64[] seq +uint64 u64 +""" + @pytest.fixture() def _comparable() -> Generator[None, None, None]: @@ -457,16 +462,22 @@ def test_padding_empty_sequence() -> None: def test_align_after_empty_sequence() -> None: """Test alignment after empty sequences.""" register_types(dict(get_types_from_msg(SU64_U64, 'test_msgs/msg/su64_u64'))) + register_types(dict(get_types_from_msg(SMSG_U64, 'test_msgs/msg/smsg_u64'))) - su64_b = get_msgdef('test_msgs/msg/su64_u64', types).cls - msg = su64_b(numpy.array([], dtype=numpy.uint64), 42) + su64_u64 = get_msgdef('test_msgs/msg/su64_u64', types).cls + smsg_u64 = get_msgdef('test_msgs/msg/smsg_u64', types).cls + msg1 = su64_u64(numpy.array([], dtype=numpy.uint64), 42) + msg2 = smsg_u64([], 42) - cdr = serialize_cdr(msg, msg.__msgtype__) + cdr = serialize_cdr(msg1, msg1.__msgtype__) assert cdr[4:] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x2a\x00\x00\x00\x00\x00\x00\x00' + assert serialize_cdr(msg2, msg2.__msgtype__) == cdr - ros1 = cdr_to_ros1(cdr, msg.__msgtype__) + ros1 = cdr_to_ros1(cdr, msg1.__msgtype__) assert ros1 == b'\x00\x00\x00\x00\x2a\x00\x00\x00\x00\x00\x00\x00' + assert cdr_to_ros1(cdr, msg2.__msgtype__) == ros1 - assert ros1_to_cdr(ros1, msg.__msgtype__) == cdr + assert ros1_to_cdr(ros1, msg1.__msgtype__) == cdr - assert deserialize_cdr(cdr, msg.__msgtype__) == msg + assert deserialize_cdr(cdr, msg1.__msgtype__) == msg1 + assert deserialize_cdr(cdr, msg2.__msgtype__) == msg2 From a1afc15dfaa74087c1860ed91493a65a203cf4ee Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Sun, 24 Apr 2022 16:12:15 +0200 Subject: [PATCH 082/114] Release 0.9.10 --- CHANGES.rst | 16 ++++++++++++++++ setup.cfg | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index f968582d..387885d9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,22 @@ Changes ======= +0.9.10 - 2022-05-04 +------------------- +- Add support for multiple type stores +- Document which types are supported out of the box `#21`_ +- Unify Connection and TopicInfo objects across rosbag1 and rosbag2 +- Add experimental all-in-one reader for rosbag1, split rosbag1, and rosbag2 +- Convert reader and writer .connection attribute from dict to list +- Add support for rosbag2 version 5 metadata `#18`_ +- Speed up opening of rosbag1 files +- Fix serialization of empty message sequences `#23`_ + +.. _#18: https://gitlab.com/ternaris/rosbags/issues/18 +.. _#21: https://gitlab.com/ternaris/rosbags/issues/21 +.. _#23: https://gitlab.com/ternaris/rosbags/issues/23 + + 0.9.9 - 2022-01-10 ------------------ - Fix documentation code samples `#15`_ diff --git a/setup.cfg b/setup.cfg index 8aa10ecb..3924f7ed 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.9 +version = 0.9.10 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 8cb7a1348508adc6a385457e713f9470812d34a2 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Tue, 17 May 2022 23:54:10 +0200 Subject: [PATCH 083/114] Report start_time and end_time on empty bags --- src/rosbags/rosbag1/reader.py | 6 +++--- src/rosbags/rosbag2/reader.py | 7 ++++--- tests/test_reader.py | 31 +++++++++++++++++++++++++++++++ tests/test_reader1.py | 4 ++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 0ee6c784..6f190db4 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -436,17 +436,17 @@ class Reader: @property def duration(self) -> int: """Duration in nanoseconds between earliest and latest messages.""" - return self.end_time - self.start_time + return self.end_time - self.start_time if self.chunk_infos else 0 @property def start_time(self) -> int: """Timestamp in nanoseconds of the earliest message.""" - return min(x.start_time for x in self.chunk_infos) + return min(x.start_time for x in self.chunk_infos) if self.chunk_infos else 2**63 - 1 @property def end_time(self) -> int: """Timestamp in nanoseconds after the latest message.""" - return max(x.end_time for x in self.chunk_infos) + return max(x.end_time for x in self.chunk_infos) if self.chunk_infos else 0 @property def message_count(self) -> int: diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index dfc19a9b..a830724a 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -191,17 +191,18 @@ class Reader: def duration(self) -> int: """Duration in nanoseconds between earliest and latest messages.""" nsecs: int = self.metadata['duration']['nanoseconds'] - return nsecs + 1 + return nsecs + 1 if self.message_count else 0 @property def start_time(self) -> int: """Timestamp in nanoseconds of the earliest message.""" - return self.metadata['starting_time']['nanoseconds_since_epoch'] + nsecs: int = self.metadata['starting_time']['nanoseconds_since_epoch'] + return nsecs if self.message_count else 2**63 - 1 @property def end_time(self) -> int: """Timestamp in nanoseconds after the latest message.""" - return self.start_time + self.duration + return self.start_time + self.duration if self.message_count else 0 @property def message_count(self) -> int: diff --git a/tests/test_reader.py b/tests/test_reader.py index 84ecddfd..a574e39b 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -55,6 +55,22 @@ rosbag2_bagfile_information: compression_mode: {compression_mode} """ +METADATA_EMPTY = """ +rosbag2_bagfile_information: + version: 4 + storage_identifier: sqlite3 + relative_file_paths: + - db.db3 + duration: + nanoseconds: 0 + starting_time: + nanoseconds_since_epoch: 0 + message_count: 0 + topics_with_message_count: [] + compression_format: "" + compression_mode: "" +""" + @pytest.fixture(params=['none', 'file', 'message']) def bag(request: SubRequest, tmp_path: Path) -> Path: @@ -117,6 +133,21 @@ def bag(request: SubRequest, tmp_path: Path) -> Path: return tmp_path +def test_empty_bag(tmp_path: Path) -> None: + """Test bags with broken fs layout.""" + (tmp_path / 'metadata.yaml').write_text(METADATA_EMPTY) + dbpath = tmp_path / 'db.db3' + dbh = sqlite3.connect(dbpath) + dbh.executescript(Writer.SQLITE_SCHEMA) + + with Reader(tmp_path) as reader: + assert reader.message_count == 0 + assert reader.start_time == 2**63 - 1 + assert reader.end_time == 0 + assert reader.duration == 0 + assert not list(reader.messages()) + + def test_reader(bag: Path) -> None: """Test reader and deserializer on simple bag.""" with Reader(bag) as reader: diff --git a/tests/test_reader1.py b/tests/test_reader1.py index 2a9cbe1d..47f9cfdd 100644 --- a/tests/test_reader1.py +++ b/tests/test_reader1.py @@ -202,6 +202,10 @@ def test_reader(tmp_path: Path) -> None: # pylint: disable=too-many-statements write_bag(bag, create_default_header()) with Reader(bag) as reader: assert reader.message_count == 0 + assert reader.start_time == 2**63 - 1 + assert reader.end_time == 0 + assert reader.duration == 0 + assert not list(reader.messages()) # empty bag, explicit encryptor bag = tmp_path / 'test.bag' From a1fa5c7cfbe114929c549dc8964df50e57eed486 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Tue, 17 May 2022 23:57:30 +0200 Subject: [PATCH 084/114] Release 0.9.11 --- CHANGES.rst | 5 +++++ setup.cfg | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 387885d9..1341603a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,11 @@ Changes ======= +0.9.11 - 2022-05-17 +------------------- +- Report start_time and end_time on empty bags + + 0.9.10 - 2022-05-04 ------------------- - Add support for multiple type stores diff --git a/setup.cfg b/setup.cfg index 3924f7ed..4c2ea9cd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.10 +version = 0.9.11 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From abc1acfecbc6a0c01d8ff39bf79d5b1c43c66620 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 20 May 2022 11:35:48 +0200 Subject: [PATCH 085/114] Update gitlab CI config --- .gitlab-ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e9558ac..d102bd4e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,14 +11,15 @@ test: - venv/bin/python -m pip install -r requirements-dev.txt - venv/bin/python -m pip install -e .[dev] - venv/bin/python -m pytest - - venv/bin/python -m coverage xml - venv/bin/sphinx-build docs public coverage: '/\d+\%\s*$/' artifacts: paths: - public reports: - cobertura: coverage.xml + coverage_report: + coverage_format: cobertura + path: coverage.xml junit: report.xml From 0bf94a6238a7d15e648ceec1bc6d871f9a663d0b Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 25 May 2022 15:39:47 +0200 Subject: [PATCH 086/114] Add typename guessing to examples --- docs/examples/register_types_files.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/examples/register_types_files.py b/docs/examples/register_types_files.py index 3cf1fd57..74eae068 100644 --- a/docs/examples/register_types_files.py +++ b/docs/examples/register_types_files.py @@ -4,21 +4,32 @@ from pathlib import Path from rosbags.typesys import get_types_from_msg, register_types + +def guess_msgtype(path: Path) -> str: + """Guess message type name from path.""" + name = path.relative_to(path.parents[2]).with_suffix('') + if 'msg' not in name.parts: + name = name.parent / 'msg' / name.name + return str(name) + + add_types = {} -msgdef = Path('/path/to/custom_msgs/msg/Speed.msg').read_text(encoding='utf-8') -add_types.update(get_types_from_msg(msgdef, 'custom_msgs/msg/Speed.msg')) - -msgdef = Path('/path/to/custom_msgs/msg/Accel.msg').read_text(encoding='utf-8') -add_types.update(get_types_from_msg(msgdef, 'custom_msgs/msg/Accel.msg')) +for pathstr in [ + '/path/to/custom_msgs/msg/Speed.msg', + '/path/to/custom_msgs/msg/Accel.msg', +]: + msgpath = Path(pathstr) + msgdef = msgpath.read_text(encoding='utf-8') + add_types.update(get_types_from_msg(msgdef, guess_msgtype(msgpath))) register_types(add_types) # Type import works only after the register_types call, -# the classname is derived from the msgtype names above +# the classname is derived from the msgtype names above. # pylint: disable=no-name-in-module,wrong-import-position -from rosbags.typesys.types import custom_msgs__msg__Speed as Speed # type: ignore # noqa from rosbags.typesys.types import custom_msgs__msg__Accel as Accel # type: ignore # noqa +from rosbags.typesys.types import custom_msgs__msg__Speed as Speed # type: ignore # noqa # pylint: enable=no-name-in-module,wrong-import-position From 501f97243c5030251e293107a5c473a7e7640b58 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 27 Jul 2022 14:30:36 +0200 Subject: [PATCH 087/114] Update python package configuration --- pyproject.toml | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 116 +----------------------------------------------- 2 files changed, 119 insertions(+), 114 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9594ad0c..1494a21e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,120 @@ [build-system] requires = ["setuptools>=56.2.0", "wheel"] build-backend = "setuptools.build_meta" + + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "if TYPE_CHECKING:", + "if __name__ == '__main__':", +] + +[tool.coverage.run] +source = [ + "src", +] + +[tool.flake8] +avoid_escape = false +docstring_convention = "all" +docstring_style = "google" +extend_exclude = ["venv*", ".venv*"] +ignore = [ + # do not require annotation of `self` + "ANN101", + # do not apply to google convention + "D203", + "D213", + "D215", + "D406", + "D407", + "D408", + "D409", + # handled by B001 + "E722", + # allow line break after binary operator + "W504", +] +max_line_length = 100 +strictness = "long" +suppress_none_returning = true + +[tool.isort] +include_trailing_comma = true +line_length = 100 +multi_line_output = 3 + +[tool.mypy] +explicit_package_bases = true +mypy_path = "$MYPY_CONFIG_FILE_DIR/src" +namespace_packages = true +strict = true + +[[tool.mypy.overrides]] +module = "lz4.frame" +ignore_missing_imports = true + +[tool.pydocstyle] +convention = "google" +add_select = ["D204", "D400", "D401", "D404", "D413"] + +[tool.pylint.'MESSAGES CONTROL'] +enable = "all" +disable = [ + "duplicate-code", + "locally-disabled", + "suppressed-message", + "ungrouped-imports", + # isort (pylint FAQ) + "wrong-import-order", + # mccabe (pylint FAQ) + "too-many-branches", + # fixme + "fixme", + # pep8-naming (pylint FAQ", keep: invalid-name) + "bad-classmethod-argument", + "bad-mcs-classmethod-argument", + "no-self-argument", + # pycodestyle (pylint FAQ) + "bad-indentation", + "bare-except", + "line-too-long", + "missing-final-newline", + "multiple-statements", + "trailing-whitespace", + "unnecessary-semicolon", + "unneeded-not", + # pydocstyle (pylint FAQ) + "missing-class-docstring", + "missing-function-docstring", + "missing-module-docstring", + # pyflakes (pylint FAQ) + "undefined-variable", + "unused-import", + "unused-variable", +] + +[tool.pytest.ini_options] +addopts = [ + "-v", + "--flake8", + "--mypy", + "--pylint", + "--yapf", + "--cov", + "--cov-branch", + "--cov-report=html", + "--cov-report=term", + "--cov-report=xml", + "--no-cov-on-fail", + "--junitxml=report.xml", +] +junit_family = "xunit2" + +[tool.yapf] +based_on_style = "google" +column_limit = 100 +allow_split_before_dict_value = false +dedent_closing_brackets = true +indent_dictionary_value = false diff --git a/setup.cfg b/setup.cfg index 4c2ea9cd..7324b291 100644 --- a/setup.cfg +++ b/setup.cfg @@ -46,9 +46,6 @@ packages = find_namespace: zip_safe = false python_requires = >=3.8.2 -setup_requires = - setuptools >=40.8.0 - wheel install_requires = lz4 numpy @@ -72,12 +69,14 @@ dev = flake8-isort flake8-mutable flake8-print + flake8-pyprojecttoml flake8-pytest-style flake8-quotes flake8-return flake8-simplify flake8-type-checking flake8-use-fstring + mypy pep8-naming pytest pytest-cov @@ -98,114 +97,3 @@ where = src [sdist] formats = gztar, zip - -[coverage:report] -exclude_lines = - pragma: no cover - if TYPE_CHECKING: - if __name__ == '__main__': - -[flake8] -avoid-escape = False -docstring_convention = google -docstring_style = google -extend-exclude = venv*,.venv* -extend-select = - # docstrings - D204, - D400, - D401, - D404, - D413, -ignore = - # do not require annotation of `self` - ANN101, - # handled by B001 - E722, - # allow line break after binary operator - W504, -max-line-length = 100 -strictness = long -suppress-none-returning = True - -[isort] -include_trailing_comma = True -line_length = 100 -multi_line_output = 3 - -[mypy] -explicit_package_bases = True -mypy_path = $MYPY_CONFIG_FILE_DIR/src -namespace_packages = True -strict = True - -[mypy-lz4.frame] -ignore_missing_imports = True - -[mypy-ruamel.yaml] -implicit_reexport = True - -[pydocstyle] -convention = google -add-select = D204,D400,D401,D404,D413 - -[pylint.FORMAT] -max-line-length = 100 - -[pylint.'MESSAGES CONTROL'] -enable = all -disable = - duplicate-code, - locally-disabled, - suppressed-message, - ungrouped-imports, - # isort (pylint FAQ) - wrong-import-order, - # mccabe (pylint FAQ) - too-many-branches, - # fixme - fixme, - # pep8-naming (pylint FAQ, keep: invalid-name) - bad-classmethod-argument, - bad-mcs-classmethod-argument, - no-self-argument, - # pycodestyle (pylint FAQ) - bad-indentation, - bare-except, - line-too-long, - missing-final-newline, - multiple-statements, - trailing-whitespace, - unnecessary-semicolon, - unneeded-not, - # pydocstyle (pylint FAQ) - missing-class-docstring, - missing-function-docstring, - missing-module-docstring, - # pyflakes (pylint FAQ) - undefined-variable, - unused-import, - unused-variable, - -[yapf] -based_on_style = google -column_limit = 100 -allow_split_before_dict_value = false -dedent_closing_brackets = true -indent_dictionary_value = false - -[tool:pytest] -addopts = - -v - --flake8 - --mypy - --pylint - --yapf - --cov=src - --cov-branch - --cov-report=html - --cov-report=term - --cov-report=xml - --no-cov-on-fail - --junitxml=report.xml -junit_family=xunit2 From 05df51aa96b4d9eea2d55565e0e20ef0f4adedbc Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 27 Jul 2022 14:31:16 +0200 Subject: [PATCH 088/114] Add badge with supported python versions --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index d0248b6b..e8985f45 100644 --- a/README.rst +++ b/README.rst @@ -6,6 +6,8 @@ :target: https://gitlab.com/ternaris/rosbags/-/commits/master :alt: coverage report +.. image:: https://img.shields.io/pypi/pyversions/rosbags + :alt: python versions ======= Rosbags From b9fd0b014b32982717ce58830fa5aac0eccd8878 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 27 Jul 2022 15:39:47 +0200 Subject: [PATCH 089/114] Add topic filters to rosbag conversion --- src/rosbags/convert/__main__.py | 9 ++++++++- src/rosbags/convert/converter.py | 34 ++++++++++++++++++++++---------- tests/test_convert.py | 28 ++++++++++++++++++++------ 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/rosbags/convert/__main__.py b/src/rosbags/convert/__main__.py index b6f14159..5fe15bd8 100644 --- a/src/rosbags/convert/__main__.py +++ b/src/rosbags/convert/__main__.py @@ -50,6 +50,13 @@ def main() -> None: type=pathtype(exists=False), help='destination path for converted rosbag', ) + parser.add_argument( + '--exclude-topic', + action='append', + default=[], + dest='exclude_topics', + help='exclude topic by name', + ) args = parser.parse_args() if args.dst is not None and (args.src.suffix == '.bag') == (args.dst.suffix == '.bag'): @@ -57,7 +64,7 @@ def main() -> None: sys.exit(1) try: - convert(args.src, args.dst) + convert(**args.__dict__) except ConverterError as err: print(f'ERROR: {err}') # noqa: T001 sys.exit(1) diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index 339ad0ce..4667541a 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -21,7 +21,7 @@ from rosbags.typesys.msg import generate_msgdef if TYPE_CHECKING: from pathlib import Path - from typing import Any, Optional + from typing import Any, Optional, Sequence LATCH = """ - history: 3 @@ -99,19 +99,25 @@ def downgrade_connection(rconn: Connection) -> Connection: ) -def convert_1to2(src: Path, dst: Path) -> None: +def convert_1to2(src: Path, dst: Path, exclude_topics: Sequence[str]) -> None: """Convert Rosbag1 to Rosbag2. Args: src: Rosbag1 path. dst: Rosbag2 path. + exclude_topics: Topics to skip. + + Raises: + ConverterError: If all connections are excluded. """ with Reader1(src) as reader, Writer2(dst) as writer: typs: dict[str, Any] = {} connmap: dict[int, Connection] = {} - - for rconn in reader.connections: + connections = [x for x in reader.connections if x.topic not in exclude_topics] + if not connections: + raise ConverterError('No connections left for conversion.') + for rconn in connections: candidate = upgrade_connection(rconn) assert isinstance(candidate.ext, ConnectionExtRosbag2) for conn in writer.connections: @@ -132,22 +138,29 @@ def convert_1to2(src: Path, dst: Path) -> None: typs.update(get_types_from_msg(rconn.msgdef, rconn.msgtype)) register_types(typs) - for rconn, timestamp, data in reader.messages(): + for rconn, timestamp, data in reader.messages(connections=connections): data = ros1_to_cdr(data, rconn.msgtype) writer.write(connmap[rconn.id], timestamp, data) -def convert_2to1(src: Path, dst: Path) -> None: +def convert_2to1(src: Path, dst: Path, exclude_topics: Sequence[str]) -> None: """Convert Rosbag2 to Rosbag1. Args: src: Rosbag2 path. dst: Rosbag1 path. + exclude_topics: Topics to skip. + + Raises: + ConverterError: If all connections are excluded. """ with Reader2(src) as reader, Writer1(dst) as writer: connmap: dict[int, Connection] = {} - for rconn in reader.connections: + connections = [x for x in reader.connections if x.topic not in exclude_topics] + if not connections: + raise ConverterError('No connections left for conversion.') + for rconn in connections: candidate = downgrade_connection(rconn) assert isinstance(candidate.ext, ConnectionExtRosbag1) for conn in writer.connections: @@ -168,17 +181,18 @@ def convert_2to1(src: Path, dst: Path) -> None: ) connmap[rconn.id] = conn - for rconn, timestamp, data in reader.messages(): + for rconn, timestamp, data in reader.messages(connections=connections): data = cdr_to_ros1(data, rconn.msgtype) writer.write(connmap[rconn.id], timestamp, data) -def convert(src: Path, dst: Optional[Path]) -> None: +def convert(src: Path, dst: Optional[Path], exclude_topics: Sequence[str] = ()) -> None: """Convert between Rosbag1 and Rosbag2. Args: src: Source rosbag. dst: Destination rosbag. + exclude_topics: Topics to skip. Raises: ConverterError: An error occured during reading, writing, or @@ -192,7 +206,7 @@ def convert(src: Path, dst: Optional[Path]) -> None: func = convert_1to2 if upgrade else convert_2to1 try: - func(src, dst) + func(src, dst, exclude_topics) except (ReaderError1, ReaderError2) as err: raise ConverterError(f'Reading source bag: {err}') from err except (WriterError1, WriterError2) as err: diff --git a/tests/test_convert.py b/tests/test_convert.py index 8efa2192..77bb5fce 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -42,7 +42,7 @@ def test_cliwrapper(tmp_path: Path) -> None: with patch('rosbags.convert.__main__.convert') as cvrt, \ patch.object(sys, 'argv', ['cvt', str(tmp_path / 'ros1.bag')]): main() - cvrt.assert_called_with(tmp_path / 'ros1.bag', None) + cvrt.assert_called_with(src=tmp_path / 'ros1.bag', dst=None, exclude_topics=[]) with patch('rosbags.convert.__main__.convert') as cvrt, \ patch.object(sys, 'argv', ['cvt', @@ -68,7 +68,7 @@ def test_cliwrapper(tmp_path: Path) -> None: '--dst', str(tmp_path / 'target')]): main() - cvrt.assert_called_with(tmp_path / 'ros1.bag', tmp_path / 'target') + cvrt.assert_called_with(src=tmp_path / 'ros1.bag', dst=tmp_path / 'target', exclude_topics=[]) with patch.object(sys, 'argv', ['cvt', str(tmp_path / 'ros1.bag')]), \ patch('builtins.print') as mock_print, \ @@ -80,7 +80,7 @@ def test_cliwrapper(tmp_path: Path) -> None: with patch('rosbags.convert.__main__.convert') as cvrt, \ patch.object(sys, 'argv', ['cvt', str(tmp_path / 'subdir')]): main() - cvrt.assert_called_with(tmp_path / 'subdir', None) + cvrt.assert_called_with(src=tmp_path / 'subdir', dst=None, exclude_topics=[]) with patch('rosbags.convert.__main__.convert') as cvrt, \ patch.object(sys, 'argv', ['cvt', @@ -97,7 +97,7 @@ def test_cliwrapper(tmp_path: Path) -> None: '--dst', str(tmp_path / 'target.bag')]): main() - cvrt.assert_called_with(tmp_path / 'subdir', tmp_path / 'target.bag') + cvrt.assert_called_with(src=tmp_path / 'subdir', dst=tmp_path / 'target.bag', exclude_topics=[]) with patch.object(sys, 'argv', ['cvt', str(tmp_path / 'subdir')]), \ patch('builtins.print') as mock_print, \ @@ -106,6 +106,14 @@ def test_cliwrapper(tmp_path: Path) -> None: main() mock_print.assert_called_with('ERROR: exc') + with patch('rosbags.convert.__main__.convert') as cvrt, \ + patch.object(sys, 'argv', ['cvt', + str(tmp_path / 'ros1.bag'), + '--exclude-topic', + '/foo']): + main() + cvrt.assert_called_with(src=tmp_path / 'ros1.bag', dst=None, exclude_topics=['/foo']) + def test_convert_1to2(tmp_path: Path) -> None: """Test conversion from rosbag1 to rosbag2.""" @@ -176,7 +184,7 @@ def test_convert_1to2(tmp_path: Path) -> None: convert(Path('foo.bag'), None) reader.assert_called_with(Path('foo.bag')) - readerinst.messages.assert_called_with() + readerinst.messages.assert_called_with(connections=readerinst.connections) writer.assert_called_with(Path('foo')) writerinst.add_connection.assert_has_calls( @@ -205,6 +213,9 @@ def test_convert_1to2(tmp_path: Path) -> None: ], ) + with pytest.raises(ConverterError, match='No connections left for conversion'): + convert(Path('foo.bag'), None, ['/topic', '/other']) + writerinst.connections.clear() ros1_to_cdr.side_effect = KeyError('exc') with pytest.raises(ConverterError, match='Converting rosbag: .*exc'): @@ -340,7 +351,9 @@ def test_convert_2to1(tmp_path: Path) -> None: convert(Path('foo'), None) reader.assert_called_with(Path('foo')) - reader.return_value.__enter__.return_value.messages.assert_called_with() + reader.return_value.__enter__.return_value.messages.assert_called_with( + connections=readerinst.connections, + ) writer.assert_called_with(Path('foo.bag')) writer.return_value.__enter__.return_value.add_connection.assert_has_calls( @@ -389,6 +402,9 @@ def test_convert_2to1(tmp_path: Path) -> None: ], ) + with pytest.raises(ConverterError, match='No connections left for conversion'): + convert(Path('foobag'), None, ['/topic', '/other']) + writerinst.connections.clear() cdr_to_ros1.side_effect = KeyError('exc') with pytest.raises(ConverterError, match='Converting rosbag: .*exc'): From ff24d7e424c6324e5406160e6d2ded690de424b5 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 27 Jul 2022 16:03:22 +0200 Subject: [PATCH 090/114] Move metadata to dedicated module --- src/rosbags/rosbag2/metadata.py | 59 +++++++++++++++++++++++++++++++++ src/rosbags/rosbag2/reader.py | 48 ++------------------------- src/rosbags/rosbag2/writer.py | 2 +- 3 files changed, 62 insertions(+), 47 deletions(-) create mode 100644 src/rosbags/rosbag2/metadata.py diff --git a/src/rosbags/rosbag2/metadata.py b/src/rosbags/rosbag2/metadata.py new file mode 100644 index 00000000..004bb436 --- /dev/null +++ b/src/rosbags/rosbag2/metadata.py @@ -0,0 +1,59 @@ +# Copyright 2020-2022 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag2 metadata.""" + +from __future__ import annotations + +from typing import TypedDict + + +class StartingTime(TypedDict): + """Bag starting time.""" + + nanoseconds_since_epoch: int + + +class Duration(TypedDict): + """Bag starting time.""" + + nanoseconds: int + + +class TopicMetadata(TypedDict): + """Topic metadata.""" + + name: str + type: str + serialization_format: str + offered_qos_profiles: str + + +class TopicWithMessageCount(TypedDict): + """Topic with message count.""" + + message_count: int + topic_metadata: TopicMetadata + + +class FileInformation(TypedDict): + """Per file metadata.""" + + path: str + starting_time: StartingTime + duration: Duration + message_count: int + + +class Metadata(TypedDict): + """Rosbag2 metadata file.""" + + version: int + storage_identifier: str + relative_file_paths: list[str] + starting_time: StartingTime + duration: Duration + message_count: int + compression_format: str + compression_mode: str + topics_with_message_count: list[TopicWithMessageCount] + files: list[FileInformation] diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index a830724a..e69360c1 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -18,53 +18,9 @@ from rosbags.interfaces import Connection, ConnectionExtRosbag2, TopicInfo if TYPE_CHECKING: from types import TracebackType - from typing import Any, Generator, Iterable, Literal, Optional, Type, TypedDict, Union + from typing import Any, Generator, Iterable, Literal, Optional, Type, Union - class StartingTime(TypedDict): - """Bag starting time.""" - - nanoseconds_since_epoch: int - - class Duration(TypedDict): - """Bag starting time.""" - - nanoseconds: int - - class TopicMetadata(TypedDict): - """Topic metadata.""" - - name: str - type: str - serialization_format: str - offered_qos_profiles: str - - class TopicWithMessageCount(TypedDict): - """Topic with message count.""" - - message_count: int - topic_metadata: TopicMetadata - - class FileInformation(TypedDict): - """Per file metadata.""" - - path: str - starting_time: StartingTime - duration: Duration - message_count: int - - class Metadata(TypedDict): - """Rosbag2 metadata file.""" - - version: int - storage_identifier: str - relative_file_paths: list[str] - starting_time: StartingTime - duration: Duration - message_count: int - compression_format: str - compression_mode: str - topics_with_message_count: list[TopicWithMessageCount] - files: list[FileInformation] + from .metadata import FileInformation, Metadata class ReaderError(Exception): diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 6454562b..79b6286e 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: from types import TracebackType from typing import Any, Literal, Optional, Type, Union - from .reader import Metadata + from .metadata import Metadata class WriterError(Exception): From 5257497a6a69cedf0fa3d0a641f01df876a20215 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 27 Jul 2022 16:14:08 +0200 Subject: [PATCH 091/114] Add support for rosbag version 6 metadata --- src/rosbags/rosbag2/metadata.py | 1 + src/rosbags/rosbag2/reader.py | 4 +++- src/rosbags/rosbag2/writer.py | 23 ++++++++++++++++++++--- tests/test_reader.py | 14 +++++++++++++- tests/test_writer.py | 9 +++++++++ 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/rosbags/rosbag2/metadata.py b/src/rosbags/rosbag2/metadata.py index 004bb436..e4e169e6 100644 --- a/src/rosbags/rosbag2/metadata.py +++ b/src/rosbags/rosbag2/metadata.py @@ -57,3 +57,4 @@ class Metadata(TypedDict): compression_mode: str topics_with_message_count: list[TopicWithMessageCount] files: list[FileInformation] + custom_data: dict[str, str] diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index e69360c1..66b37fe3 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -65,6 +65,7 @@ class Reader: - Version 3: Added compression. - Version 4: Added QoS metadata to topics, changed relative file paths - Version 5: Added per file metadata + - Version 6: Added custom_data dict to metadata """ @@ -92,7 +93,7 @@ class Reader: try: self.metadata: Metadata = dct['rosbag2_bagfile_information'] - if (ver := self.metadata['version']) > 5: + if (ver := self.metadata['version']) > 6: raise ReaderError(f'Rosbag2 version {ver} not supported; please report issue.') if storageid := self.metadata['storage_identifier'] != 'sqlite3': raise ReaderError( @@ -129,6 +130,7 @@ class Reader: raise ReaderError(f'Compression format {cfmt!r} is not supported.') self.files: list[FileInformation] = self.metadata.get('files', [])[:] + self.custom_data: dict[str, str] = self.metadata.get('custom_data', {}) except KeyError as exc: raise ReaderError(f'A metadata key is missing {exc!r}.') from None diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index 79b6286e..ae93b629 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -85,6 +85,7 @@ class Writer: # pylint: disable=too-many-instance-attributes self.counts: dict[int, int] = {} self.conn: Optional[sqlite3.Connection] = None self.cursor: Optional[sqlite3.Cursor] = None + self.custom_data: dict[str, str] = {} def set_compression(self, mode: CompressionMode, fmt: CompressionFormat) -> None: """Enable compression on bag. @@ -92,8 +93,8 @@ class Writer: # pylint: disable=too-many-instance-attributes This function has to be called before opening. Args: - mode: Compression mode to use, either 'file' or 'message' - fmt: Compressor to use, currently only 'zstd' + mode: Compression mode to use, either 'file' or 'message'. + fmt: Compressor to use, currently only 'zstd'. Raises: WriterError: Bag already open. @@ -107,6 +108,21 @@ class Writer: # pylint: disable=too-many-instance-attributes self.compression_format = fmt.name.lower() self.compressor = zstandard.ZstdCompressor() + def set_custom_data(self, key: str, value: str) -> None: + """Set key value pair in custom_data. + + Args: + key: Key to set. + value: Value to set. + + Raises: + WriterError: If value has incorrect type. + + """ + if not isinstance(value, str): + raise WriterError(f'Cannot set non-string value {value!r} in custom_data.') + self.custom_data[key] = value + def open(self) -> None: """Open rosbag2 for writing. @@ -233,7 +249,7 @@ class Writer: # pylint: disable=too-many-instance-attributes metadata: dict[str, Metadata] = { 'rosbag2_bagfile_information': { - 'version': 5, + 'version': 6, 'storage_identifier': 'sqlite3', 'relative_file_paths': [self.dbpath.name], 'duration': { @@ -268,6 +284,7 @@ class Writer: # pylint: disable=too-many-instance-attributes 'message_count': count, }, ], + 'custom_data': self.custom_data, }, } with self.metapath.open('w') as metafile: diff --git a/tests/test_reader.py b/tests/test_reader.py index a574e39b..b0dcca30 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -57,7 +57,7 @@ rosbag2_bagfile_information: METADATA_EMPTY = """ rosbag2_bagfile_information: - version: 4 + version: 6 storage_identifier: sqlite3 relative_file_paths: - db.db3 @@ -69,6 +69,16 @@ rosbag2_bagfile_information: topics_with_message_count: [] compression_format: "" compression_mode: "" + files: + - duration: + nanoseconds: 0 + message_count: 0 + path: db.db3 + starting_time: + nanoseconds_since_epoch: 0 + custom_data: + key1: value1 + key2: value2 """ @@ -146,6 +156,8 @@ def test_empty_bag(tmp_path: Path) -> None: assert reader.end_time == 0 assert reader.duration == 0 assert not list(reader.messages()) + assert reader.custom_data['key1'] == 'value1' + assert reader.custom_data['key2'] == 'value2' def test_reader(bag: Path) -> None: diff --git a/tests/test_writer.py b/tests/test_writer.py index b4efa1e8..6f39edc3 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -59,6 +59,15 @@ def test_writer(tmp_path: Path) -> None: assert (path / 'compress_message.db3').exists() assert size > (path / 'compress_message.db3').stat().st_size + path = (tmp_path / 'with_custom_data') + bag = Writer(path) + bag.open() + bag.set_custom_data('key1', 'value1') + with pytest.raises(WriterError, match='non-string value'): + bag.set_custom_data('key1', 42) # type: ignore + bag.close() + assert b'key1: value1' in (path / 'metadata.yaml').read_bytes() + def test_failure_cases(tmp_path: Path) -> None: """Test writer failure cases.""" From 8333cfb9712c9d83bf1abb42c7eefb06bbbc4720 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 27 Jul 2022 16:39:26 +0200 Subject: [PATCH 092/114] Update code for current linters --- src/rosbags/convert/__main__.py | 4 ++-- src/rosbags/typesys/idl.py | 2 -- src/rosbags/typesys/msg.py | 2 -- tests/test_serde.py | 2 +- tools/bench/bench.py | 8 ++++---- tools/compare/compare.py | 8 ++++---- 6 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/rosbags/convert/__main__.py b/src/rosbags/convert/__main__.py index 5fe15bd8..dcd09ce2 100644 --- a/src/rosbags/convert/__main__.py +++ b/src/rosbags/convert/__main__.py @@ -60,13 +60,13 @@ def main() -> None: args = parser.parse_args() if args.dst is not None and (args.src.suffix == '.bag') == (args.dst.suffix == '.bag'): - print('Source and destination rosbag versions must differ.') # noqa: T001 + print('Source and destination rosbag versions must differ.') # noqa: T201 sys.exit(1) try: convert(**args.__dict__) except ConverterError as err: - print(f'ERROR: {err}') # noqa: T001 + print(f'ERROR: {err}') # noqa: T201 sys.exit(1) diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index 28cfcd85..84522269 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -254,8 +254,6 @@ string_literal class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods """IDL file visitor.""" - # pylint: disable=no-self-use - RULES = parse_grammar(GRAMMAR_IDL) def __init__(self) -> None: diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 3b9a110f..0ba942b4 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -205,8 +205,6 @@ def denormalize_msgtype(typename: str) -> str: class VisitorMSG(Visitor): """MSG file visitor.""" - # pylint: disable=no-self-use - RULES = parse_grammar(GRAMMAR_MSG) BASETYPES = { diff --git a/tests/test_serde.py b/tests/test_serde.py index 8142ac79..586e3573 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -254,7 +254,7 @@ def test_deserializer() -> None: assert msg.header.frame_id == 'foo42' field = msg.magnetic_field assert (field.x, field.y, field.z) == (128., 128., 128.) - diag = numpy.diag(msg.magnetic_field_covariance.reshape(3, 3)) # type: ignore + diag = numpy.diag(msg.magnetic_field_covariance.reshape(3, 3)) assert (diag == [1., 1., 1.]).all() msg_big = deserialize_cdr(*MSG_MAGN_BIG[:2]) diff --git a/tools/bench/bench.py b/tools/bench/bench.py index b8e70b17..8cc84606 100644 --- a/tools/bench/bench.py +++ b/tools/bench/bench.py @@ -133,16 +133,16 @@ def main() -> None: """Benchmark rosbag2 against rosbag2_py.""" path = Path(sys.argv[1]) try: - print('Comparing messages from rosbag2 and rosbag2_py.') # noqa: T001 + print('Comparing messages from rosbag2 and rosbag2_py.') # noqa: T201 compare(path) except AssertionError as err: - print(f'Comparison failed {err!r}') # noqa: T001 + print(f'Comparison failed {err!r}') # noqa: T201 sys.exit(1) - print('Measuring execution times of rosbag2 and rosbag2_py.') # noqa: T001 + print('Measuring execution times of rosbag2 and rosbag2_py.') # noqa: T201 time_py = timeit(lambda: read_deser_rosbag2_py(path), number=1) time = timeit(lambda: read_deser_rosbag2(path), number=1) - print( # noqa: T001 + print( # noqa: T201 f'Processing times:\n' f'rosbag2_py {time_py:.3f}\n' f'rosbag2 {time:.3f}\n' diff --git a/tools/compare/compare.py b/tools/compare/compare.py index f928737f..02fe5aea 100644 --- a/tools/compare/compare.py +++ b/tools/compare/compare.py @@ -67,7 +67,7 @@ def fixup_ros1(conns: List[rosbag.bag._Connection_Info]) -> None: genpy.Duration.nanosec = property(lambda x: x.nsecs) if conn := next((x for x in conns if x.datatype == 'sensor_msgs/CameraInfo'), None): - print('Patching CameraInfo') # noqa: T001 + print('Patching CameraInfo') # noqa: T201 cls = rosbag.bag._get_message_type(conn) # pylint: disable=protected-access cls.d = property(lambda x: x.D, lambda x, y: setattr(x, 'D', y)) # noqa: B010 cls.k = property(lambda x: x.K, lambda x, y: setattr(x, 'K', y)) # noqa: B010 @@ -135,7 +135,7 @@ def main_bag1_bag1(path1: Path, path2: Path) -> None: assert next(src1, None) is None assert next(src2, None) is None - print('Bags are identical.') # noqa: T001 + print('Bags are identical.') # noqa: T201 def main_bag1_bag2(path1: Path, path2: Path) -> None: @@ -160,12 +160,12 @@ def main_bag1_bag2(path1: Path, path2: Path) -> None: assert next(src1, None) is None assert next(src2, None) is None - print('Bags are identical.') # noqa: T001 + print('Bags are identical.') # noqa: T201 if __name__ == '__main__': if len(sys.argv) != 3: - print(f'Usage: {sys.argv} [rosbag1] [rosbag2]') # noqa: T001 + print(f'Usage: {sys.argv} [rosbag1] [rosbag2]') # noqa: T201 sys.exit(1) arg1 = Path(sys.argv[1]) arg2 = Path(sys.argv[2]) From b86cd70ee97e391215c16528f8a431eac4afc514 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 27 Jul 2022 16:39:47 +0200 Subject: [PATCH 093/114] Update dependencies --- requirements-dev.txt | 642 ++++++++++++++++++++++--------------------- requirements.txt | 178 ++++++------ 2 files changed, 417 insertions(+), 403 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index b82c85fc..bcd0bed2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -12,9 +12,9 @@ astor==0.8.1 \ --hash=sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5 \ --hash=sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e # via flake8-simplify -astroid==2.11.2 \ - --hash=sha256:8d0a30fe6481ce919f56690076eafbb2fb649142a89dc874f1ec0e7a011492d0 \ - --hash=sha256:cc8cc0d2d916c42d0a7c476c57550a4557a083081976bf42a73414322a6411d9 +astroid==2.11.7 \ + --hash=sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b \ + --hash=sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946 # via pylint attrs==21.4.0 \ --hash=sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4 \ @@ -24,68 +24,72 @@ attrs==21.4.0 \ # flake8-bugbear # pytest # pytest-mypy -babel==2.9.1 \ - --hash=sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9 \ - --hash=sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0 +babel==2.10.3 \ + --hash=sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51 \ + --hash=sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb # via sphinx -certifi==2021.10.8 \ - --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ - --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 +certifi==2022.6.15 \ + --hash=sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d \ + --hash=sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412 # via requests -charset-normalizer==2.0.12 \ - --hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 \ - --hash=sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df +charset-normalizer==2.1.0 \ + --hash=sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5 \ + --hash=sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413 # via requests -coverage[toml]==6.3.2 \ - --hash=sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9 \ - --hash=sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d \ - --hash=sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf \ - --hash=sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7 \ - --hash=sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6 \ - --hash=sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4 \ - --hash=sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059 \ - --hash=sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39 \ - --hash=sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536 \ - --hash=sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac \ - --hash=sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c \ - --hash=sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903 \ - --hash=sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d \ - --hash=sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05 \ - --hash=sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684 \ - --hash=sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1 \ - --hash=sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f \ - --hash=sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7 \ - --hash=sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca \ - --hash=sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad \ - --hash=sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca \ - --hash=sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d \ - --hash=sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92 \ - --hash=sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4 \ - --hash=sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf \ - --hash=sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6 \ - --hash=sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1 \ - --hash=sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4 \ - --hash=sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359 \ - --hash=sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3 \ - --hash=sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620 \ - --hash=sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512 \ - --hash=sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69 \ - --hash=sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2 \ - --hash=sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518 \ - --hash=sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0 \ - --hash=sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa \ - --hash=sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4 \ - --hash=sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e \ - --hash=sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1 \ - --hash=sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2 +classify-imports==4.1.0 \ + --hash=sha256:45436d3c4c886ca9092a2c90551b392ba120360e7a782574169ddeb866bbc08a \ + --hash=sha256:69ddc4320690c26aa8baa66bf7e0fa0eecfda49d99cf71a59dee0b57dac82616 + # via flake8-type-checking +coverage[toml]==6.4.2 \ + --hash=sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32 \ + --hash=sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7 \ + --hash=sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996 \ + --hash=sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55 \ + --hash=sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46 \ + --hash=sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de \ + --hash=sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039 \ + --hash=sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee \ + --hash=sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1 \ + --hash=sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f \ + --hash=sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63 \ + --hash=sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083 \ + --hash=sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe \ + --hash=sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0 \ + --hash=sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6 \ + --hash=sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe \ + --hash=sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933 \ + --hash=sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0 \ + --hash=sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c \ + --hash=sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07 \ + --hash=sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8 \ + --hash=sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b \ + --hash=sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e \ + --hash=sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120 \ + --hash=sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f \ + --hash=sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e \ + --hash=sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd \ + --hash=sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f \ + --hash=sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386 \ + --hash=sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8 \ + --hash=sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae \ + --hash=sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc \ + --hash=sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783 \ + --hash=sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d \ + --hash=sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c \ + --hash=sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97 \ + --hash=sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978 \ + --hash=sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf \ + --hash=sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29 \ + --hash=sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39 \ + --hash=sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452 # via pytest-cov darglint==1.8.1 \ --hash=sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da \ --hash=sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d # via rosbags (setup.cfg) -dill==0.3.4 \ - --hash=sha256:7e40e4a70304fd9ceab3535d36e58791d9c4a776b38ec7f7ec9afc8d3dca4d4f \ - --hash=sha256:9f9734205146b2b353ab3fec9af0070237b6ddae78452af83d2fca84d739e675 +dill==0.3.5.1 \ + --hash=sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302 \ + --hash=sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86 # via pylint docutils==0.17.1 \ --hash=sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125 \ @@ -93,9 +97,9 @@ docutils==0.17.1 \ # via # sphinx # sphinx-rtd-theme -filelock==3.6.0 \ - --hash=sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85 \ - --hash=sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0 +filelock==3.7.1 \ + --hash=sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404 \ + --hash=sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04 # via pytest-mypy flake8==4.0.1 \ --hash=sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d \ @@ -108,8 +112,8 @@ flake8==4.0.1 \ # flake8-docstrings # flake8-isort # flake8-mutable - # flake8-polyfill # flake8-print + # flake8-pyprojecttoml # flake8-quotes # flake8-simplify # flake8-type-checking @@ -117,21 +121,21 @@ flake8==4.0.1 \ # pep8-naming # pytest-flake8 # rosbags (setup.cfg) -flake8-annotations==2.8.0 \ - --hash=sha256:880f9bb0677b82655f9021112d64513e03caefd2e0d786ab4a59ddb5b262caa9 \ - --hash=sha256:a2765c6043098aab0a3f519b871b33586c7fba7037686404b920cf8100cc1cdc +flake8-annotations==2.9.0 \ + --hash=sha256:63fb3f538970b6a8dfd84125cf5af16f7b22e52d5032acb3b7eb23645ecbda9b \ + --hash=sha256:84f46de2964cb18fccea968d9eafce7cf857e34d913d515120795b9af6498d56 # via rosbags (setup.cfg) -flake8-bugbear==22.3.23 \ - --hash=sha256:e0dc2a36474490d5b1a2d57f9e4ef570abc09f07cbb712b29802e28a2367ff19 \ - --hash=sha256:ec5ec92195720cee1589315416b844ffa5e82f73a78e65329e8055322df1e939 +flake8-bugbear==22.7.1 \ + --hash=sha256:db5d7a831ef4412a224b26c708967ff816818cabae415e76b8c58df156c4b8e5 \ + --hash=sha256:e450976a07e4f9d6c043d4f72b17ec1baf717fe37f7997009c8ae58064f88305 # via rosbags (setup.cfg) flake8-commas==2.1.0 \ --hash=sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263 \ --hash=sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54 # via rosbags (setup.cfg) -flake8-comprehensions==3.8.0 \ - --hash=sha256:8e108707637b1d13734f38e03435984f6b7854fa6b5a4e34f93e69534be8e521 \ - --hash=sha256:9406314803abe1193c064544ab14fdc43c58424c0882f6ff8a581eb73fc9bb58 +flake8-comprehensions==3.10.0 \ + --hash=sha256:181158f7e7aa26a63a0a38e6017cef28c6adee71278ce56ce11f6ec9c4905058 \ + --hash=sha256:dad454fd3d525039121e98fa1dd90c46bc138708196a4ebbc949ad3c859adedb # via rosbags (setup.cfg) flake8-docstrings==1.6.0 \ --hash=sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde \ @@ -141,9 +145,9 @@ flake8-fixme==1.1.1 \ --hash=sha256:226a6f2ef916730899f29ac140bed5d4a17e5aba79f00a0e3ae1eff1997cb1ac \ --hash=sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a # via rosbags (setup.cfg) -flake8-isort==4.1.1 \ - --hash=sha256:c4e8b6dcb7be9b71a02e6e5d4196cefcef0f3447be51e82730fb336fff164949 \ - --hash=sha256:d814304ab70e6e58859bc5c3e221e2e6e71c958e7005239202fee19c24f82717 +flake8-isort==4.1.2.post0 \ + --hash=sha256:4f95b40706dbb507cff872b34683283662e945d6028d3c8257e69de5fc6b7446 \ + --hash=sha256:dee69bc3c09f0832df88acf795845db8a6673b79237371a05fa927ce095248e5 # via rosbags (setup.cfg) flake8-mutable==1.2.0 \ --hash=sha256:38fd9dadcbcda6550a916197bc40ed76908119dabb37fbcca30873666c31d2d5 \ @@ -155,13 +159,13 @@ flake8-plugin-utils==1.3.2 \ # via # flake8-pytest-style # flake8-return -flake8-polyfill==1.0.2 \ - --hash=sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9 \ - --hash=sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda - # via pep8-naming -flake8-print==4.0.0 \ - --hash=sha256:5afac374b7dc49aac2c36d04b5eb1d746d72e6f5df75a6ecaecd99e9f79c6516 \ - --hash=sha256:6c0efce658513169f96d7a24cf136c434dc711eb00ebd0a985eb1120103fe584 +flake8-print==5.0.0 \ + --hash=sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9 \ + --hash=sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8 + # via rosbags (setup.cfg) +flake8-pyprojecttoml==0.0.1 \ + --hash=sha256:644c3dcf558209ca246239ca4f56844769c863181eab05177d8906dc77bbdb8f \ + --hash=sha256:654d91135ee7f7429ab56dc39a9fb37a48f3e2412e189ae7a756921cb0ffffe5 # via rosbags (setup.cfg) flake8-pytest-style==1.6.0 \ --hash=sha256:5fedb371a950e9fe0e0e6bfc854be7d99151271208f34cd2cc517681ece27780 \ @@ -178,9 +182,9 @@ flake8-simplify==0.19.2 \ --hash=sha256:9c1e96ba738f9057a561697d67df7a9058898ac6313a67eda09942255cba6d38 \ --hash=sha256:a30ef76bf1c0cb89a52a8a5cee37667688e61a66735e997c08d56cd002f9e3e9 # via rosbags (setup.cfg) -flake8-type-checking==1.5.0 \ - --hash=sha256:b9873f011e18e20dee60e2b633f2c675f2aa1318499e5294b46c860fc75bdcd6 \ - --hash=sha256:d01234fc2d3ffc9661dd797fb1bed19e2d92a1fb4041a4e8dc0200ea14f357c0 +flake8-type-checking==2.0.7 \ + --hash=sha256:055d33558938b599cbf754987fa3e11f51c3a2a57e26c575674ba1ea392c93ca \ + --hash=sha256:a326830f9e12fc86b2bf3c5b56b786dc9541b8cd7e9755d73938d6a0e36d7b6d # via rosbags (setup.cfg) flake8-use-fstring==1.3 \ --hash=sha256:1bd4a409adbb93e64e711fdd26b88759c33792e3899f174edc68ddf7307e81b6 @@ -189,13 +193,13 @@ idna==3.3 \ --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d # via requests -imagesize==1.3.0 \ - --hash=sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c \ - --hash=sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d +imagesize==1.4.1 \ + --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \ + --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a # via sphinx -importlib-metadata==4.11.3 \ - --hash=sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6 \ - --hash=sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539 +importlib-metadata==4.12.0 \ + --hash=sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670 \ + --hash=sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23 # via sphinx iniconfig==1.1.1 \ --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ @@ -207,9 +211,9 @@ isort==5.10.1 \ # via # flake8-isort # pylint -jinja2==3.1.1 \ - --hash=sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119 \ - --hash=sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9 +jinja2==3.1.2 \ + --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ + --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 # via sphinx lazy-object-proxy==1.7.1 \ --hash=sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7 \ @@ -250,28 +254,28 @@ lazy-object-proxy==1.7.1 \ --hash=sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b \ --hash=sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb # via astroid -lz4==4.0.0 \ - --hash=sha256:04067086a443eef46eb2dfc26e1e5a76165149ceb4a88f0b540b46ead95e39c8 \ - --hash=sha256:19e939cd1e5d1776ca8f431c18c10a59970cf98caceeaa6edc98fdcf58499f29 \ - --hash=sha256:1ef9b03386757546f962e0598ac1d863960018dd9c04ec207059d3eb52bba12b \ - --hash=sha256:2428f5525c214d8cca332a96a2561415fd1261be2372b68b32a1aa40b9c9000f \ - --hash=sha256:26e4e7f88757c31e5240016b863f37ad79fc2898be272a6d78f267963c1b094b \ - --hash=sha256:2e1fa0dba94a7dece5d0fe109e317242c28f09e1ad488b8db692fcafb094db79 \ - --hash=sha256:3221e9a46d343175cefe932b0e812f2ecd3de70c7d036b951488d664587bee4a \ - --hash=sha256:463814c29f1201ef876c031ad32a185adee807ae201c228b28d65f17b203ee11 \ - --hash=sha256:4bf2880cae9a5255f86698f60af635f184e49d5f6878a7e0520b6cfcdb0a0d50 \ - --hash=sha256:4d96e913877b687bb8e8c6f8a90ce1e2a9a5c5268d9bab489f49bd3ce9ae8afa \ - --hash=sha256:5721ec225a37794fbaabcfa5cf2289ebb4b9e770e73e4e779d514c1fc784df5b \ - --hash=sha256:57c5dfd3b7dae833b0d2b2c1aafd7f9d0dfcab40683d183d010c67c9fd1beca3 \ - --hash=sha256:6a4c004e664d8185e2bfeffb90e1bfe554a0cd1a764662648b528e37220822cb \ - --hash=sha256:6c9acd054426840de2e4bbf83321945c3a20e90402ad221f4302e983f8031e14 \ - --hash=sha256:79246da3207b9eb4e53b3bed86189a60631e74f9b3d2579918b032fb1c7b114b \ - --hash=sha256:7ec46449892159c869c88848ee2c9b3b5170d193ba79524732334e7bbc39639c \ - --hash=sha256:a4afc2c12c37896e5ac7c5343f255cc242962ed7af940f6ef5276cc5c22e81fd \ - --hash=sha256:afc6bebfcbc48873854c05366b35a69b9c4e440ce17571ade032941cb89585ac \ - --hash=sha256:b83fce61cec36cdc21d234524d60a96d70f1a928533228eae6f46a9de21dc218 \ - --hash=sha256:e05542c6cbdb1128c43cfefd7518e231b39329d212b19ab89fd3868596140bdf \ - --hash=sha256:f69405f196c6fb38b94ac6000baa59c0364a1ac264e64194bb2fc48513df79ad +lz4==4.0.2 \ + --hash=sha256:083b7172c2938412ae37c3a090250bfdd9e4a6e855442594f86c3608ed12729b \ + --hash=sha256:154e6e9f58a7bafc4d2a1395160305b78fc82fa708bfa58cf0ad977c443d1f8f \ + --hash=sha256:1bd56282f6993e013ccf7f6edf1530c2a13d1662741e2be072349c7f70bc0682 \ + --hash=sha256:1ed9a1875dc2a489f3b665d0211984689d0e76585e55650b044a64dbd2d22992 \ + --hash=sha256:345608de23b4d68fbdef373f1e53d6c5abd99a062d4ff922e3350f47775ab123 \ + --hash=sha256:35e6caced0229b90151d31d9cf1eaa541e597f8021bf5b70ff9e6374e3e43b23 \ + --hash=sha256:3881573c3db902db370e072eb64b40c7c8289b94b2a731e051858cc198f890e8 \ + --hash=sha256:3fa0f000d8ce39e643e9e5c49fc4d1985156ffb177e3123a0f22551f5864841b \ + --hash=sha256:439898dd4176a724243002003c3f733eb6ce48a5988175f54c8560e0b100b7a6 \ + --hash=sha256:4cfa82f26b4f1835c797bd70e5ce20d5f1ee897b9a0c53e62d607f9029f521ce \ + --hash=sha256:56ea660097fec87f0c6746146b316775037f8dd886a4c5915360e5b32b7112d0 \ + --hash=sha256:5fe9db7627674875e4279c2ed50b1e38fb91ec3093347f871ed996e58edbb488 \ + --hash=sha256:61dbcca64e8e1655e06b588356c4b2515bccc1d7e84065f858a685abd96f0cf2 \ + --hash=sha256:6f3b3670f52f0871885258bcbc746f483760434336f0bc5581f161cc5d4b0c9a \ + --hash=sha256:9d141719d3cbb7933809642a61b68b8f595ddf85657016521756ddcf826b85cd \ + --hash=sha256:a8e02c2477bd704f43113ac8dd966c361187383591388818d74e1b73e4674759 \ + --hash=sha256:d2b18a6d6d9071c03dbf9e30bbe22e4476f24f1a4d73b1e975605ad3ce725e6c \ + --hash=sha256:ea2c2182a5b0ad03f33ac09db0925a1738a1d65751a3e058110bd900c643d359 \ + --hash=sha256:ed86ab22bfe1f4cd4fc983704134a8fdf746c1121a398f8f14cbd014c1a5b0ae \ + --hash=sha256:ee73357412c5505f6ba0ea61ff71455e2e4c1e04d8e60f17f3cd937261d773fa \ + --hash=sha256:fba1730cd2327a9d013192a9878714cc82f4877d2ada556222d03ea6428a80ed # via rosbags (setup.cfg) markupsafe==2.1.1 \ --hash=sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003 \ @@ -321,56 +325,60 @@ mccabe==0.6.1 \ # via # flake8 # pylint -mypy==0.942 \ - --hash=sha256:0e2dd88410937423fba18e57147dd07cd8381291b93d5b1984626f173a26543e \ - --hash=sha256:10daab80bc40f84e3f087d896cdb53dc811a9f04eae4b3f95779c26edee89d16 \ - --hash=sha256:17e44649fec92e9f82102b48a3bf7b4a5510ad0cd22fa21a104826b5db4903e2 \ - --hash=sha256:1a0459c333f00e6a11cbf6b468b870c2b99a906cb72d6eadf3d1d95d38c9352c \ - --hash=sha256:246e1aa127d5b78488a4a0594bd95f6d6fb9d63cf08a66dafbff8595d8891f67 \ - --hash=sha256:2b184db8c618c43c3a31b32ff00cd28195d39e9c24e7c3b401f3db7f6e5767f5 \ - --hash=sha256:2bc249409a7168d37c658e062e1ab5173300984a2dada2589638568ddc1db02b \ - --hash=sha256:3841b5433ff936bff2f4dc8d54cf2cdbfea5d8e88cedfac45c161368e5770ba6 \ - --hash=sha256:4c3e497588afccfa4334a9986b56f703e75793133c4be3a02d06a3df16b67a58 \ - --hash=sha256:5bf44840fb43ac4074636fd47ee476d73f0039f4f54e86d7265077dc199be24d \ - --hash=sha256:64235137edc16bee6f095aba73be5334677d6f6bdb7fa03cfab90164fa294a17 \ - --hash=sha256:6776e5fa22381cc761df53e7496a805801c1a751b27b99a9ff2f0ca848c7eca0 \ - --hash=sha256:6ce34a118d1a898f47def970a2042b8af6bdcc01546454726c7dd2171aa6dfca \ - --hash=sha256:6f6ad963172152e112b87cc7ec103ba0f2db2f1cd8997237827c052a3903eaa6 \ - --hash=sha256:6f7106cbf9cc2f403693bf50ed7c9fa5bb3dfa9007b240db3c910929abe2a322 \ - --hash=sha256:7742d2c4e46bb5017b51c810283a6a389296cda03df805a4f7869a6f41246534 \ - --hash=sha256:9521c1265ccaaa1791d2c13582f06facf815f426cd8b07c3a485f486a8ffc1f3 \ - --hash=sha256:a1b383fe99678d7402754fe90448d4037f9512ce70c21f8aee3b8bf48ffc51db \ - --hash=sha256:b840cfe89c4ab6386c40300689cd8645fc8d2d5f20101c7f8bd23d15fca14904 \ - --hash=sha256:d8d3ba77e56b84cd47a8ee45b62c84b6d80d32383928fe2548c9a124ea0a725c \ - --hash=sha256:dcd955f36e0180258a96f880348fbca54ce092b40fbb4b37372ae3b25a0b0a46 \ - --hash=sha256:e865fec858d75b78b4d63266c9aff770ecb6a39dfb6d6b56c47f7f8aba6baba8 \ - --hash=sha256:edf7237137a1a9330046dbb14796963d734dd740a98d5e144a3eb1d267f5f9ee - # via pytest-mypy +mypy==0.961 \ + --hash=sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5 \ + --hash=sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66 \ + --hash=sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e \ + --hash=sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56 \ + --hash=sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e \ + --hash=sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d \ + --hash=sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813 \ + --hash=sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932 \ + --hash=sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569 \ + --hash=sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b \ + --hash=sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0 \ + --hash=sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648 \ + --hash=sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6 \ + --hash=sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950 \ + --hash=sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15 \ + --hash=sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723 \ + --hash=sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a \ + --hash=sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3 \ + --hash=sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6 \ + --hash=sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24 \ + --hash=sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b \ + --hash=sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d \ + --hash=sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492 + # via + # pytest-mypy + # rosbags (setup.cfg) mypy-extensions==0.4.3 \ --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 # via mypy -numpy==1.22.3 \ - --hash=sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676 \ - --hash=sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4 \ - --hash=sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce \ - --hash=sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123 \ - --hash=sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1 \ - --hash=sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e \ - --hash=sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5 \ - --hash=sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d \ - --hash=sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a \ - --hash=sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab \ - --hash=sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75 \ - --hash=sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168 \ - --hash=sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4 \ - --hash=sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f \ - --hash=sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18 \ - --hash=sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62 \ - --hash=sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe \ - --hash=sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430 \ - --hash=sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802 \ - --hash=sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa +numpy==1.23.1 \ + --hash=sha256:1408c3527a74a0209c781ac82bde2182b0f0bf54dea6e6a363fe0cc4488a7ce7 \ + --hash=sha256:173f28921b15d341afadf6c3898a34f20a0569e4ad5435297ba262ee8941e77b \ + --hash=sha256:1865fdf51446839ca3fffaab172461f2b781163f6f395f1aed256b1ddc253622 \ + --hash=sha256:3119daed207e9410eaf57dcf9591fdc68045f60483d94956bee0bfdcba790953 \ + --hash=sha256:35590b9c33c0f1c9732b3231bb6a72d1e4f77872390c47d50a615686ae7ed3fd \ + --hash=sha256:37e5ebebb0eb54c5b4a9b04e6f3018e16b8ef257d26c8945925ba8105008e645 \ + --hash=sha256:37ece2bd095e9781a7156852e43d18044fd0d742934833335599c583618181b9 \ + --hash=sha256:3ab67966c8d45d55a2bdf40701536af6443763907086c0a6d1232688e27e5447 \ + --hash=sha256:47f10ab202fe4d8495ff484b5561c65dd59177949ca07975663f4494f7269e3e \ + --hash=sha256:55df0f7483b822855af67e38fb3a526e787adf189383b4934305565d71c4b148 \ + --hash=sha256:5d732d17b8a9061540a10fda5bfeabca5785700ab5469a5e9b93aca5e2d3a5fb \ + --hash=sha256:68b69f52e6545af010b76516f5daaef6173e73353e3295c5cb9f96c35d755641 \ + --hash=sha256:7e8229f3687cdadba2c4faef39204feb51ef7c1a9b669247d49a24f3e2e1617c \ + --hash=sha256:8002574a6b46ac3b5739a003b5233376aeac5163e5dcd43dd7ad062f3e186129 \ + --hash=sha256:876f60de09734fbcb4e27a97c9a286b51284df1326b1ac5f1bf0ad3678236b22 \ + --hash=sha256:9ce242162015b7e88092dccd0e854548c0926b75c7924a3495e02c6067aba1f5 \ + --hash=sha256:a35c4e64dfca659fe4d0f1421fc0f05b8ed1ca8c46fb73d9e5a7f175f85696bb \ + --hash=sha256:aeba539285dcf0a1ba755945865ec61240ede5432df41d6e29fab305f4384db2 \ + --hash=sha256:b15c3f1ed08df4980e02cc79ee058b788a3d0bef2fb3c9ca90bb8cbd5b8a3a04 \ + --hash=sha256:c2f91f88230042a130ceb1b496932aa717dcbd665350beb821534c5c7e15881c \ + --hash=sha256:d748ef349bfef2e1194b59da37ed5a29c19ea8d7e6342019921ba2ba4fd8b624 \ + --hash=sha256:e0d7447679ae9a7124385ccf0ea990bb85bb869cef217e2ea6c844b6a6855073 # via rosbags (setup.cfg) packaging==21.3 \ --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \ @@ -378,13 +386,13 @@ packaging==21.3 \ # via # pytest # sphinx -pep8-naming==0.12.1 \ - --hash=sha256:4a8daeaeb33cfcde779309fc0c9c0a68a3bbe2ad8a8308b763c5068f86eb9f37 \ - --hash=sha256:bb2455947757d162aa4cad55dba4ce029005cd1692f2899a21d51d8630ca7841 +pep8-naming==0.13.1 \ + --hash=sha256:3af77cdaa9c7965f7c85a56cd579354553c9bbd3fdf3078a776f12db54dd6944 \ + --hash=sha256:f7867c1a464fe769be4f972ef7b79d6df1d9aff1b1f04ecf738d471963d3ab9c # via rosbags (setup.cfg) -platformdirs==2.5.1 \ - --hash=sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d \ - --hash=sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227 +platformdirs==2.5.2 \ + --hash=sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788 \ + --hash=sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19 # via pylint pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ @@ -408,21 +416,21 @@ pyflakes==2.4.0 \ --hash=sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c \ --hash=sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e # via flake8 -pygments==2.11.2 \ - --hash=sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65 \ - --hash=sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a +pygments==2.12.0 \ + --hash=sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb \ + --hash=sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519 # via sphinx -pylint==2.13.5 \ - --hash=sha256:c149694cfdeaee1aa2465e6eaab84c87a881a7d55e6e93e09466be7164764d1e \ - --hash=sha256:dab221658368c7a05242e673c275c488670144123f4bd262b2777249c1c0de9b +pylint==2.14.5 \ + --hash=sha256:487ce2192eee48211269a0e976421f334cf94de1806ca9d0a99449adcdf0285e \ + --hash=sha256:fabe30000de7d07636d2e82c9a518ad5ad7908590fe135ace169b44839c15f90 # via pytest-pylint -pyparsing==3.0.8 \ - --hash=sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954 \ - --hash=sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06 +pyparsing==3.0.9 \ + --hash=sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb \ + --hash=sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc # via packaging -pytest==7.1.1 \ - --hash=sha256:841132caef6b1ad17a9afde46dc4f6cfa59a05f9555aae5151f73bdf2820ca63 \ - --hash=sha256:92f723789a8fdd7180b6b06483874feca4c48a5c76968e03bb3e7f806a1869ea +pytest==7.1.2 \ + --hash=sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c \ + --hash=sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45 # via # pytest-cov # pytest-flake8 @@ -454,9 +462,9 @@ pytz==2022.1 \ --hash=sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7 \ --hash=sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c # via babel -requests==2.27.1 \ - --hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61 \ - --hash=sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d +requests==2.28.1 \ + --hash=sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983 \ + --hash=sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349 # via sphinx ruamel-yaml==0.17.21 \ --hash=sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7 \ @@ -489,26 +497,22 @@ ruamel-yaml-clib==0.2.6 \ --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c # via ruamel-yaml -six==1.16.0 \ - --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ - --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 - # via flake8-print snowballstemmer==2.2.0 \ --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \ --hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a # via # pydocstyle # sphinx -sphinx==4.5.0 \ - --hash=sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6 \ - --hash=sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226 +sphinx==5.1.1 \ + --hash=sha256:309a8da80cb6da9f4713438e5b55861877d5d7976b69d87e336733637ea12693 \ + --hash=sha256:ba3224a4e206e1fbdecf98a4fae4992ef9b24b85ebf7b584bb340156eaf08d89 # via # rosbags (setup.cfg) # sphinx-autodoc-typehints # sphinx-rtd-theme -sphinx-autodoc-typehints==1.17.0 \ - --hash=sha256:081daf53077b4ae1c28347d6d858e13e63aefe3b4aacef79fd717dd60687b470 \ - --hash=sha256:51c7b3f5cb9ccd15d0b52088c62df3094f1abd9612930340365c26def8629a14 +sphinx-autodoc-typehints==1.18.3 \ + --hash=sha256:20294de2a818bda04953c5cb302ec5af46138c81980ad9efa6d8fc1fc4242518 \ + --hash=sha256:c04d8f8d70e988960e25b206af39a90df84e7e2c085bb24e123bc3684021b313 # via rosbags (setup.cfg) sphinx-rtd-theme==1.0.0 \ --hash=sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8 \ @@ -538,10 +542,6 @@ sphinxcontrib-serializinghtml==1.1.5 \ --hash=sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd \ --hash=sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952 # via sphinx -testfixtures==6.18.5 \ - --hash=sha256:02dae883f567f5b70fd3ad3c9eefb95912e78ac90be6c7444b5e2f46bf572c84 \ - --hash=sha256:7de200e24f50a4a5d6da7019fb1197aaf5abd475efb2ec2422fdcf2f2eb98c1d - # via flake8-isort toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f @@ -551,82 +551,90 @@ tomli==2.0.1 \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f # via # coverage + # flake8-pyprojecttoml # mypy # pylint # pytest -typing-extensions==4.1.1 \ - --hash=sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42 \ - --hash=sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2 - # via mypy -urllib3==1.26.9 \ - --hash=sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14 \ - --hash=sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e +tomlkit==0.11.1 \ + --hash=sha256:1c5bebdf19d5051e2e1de6cf70adfc5948d47221f097fcff7a3ffc91e953eaf5 \ + --hash=sha256:61901f81ff4017951119cd0d1ed9b7af31c821d6845c8c477587bbdcd5e5854e + # via pylint +typing-extensions==4.3.0 \ + --hash=sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02 \ + --hash=sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6 + # via + # astroid + # mypy + # pylint +urllib3==1.26.11 \ + --hash=sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc \ + --hash=sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a # via requests -wrapt==1.14.0 \ - --hash=sha256:00108411e0f34c52ce16f81f1d308a571df7784932cc7491d1e94be2ee93374b \ - --hash=sha256:01f799def9b96a8ec1ef6b9c1bbaf2bbc859b87545efbecc4a78faea13d0e3a0 \ - --hash=sha256:09d16ae7a13cff43660155383a2372b4aa09109c7127aa3f24c3cf99b891c330 \ - --hash=sha256:14e7e2c5f5fca67e9a6d5f753d21f138398cad2b1159913ec9e9a67745f09ba3 \ - --hash=sha256:167e4793dc987f77fd476862d32fa404d42b71f6a85d3b38cbce711dba5e6b68 \ - --hash=sha256:1807054aa7b61ad8d8103b3b30c9764de2e9d0c0978e9d3fc337e4e74bf25faa \ - --hash=sha256:1f83e9c21cd5275991076b2ba1cd35418af3504667affb4745b48937e214bafe \ - --hash=sha256:21b1106bff6ece8cb203ef45b4f5778d7226c941c83aaaa1e1f0f4f32cc148cd \ - --hash=sha256:22626dca56fd7f55a0733e604f1027277eb0f4f3d95ff28f15d27ac25a45f71b \ - --hash=sha256:23f96134a3aa24cc50614920cc087e22f87439053d886e474638c68c8d15dc80 \ - --hash=sha256:2498762814dd7dd2a1d0248eda2afbc3dd9c11537bc8200a4b21789b6df6cd38 \ - --hash=sha256:28c659878f684365d53cf59dc9a1929ea2eecd7ac65da762be8b1ba193f7e84f \ - --hash=sha256:2eca15d6b947cfff51ed76b2d60fd172c6ecd418ddab1c5126032d27f74bc350 \ - --hash=sha256:354d9fc6b1e44750e2a67b4b108841f5f5ea08853453ecbf44c81fdc2e0d50bd \ - --hash=sha256:36a76a7527df8583112b24adc01748cd51a2d14e905b337a6fefa8b96fc708fb \ - --hash=sha256:3a0a4ca02752ced5f37498827e49c414d694ad7cf451ee850e3ff160f2bee9d3 \ - --hash=sha256:3a71dbd792cc7a3d772ef8cd08d3048593f13d6f40a11f3427c000cf0a5b36a0 \ - --hash=sha256:3a88254881e8a8c4784ecc9cb2249ff757fd94b911d5df9a5984961b96113fff \ - --hash=sha256:47045ed35481e857918ae78b54891fac0c1d197f22c95778e66302668309336c \ - --hash=sha256:4775a574e9d84e0212f5b18886cace049a42e13e12009bb0491562a48bb2b758 \ - --hash=sha256:493da1f8b1bb8a623c16552fb4a1e164c0200447eb83d3f68b44315ead3f9036 \ - --hash=sha256:4b847029e2d5e11fd536c9ac3136ddc3f54bc9488a75ef7d040a3900406a91eb \ - --hash=sha256:59d7d92cee84a547d91267f0fea381c363121d70fe90b12cd88241bd9b0e1763 \ - --hash=sha256:5a0898a640559dec00f3614ffb11d97a2666ee9a2a6bad1259c9facd01a1d4d9 \ - --hash=sha256:5a9a1889cc01ed2ed5f34574c90745fab1dd06ec2eee663e8ebeefe363e8efd7 \ - --hash=sha256:5b835b86bd5a1bdbe257d610eecab07bf685b1af2a7563093e0e69180c1d4af1 \ - --hash=sha256:5f24ca7953f2643d59a9c87d6e272d8adddd4a53bb62b9208f36db408d7aafc7 \ - --hash=sha256:61e1a064906ccba038aa3c4a5a82f6199749efbbb3cef0804ae5c37f550eded0 \ - --hash=sha256:65bf3eb34721bf18b5a021a1ad7aa05947a1767d1aa272b725728014475ea7d5 \ - --hash=sha256:6807bcee549a8cb2f38f73f469703a1d8d5d990815c3004f21ddb68a567385ce \ - --hash=sha256:68aeefac31c1f73949662ba8affaf9950b9938b712fb9d428fa2a07e40ee57f8 \ - --hash=sha256:6915682f9a9bc4cf2908e83caf5895a685da1fbd20b6d485dafb8e218a338279 \ - --hash=sha256:6d9810d4f697d58fd66039ab959e6d37e63ab377008ef1d63904df25956c7db0 \ - --hash=sha256:729d5e96566f44fccac6c4447ec2332636b4fe273f03da128fff8d5559782b06 \ - --hash=sha256:748df39ed634851350efa87690c2237a678ed794fe9ede3f0d79f071ee042561 \ - --hash=sha256:763a73ab377390e2af26042f685a26787c402390f682443727b847e9496e4a2a \ - --hash=sha256:8323a43bd9c91f62bb7d4be74cc9ff10090e7ef820e27bfe8815c57e68261311 \ - --hash=sha256:8529b07b49b2d89d6917cfa157d3ea1dfb4d319d51e23030664a827fe5fd2131 \ - --hash=sha256:87fa943e8bbe40c8c1ba4086971a6fefbf75e9991217c55ed1bcb2f1985bd3d4 \ - --hash=sha256:88236b90dda77f0394f878324cfbae05ae6fde8a84d548cfe73a75278d760291 \ - --hash=sha256:891c353e95bb11abb548ca95c8b98050f3620a7378332eb90d6acdef35b401d4 \ - --hash=sha256:89ba3d548ee1e6291a20f3c7380c92f71e358ce8b9e48161401e087e0bc740f8 \ - --hash=sha256:8c6be72eac3c14baa473620e04f74186c5d8f45d80f8f2b4eda6e1d18af808e8 \ - --hash=sha256:9a242871b3d8eecc56d350e5e03ea1854de47b17f040446da0e47dc3e0b9ad4d \ - --hash=sha256:9a3ff5fb015f6feb78340143584d9f8a0b91b6293d6b5cf4295b3e95d179b88c \ - --hash=sha256:9a5a544861b21e0e7575b6023adebe7a8c6321127bb1d238eb40d99803a0e8bd \ - --hash=sha256:9d57677238a0c5411c76097b8b93bdebb02eb845814c90f0b01727527a179e4d \ - --hash=sha256:9d8c68c4145041b4eeae96239802cfdfd9ef927754a5be3f50505f09f309d8c6 \ - --hash=sha256:9d9fcd06c952efa4b6b95f3d788a819b7f33d11bea377be6b8980c95e7d10775 \ - --hash=sha256:a0057b5435a65b933cbf5d859cd4956624df37b8bf0917c71756e4b3d9958b9e \ - --hash=sha256:a65bffd24409454b889af33b6c49d0d9bcd1a219b972fba975ac935f17bdf627 \ - --hash=sha256:b0ed6ad6c9640671689c2dbe6244680fe8b897c08fd1fab2228429b66c518e5e \ - --hash=sha256:b21650fa6907e523869e0396c5bd591cc326e5c1dd594dcdccac089561cacfb8 \ - --hash=sha256:b3f7e671fb19734c872566e57ce7fc235fa953d7c181bb4ef138e17d607dc8a1 \ - --hash=sha256:b77159d9862374da213f741af0c361720200ab7ad21b9f12556e0eb95912cd48 \ - --hash=sha256:bb36fbb48b22985d13a6b496ea5fb9bb2a076fea943831643836c9f6febbcfdc \ - --hash=sha256:d066ffc5ed0be00cd0352c95800a519cf9e4b5dd34a028d301bdc7177c72daf3 \ - --hash=sha256:d332eecf307fca852d02b63f35a7872de32d5ba8b4ec32da82f45df986b39ff6 \ - --hash=sha256:d808a5a5411982a09fef6b49aac62986274ab050e9d3e9817ad65b2791ed1425 \ - --hash=sha256:d9bdfa74d369256e4218000a629978590fd7cb6cf6893251dad13d051090436d \ - --hash=sha256:db6a0ddc1282ceb9032e41853e659c9b638789be38e5b8ad7498caac00231c23 \ - --hash=sha256:debaf04f813ada978d7d16c7dfa16f3c9c2ec9adf4656efdc4defdf841fc2f0c \ - --hash=sha256:f0408e2dbad9e82b4c960274214af533f856a199c9274bd4aff55d4634dedc33 \ - --hash=sha256:f2f3bc7cd9c9fcd39143f11342eb5963317bd54ecc98e3650ca22704b69d9653 +wrapt==1.14.1 \ + --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \ + --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \ + --hash=sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4 \ + --hash=sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2 \ + --hash=sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656 \ + --hash=sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3 \ + --hash=sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff \ + --hash=sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310 \ + --hash=sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a \ + --hash=sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57 \ + --hash=sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069 \ + --hash=sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383 \ + --hash=sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe \ + --hash=sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87 \ + --hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d \ + --hash=sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b \ + --hash=sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907 \ + --hash=sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f \ + --hash=sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0 \ + --hash=sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28 \ + --hash=sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1 \ + --hash=sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853 \ + --hash=sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc \ + --hash=sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3 \ + --hash=sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3 \ + --hash=sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164 \ + --hash=sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1 \ + --hash=sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c \ + --hash=sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1 \ + --hash=sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7 \ + --hash=sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1 \ + --hash=sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320 \ + --hash=sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed \ + --hash=sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1 \ + --hash=sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248 \ + --hash=sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c \ + --hash=sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456 \ + --hash=sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77 \ + --hash=sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef \ + --hash=sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1 \ + --hash=sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7 \ + --hash=sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86 \ + --hash=sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4 \ + --hash=sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d \ + --hash=sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d \ + --hash=sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8 \ + --hash=sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5 \ + --hash=sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471 \ + --hash=sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00 \ + --hash=sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68 \ + --hash=sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3 \ + --hash=sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d \ + --hash=sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735 \ + --hash=sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d \ + --hash=sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569 \ + --hash=sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7 \ + --hash=sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59 \ + --hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \ + --hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \ + --hash=sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b \ + --hash=sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f \ + --hash=sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462 \ + --hash=sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015 \ + --hash=sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af # via astroid yapf==0.32.0 \ --hash=sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32 \ @@ -634,53 +642,57 @@ yapf==0.32.0 \ # via # pytest-yapf3 # rosbags (setup.cfg) -zipp==3.8.0 \ - --hash=sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad \ - --hash=sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099 +zipp==3.8.1 \ + --hash=sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2 \ + --hash=sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009 # via importlib-metadata -zstandard==0.17.0 \ - --hash=sha256:208fa6bead577b2607205640078ee452e81fe20fe96321623c632bad9ebd7148 \ - --hash=sha256:2a2ac752162ba5cbc869c60c4a4e54e890b2ee2ffb57d3ff159feab1ae4518db \ - --hash=sha256:37e50501baaa935f13a1820ab2114f74313b5cb4cfff8146acb8c5b18cdced2a \ - --hash=sha256:3cf96ace804945e53bc3e5294097e5fa32a2d43bc52416c632b414b870ee0a21 \ - --hash=sha256:42f3c02c7021073cafbc6cd152b288c56a25e585518861589bb08b063b6d2ad2 \ - --hash=sha256:4768449d8d1b0785309ace288e017cc5fa42e11a52bf08c90d9c3eb3a7a73cc6 \ - --hash=sha256:477f172807a9fa83467b30d7c58876af1410d20177c554c27525211edf535bae \ - --hash=sha256:49cd09ccbd1e3c0e2690dd62ebf95064d84aa42b9db381867e0b138631f969f2 \ - --hash=sha256:59eadb9f347d40e8f7ef77caffd0c04a31e82c1df82fe2d2a688032429d750ac \ - --hash=sha256:60943f71e3117583655a1eb76188a7cc78a25267ef09cc74be4d25a0b0c8b947 \ - --hash=sha256:787efc741e61e00ffe5e65dac99b0dc5c88b9421012a207a91b869a8b1164921 \ - --hash=sha256:7a3a1aa9528087f6f4c47f4ece2d5e6a160527821263fb8174ff36429233e093 \ - --hash=sha256:7d2e7abac41d2b4b18f03575aca860d2cb647c343e13c23d6c769106a3db2f6f \ - --hash=sha256:802109f67328c5b822d4fdac28e1cf65a24de2e2e99d76cdbeee9121cedb1b6c \ - --hash=sha256:8aedd38d357f6d5e2facd88ce62b4976afdc29db57216a23f14a0cd0ca05a8a3 \ - --hash=sha256:8fd386d0ec1f9343f1776391d9e60d4eedced0a0b0e625bb89b91f6d05f70e83 \ - --hash=sha256:90a9ba3a9c16b86afcb785b3c9418af39ccfb238fd5f6e429166e3ca8542b01f \ - --hash=sha256:91a228a077fc7cd8486c273788d4a006a37d060cb4293f471eb0325c3113af68 \ - --hash=sha256:9cf18c156b3a108197a8bf90b37d03c31c8ef35a7c18807b321d96b74e12c301 \ - --hash=sha256:9ec62a4c2dbb0a86ee5138c16ef133e59a23ac108f8d7ac97aeb61d410ce6857 \ - --hash=sha256:a1991cdf2e81e643b53fb8d272931d2bdf5f4e70d56a457e1ef95bde147ae627 \ - --hash=sha256:a628f20d019feb0f3a171c7a55cc4f75681f3b8c1bd7a5009165a487314887cd \ - --hash=sha256:a71809ec062c5b7acf286ba6d4484e6fe8130fc2b93c25e596bb34e7810c79b2 \ - --hash=sha256:a7756a9446f83c81101f6c0a48c3bfd8d387a249933c57b0d095ca8b20541337 \ - --hash=sha256:a827b9c464ee966524f8e82ec1aabb4a77ff9514cae041667fa81ae2ec8bd3e9 \ - --hash=sha256:b1ad6d2952b41d9a0ea702a474cc08c05210c6289e29dd496935c9ca3c7fb45c \ - --hash=sha256:b4e671c4c0804cdf752be26f260058bb858fbdaaef1340af170635913ecca01e \ - --hash=sha256:bd842ae3dbb7cba88beb022161c819fa80ca7d0c5a4ddd209e7daae85d904e49 \ - --hash=sha256:bdf691a205bc492956e6daef7a06fb38f8cbe8b2c1cb0386f35f4412c360c9e9 \ - --hash=sha256:c19d1e06569c277dcc872d80cbadf14a29e8199e013ff2a176d169f461439a40 \ - --hash=sha256:c81fd9386449df0ebf1ab3e01187bb30d61122c74df53ba4880a2454d866e55d \ - --hash=sha256:d0e9fec68e304fb35c559c44530213adbc7d5918bdab906a45a0f40cd56c4de2 \ - --hash=sha256:d1405caa964ba11b2396bd9fd19940440217345752e192c936d084ba5fe67dcb \ - --hash=sha256:d5373a56b90052f171c8634fedc53a6ac371e6c742606e9825772a394bdbd4b0 \ - --hash=sha256:d78aac2ffc4e88ab1cbcad844669924c24e24c7c255de9628a18f14d832007c5 \ - --hash=sha256:d916018289d2f9a882e90d2e3bd41652861ce11b5ecd8515fa07ad31d97d56e5 \ - --hash=sha256:db993a56e21d903893933887984ca9b0d274f2b1db7b3cf21ba129783953864f \ - --hash=sha256:de1aa618306a741e0497878b7f845fd6c397e52dd096fb76ed791e7268887176 \ - --hash=sha256:e37c4e21f696d6bcdbbc7caf98dffa505d04c0053909b9db0a6e8ca3b935eb07 \ - --hash=sha256:ef62eb3bcfd6d786f439828bb544ebd3936432db669403e0b8f48e424f1d55f1 \ - --hash=sha256:f0c87f097d6867833a839b086eb8d03676bb87c2efa067a131099f04aa790683 \ - --hash=sha256:f2e3ea5e4d5ecf3faefd4a5294acb6af1f0578b0cdd75d6b4529c45deaa54d6f \ - --hash=sha256:f502fe79757434292174b04db114f9e25c767b2d5ca9e759d118b22a66f445f8 \ - --hash=sha256:fa9194cb91441df7242aa3ddc4cb184be38876cb10dd973674887f334bafbfb6 +zstandard==0.18.0 \ + --hash=sha256:083dc08abf03807af9beeb2b6a91c23ad78add2499f828176a3c7b742c44df02 \ + --hash=sha256:0ac0357a0d985b4ff31a854744040d7b5754385d1f98f7145c30e02c6865cb6f \ + --hash=sha256:19cac7108ff2c342317fad6dc97604b47a41f403c8f19d0bfc396dfadc3638b8 \ + --hash=sha256:1af1268a7dc870eb27515fb8db1f3e6c5a555d2b7bcc476fc3bab8886c7265ab \ + --hash=sha256:1be31e9e3f7607ee0cdd60915410a5968b205d3e7aa83b7fcf3dd76dbbdb39e0 \ + --hash=sha256:1dc2d3809e763055a1a6c1a73f2b677320cc9a5aa1a7c6cfb35aee59bddc42d9 \ + --hash=sha256:266aba27fa9cc5e9091d3d325ebab1fa260f64e83e42516d5e73947c70216a5b \ + --hash=sha256:28723a1d2e4df778573b76b321ebe9f3469ac98988104c2af116dd344802c3f8 \ + --hash=sha256:2dc466207016564805e56d28375f4f533b525ff50d6776946980dff5465566ac \ + --hash=sha256:39e98cf4773234bd9cebf9f9db730e451dfcfe435e220f8921242afda8321887 \ + --hash=sha256:3af8c2383d02feb6650e9255491ec7d0824f6e6dd2bbe3e521c469c985f31fb1 \ + --hash=sha256:46f679bc5dfd938db4fb058218d9dc4db1336ffaf1ea774ff152ecadabd40805 \ + --hash=sha256:490d11b705b8ae9dc845431bacc8dd1cef2408aede176620a5cd0cd411027936 \ + --hash=sha256:49685bf9a55d1ab34bd8423ea22db836ba43a181ac6b045ac4272093d5cb874e \ + --hash=sha256:4a2ee1d4f98447f3e5183ecfce5626f983504a4a0c005fbe92e60fa8e5d547ec \ + --hash=sha256:4cbb85f29a990c2fdbf7bc63246567061a362ddca886d7fae6f780267c0a9e67 \ + --hash=sha256:5228e596eb1554598c872a337bbe4e5afe41cd1f8b1b15f2e35b50d061e35244 \ + --hash=sha256:533db8a6fac6248b2cb2c935e7b92f994efbdeb72e1ffa0b354432e087bb5a3e \ + --hash=sha256:63694a376cde0aa8b1971d06ca28e8f8b5f492779cb6ee1cc46bbc3f019a42a5 \ + --hash=sha256:702a8324cd90c74d9c8780d02bf55e79da3193c870c9665ad3a11647e3ad1435 \ + --hash=sha256:7231543d38d2b7e02ef7cc78ef7ffd86419437e1114ff08709fe25a160e24bd6 \ + --hash=sha256:75479e7c2b3eebf402c59fbe57d21bc400cefa145ca356ee053b0a08908c5784 \ + --hash=sha256:76725d1ee83a8915100a310bbad5d9c1fc6397410259c94033b8318d548d9990 \ + --hash=sha256:8677ffc6a6096cccbd892e558471c901fd821aba12b7fbc63833c7346f549224 \ + --hash=sha256:8b2260c4e07dd0723eadb586de7718b61acca4083a490dda69c5719d79bc715c \ + --hash=sha256:999a4e1768f219826ba3fa2064fab1c86dd72fdd47a42536235478c3bb3ca3e2 \ + --hash=sha256:9df59cd1cf3c62075ee2a4da767089d19d874ac3ad42b04a71a167e91b384722 \ + --hash=sha256:a7fa67cba473623848b6e88acf8d799b1906178fd883fb3a1da24561c779593b \ + --hash=sha256:bd3220d7627fd4d26397211cb3b560ec7cc4a94b75cfce89e847e8ce7fabe32d \ + --hash=sha256:bfa6c8549fa18e6497a738b7033c49f94a8e2e30c5fbe2d14d0b5aa8bbc1695d \ + --hash=sha256:c86befac87445927488f5c8f205d11566f64c11519db223e9d282b945fa60dab \ + --hash=sha256:c990063664c08169c84474acecc9251ee035871589025cac47c060ff4ec4bc1a \ + --hash=sha256:cdb44d7284c8c5dd1b66dfb86dda7f4560fa94bfbbc1d2da749ba44831335e32 \ + --hash=sha256:ce6f59cba9854fd14da5bfe34217a1501143057313966637b7291d1b0267bd1e \ + --hash=sha256:d4a8fd45746a6c31e729f35196e80b8f1e9987c59f5ccb8859d7c6a6fbeb9c63 \ + --hash=sha256:d6c85ca5162049ede475b7ec98e87f9390501d44a3d6776ddd504e872464ec25 \ + --hash=sha256:d716a7694ce1fa60b20bc10f35c4a22be446ef7f514c8dbc8f858b61976de2fb \ + --hash=sha256:d85bfabad444812133a92fc6fbe463e1d07581dba72f041f07a360e63808b23c \ + --hash=sha256:d956e2f03c7200d7e61345e0880c292783ec26618d0d921dcad470cb195bbce2 \ + --hash=sha256:dbb3cb8a082d62b8a73af42291569d266b05605e017a3d8a06a0e5c30b5f10f0 \ + --hash=sha256:dc2a4de9f363b3247d472362a65041fe4c0f59e01a2846b15d13046be866a885 \ + --hash=sha256:e02043297c1832f2666cd2204f381bef43b10d56929e13c42c10c732c6e3b4ed \ + --hash=sha256:eea18c1e7442f2aa9aff1bb84550dbb6a1f711faf6e48e7319de8f2b2e923c2a \ + --hash=sha256:ef7e8a200e4c8ac9102ed3c90ed2aa379f6b880f63032200909c1be21951f556 # via rosbags (setup.cfg) + +# WARNING: The following packages were not pinned, but pip requires them to be +# pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag. +# setuptools diff --git a/requirements.txt b/requirements.txt index 7b0f44ba..42a0f1ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,50 +4,52 @@ # # pip-compile --generate-hashes setup.cfg # -lz4==4.0.0 \ - --hash=sha256:04067086a443eef46eb2dfc26e1e5a76165149ceb4a88f0b540b46ead95e39c8 \ - --hash=sha256:19e939cd1e5d1776ca8f431c18c10a59970cf98caceeaa6edc98fdcf58499f29 \ - --hash=sha256:1ef9b03386757546f962e0598ac1d863960018dd9c04ec207059d3eb52bba12b \ - --hash=sha256:2428f5525c214d8cca332a96a2561415fd1261be2372b68b32a1aa40b9c9000f \ - --hash=sha256:26e4e7f88757c31e5240016b863f37ad79fc2898be272a6d78f267963c1b094b \ - --hash=sha256:2e1fa0dba94a7dece5d0fe109e317242c28f09e1ad488b8db692fcafb094db79 \ - --hash=sha256:3221e9a46d343175cefe932b0e812f2ecd3de70c7d036b951488d664587bee4a \ - --hash=sha256:463814c29f1201ef876c031ad32a185adee807ae201c228b28d65f17b203ee11 \ - --hash=sha256:4bf2880cae9a5255f86698f60af635f184e49d5f6878a7e0520b6cfcdb0a0d50 \ - --hash=sha256:4d96e913877b687bb8e8c6f8a90ce1e2a9a5c5268d9bab489f49bd3ce9ae8afa \ - --hash=sha256:5721ec225a37794fbaabcfa5cf2289ebb4b9e770e73e4e779d514c1fc784df5b \ - --hash=sha256:57c5dfd3b7dae833b0d2b2c1aafd7f9d0dfcab40683d183d010c67c9fd1beca3 \ - --hash=sha256:6a4c004e664d8185e2bfeffb90e1bfe554a0cd1a764662648b528e37220822cb \ - --hash=sha256:6c9acd054426840de2e4bbf83321945c3a20e90402ad221f4302e983f8031e14 \ - --hash=sha256:79246da3207b9eb4e53b3bed86189a60631e74f9b3d2579918b032fb1c7b114b \ - --hash=sha256:7ec46449892159c869c88848ee2c9b3b5170d193ba79524732334e7bbc39639c \ - --hash=sha256:a4afc2c12c37896e5ac7c5343f255cc242962ed7af940f6ef5276cc5c22e81fd \ - --hash=sha256:afc6bebfcbc48873854c05366b35a69b9c4e440ce17571ade032941cb89585ac \ - --hash=sha256:b83fce61cec36cdc21d234524d60a96d70f1a928533228eae6f46a9de21dc218 \ - --hash=sha256:e05542c6cbdb1128c43cfefd7518e231b39329d212b19ab89fd3868596140bdf \ - --hash=sha256:f69405f196c6fb38b94ac6000baa59c0364a1ac264e64194bb2fc48513df79ad +lz4==4.0.2 \ + --hash=sha256:083b7172c2938412ae37c3a090250bfdd9e4a6e855442594f86c3608ed12729b \ + --hash=sha256:154e6e9f58a7bafc4d2a1395160305b78fc82fa708bfa58cf0ad977c443d1f8f \ + --hash=sha256:1bd56282f6993e013ccf7f6edf1530c2a13d1662741e2be072349c7f70bc0682 \ + --hash=sha256:1ed9a1875dc2a489f3b665d0211984689d0e76585e55650b044a64dbd2d22992 \ + --hash=sha256:345608de23b4d68fbdef373f1e53d6c5abd99a062d4ff922e3350f47775ab123 \ + --hash=sha256:35e6caced0229b90151d31d9cf1eaa541e597f8021bf5b70ff9e6374e3e43b23 \ + --hash=sha256:3881573c3db902db370e072eb64b40c7c8289b94b2a731e051858cc198f890e8 \ + --hash=sha256:3fa0f000d8ce39e643e9e5c49fc4d1985156ffb177e3123a0f22551f5864841b \ + --hash=sha256:439898dd4176a724243002003c3f733eb6ce48a5988175f54c8560e0b100b7a6 \ + --hash=sha256:4cfa82f26b4f1835c797bd70e5ce20d5f1ee897b9a0c53e62d607f9029f521ce \ + --hash=sha256:56ea660097fec87f0c6746146b316775037f8dd886a4c5915360e5b32b7112d0 \ + --hash=sha256:5fe9db7627674875e4279c2ed50b1e38fb91ec3093347f871ed996e58edbb488 \ + --hash=sha256:61dbcca64e8e1655e06b588356c4b2515bccc1d7e84065f858a685abd96f0cf2 \ + --hash=sha256:6f3b3670f52f0871885258bcbc746f483760434336f0bc5581f161cc5d4b0c9a \ + --hash=sha256:9d141719d3cbb7933809642a61b68b8f595ddf85657016521756ddcf826b85cd \ + --hash=sha256:a8e02c2477bd704f43113ac8dd966c361187383591388818d74e1b73e4674759 \ + --hash=sha256:d2b18a6d6d9071c03dbf9e30bbe22e4476f24f1a4d73b1e975605ad3ce725e6c \ + --hash=sha256:ea2c2182a5b0ad03f33ac09db0925a1738a1d65751a3e058110bd900c643d359 \ + --hash=sha256:ed86ab22bfe1f4cd4fc983704134a8fdf746c1121a398f8f14cbd014c1a5b0ae \ + --hash=sha256:ee73357412c5505f6ba0ea61ff71455e2e4c1e04d8e60f17f3cd937261d773fa \ + --hash=sha256:fba1730cd2327a9d013192a9878714cc82f4877d2ada556222d03ea6428a80ed # via rosbags (setup.cfg) -numpy==1.22.3 \ - --hash=sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676 \ - --hash=sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4 \ - --hash=sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce \ - --hash=sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123 \ - --hash=sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1 \ - --hash=sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e \ - --hash=sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5 \ - --hash=sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d \ - --hash=sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a \ - --hash=sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab \ - --hash=sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75 \ - --hash=sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168 \ - --hash=sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4 \ - --hash=sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f \ - --hash=sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18 \ - --hash=sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62 \ - --hash=sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe \ - --hash=sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430 \ - --hash=sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802 \ - --hash=sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa +numpy==1.23.1 \ + --hash=sha256:1408c3527a74a0209c781ac82bde2182b0f0bf54dea6e6a363fe0cc4488a7ce7 \ + --hash=sha256:173f28921b15d341afadf6c3898a34f20a0569e4ad5435297ba262ee8941e77b \ + --hash=sha256:1865fdf51446839ca3fffaab172461f2b781163f6f395f1aed256b1ddc253622 \ + --hash=sha256:3119daed207e9410eaf57dcf9591fdc68045f60483d94956bee0bfdcba790953 \ + --hash=sha256:35590b9c33c0f1c9732b3231bb6a72d1e4f77872390c47d50a615686ae7ed3fd \ + --hash=sha256:37e5ebebb0eb54c5b4a9b04e6f3018e16b8ef257d26c8945925ba8105008e645 \ + --hash=sha256:37ece2bd095e9781a7156852e43d18044fd0d742934833335599c583618181b9 \ + --hash=sha256:3ab67966c8d45d55a2bdf40701536af6443763907086c0a6d1232688e27e5447 \ + --hash=sha256:47f10ab202fe4d8495ff484b5561c65dd59177949ca07975663f4494f7269e3e \ + --hash=sha256:55df0f7483b822855af67e38fb3a526e787adf189383b4934305565d71c4b148 \ + --hash=sha256:5d732d17b8a9061540a10fda5bfeabca5785700ab5469a5e9b93aca5e2d3a5fb \ + --hash=sha256:68b69f52e6545af010b76516f5daaef6173e73353e3295c5cb9f96c35d755641 \ + --hash=sha256:7e8229f3687cdadba2c4faef39204feb51ef7c1a9b669247d49a24f3e2e1617c \ + --hash=sha256:8002574a6b46ac3b5739a003b5233376aeac5163e5dcd43dd7ad062f3e186129 \ + --hash=sha256:876f60de09734fbcb4e27a97c9a286b51284df1326b1ac5f1bf0ad3678236b22 \ + --hash=sha256:9ce242162015b7e88092dccd0e854548c0926b75c7924a3495e02c6067aba1f5 \ + --hash=sha256:a35c4e64dfca659fe4d0f1421fc0f05b8ed1ca8c46fb73d9e5a7f175f85696bb \ + --hash=sha256:aeba539285dcf0a1ba755945865ec61240ede5432df41d6e29fab305f4384db2 \ + --hash=sha256:b15c3f1ed08df4980e02cc79ee058b788a3d0bef2fb3c9ca90bb8cbd5b8a3a04 \ + --hash=sha256:c2f91f88230042a130ceb1b496932aa717dcbd665350beb821534c5c7e15881c \ + --hash=sha256:d748ef349bfef2e1194b59da37ed5a29c19ea8d7e6342019921ba2ba4fd8b624 \ + --hash=sha256:e0d7447679ae9a7124385ccf0ea990bb85bb869cef217e2ea6c844b6a6855073 # via rosbags (setup.cfg) ruamel-yaml==0.17.21 \ --hash=sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7 \ @@ -80,49 +82,49 @@ ruamel-yaml-clib==0.2.6 \ --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c # via ruamel-yaml -zstandard==0.17.0 \ - --hash=sha256:208fa6bead577b2607205640078ee452e81fe20fe96321623c632bad9ebd7148 \ - --hash=sha256:2a2ac752162ba5cbc869c60c4a4e54e890b2ee2ffb57d3ff159feab1ae4518db \ - --hash=sha256:37e50501baaa935f13a1820ab2114f74313b5cb4cfff8146acb8c5b18cdced2a \ - --hash=sha256:3cf96ace804945e53bc3e5294097e5fa32a2d43bc52416c632b414b870ee0a21 \ - --hash=sha256:42f3c02c7021073cafbc6cd152b288c56a25e585518861589bb08b063b6d2ad2 \ - --hash=sha256:4768449d8d1b0785309ace288e017cc5fa42e11a52bf08c90d9c3eb3a7a73cc6 \ - --hash=sha256:477f172807a9fa83467b30d7c58876af1410d20177c554c27525211edf535bae \ - --hash=sha256:49cd09ccbd1e3c0e2690dd62ebf95064d84aa42b9db381867e0b138631f969f2 \ - --hash=sha256:59eadb9f347d40e8f7ef77caffd0c04a31e82c1df82fe2d2a688032429d750ac \ - --hash=sha256:60943f71e3117583655a1eb76188a7cc78a25267ef09cc74be4d25a0b0c8b947 \ - --hash=sha256:787efc741e61e00ffe5e65dac99b0dc5c88b9421012a207a91b869a8b1164921 \ - --hash=sha256:7a3a1aa9528087f6f4c47f4ece2d5e6a160527821263fb8174ff36429233e093 \ - --hash=sha256:7d2e7abac41d2b4b18f03575aca860d2cb647c343e13c23d6c769106a3db2f6f \ - --hash=sha256:802109f67328c5b822d4fdac28e1cf65a24de2e2e99d76cdbeee9121cedb1b6c \ - --hash=sha256:8aedd38d357f6d5e2facd88ce62b4976afdc29db57216a23f14a0cd0ca05a8a3 \ - --hash=sha256:8fd386d0ec1f9343f1776391d9e60d4eedced0a0b0e625bb89b91f6d05f70e83 \ - --hash=sha256:90a9ba3a9c16b86afcb785b3c9418af39ccfb238fd5f6e429166e3ca8542b01f \ - --hash=sha256:91a228a077fc7cd8486c273788d4a006a37d060cb4293f471eb0325c3113af68 \ - --hash=sha256:9cf18c156b3a108197a8bf90b37d03c31c8ef35a7c18807b321d96b74e12c301 \ - --hash=sha256:9ec62a4c2dbb0a86ee5138c16ef133e59a23ac108f8d7ac97aeb61d410ce6857 \ - --hash=sha256:a1991cdf2e81e643b53fb8d272931d2bdf5f4e70d56a457e1ef95bde147ae627 \ - --hash=sha256:a628f20d019feb0f3a171c7a55cc4f75681f3b8c1bd7a5009165a487314887cd \ - --hash=sha256:a71809ec062c5b7acf286ba6d4484e6fe8130fc2b93c25e596bb34e7810c79b2 \ - --hash=sha256:a7756a9446f83c81101f6c0a48c3bfd8d387a249933c57b0d095ca8b20541337 \ - --hash=sha256:a827b9c464ee966524f8e82ec1aabb4a77ff9514cae041667fa81ae2ec8bd3e9 \ - --hash=sha256:b1ad6d2952b41d9a0ea702a474cc08c05210c6289e29dd496935c9ca3c7fb45c \ - --hash=sha256:b4e671c4c0804cdf752be26f260058bb858fbdaaef1340af170635913ecca01e \ - --hash=sha256:bd842ae3dbb7cba88beb022161c819fa80ca7d0c5a4ddd209e7daae85d904e49 \ - --hash=sha256:bdf691a205bc492956e6daef7a06fb38f8cbe8b2c1cb0386f35f4412c360c9e9 \ - --hash=sha256:c19d1e06569c277dcc872d80cbadf14a29e8199e013ff2a176d169f461439a40 \ - --hash=sha256:c81fd9386449df0ebf1ab3e01187bb30d61122c74df53ba4880a2454d866e55d \ - --hash=sha256:d0e9fec68e304fb35c559c44530213adbc7d5918bdab906a45a0f40cd56c4de2 \ - --hash=sha256:d1405caa964ba11b2396bd9fd19940440217345752e192c936d084ba5fe67dcb \ - --hash=sha256:d5373a56b90052f171c8634fedc53a6ac371e6c742606e9825772a394bdbd4b0 \ - --hash=sha256:d78aac2ffc4e88ab1cbcad844669924c24e24c7c255de9628a18f14d832007c5 \ - --hash=sha256:d916018289d2f9a882e90d2e3bd41652861ce11b5ecd8515fa07ad31d97d56e5 \ - --hash=sha256:db993a56e21d903893933887984ca9b0d274f2b1db7b3cf21ba129783953864f \ - --hash=sha256:de1aa618306a741e0497878b7f845fd6c397e52dd096fb76ed791e7268887176 \ - --hash=sha256:e37c4e21f696d6bcdbbc7caf98dffa505d04c0053909b9db0a6e8ca3b935eb07 \ - --hash=sha256:ef62eb3bcfd6d786f439828bb544ebd3936432db669403e0b8f48e424f1d55f1 \ - --hash=sha256:f0c87f097d6867833a839b086eb8d03676bb87c2efa067a131099f04aa790683 \ - --hash=sha256:f2e3ea5e4d5ecf3faefd4a5294acb6af1f0578b0cdd75d6b4529c45deaa54d6f \ - --hash=sha256:f502fe79757434292174b04db114f9e25c767b2d5ca9e759d118b22a66f445f8 \ - --hash=sha256:fa9194cb91441df7242aa3ddc4cb184be38876cb10dd973674887f334bafbfb6 +zstandard==0.18.0 \ + --hash=sha256:083dc08abf03807af9beeb2b6a91c23ad78add2499f828176a3c7b742c44df02 \ + --hash=sha256:0ac0357a0d985b4ff31a854744040d7b5754385d1f98f7145c30e02c6865cb6f \ + --hash=sha256:19cac7108ff2c342317fad6dc97604b47a41f403c8f19d0bfc396dfadc3638b8 \ + --hash=sha256:1af1268a7dc870eb27515fb8db1f3e6c5a555d2b7bcc476fc3bab8886c7265ab \ + --hash=sha256:1be31e9e3f7607ee0cdd60915410a5968b205d3e7aa83b7fcf3dd76dbbdb39e0 \ + --hash=sha256:1dc2d3809e763055a1a6c1a73f2b677320cc9a5aa1a7c6cfb35aee59bddc42d9 \ + --hash=sha256:266aba27fa9cc5e9091d3d325ebab1fa260f64e83e42516d5e73947c70216a5b \ + --hash=sha256:28723a1d2e4df778573b76b321ebe9f3469ac98988104c2af116dd344802c3f8 \ + --hash=sha256:2dc466207016564805e56d28375f4f533b525ff50d6776946980dff5465566ac \ + --hash=sha256:39e98cf4773234bd9cebf9f9db730e451dfcfe435e220f8921242afda8321887 \ + --hash=sha256:3af8c2383d02feb6650e9255491ec7d0824f6e6dd2bbe3e521c469c985f31fb1 \ + --hash=sha256:46f679bc5dfd938db4fb058218d9dc4db1336ffaf1ea774ff152ecadabd40805 \ + --hash=sha256:490d11b705b8ae9dc845431bacc8dd1cef2408aede176620a5cd0cd411027936 \ + --hash=sha256:49685bf9a55d1ab34bd8423ea22db836ba43a181ac6b045ac4272093d5cb874e \ + --hash=sha256:4a2ee1d4f98447f3e5183ecfce5626f983504a4a0c005fbe92e60fa8e5d547ec \ + --hash=sha256:4cbb85f29a990c2fdbf7bc63246567061a362ddca886d7fae6f780267c0a9e67 \ + --hash=sha256:5228e596eb1554598c872a337bbe4e5afe41cd1f8b1b15f2e35b50d061e35244 \ + --hash=sha256:533db8a6fac6248b2cb2c935e7b92f994efbdeb72e1ffa0b354432e087bb5a3e \ + --hash=sha256:63694a376cde0aa8b1971d06ca28e8f8b5f492779cb6ee1cc46bbc3f019a42a5 \ + --hash=sha256:702a8324cd90c74d9c8780d02bf55e79da3193c870c9665ad3a11647e3ad1435 \ + --hash=sha256:7231543d38d2b7e02ef7cc78ef7ffd86419437e1114ff08709fe25a160e24bd6 \ + --hash=sha256:75479e7c2b3eebf402c59fbe57d21bc400cefa145ca356ee053b0a08908c5784 \ + --hash=sha256:76725d1ee83a8915100a310bbad5d9c1fc6397410259c94033b8318d548d9990 \ + --hash=sha256:8677ffc6a6096cccbd892e558471c901fd821aba12b7fbc63833c7346f549224 \ + --hash=sha256:8b2260c4e07dd0723eadb586de7718b61acca4083a490dda69c5719d79bc715c \ + --hash=sha256:999a4e1768f219826ba3fa2064fab1c86dd72fdd47a42536235478c3bb3ca3e2 \ + --hash=sha256:9df59cd1cf3c62075ee2a4da767089d19d874ac3ad42b04a71a167e91b384722 \ + --hash=sha256:a7fa67cba473623848b6e88acf8d799b1906178fd883fb3a1da24561c779593b \ + --hash=sha256:bd3220d7627fd4d26397211cb3b560ec7cc4a94b75cfce89e847e8ce7fabe32d \ + --hash=sha256:bfa6c8549fa18e6497a738b7033c49f94a8e2e30c5fbe2d14d0b5aa8bbc1695d \ + --hash=sha256:c86befac87445927488f5c8f205d11566f64c11519db223e9d282b945fa60dab \ + --hash=sha256:c990063664c08169c84474acecc9251ee035871589025cac47c060ff4ec4bc1a \ + --hash=sha256:cdb44d7284c8c5dd1b66dfb86dda7f4560fa94bfbbc1d2da749ba44831335e32 \ + --hash=sha256:ce6f59cba9854fd14da5bfe34217a1501143057313966637b7291d1b0267bd1e \ + --hash=sha256:d4a8fd45746a6c31e729f35196e80b8f1e9987c59f5ccb8859d7c6a6fbeb9c63 \ + --hash=sha256:d6c85ca5162049ede475b7ec98e87f9390501d44a3d6776ddd504e872464ec25 \ + --hash=sha256:d716a7694ce1fa60b20bc10f35c4a22be446ef7f514c8dbc8f858b61976de2fb \ + --hash=sha256:d85bfabad444812133a92fc6fbe463e1d07581dba72f041f07a360e63808b23c \ + --hash=sha256:d956e2f03c7200d7e61345e0880c292783ec26618d0d921dcad470cb195bbce2 \ + --hash=sha256:dbb3cb8a082d62b8a73af42291569d266b05605e017a3d8a06a0e5c30b5f10f0 \ + --hash=sha256:dc2a4de9f363b3247d472362a65041fe4c0f59e01a2846b15d13046be866a885 \ + --hash=sha256:e02043297c1832f2666cd2204f381bef43b10d56929e13c42c10c732c6e3b4ed \ + --hash=sha256:eea18c1e7442f2aa9aff1bb84550dbb6a1f711faf6e48e7319de8f2b2e923c2a \ + --hash=sha256:ef7e8a200e4c8ac9102ed3c90ed2aa379f6b880f63032200909c1be21951f556 # via rosbags (setup.cfg) From 17f4d54449c7c345cf34a447a0b0abb34ce807ad Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 27 Jul 2022 17:28:39 +0200 Subject: [PATCH 094/114] Release 0.9.12 --- CHANGES.rst | 9 +++++++++ setup.cfg | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1341603a..b834c109 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,15 @@ Changes ======= +0.9.12 - 2022-07-27 +------------------- +- Add support for rosbag2 version 6 metadata `#30`_ +- Enable rosbags-convert to exclude topics `#25`_ + +.. _#30: https://gitlab.com/ternaris/rosbags/issues/30 +.. _#25: https://gitlab.com/ternaris/rosbags/issues/25 + + 0.9.11 - 2022-05-17 ------------------- - Report start_time and end_time on empty bags diff --git a/setup.cfg b/setup.cfg index 7324b291..10838a3c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.11 +version = 0.9.12 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From 1de738013890245b10db12770daf1c3398e8030d Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 28 Jul 2022 19:14:39 +0200 Subject: [PATCH 095/114] Fix comment parsing in message definitions --- src/rosbags/typesys/idl.py | 16 ++++------- src/rosbags/typesys/msg.py | 12 ++------ src/rosbags/typesys/peg.py | 57 ++++++++++++++++++++++++++------------ tests/test_parse.py | 9 +++++- 4 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index 84522269..cd8d3b47 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -11,6 +11,7 @@ Grammar, parse tree visitor and conversion functions for message definitions in from __future__ import annotations +import re from typing import TYPE_CHECKING from .base import Nodetype, parse_message_definition @@ -31,17 +32,12 @@ specification = definition+ definition - = comment - / macro + = macro / include / module_dcl ';' / const_dcl ';' / type_dcl ';' -comment - = r'/\*.*?\*/' - / r'[/][/][^\n]*' - macro = ifndef / define @@ -254,7 +250,10 @@ string_literal class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods """IDL file visitor.""" - RULES = parse_grammar(GRAMMAR_IDL) + RULES = parse_grammar( + GRAMMAR_IDL, + re.compile(r'(\s|/[*]([^*]|[*](?!/))*[*]/|//[^\n]*$)+', re.M | re.S), + ) def __init__(self) -> None: """Initialize.""" @@ -299,9 +298,6 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods return {k: (consts[k], v) for k, v in structs.items()} # yapf: enable - def visit_comment(self, _: str) -> None: - """Process comment, suppress output.""" - def visit_macro(self, _: Union[LiteralMatch, tuple[LiteralMatch, str]]) -> None: """Process macro, suppress output.""" diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 0ba942b4..61245dec 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -12,6 +12,7 @@ Rosbag1 connection information. from __future__ import annotations +import re from hashlib import md5 from pathlib import PurePosixPath as Path from typing import TYPE_CHECKING @@ -43,13 +44,9 @@ msgsep = r'================================================================================' definition - = comment - / const_dcl + = const_dcl / field_dcl -comment - = r'#[^\n]*' - const_dcl = 'string' identifier '=' r'(?!={79}\n)[^\n]+' / type_spec identifier '=' float_literal @@ -205,7 +202,7 @@ def denormalize_msgtype(typename: str) -> str: class VisitorMSG(Visitor): """MSG file visitor.""" - RULES = parse_grammar(GRAMMAR_MSG) + RULES = parse_grammar(GRAMMAR_MSG, re.compile(r'(\s|#[^\n]*$)+', re.M | re.S)) BASETYPES = { 'bool', @@ -222,9 +219,6 @@ class VisitorMSG(Visitor): 'string', } - def visit_comment(self, _: str) -> None: - """Process comment, suppress output.""" - def visit_const_dcl( self, children: tuple[StringNode, StringNode, LiteralMatch, ConstValue], diff --git a/src/rosbags/typesys/peg.py b/src/rosbags/typesys/peg.py index bb2b9dba..1c296ea9 100644 --- a/src/rosbags/typesys/peg.py +++ b/src/rosbags/typesys/peg.py @@ -24,12 +24,12 @@ class Rule: """Rule base class.""" LIT = 'LITERAL' - WS = re.compile(r'\s+', re.M | re.S) def __init__( self, value: Union[str, Pattern[str], Rule, list[Rule]], rules: dict[str, Rule], + whitespace: Pattern[str], name: Optional[str] = None, ): """Initialize. @@ -37,16 +37,18 @@ class Rule: Args: value: Value of this rule. rules: Grammar containing all rules. + whitespace: Whitespace pattern. name: Name of this rule. """ self.value = value self.rules = rules self.name = name + self.whitespace = whitespace def skip_ws(self, text: str, pos: int) -> int: """Skip whitespace.""" - match = self.WS.match(text, pos) + match = self.whitespace.match(text, pos) return match.span()[1] if match else pos def make_node(self, data: T) -> Union[T, dict[str, Union[str, T]]]: @@ -61,16 +63,23 @@ class Rule: class RuleLiteral(Rule): """Rule to match string literal.""" - def __init__(self, value: str, rules: dict[str, Rule], name: Optional[str] = None): + def __init__( + self, + value: str, + rules: dict[str, Rule], + whitespace: Pattern[str], + name: Optional[str] = None, + ): """Initialize. Args: value: Value of this rule. rules: Grammar containing all rules. + whitespace: Whitespace pattern. name: Name of this rule. """ - super().__init__(value, rules, name) + super().__init__(value, rules, whitespace, name) self.value = value[1:-1].replace('\\\'', '\'') def parse(self, text: str, pos: int) -> tuple[int, Any]: @@ -89,16 +98,23 @@ class RuleRegex(Rule): value: Pattern[str] - def __init__(self, value: str, rules: dict[str, Rule], name: Optional[str] = None): + def __init__( + self, + value: str, + rules: dict[str, Rule], + whitespace: Pattern[str], + name: Optional[str] = None, + ): """Initialize. Args: value: Value of this rule. rules: Grammar containing all rules. + whitespace: Whitespace pattern. name: Name of this rule. """ - super().__init__(value, rules, name) + super().__init__(value, rules, whitespace, name) self.value = re.compile(value[2:-1], re.M | re.S) def parse(self, text: str, pos: int) -> tuple[int, Any]: @@ -234,7 +250,11 @@ def split_token(tok: str) -> list[str]: return list(filter(None, re.split(r'(^\()|(\)(?=[*+?]?$))|([*+?]$)', tok))) -def collapse_tokens(toks: list[Optional[Rule]], rules: dict[str, Rule]) -> Rule: +def collapse_tokens( + toks: list[Optional[Rule]], + rules: dict[str, Rule], + whitespace: Pattern[str], +) -> Rule: """Collapse linear list of tokens to oneof of sequences.""" value: list[Rule] = [] seq: list[Rule] = [] @@ -242,13 +262,16 @@ def collapse_tokens(toks: list[Optional[Rule]], rules: dict[str, Rule]) -> Rule: if tok: seq.append(tok) else: - value.append(RuleSequence(seq, rules) if len(seq) > 1 else seq[0]) + value.append(RuleSequence(seq, rules, whitespace) if len(seq) > 1 else seq[0]) seq = [] - value.append(RuleSequence(seq, rules) if len(seq) > 1 else seq[0]) - return RuleOneof(value, rules) if len(value) > 1 else value[0] + value.append(RuleSequence(seq, rules, whitespace) if len(seq) > 1 else seq[0]) + return RuleOneof(value, rules, whitespace) if len(value) > 1 else value[0] -def parse_grammar(grammar: str) -> dict[str, Rule]: +def parse_grammar( + grammar: str, + whitespace: Pattern[str] = re.compile(r'\s+', re.M | re.S), +) -> dict[str, Rule]: """Parse grammar into rule dictionary.""" rules: dict[str, Rule] = {} for token in grammar.split('\n\n'): @@ -268,24 +291,24 @@ def parse_grammar(grammar: str) -> dict[str, Rule]: '*': RuleZeroPlus, '+': RuleOnePlus, '?': RuleZeroOne, - }[tok](stack[-1], rules) + }[tok](stack[-1], rules, whitespace) elif tok == '/': stack.append(None) elif tok == '(': parens.append(len(stack)) elif tok == ')': index = parens.pop() - rule = collapse_tokens(stack[index:], rules) + rule = collapse_tokens(stack[index:], rules, whitespace) stack = stack[:index] stack.append(rule) elif len(tok) > 2 and tok[:2] == 'r\'': - stack.append(RuleRegex(tok, rules)) + stack.append(RuleRegex(tok, rules, whitespace)) elif tok[0] == '\'': - stack.append(RuleLiteral(tok, rules)) + stack.append(RuleLiteral(tok, rules, whitespace)) else: - stack.append(RuleToken(tok, rules)) + stack.append(RuleToken(tok, rules, whitespace)) - res = collapse_tokens(stack, rules) + res = collapse_tokens(stack, rules, whitespace) res.name = name rules[name] = res return rules diff --git a/tests/test_parse.py b/tests/test_parse.py index 662aecfd..129f16d2 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -118,6 +118,12 @@ module test_msgs { // comment in module typedef std_msgs::msg::Bool Bool; + /**/ /***/ /* block comment */ + + /* + * block comment + */ + module msg { // comment in submodule typedef Bool Balias; @@ -131,10 +137,11 @@ module test_msgs { @comment(type="text", text="ignore") struct Foo { + // comment in struct std_msgs::msg::Header header; Balias bool; Bar sibling; - double x; + double/* comment in member declaration */x; sequence seq1; sequence seq2; d4 array; From a0c4516e2faf2ff864899f91daa808f855b66455 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 23 Sep 2022 12:15:08 +0200 Subject: [PATCH 096/114] Fix parsing of members starting with `string` --- src/rosbags/typesys/idl.py | 2 +- tests/test_parse.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index cd8d3b47..8fee131b 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -169,7 +169,7 @@ octet_type string_type = 'string' '<' expression '>' - / 'string' + / 'string\b' scoped_name = identifier '::' scoped_name diff --git a/tests/test_parse.py b/tests/test_parse.py index 129f16d2..49e6f1b4 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -154,6 +154,17 @@ module test_msgs { }; """ +IDL_STRINGARRAY = """ +module test_msgs { + module msg { + typedef string string__3[3]; + struct Strings { + string__3 values; + }; + }; +}; +""" + def test_parse_empty_msg() -> None: """Test msg parser with empty message.""" @@ -291,6 +302,13 @@ def test_parse_idl() -> None: assert fields[0][0] == 'i' assert fields[0][1][1] == 'int' + ret = get_types_from_idl(IDL_STRINGARRAY) + consts, fields = ret['test_msgs/msg/Strings'] + assert consts == [] + assert len(fields) == 1 + assert fields[0][0] == 'values' + assert fields[0][1] == (Nodetype.ARRAY, ((Nodetype.NAME, 'string'), 3)) + def test_register_types() -> None: """Test type registeration.""" From 4437512f05f68056e87e8e9b16808ae856f93840 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 23 Sep 2022 12:19:08 +0200 Subject: [PATCH 097/114] Change lz4 compression level to 0 --- src/rosbags/rosbag1/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index d1ba14ed..965e31aa 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -183,7 +183,7 @@ class Writer: self.compression_format = fmt.name.lower() bz2: Callable[[bytes], bytes] = lambda x: bz2_compress(x, 9) - lz4: Callable[[bytes], bytes] = lambda x: lz4_compress(x, 16) # type: ignore + lz4: Callable[[bytes], bytes] = lambda x: lz4_compress(x, 0) # type: ignore self.compressor = { 'bz2': bz2, 'lz4': lz4, From 1309d42b64b3b019963b1e7b193e66de1b000f50 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 23 Sep 2022 12:35:37 +0200 Subject: [PATCH 098/114] Add include filters to rosbag conversion --- src/rosbags/convert/__main__.py | 12 +++++++-- src/rosbags/convert/converter.py | 42 +++++++++++++++++++++++++------- tests/test_convert.py | 35 ++++++++++++++++++++++---- 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/rosbags/convert/__main__.py b/src/rosbags/convert/__main__.py index dcd09ce2..07e62317 100644 --- a/src/rosbags/convert/__main__.py +++ b/src/rosbags/convert/__main__.py @@ -50,12 +50,20 @@ def main() -> None: type=pathtype(exists=False), help='destination path for converted rosbag', ) - parser.add_argument( + topic_group = parser.add_argument_group('filtering').add_mutually_exclusive_group() + topic_group.add_argument( '--exclude-topic', action='append', default=[], dest='exclude_topics', - help='exclude topic by name', + help='topic to exclude from conversion, even if included explicitly', + ) + topic_group.add_argument( + '--include-topic', + action='append', + default=[], + dest='include_topics', + help='topic to include in conversion, instead of all', ) args = parser.parse_args() diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index 4667541a..11e2e488 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -99,13 +99,19 @@ def downgrade_connection(rconn: Connection) -> Connection: ) -def convert_1to2(src: Path, dst: Path, exclude_topics: Sequence[str]) -> None: +def convert_1to2( + src: Path, + dst: Path, + exclude_topics: Sequence[str], + include_topics: Sequence[str], +) -> None: """Convert Rosbag1 to Rosbag2. Args: src: Rosbag1 path. dst: Rosbag2 path. - exclude_topics: Topics to skip. + exclude_topics: Topics to exclude from conversion, even if included explicitly. + include_topics: Topics to include in conversion, instead of all. Raises: ConverterError: If all connections are excluded. @@ -114,7 +120,10 @@ def convert_1to2(src: Path, dst: Path, exclude_topics: Sequence[str]) -> None: with Reader1(src) as reader, Writer2(dst) as writer: typs: dict[str, Any] = {} connmap: dict[int, Connection] = {} - connections = [x for x in reader.connections if x.topic not in exclude_topics] + connections = [ + x for x in reader.connections + if x.topic not in exclude_topics and (not include_topics or x.topic in include_topics) + ] if not connections: raise ConverterError('No connections left for conversion.') for rconn in connections: @@ -143,13 +152,19 @@ def convert_1to2(src: Path, dst: Path, exclude_topics: Sequence[str]) -> None: writer.write(connmap[rconn.id], timestamp, data) -def convert_2to1(src: Path, dst: Path, exclude_topics: Sequence[str]) -> None: +def convert_2to1( + src: Path, + dst: Path, + exclude_topics: Sequence[str], + include_topics: Sequence[str], +) -> None: """Convert Rosbag2 to Rosbag1. Args: src: Rosbag2 path. dst: Rosbag1 path. - exclude_topics: Topics to skip. + exclude_topics: Topics to exclude from conversion, even if included explicitly. + include_topics: Topics to include in conversion, instead of all. Raises: ConverterError: If all connections are excluded. @@ -157,7 +172,10 @@ def convert_2to1(src: Path, dst: Path, exclude_topics: Sequence[str]) -> None: """ with Reader2(src) as reader, Writer1(dst) as writer: connmap: dict[int, Connection] = {} - connections = [x for x in reader.connections if x.topic not in exclude_topics] + connections = [ + x for x in reader.connections + if x.topic not in exclude_topics and (not include_topics or x.topic in include_topics) + ] if not connections: raise ConverterError('No connections left for conversion.') for rconn in connections: @@ -186,13 +204,19 @@ def convert_2to1(src: Path, dst: Path, exclude_topics: Sequence[str]) -> None: writer.write(connmap[rconn.id], timestamp, data) -def convert(src: Path, dst: Optional[Path], exclude_topics: Sequence[str] = ()) -> None: +def convert( + src: Path, + dst: Optional[Path], + exclude_topics: Sequence[str] = (), + include_topics: Sequence[str] = (), +) -> None: """Convert between Rosbag1 and Rosbag2. Args: src: Source rosbag. dst: Destination rosbag. - exclude_topics: Topics to skip. + exclude_topics: Topics to exclude from conversion, even if included explicitly. + include_topics: Topics to include in conversion, instead of all. Raises: ConverterError: An error occured during reading, writing, or @@ -206,7 +230,7 @@ def convert(src: Path, dst: Optional[Path], exclude_topics: Sequence[str] = ()) func = convert_1to2 if upgrade else convert_2to1 try: - func(src, dst, exclude_topics) + func(src, dst, exclude_topics, include_topics) except (ReaderError1, ReaderError2) as err: raise ConverterError(f'Reading source bag: {err}') from err except (WriterError1, WriterError2) as err: diff --git a/tests/test_convert.py b/tests/test_convert.py index 77bb5fce..abe3bcb8 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -42,7 +42,12 @@ def test_cliwrapper(tmp_path: Path) -> None: with patch('rosbags.convert.__main__.convert') as cvrt, \ patch.object(sys, 'argv', ['cvt', str(tmp_path / 'ros1.bag')]): main() - cvrt.assert_called_with(src=tmp_path / 'ros1.bag', dst=None, exclude_topics=[]) + cvrt.assert_called_with( + src=tmp_path / 'ros1.bag', + dst=None, + exclude_topics=[], + include_topics=[], + ) with patch('rosbags.convert.__main__.convert') as cvrt, \ patch.object(sys, 'argv', ['cvt', @@ -68,7 +73,12 @@ def test_cliwrapper(tmp_path: Path) -> None: '--dst', str(tmp_path / 'target')]): main() - cvrt.assert_called_with(src=tmp_path / 'ros1.bag', dst=tmp_path / 'target', exclude_topics=[]) + cvrt.assert_called_with( + src=tmp_path / 'ros1.bag', + dst=tmp_path / 'target', + exclude_topics=[], + include_topics=[], + ) with patch.object(sys, 'argv', ['cvt', str(tmp_path / 'ros1.bag')]), \ patch('builtins.print') as mock_print, \ @@ -80,7 +90,12 @@ def test_cliwrapper(tmp_path: Path) -> None: with patch('rosbags.convert.__main__.convert') as cvrt, \ patch.object(sys, 'argv', ['cvt', str(tmp_path / 'subdir')]): main() - cvrt.assert_called_with(src=tmp_path / 'subdir', dst=None, exclude_topics=[]) + cvrt.assert_called_with( + src=tmp_path / 'subdir', + dst=None, + exclude_topics=[], + include_topics=[], + ) with patch('rosbags.convert.__main__.convert') as cvrt, \ patch.object(sys, 'argv', ['cvt', @@ -97,7 +112,12 @@ def test_cliwrapper(tmp_path: Path) -> None: '--dst', str(tmp_path / 'target.bag')]): main() - cvrt.assert_called_with(src=tmp_path / 'subdir', dst=tmp_path / 'target.bag', exclude_topics=[]) + cvrt.assert_called_with( + src=tmp_path / 'subdir', + dst=tmp_path / 'target.bag', + exclude_topics=[], + include_topics=[], + ) with patch.object(sys, 'argv', ['cvt', str(tmp_path / 'subdir')]), \ patch('builtins.print') as mock_print, \ @@ -112,7 +132,12 @@ def test_cliwrapper(tmp_path: Path) -> None: '--exclude-topic', '/foo']): main() - cvrt.assert_called_with(src=tmp_path / 'ros1.bag', dst=None, exclude_topics=['/foo']) + cvrt.assert_called_with( + src=tmp_path / 'ros1.bag', + dst=None, + exclude_topics=['/foo'], + include_topics=[], + ) def test_convert_1to2(tmp_path: Path) -> None: From 219a4d98462e12820932d0f04ebda3f9345ad47b Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 22 Sep 2022 21:46:00 +0200 Subject: [PATCH 099/114] Implement direct ros1 (de)serialization --- docs/examples/save_images_rosbag1.py | 7 +- docs/topics/serde.rst | 18 ++ src/rosbags/highlevel/anyreader.py | 4 +- src/rosbags/serde/__init__.py | 11 +- src/rosbags/serde/messages.py | 13 +- src/rosbags/serde/ros1.py | 349 ++++++++++++++++++++++++++- src/rosbags/serde/serdes.py | 48 ++++ src/rosbags/serde/typing.py | 4 + tests/test_serde.py | 48 +++- 9 files changed, 483 insertions(+), 19 deletions(-) diff --git a/docs/examples/save_images_rosbag1.py b/docs/examples/save_images_rosbag1.py index 3390407c..4789edd1 100644 --- a/docs/examples/save_images_rosbag1.py +++ b/docs/examples/save_images_rosbag1.py @@ -3,7 +3,7 @@ import numpy from rosbags.rosbag1 import Writer -from rosbags.serde import cdr_to_ros1, serialize_cdr +from rosbags.serde import serialize_ros1 from rosbags.typesys.types import builtin_interfaces__msg__Time as Time from rosbags.typesys.types import sensor_msgs__msg__CompressedImage as CompressedImage from rosbags.typesys.types import std_msgs__msg__Header as Header @@ -39,8 +39,5 @@ def save_images() -> None: writer.write( conn, timestamp, - cdr_to_ros1( - serialize_cdr(message, CompressedImage.__msgtype__), - CompressedImage.__msgtype__, - ), + serialize_ros1(message, CompressedImage.__msgtype__), ) diff --git a/docs/topics/serde.rst b/docs/topics/serde.rst index 69b95ebd..f476aa09 100644 --- a/docs/topics/serde.rst +++ b/docs/topics/serde.rst @@ -15,6 +15,16 @@ Deserialize a CDR bytes object using :py:func:`deserialize_cdr() `: + +.. code-block:: python + + from rosbags.serde import deserialize_ros1 + + # rawdata is of type bytes and contains serialized message + msg = deserialize_ros1(rawdata, 'geometry_msgs/msg/Quaternion') + + Serialization --------------- @@ -29,3 +39,11 @@ Serialize a message with CDR using :py:func:`serialize_cdr() `: + +.. code-block:: python + + from rosbags.serde import serialize_ros1 + + serialized = serialize_ros1(msg, 'geometry_msgs/msg/Quaternion') diff --git a/src/rosbags/highlevel/anyreader.py b/src/rosbags/highlevel/anyreader.py index ba265b56..679afbd5 100644 --- a/src/rosbags/highlevel/anyreader.py +++ b/src/rosbags/highlevel/anyreader.py @@ -15,7 +15,7 @@ from rosbags.rosbag1 import Reader as Reader1 from rosbags.rosbag1 import ReaderError as ReaderError1 from rosbags.rosbag2 import Reader as Reader2 from rosbags.rosbag2 import ReaderError as ReaderError2 -from rosbags.serde import deserialize_cdr, ros1_to_cdr +from rosbags.serde import deserialize_cdr, deserialize_ros1 from rosbags.typesys import get_types_from_msg, register_types, types if TYPE_CHECKING: @@ -101,7 +101,7 @@ class AnyReader: def _deser_ros1(self, rawdata: bytes, typ: str) -> object: """Deserialize ROS1 message.""" - return deserialize_cdr(ros1_to_cdr(rawdata, typ, self.typestore), typ, self.typestore) + return deserialize_ros1(rawdata, typ, self.typestore) def _deser_ros2(self, rawdata: bytes, typ: str) -> object: """Deserialize CDR message.""" diff --git a/src/rosbags/serde/__init__.py b/src/rosbags/serde/__init__.py index ef56d345..1210ae6e 100644 --- a/src/rosbags/serde/__init__.py +++ b/src/rosbags/serde/__init__.py @@ -9,12 +9,21 @@ convert directly between different serialization formats. """ from .messages import SerdeError -from .serdes import cdr_to_ros1, deserialize_cdr, ros1_to_cdr, serialize_cdr +from .serdes import ( + cdr_to_ros1, + deserialize_cdr, + deserialize_ros1, + ros1_to_cdr, + serialize_cdr, + serialize_ros1, +) __all__ = [ 'SerdeError', 'cdr_to_ros1', 'deserialize_cdr', + 'deserialize_ros1', 'ros1_to_cdr', 'serialize_cdr', + 'serialize_ros1', ] diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py index c4f52097..b31691be 100644 --- a/src/rosbags/serde/messages.py +++ b/src/rosbags/serde/messages.py @@ -7,7 +7,13 @@ from __future__ import annotations from typing import TYPE_CHECKING from .cdr import generate_deserialize_cdr, generate_getsize_cdr, generate_serialize_cdr -from .ros1 import generate_cdr_to_ros1, generate_ros1_to_cdr +from .ros1 import ( + generate_cdr_to_ros1, + generate_deserialize_ros1, + generate_getsize_ros1, + generate_ros1_to_cdr, + generate_serialize_ros1, +) from .typing import Descriptor, Field, Msgdef from .utils import Valtype @@ -62,6 +68,7 @@ def get_msgdef(typename: str, typestore: Typestore) -> Msgdef: fields = [Field(name, fixup(desc)) for name, desc in entries] getsize_cdr, size_cdr = generate_getsize_cdr(fields) + getsize_ros1, size_ros1 = generate_getsize_ros1(fields, typename) cache[typename] = Msgdef( typename, @@ -73,6 +80,10 @@ def get_msgdef(typename: str, typestore: Typestore) -> Msgdef: generate_serialize_cdr(fields, 'be'), generate_deserialize_cdr(fields, 'le'), generate_deserialize_cdr(fields, 'be'), + size_ros1, + getsize_ros1, + generate_serialize_ros1(fields, typename), + generate_deserialize_ros1(fields, typename), generate_ros1_to_cdr(fields, typename, False), # type: ignore generate_ros1_to_cdr(fields, typename, True), # type: ignore generate_cdr_to_ros1(fields, typename, False), # type: ignore diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index 38322e4c..d9e4eaa0 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -11,6 +11,7 @@ conversion of ROS1 to CDR. from __future__ import annotations +import sys from itertools import tee from typing import TYPE_CHECKING, Iterator, cast @@ -20,7 +21,7 @@ from .utils import SIZEMAP, Valtype, align, align_after, compile_lines if TYPE_CHECKING: from typing import Union - from .typing import Bitcvt, BitcvtSize + from .typing import Bitcvt, BitcvtSize, CDRDeser, CDRSer, CDRSerSize def generate_ros1_to_cdr( @@ -331,3 +332,349 @@ def generate_cdr_to_ros1( lines.append(' return ipos, opos') return getattr(compile_lines(lines), funcname) # type: ignore + + +def generate_getsize_ros1(fields: list[Field], typename: str) -> tuple[CDRSerSize, int]: + """Generate ros1 size calculation function. + + Args: + fields: Fields of message. + typename: Message type name. + + Returns: + Size calculation function and static size. + + """ + # pylint: disable=too-many-branches,too-many-statements + size = 0 + is_stat = True + + lines = [ + 'import sys', + 'from rosbags.serde.messages import get_msgdef', + 'def getsize_ros1(pos, message, typestore):', + ] + + if typename == 'std_msgs/msg/Header': + lines.append(' pos += 4') + + for fcurr in fields: + fieldname, desc = fcurr + + if desc.valtype == Valtype.MESSAGE: + if desc.args.size_ros1: + lines.append(f' pos += {desc.args.size_ros1}') + size += desc.args.size_ros1 + else: + lines.append(f' func = get_msgdef("{desc.args.name}", typestore).getsize_ros1') + lines.append(f' pos = func(pos, message.{fieldname}, typestore)') + is_stat = False + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + lines.append(f' pos += 4 + len(message.{fieldname}.encode())') + is_stat = False + else: + lines.append(f' pos += {SIZEMAP[desc.args]}') + size += SIZEMAP[desc.args] + + elif desc.valtype == Valtype.ARRAY: + subdesc, length = desc.args + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(f' val = message.{fieldname}') + for idx in range(length): + lines.append(f' pos += 4 + len(val[{idx}].encode())') + is_stat = False + else: + lines.append(f' pos += {length * SIZEMAP[subdesc.args]}') + size += length * SIZEMAP[subdesc.args] + + else: + assert subdesc.valtype == Valtype.MESSAGE + if subdesc.args.size_ros1: + for _ in range(length): + lines.append(f' pos += {subdesc.args.size_ros1}') + size += subdesc.args.size_ros1 + else: + lines.append( + f' func = get_msgdef("{subdesc.args.name}", typestore).getsize_ros1', + ) + lines.append(f' val = message.{fieldname}') + for idx in range(length): + lines.append(f' pos = func(pos, val[{idx}], typestore)') + is_stat = False + else: + assert desc.valtype == Valtype.SEQUENCE + lines.append(' pos += 4') + subdesc = desc.args[0] + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(f' for val in message.{fieldname}:') + lines.append(' pos += 4 + len(val.encode())') + else: + lines.append(f' pos += len(message.{fieldname}) * {SIZEMAP[subdesc.args]}') + + else: + assert subdesc.valtype == Valtype.MESSAGE + lines.append(f' val = message.{fieldname}') + if subdesc.args.size_ros1: + lines.append(f' pos += {subdesc.args.size_ros1} * len(val)') + + else: + lines.append( + f' func = get_msgdef("{subdesc.args.name}", typestore).getsize_ros1', + ) + lines.append(' for item in val:') + lines.append(' pos = func(pos, item, typestore)') + + is_stat = False + lines.append(' return pos') + return compile_lines(lines).getsize_ros1, is_stat * size + + +def generate_serialize_ros1(fields: list[Field], typename: str) -> CDRSer: + """Generate ros1 serialization function. + + Args: + fields: Fields of message. + typename: Message type name. + + Returns: + Serializer function. + + """ + # pylint: disable=too-many-branches,too-many-statements + lines = [ + 'import sys', + 'import numpy', + 'from rosbags.serde.messages import SerdeError, get_msgdef', + 'from rosbags.serde.primitives import pack_bool_le', + 'from rosbags.serde.primitives import pack_int8_le', + 'from rosbags.serde.primitives import pack_int16_le', + 'from rosbags.serde.primitives import pack_int32_le', + 'from rosbags.serde.primitives import pack_int64_le', + 'from rosbags.serde.primitives import pack_uint8_le', + 'from rosbags.serde.primitives import pack_uint16_le', + 'from rosbags.serde.primitives import pack_uint32_le', + 'from rosbags.serde.primitives import pack_uint64_le', + 'from rosbags.serde.primitives import pack_float32_le', + 'from rosbags.serde.primitives import pack_float64_le', + 'def serialize_ros1(rawdata, pos, message, typestore):', + ] + + if typename == 'std_msgs/msg/Header': + lines.append(' pos += 4') + + be_syms = ('>',) if sys.byteorder == 'little' else ('=', '>') + + for fcurr in fields: + fieldname, desc = fcurr + + lines.append(f' val = message.{fieldname}') + if desc.valtype == Valtype.MESSAGE: + name = desc.args.name + lines.append(f' func = get_msgdef("{name}", typestore).serialize_ros1') + lines.append(' pos = func(rawdata, pos, val, typestore)') + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + lines.append(' bval = memoryview(val.encode())') + lines.append(' length = len(bval)') + lines.append(' pack_int32_le(rawdata, pos, length)') + lines.append(' pos += 4') + lines.append(' rawdata[pos:pos + length] = bval') + lines.append(' pos += length') + else: + lines.append(f' pack_{desc.args}_le(rawdata, pos, val)') + lines.append(f' pos += {SIZEMAP[desc.args]}') + + elif desc.valtype == Valtype.ARRAY: + subdesc, length = desc.args + lines.append(f' if len(val) != {length}:') + lines.append(' raise SerdeError(\'Unexpected array length\')') + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + for idx in range(length): + lines.append(f' bval = memoryview(val[{idx}].encode())') + lines.append(' length = len(bval)') + lines.append(' pack_int32_le(rawdata, pos, length)') + lines.append(' pos += 4') + lines.append(' rawdata[pos:pos + length] = bval') + lines.append(' pos += length') + else: + lines.append(f' if val.dtype.byteorder in {be_syms}:') + lines.append(' val = val.byteswap()') + size = length * SIZEMAP[subdesc.args] + lines.append(f' rawdata[pos:pos + {size}] = val.view(numpy.uint8)') + lines.append(f' pos += {size}') + + else: + assert subdesc.valtype == Valtype.MESSAGE + name = subdesc.args.name + lines.append(f' func = get_msgdef("{name}", typestore).serialize_ros1') + for idx in range(length): + lines.append(f' pos = func(rawdata, pos, val[{idx}], typestore)') + else: + assert desc.valtype == Valtype.SEQUENCE + lines.append(' pack_int32_le(rawdata, pos, len(val))') + lines.append(' pos += 4') + subdesc = desc.args[0] + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(' for item in val:') + lines.append(' bval = memoryview(item.encode())') + lines.append(' length = len(bval)') + lines.append(' pack_int32_le(rawdata, pos, length)') + lines.append(' pos += 4') + lines.append(' rawdata[pos:pos + length] = bval') + lines.append(' pos += length') + else: + lines.append(f' size = len(val) * {SIZEMAP[subdesc.args]}') + lines.append(f' if val.dtype.byteorder in {be_syms}:') + lines.append(' val = val.byteswap()') + lines.append(' rawdata[pos:pos + size] = val.view(numpy.uint8)') + lines.append(' pos += size') + + if subdesc.valtype == Valtype.MESSAGE: + name = subdesc.args.name + lines.append(f' func = get_msgdef("{name}", typestore).serialize_ros1') + lines.append(' for item in val:') + lines.append(' pos = func(rawdata, pos, item, typestore)') + + lines.append(' return pos') + return compile_lines(lines).serialize_ros1 # type: ignore + + +def generate_deserialize_ros1(fields: list[Field], typename: str) -> CDRDeser: + """Generate ros1 deserialization function. + + Args: + fields: Fields of message. + typename: Message type name. + + Returns: + Deserializer function. + + """ + # pylint: disable=too-many-branches,too-many-statements + lines = [ + 'import sys', + 'import numpy', + 'from rosbags.serde.messages import SerdeError, get_msgdef', + 'from rosbags.serde.primitives import unpack_bool_le', + 'from rosbags.serde.primitives import unpack_int8_le', + 'from rosbags.serde.primitives import unpack_int16_le', + 'from rosbags.serde.primitives import unpack_int32_le', + 'from rosbags.serde.primitives import unpack_int64_le', + 'from rosbags.serde.primitives import unpack_uint8_le', + 'from rosbags.serde.primitives import unpack_uint16_le', + 'from rosbags.serde.primitives import unpack_uint32_le', + 'from rosbags.serde.primitives import unpack_uint64_le', + 'from rosbags.serde.primitives import unpack_float32_le', + 'from rosbags.serde.primitives import unpack_float64_le', + 'def deserialize_ros1(rawdata, pos, cls, typestore):', + ] + + if typename == 'std_msgs/msg/Header': + lines.append(' pos += 4') + + be_syms = ('>',) if sys.byteorder == 'little' else ('=', '>') + + funcname = 'deserialize_ros1' + lines.append(' values = []') + for fcurr in fields: + desc = fcurr[1] + + if desc.valtype == Valtype.MESSAGE: + lines.append(f' msgdef = get_msgdef("{desc.args.name}", typestore)') + lines.append(f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls, typestore)') + lines.append(' values.append(obj)') + + elif desc.valtype == Valtype.BASE: + if desc.args == 'string': + lines.append(' length = unpack_int32_le(rawdata, pos)[0]') + lines.append(' string = bytes(rawdata[pos + 4:pos + 4 + length]).decode()') + lines.append(' values.append(string)') + lines.append(' pos += 4 + length') + else: + lines.append(f' value = unpack_{desc.args}_le(rawdata, pos)[0]') + lines.append(' values.append(value)') + lines.append(f' pos += {SIZEMAP[desc.args]}') + + elif desc.valtype == Valtype.ARRAY: + subdesc, length = desc.args + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(' value = []') + for _ in range(length): + lines.append(' length = unpack_int32_le(rawdata, pos)[0]') + lines.append( + ' value.append(bytes(rawdata[pos + 4:pos + 4 + length]).decode())', + ) + lines.append(' pos += 4 + length') + lines.append(' values.append(value)') + else: + size = length * SIZEMAP[subdesc.args] + lines.append( + f' val = numpy.frombuffer(rawdata, ' + f'dtype=numpy.{subdesc.args}, count={length}, offset=pos)', + ) + lines.append(f' if val.dtype.byteorder in {be_syms}:') + lines.append(' val = val.byteswap()') + lines.append(' values.append(val)') + lines.append(f' pos += {size}') + else: + assert subdesc.valtype == Valtype.MESSAGE + lines.append(f' msgdef = get_msgdef("{subdesc.args.name}", typestore)') + lines.append(' value = []') + for _ in range(length): + lines.append( + f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls, typestore)', + ) + lines.append(' value.append(obj)') + lines.append(' values.append(value)') + + else: + assert desc.valtype == Valtype.SEQUENCE + lines.append(' size = unpack_int32_le(rawdata, pos)[0]') + lines.append(' pos += 4') + subdesc = desc.args[0] + + if subdesc.valtype == Valtype.BASE: + if subdesc.args == 'string': + lines.append(' value = []') + lines.append(' for _ in range(size):') + lines.append(' length = unpack_int32_le(rawdata, pos)[0]') + lines.append( + ' value.append(bytes(rawdata[pos + 4:pos + 4 + length])' + '.decode())', + ) + lines.append(' pos += 4 + length') + lines.append(' values.append(value)') + else: + lines.append(f' length = size * {SIZEMAP[subdesc.args]}') + lines.append( + f' val = numpy.frombuffer(rawdata, ' + f'dtype=numpy.{subdesc.args}, count=size, offset=pos)', + ) + lines.append(f' if val.dtype.byteorder in {be_syms}:') + lines.append(' val = val.byteswap()') + lines.append(' values.append(val)') + lines.append(' pos += length') + + if subdesc.valtype == Valtype.MESSAGE: + lines.append(f' msgdef = get_msgdef("{subdesc.args.name}", typestore)') + lines.append(' value = []') + lines.append(' for _ in range(size):') + lines.append( + f' obj, pos = msgdef.{funcname}(rawdata, pos, msgdef.cls, typestore)', + ) + lines.append(' value.append(obj)') + lines.append(' values.append(value)') + + lines.append(' return cls(*values), pos') + return compile_lines(lines).deserialize_ros1 # type: ignore diff --git a/src/rosbags/serde/serdes.py b/src/rosbags/serde/serdes.py index 6d56a7b0..ab38af61 100644 --- a/src/rosbags/serde/serdes.py +++ b/src/rosbags/serde/serdes.py @@ -73,6 +73,54 @@ def serialize_cdr( return rawdata.toreadonly() +def deserialize_ros1( + rawdata: bytes, + typename: str, + typestore: Typestore = types, +) -> Any: # noqa: ANN401 + """Deserialize raw data into a message object. + + Args: + rawdata: Serialized data. + typename: Message type name. + typestore: Type store. + + Returns: + Deserialized message object. + + """ + msgdef = get_msgdef(typename, typestore) + func = msgdef.deserialize_ros1 + message, pos = func(rawdata, 0, msgdef.cls, typestore) + assert pos == len(rawdata) + return message + + +def serialize_ros1( + message: object, + typename: str, + typestore: Typestore = types, +) -> memoryview: + """Serialize message object to bytes. + + Args: + message: Message object. + typename: Message type name. + typestore: Type store. + + Returns: + Serialized bytes. + + """ + msgdef = get_msgdef(typename, typestore) + size = msgdef.getsize_ros1(0, message, typestore) + rawdata = memoryview(bytearray(size)) + func = msgdef.serialize_ros1 + pos = func(rawdata, 0, message, typestore) + assert pos == size + return rawdata.toreadonly() + + def ros1_to_cdr(raw: bytes, typename: str, typestore: Typestore = types) -> memoryview: """Convert serialized ROS1 message directly to CDR. diff --git a/src/rosbags/serde/typing.py b/src/rosbags/serde/typing.py index 5275cc2b..8d563308 100644 --- a/src/rosbags/serde/typing.py +++ b/src/rosbags/serde/typing.py @@ -45,6 +45,10 @@ class Msgdef(NamedTuple): serialize_cdr_be: CDRSer deserialize_cdr_le: CDRDeser deserialize_cdr_be: CDRDeser + size_ros1: int + getsize_ros1: CDRSerSize + serialize_ros1: CDRSer + deserialize_ros1: CDRDeser getsize_ros1_to_cdr: BitcvtSize ros1_to_cdr: Bitcvt getsize_cdr_to_ros1: BitcvtSize diff --git a/tests/test_serde.py b/tests/test_serde.py index 586e3573..88542cbc 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -10,7 +10,15 @@ from unittest.mock import MagicMock, patch import numpy import pytest -from rosbags.serde import SerdeError, cdr_to_ros1, deserialize_cdr, ros1_to_cdr, serialize_cdr +from rosbags.serde import ( + SerdeError, + cdr_to_ros1, + deserialize_cdr, + deserialize_ros1, + ros1_to_cdr, + serialize_cdr, + serialize_ros1, +) from rosbags.serde.messages import get_msgdef from rosbags.typesys import get_types_from_msg, register_types, types from rosbags.typesys.types import builtin_interfaces__msg__Time as Time @@ -206,6 +214,8 @@ def _comparable() -> Generator[None, None, None]: def __init__(self, *args: Any, **kwargs: Any): # noqa: ANN401 super().__init__(*args, **kwargs) + self.dtype = kwargs['wraps'].dtype + self.reshape = kwargs['wraps'].reshape self.__eq__ = arreq # type: ignore def byteswap(self, *args: Any) -> CNDArray: # noqa: ANN401 @@ -230,6 +240,11 @@ def test_serde(message: tuple[bytes, str, bool]) -> None: assert len(rawdata) - len(serdeser) < 4 assert all(x == 0 for x in rawdata[len(serdeser):]) + if rawdata[1] == 1: + rawdata = cdr_to_ros1(rawdata, typ) + serdeser = serialize_ros1(deserialize_ros1(rawdata, typ), typ) + assert serdeser == rawdata + @pytest.mark.usefixtures('_comparable') def test_deserializer() -> None: @@ -244,6 +259,8 @@ def test_deserializer() -> None: assert msg.points[1].x == 1.25 assert msg.points[1].y == 2.25 assert msg.points[1].z == 3.25 + msg_ros1 = deserialize_ros1(cdr_to_ros1(*MSG_POLY[:2]), MSG_POLY[1]) + assert msg_ros1 == msg msg = deserialize_cdr(*MSG_MAGN[:2]) assert msg == deserialize(*MSG_MAGN[:2]) @@ -256,6 +273,8 @@ def test_deserializer() -> None: assert (field.x, field.y, field.z) == (128., 128., 128.) diag = numpy.diag(msg.magnetic_field_covariance.reshape(3, 3)) assert (diag == [1., 1., 1.]).all() + msg_ros1 = deserialize_ros1(cdr_to_ros1(*MSG_MAGN[:2]), MSG_MAGN[1]) + assert msg_ros1 == msg msg_big = deserialize_cdr(*MSG_MAGN_BIG[:2]) assert msg_big == deserialize(*MSG_MAGN_BIG[:2]) @@ -384,10 +403,14 @@ def test_custom_type() -> None: assert res == deserialize(serialize(msg, cname), cname) assert res == msg + res = deserialize_ros1(serialize_ros1(msg, cname), cname) + assert res == msg + def test_ros1_to_cdr() -> None: """Test ROS1 to CDR conversion.""" - register_types(dict(get_types_from_msg(STATIC_16_64, 'test_msgs/msg/static_16_64'))) + msgtype = 'test_msgs/msg/static_16_64' + register_types(dict(get_types_from_msg(STATIC_16_64, msgtype))) msg_ros = (b'\x01\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02') msg_cdr = ( @@ -396,9 +419,11 @@ def test_ros1_to_cdr() -> None: b'\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02' ) - assert ros1_to_cdr(msg_ros, 'test_msgs/msg/static_16_64') == msg_cdr + assert ros1_to_cdr(msg_ros, msgtype) == msg_cdr + assert serialize_cdr(deserialize_ros1(msg_ros, msgtype), msgtype) == msg_cdr - register_types(dict(get_types_from_msg(DYNAMIC_S_64, 'test_msgs/msg/dynamic_s_64'))) + msgtype = 'test_msgs/msg/dynamic_s_64' + register_types(dict(get_types_from_msg(DYNAMIC_S_64, msgtype))) msg_ros = (b'\x01\x00\x00\x00X' b'\x00\x00\x00\x00\x00\x00\x00\x02') msg_cdr = ( @@ -407,12 +432,14 @@ def test_ros1_to_cdr() -> None: b'\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02' ) - assert ros1_to_cdr(msg_ros, 'test_msgs/msg/dynamic_s_64') == msg_cdr + assert ros1_to_cdr(msg_ros, msgtype) == msg_cdr + assert serialize_cdr(deserialize_ros1(msg_ros, msgtype), msgtype) == msg_cdr def test_cdr_to_ros1() -> None: """Test CDR to ROS1 conversion.""" - register_types(dict(get_types_from_msg(STATIC_16_64, 'test_msgs/msg/static_16_64'))) + msgtype = 'test_msgs/msg/static_16_64' + register_types(dict(get_types_from_msg(STATIC_16_64, msgtype))) msg_ros = (b'\x01\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02') msg_cdr = ( @@ -421,9 +448,11 @@ def test_cdr_to_ros1() -> None: b'\x00\x00\x00\x00\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02' ) - assert cdr_to_ros1(msg_cdr, 'test_msgs/msg/static_16_64') == msg_ros + assert cdr_to_ros1(msg_cdr, msgtype) == msg_ros + assert serialize_ros1(deserialize_cdr(msg_cdr, msgtype), msgtype) == msg_ros - register_types(dict(get_types_from_msg(DYNAMIC_S_64, 'test_msgs/msg/dynamic_s_64'))) + msgtype = 'test_msgs/msg/dynamic_s_64' + register_types(dict(get_types_from_msg(DYNAMIC_S_64, msgtype))) msg_ros = (b'\x01\x00\x00\x00X' b'\x00\x00\x00\x00\x00\x00\x00\x02') msg_cdr = ( @@ -432,7 +461,8 @@ def test_cdr_to_ros1() -> None: b'\x00\x00' b'\x00\x00\x00\x00\x00\x00\x00\x02' ) - assert cdr_to_ros1(msg_cdr, 'test_msgs/msg/dynamic_s_64') == msg_ros + assert cdr_to_ros1(msg_cdr, msgtype) == msg_ros + assert serialize_ros1(deserialize_cdr(msg_cdr, msgtype), msgtype) == msg_ros header = Header(stamp=Time(42, 666), frame_id='frame') msg_ros = cdr_to_ros1(serialize_cdr(header, 'std_msgs/msg/Header'), 'std_msgs/msg/Header') From 88bbf1b5e3c05e6db3c4b9445a744b1a6c17b1e4 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 23 Sep 2022 12:45:38 +0200 Subject: [PATCH 100/114] Release 0.9.13 --- CHANGES.rst | 14 ++++++++++++++ setup.cfg | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index b834c109..e42544be 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,20 @@ Changes ======= +0.9.13 - 2022-09-23 +------------------- +- Fix parsing of comments in message definitions `#31`_ +- Fix parsing of members starting with ``string`` in message definitions `#35`_ +- Change lz4 compression level to 0 `#36`_ +- Add include filters to rosbag conversion `#38`_ +- Implement direct ros1 (de)serialization + +.. _#31: https://gitlab.com/ternaris/rosbags/issues/31 +.. _#35: https://gitlab.com/ternaris/rosbags/issues/35 +.. _#36: https://gitlab.com/ternaris/rosbags/issues/36 +.. _#38: https://gitlab.com/ternaris/rosbags/issues/38 + + 0.9.12 - 2022-07-27 ------------------- - Add support for rosbag2 version 6 metadata `#30`_ diff --git a/setup.cfg b/setup.cfg index 10838a3c..608ea01a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.12 +version = 0.9.13 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From ec7cd4a4e58d558d19607f5dcfe9a5471bc05c6e Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 11 Jan 2023 15:21:14 +0100 Subject: [PATCH 101/114] Update copyright headers --- docs/conf.py | 4 ++-- src/rosbags/convert/__init__.py | 2 +- src/rosbags/convert/__main__.py | 2 +- src/rosbags/convert/converter.py | 2 +- src/rosbags/highlevel/__init__.py | 2 +- src/rosbags/highlevel/anyreader.py | 2 +- src/rosbags/interfaces/__init__.py | 2 +- src/rosbags/rosbag1/__init__.py | 2 +- src/rosbags/rosbag1/reader.py | 2 +- src/rosbags/rosbag1/writer.py | 2 +- src/rosbags/rosbag2/__init__.py | 2 +- src/rosbags/rosbag2/metadata.py | 2 +- src/rosbags/rosbag2/reader.py | 2 +- src/rosbags/rosbag2/writer.py | 2 +- src/rosbags/serde/__init__.py | 2 +- src/rosbags/serde/cdr.py | 2 +- src/rosbags/serde/messages.py | 2 +- src/rosbags/serde/primitives.py | 2 +- src/rosbags/serde/ros1.py | 2 +- src/rosbags/serde/serdes.py | 2 +- src/rosbags/serde/typing.py | 2 +- src/rosbags/serde/utils.py | 2 +- src/rosbags/typesys/__init__.py | 2 +- src/rosbags/typesys/__main__.py | 2 +- src/rosbags/typesys/base.py | 2 +- src/rosbags/typesys/idl.py | 2 +- src/rosbags/typesys/msg.py | 2 +- src/rosbags/typesys/peg.py | 2 +- src/rosbags/typesys/register.py | 4 ++-- src/rosbags/typesys/types.py | 2 +- tests/__init__.py | 2 +- tests/cdr.py | 2 +- tests/test_convert.py | 2 +- tests/test_highlevel.py | 2 +- tests/test_parse.py | 2 +- tests/test_reader.py | 2 +- tests/test_reader1.py | 2 +- tests/test_roundtrip.py | 2 +- tests/test_roundtrip1.py | 2 +- tests/test_serde.py | 2 +- tests/test_writer.py | 2 +- tests/test_writer1.py | 2 +- tools/bench/bench.py | 2 +- tools/compare/compare.py | 2 +- 44 files changed, 46 insertions(+), 46 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index ace01c87..3aeff246 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Sphinx config.""" @@ -18,7 +18,7 @@ __all__ = ['_1', '_2', '_3', '_4', '_5'] typing.TYPE_CHECKING = True project = 'Rosbags' -copyright = '2020-2022, Ternaris' +copyright = '2020-2023, Ternaris' author = 'Ternaris' autoapi_python_use_implicit_namespaces = True diff --git a/src/rosbags/convert/__init__.py b/src/rosbags/convert/__init__.py index 0fe62a9d..db01c3dc 100644 --- a/src/rosbags/convert/__init__.py +++ b/src/rosbags/convert/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags file format conversion. diff --git a/src/rosbags/convert/__main__.py b/src/rosbags/convert/__main__.py index 07e62317..bc01c9ad 100644 --- a/src/rosbags/convert/__main__.py +++ b/src/rosbags/convert/__main__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """CLI tool for rosbag conversion.""" diff --git a/src/rosbags/convert/converter.py b/src/rosbags/convert/converter.py index 11e2e488..f08635d6 100644 --- a/src/rosbags/convert/converter.py +++ b/src/rosbags/convert/converter.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag1 to Rosbag2 Converter.""" diff --git a/src/rosbags/highlevel/__init__.py b/src/rosbags/highlevel/__init__.py index 3ce7382d..befcb908 100644 --- a/src/rosbags/highlevel/__init__.py +++ b/src/rosbags/highlevel/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Highlevel interfaces for rosbags.""" diff --git a/src/rosbags/highlevel/anyreader.py b/src/rosbags/highlevel/anyreader.py index 679afbd5..d9562af0 100644 --- a/src/rosbags/highlevel/anyreader.py +++ b/src/rosbags/highlevel/anyreader.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Tools for reading all rosbag versions with unified api.""" diff --git a/src/rosbags/interfaces/__init__.py b/src/rosbags/interfaces/__init__.py index 76988fc1..9349db85 100644 --- a/src/rosbags/interfaces/__init__.py +++ b/src/rosbags/interfaces/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Shared interfaces.""" diff --git a/src/rosbags/rosbag1/__init__.py b/src/rosbags/rosbag1/__init__.py index 4e668933..9d53cd96 100644 --- a/src/rosbags/rosbag1/__init__.py +++ b/src/rosbags/rosbag1/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags support for rosbag1 files. diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 6f190db4..60569c91 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag1 v2.0 reader.""" diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index 965e31aa..ca480dcb 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag1 writer.""" diff --git a/src/rosbags/rosbag2/__init__.py b/src/rosbags/rosbag2/__init__.py index c11043cb..8c8ae4c3 100644 --- a/src/rosbags/rosbag2/__init__.py +++ b/src/rosbags/rosbag2/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags support for rosbag2 files. diff --git a/src/rosbags/rosbag2/metadata.py b/src/rosbags/rosbag2/metadata.py index e4e169e6..65a0a177 100644 --- a/src/rosbags/rosbag2/metadata.py +++ b/src/rosbags/rosbag2/metadata.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag2 metadata.""" diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 66b37fe3..754941b8 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag2 reader.""" diff --git a/src/rosbags/rosbag2/writer.py b/src/rosbags/rosbag2/writer.py index ae93b629..cba30c8e 100644 --- a/src/rosbags/rosbag2/writer.py +++ b/src/rosbags/rosbag2/writer.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag2 writer.""" diff --git a/src/rosbags/serde/__init__.py b/src/rosbags/serde/__init__.py index 1210ae6e..0895bea8 100644 --- a/src/rosbags/serde/__init__.py +++ b/src/rosbags/serde/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags message serialization and deserialization. diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index 0ef393af..369b526e 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Code generators for CDR. diff --git a/src/rosbags/serde/messages.py b/src/rosbags/serde/messages.py index b31691be..545b5565 100644 --- a/src/rosbags/serde/messages.py +++ b/src/rosbags/serde/messages.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Runtime message loader and cache.""" diff --git a/src/rosbags/serde/primitives.py b/src/rosbags/serde/primitives.py index a5121657..4b035735 100644 --- a/src/rosbags/serde/primitives.py +++ b/src/rosbags/serde/primitives.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Serialization primitives. diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index d9e4eaa0..32a543db 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Code generators for ROS1. diff --git a/src/rosbags/serde/serdes.py b/src/rosbags/serde/serdes.py index ab38af61..eee72b96 100644 --- a/src/rosbags/serde/serdes.py +++ b/src/rosbags/serde/serdes.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Serialization, deserializion and conversion functions.""" diff --git a/src/rosbags/serde/typing.py b/src/rosbags/serde/typing.py index 8d563308..08b13231 100644 --- a/src/rosbags/serde/typing.py +++ b/src/rosbags/serde/typing.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Python types used in this package.""" diff --git a/src/rosbags/serde/utils.py b/src/rosbags/serde/utils.py index 67407241..bcc0a660 100644 --- a/src/rosbags/serde/utils.py +++ b/src/rosbags/serde/utils.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Helpers used by code generators.""" diff --git a/src/rosbags/typesys/__init__.py b/src/rosbags/typesys/__init__.py index 4e398555..b8ee2d5f 100644 --- a/src/rosbags/typesys/__init__.py +++ b/src/rosbags/typesys/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbags Type System. diff --git a/src/rosbags/typesys/__main__.py b/src/rosbags/typesys/__main__.py index f7dd37ff..a9d9675b 100644 --- a/src/rosbags/typesys/__main__.py +++ b/src/rosbags/typesys/__main__.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Tool to update builtin types shipped with rosbags.""" diff --git a/src/rosbags/typesys/base.py b/src/rosbags/typesys/base.py index 79814ab0..c390e9b9 100644 --- a/src/rosbags/typesys/base.py +++ b/src/rosbags/typesys/base.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Types and helpers used by message definition converters.""" diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index 8fee131b..821ec173 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """IDL Parser. diff --git a/src/rosbags/typesys/msg.py b/src/rosbags/typesys/msg.py index 61245dec..878a3549 100644 --- a/src/rosbags/typesys/msg.py +++ b/src/rosbags/typesys/msg.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """MSG Parser. diff --git a/src/rosbags/typesys/peg.py b/src/rosbags/typesys/peg.py index 1c296ea9..2503e061 100644 --- a/src/rosbags/typesys/peg.py +++ b/src/rosbags/typesys/peg.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """PEG Parser. diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index 21b0714d..299c2bc6 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Code generators and registration functions for the extensible type system.""" @@ -63,7 +63,7 @@ def generate_python_code(typs: Typesdict) -> str: """ lines = [ - '# Copyright 2020-2022 Ternaris.', + '# Copyright 2020-2023 Ternaris.', '# SPDX-License-Identifier: Apache-2.0', '#', '# THIS FILE IS GENERATED, DO NOT EDIT', diff --git a/src/rosbags/typesys/types.py b/src/rosbags/typesys/types.py index c393be6d..c4f7f175 100644 --- a/src/rosbags/typesys/types.py +++ b/src/rosbags/typesys/types.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 # # THIS FILE IS GENERATED, DO NOT EDIT diff --git a/tests/__init__.py b/tests/__init__.py index bec3ab4e..c7648341 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,3 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag tests.""" diff --git a/tests/cdr.py b/tests/cdr.py index c7dbd76a..ee3d7f64 100644 --- a/tests/cdr.py +++ b/tests/cdr.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Reference CDR message serializer and deserializer.""" diff --git a/tests/test_convert.py b/tests/test_convert.py index abe3bcb8..223e2935 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Rosbag1to2 converter tests.""" diff --git a/tests/test_highlevel.py b/tests/test_highlevel.py index 17fcaa7c..67189f42 100644 --- a/tests/test_highlevel.py +++ b/tests/test_highlevel.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Reader tests.""" diff --git a/tests/test_parse.py b/tests/test_parse.py index 49e6f1b4..6eeee92c 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Message definition parser tests.""" diff --git a/tests/test_reader.py b/tests/test_reader.py index b0dcca30..40583078 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Reader tests.""" diff --git a/tests/test_reader1.py b/tests/test_reader1.py index 47f9cfdd..20cb1e0f 100644 --- a/tests/test_reader1.py +++ b/tests/test_reader1.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Reader tests.""" diff --git a/tests/test_roundtrip.py b/tests/test_roundtrip.py index d3ce5d44..a7df41de 100644 --- a/tests/test_roundtrip.py +++ b/tests/test_roundtrip.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Test full data roundtrip.""" diff --git a/tests/test_roundtrip1.py b/tests/test_roundtrip1.py index 60758b91..45f3f963 100644 --- a/tests/test_roundtrip1.py +++ b/tests/test_roundtrip1.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Test full data roundtrip.""" diff --git a/tests/test_serde.py b/tests/test_serde.py index 88542cbc..c0447394 100644 --- a/tests/test_serde.py +++ b/tests/test_serde.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Serializer and deserializer tests.""" diff --git a/tests/test_writer.py b/tests/test_writer.py index 6f39edc3..a39e3f50 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Writer tests.""" diff --git a/tests/test_writer1.py b/tests/test_writer1.py index dd188525..cf6a381b 100644 --- a/tests/test_writer1.py +++ b/tests/test_writer1.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Writer tests.""" diff --git a/tools/bench/bench.py b/tools/bench/bench.py index 8cc84606..bcfc0b35 100644 --- a/tools/bench/bench.py +++ b/tools/bench/bench.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Check and benchmark rosbag2 read implementations.""" diff --git a/tools/compare/compare.py b/tools/compare/compare.py index 02fe5aea..137873f3 100644 --- a/tools/compare/compare.py +++ b/tools/compare/compare.py @@ -1,4 +1,4 @@ -# Copyright 2020-2022 Ternaris. +# Copyright 2020-2023 Ternaris. # SPDX-License-Identifier: Apache-2.0 """Tool checking if contents of two rosbags are equal.""" From 51c0b30cbc9f1dbbf7988e4bded657b2e09164f5 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 11 Jan 2023 11:18:49 +0100 Subject: [PATCH 102/114] Update reader example in README --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index e8985f45..ab378151 100644 --- a/README.rst +++ b/README.rst @@ -43,7 +43,8 @@ Read and deserialize rosbag2 messages: # create reader instance and open for reading with Reader('/home/ros/rosbag_2020_03_24') as reader: - for connection, timestamp, rawdata in reader.messages(['/imu_raw/Imu']): + connections = [x for x in reader.connections if x.topic == '/imu_raw/Imu'] + for connection, timestamp, rawdata in reader.messages(connections=connections): msg = deserialize_cdr(rawdata, connection.msgtype) print(msg.header.frame_id) From 2732b8876e0537ddda2d3b6b6001825a90508fd5 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 11 Jan 2023 13:22:46 +0100 Subject: [PATCH 103/114] Flush decompressed files in rosbag2.Reader --- src/rosbags/rosbag2/reader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 754941b8..f345e0eb 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -47,7 +47,7 @@ def decompress(path: Path, do_decompress: bool) -> Generator[Path, None, None]: dbfile = Path(tempdir, path.stem) with path.open('rb') as infile, dbfile.open('wb') as outfile: decomp.copy_stream(infile, outfile) - yield dbfile + yield dbfile else: yield path From f2020fa929a41a805d48d21abb407e1356e6b3ab Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 11 Jan 2023 13:27:00 +0100 Subject: [PATCH 104/114] Advertise Python 3.11 compatibility --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 608ea01a..a628095a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,6 +31,7 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Topic :: Scientific/Engineering Typing :: Typed project_urls = From d7d24c4478469c6f3ac33b23fefb2d2be69b8447 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 12 Jan 2023 12:52:59 +0100 Subject: [PATCH 105/114] Release 0.9.14 --- CHANGES.rst | 9 +++++++++ setup.cfg | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index e42544be..a899f606 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,15 @@ Changes ======= +0.9.14 - 2023-01-12 +------------------- +- Fix reader example in README `#40`_ +- Flush decompressed files rosbag2.Reader +- Advertise Python 3.11 compatibility + +.. _#40: https://gitlab.com/ternaris/rosbags/issues/40 + + 0.9.13 - 2022-09-23 ------------------- - Fix parsing of comments in message definitions `#31`_ diff --git a/setup.cfg b/setup.cfg index a628095a..cf214f18 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.13 +version = 0.9.14 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags From eaa64002b87c4b45013e446252f01ddd77961f30 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 11 Jan 2023 15:26:05 +0100 Subject: [PATCH 106/114] Refactor rosbag2 Reader --- src/rosbags/rosbag2/__init__.py | 3 +- src/rosbags/rosbag2/errors.py | 9 ++ src/rosbags/rosbag2/reader.py | 186 ++++++++++++------------- src/rosbags/rosbag2/storage_sqlite3.py | 119 ++++++++++++++++ 4 files changed, 219 insertions(+), 98 deletions(-) create mode 100644 src/rosbags/rosbag2/errors.py create mode 100644 src/rosbags/rosbag2/storage_sqlite3.py diff --git a/src/rosbags/rosbag2/__init__.py b/src/rosbags/rosbag2/__init__.py index 8c8ae4c3..d487f0c3 100644 --- a/src/rosbags/rosbag2/__init__.py +++ b/src/rosbags/rosbag2/__init__.py @@ -7,7 +7,8 @@ in the rosbag2 format. """ -from .reader import Reader, ReaderError +from .errors import ReaderError +from .reader import Reader from .writer import Writer, WriterError __all__ = [ diff --git a/src/rosbags/rosbag2/errors.py b/src/rosbags/rosbag2/errors.py new file mode 100644 index 00000000..698f1f9d --- /dev/null +++ b/src/rosbags/rosbag2/errors.py @@ -0,0 +1,9 @@ +# Copyright 2020-2023 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Rosbag2 errors.""" + +from __future__ import annotations + + +class ReaderError(Exception): + """Reader Error.""" diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index f345e0eb..27de18f8 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -4,11 +4,9 @@ from __future__ import annotations -import sqlite3 -from contextlib import contextmanager from pathlib import Path from tempfile import TemporaryDirectory -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Protocol import zstandard from ruamel.yaml import YAML @@ -16,40 +14,43 @@ from ruamel.yaml.error import YAMLError from rosbags.interfaces import Connection, ConnectionExtRosbag2, TopicInfo +from .errors import ReaderError +from .storage_sqlite3 import ReaderSqlite3 + if TYPE_CHECKING: from types import TracebackType - from typing import Any, Generator, Iterable, Literal, Optional, Type, Union + from typing import Generator, Iterable, Literal, Optional, Type, Union from .metadata import FileInformation, Metadata -class ReaderError(Exception): - """Reader Error.""" +class StorageProtocol(Protocol): + """Storage Protocol.""" + def __init__(self, paths: Iterable[Path], connections: Iterable[Connection]): + """Initialize.""" + raise NotImplementedError -@contextmanager -def decompress(path: Path, do_decompress: bool) -> Generator[Path, None, None]: - """Transparent rosbag2 database decompression context. + def open(self) -> None: + """Open file.""" + raise NotImplementedError - This context manager will yield a path to the decompressed file contents. + def close(self) -> None: + """Close file.""" + raise NotImplementedError - Args: - path: Potentially compressed file. - do_decompress: Flag indicating if decompression shall occur. + def get_definitions(self) -> dict[str, tuple[str, str]]: + """Get message definitions.""" + raise NotImplementedError - Yields: - Path of transparently decompressed file. - - """ - if do_decompress: - decomp = zstandard.ZstdDecompressor() - with TemporaryDirectory() as tempdir: - dbfile = Path(tempdir, path.stem) - with path.open('rb') as infile, dbfile.open('wb') as outfile: - decomp.copy_stream(infile, outfile) - yield dbfile - else: - yield path + def messages( + self, + connections: Iterable[Connection] = (), + start: Optional[int] = None, + stop: Optional[int] = None, + ) -> Generator[tuple[Connection, int, bytes], None, None]: + """Get messages from file.""" + raise NotImplementedError class Reader: @@ -69,6 +70,12 @@ class Reader: """ + # pylint: disable=too-many-instance-attributes + + STORAGE_PLUGINS: dict[str, Type[StorageProtocol]] = { + 'sqlite3': ReaderSqlite3, + } + def __init__(self, path: Union[Path, str]): """Open rosbag and check metadata. @@ -82,7 +89,6 @@ class Reader: path = Path(path) yamlpath = path / 'metadata.yaml' self.path = path - self.bio = False try: yaml = YAML(typ='safe') dct = yaml.load(yamlpath.read_text()) @@ -95,7 +101,7 @@ class Reader: self.metadata: Metadata = dct['rosbag2_bagfile_information'] if (ver := self.metadata['version']) > 6: raise ReaderError(f'Rosbag2 version {ver} not supported; please report issue.') - if storageid := self.metadata['storage_identifier'] != 'sqlite3': + if (storageid := self.metadata['storage_identifier']) not in self.STORAGE_PLUGINS: raise ReaderError( f'Storage plugin {storageid!r} not supported; please report issue.', ) @@ -131,20 +137,12 @@ class Reader: self.files: list[FileInformation] = self.metadata.get('files', [])[:] self.custom_data: dict[str, str] = self.metadata.get('custom_data', {}) + + self.tmpdir: Optional[TemporaryDirectory] = None + self.storage: Optional[StorageProtocol] = None except KeyError as exc: raise ReaderError(f'A metadata key is missing {exc!r}.') from None - def open(self) -> None: - """Open rosbag2.""" - # Future storage formats will require file handles. - self.bio = True - - def close(self) -> None: - """Close rosbag2.""" - # Future storage formats will require file handles. - assert self.bio - self.bio = False - @property def duration(self) -> int: """Duration in nanoseconds between earliest and latest messages.""" @@ -183,7 +181,50 @@ class Reader: """Topic information.""" return {x.topic: TopicInfo(x.msgtype, x.msgdef, x.msgcount, [x]) for x in self.connections} - def messages( # pylint: disable=too-many-locals + def open(self) -> None: + """Open rosbag2.""" + storage_paths = [] + if self.compression_mode == 'file': + self.tmpdir = TemporaryDirectory() # pylint: disable=consider-using-with + tmpdir = self.tmpdir.name + decomp = zstandard.ZstdDecompressor() + for path in self.paths: + storage_file = Path(tmpdir, path.stem) + with path.open('rb') as infile, storage_file.open('wb') as outfile: + decomp.copy_stream(infile, outfile) + storage_paths.append(storage_file) + else: + storage_paths = self.paths[:] + + self.storage = self.STORAGE_PLUGINS[self.metadata['storage_identifier']]( + storage_paths, + self.connections, + ) + self.storage.open() + definitions = self.storage.get_definitions() + for idx, conn in enumerate(self.connections): + if desc := definitions.get(conn.msgtype): + self.connections[idx] = Connection( + id=conn.id, + topic=conn.topic, + msgtype=conn.msgtype, + msgdef=desc[1], + md5sum=desc[0], + msgcount=conn.msgcount, + ext=conn.ext, + owner=conn.owner, + ) + + def close(self) -> None: + """Close rosbag2.""" + assert self.storage + self.storage.close() + self.storage = None + if self.tmpdir: + self.tmpdir.cleanup() + self.tmpdir = None + + def messages( self, connections: Iterable[Connection] = (), start: Optional[int] = None, @@ -201,67 +242,18 @@ class Reader: tuples of connection, timestamp (ns), and rawdata. Raises: - ReaderError: Bag not open. + ReaderError: If reader was not opened. """ - if not self.bio: + if not self.storage: raise ReaderError('Rosbag is not open.') - query = [ - 'SELECT topics.id,messages.timestamp,messages.data', - 'FROM messages JOIN topics ON messages.topic_id=topics.id', - ] - args: list[Any] = [] - clause = 'WHERE' - - if connections: - topics = {x.topic for x in connections} - query.append(f'{clause} topics.name IN ({",".join("?" for _ in topics)})') - args += topics - clause = 'AND' - - if start is not None: - query.append(f'{clause} messages.timestamp >= ?') - args.append(start) - clause = 'AND' - - if stop is not None: - query.append(f'{clause} messages.timestamp < ?') - args.append(stop) - clause = 'AND' - - query.append('ORDER BY timestamp') - querystr = ' '.join(query) - - for filepath in self.paths: - with decompress(filepath, self.compression_mode == 'file') as path: - conn = sqlite3.connect(f'file:{path}?immutable=1', uri=True) - conn.row_factory = lambda _, x: x - cur = conn.cursor() - cur.execute( - 'SELECT count(*) FROM sqlite_master ' - 'WHERE type="table" AND name IN ("messages", "topics")', - ) - if cur.fetchone()[0] != 2: - raise ReaderError(f'Cannot open database {path} or database missing tables.') - - cur.execute('SELECT name,id FROM topics') - connmap: dict[int, Connection] = { - row[1]: next((x for x in self.connections if x.topic == row[0]), - None) # type: ignore - for row in cur - } - - cur.execute(querystr, args) - - if self.compression_mode == 'message': - decomp = zstandard.ZstdDecompressor().decompress - for row in cur: - cid, timestamp, data = row - yield connmap[cid], timestamp, decomp(data) - else: - for cid, timestamp, data in cur: - yield connmap[cid], timestamp, data + if self.compression_mode == 'message': + decomp = zstandard.ZstdDecompressor().decompress + for connection, timestamp, data in self.storage.messages(connections, start, stop): + yield connection, timestamp, decomp(data) + else: + yield from self.storage.messages(connections, start, stop) def __enter__(self) -> Reader: """Open rosbag2 when entering contextmanager.""" diff --git a/src/rosbags/rosbag2/storage_sqlite3.py b/src/rosbags/rosbag2/storage_sqlite3.py new file mode 100644 index 00000000..bddfc038 --- /dev/null +++ b/src/rosbags/rosbag2/storage_sqlite3.py @@ -0,0 +1,119 @@ +# Copyright 2020-2023 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Sqlite3 storage.""" + +from __future__ import annotations + +import sqlite3 +from typing import TYPE_CHECKING + +from .errors import ReaderError + +if TYPE_CHECKING: + from pathlib import Path + from typing import Any, Generator, Iterable, Optional + + from rosbags.interfaces import Connection + + +class ReaderSqlite3: + """Sqlite3 storage reader.""" + + def __init__( + self, + paths: Iterable[Path], + connections: Iterable[Connection], + ): + """Set up storage reader. + + Args: + paths: Paths of storage files. + connections: List of connections. + + """ + self.opened = False + self.paths = paths + self.connections = connections + + def open(self) -> None: + """Open rosbag2.""" + self.opened = True + + def close(self) -> None: + """Close rosbag2.""" + assert self.opened + self.opened = False + + def get_definitions(self) -> dict[str, tuple[str, str]]: + """Get message definitions.""" + return {} + + def messages( # pylint: disable=too-many-locals + self, + connections: Iterable[Connection] = (), + start: Optional[int] = None, + stop: Optional[int] = None, + ) -> Generator[tuple[Connection, int, bytes], None, None]: + """Read messages from bag. + + Args: + connections: Iterable with connections to filter for. An empty + iterable disables filtering on connections. + start: Yield only messages at or after this timestamp (ns). + stop: Yield only messages before this timestamp (ns). + + Yields: + tuples of connection, timestamp (ns), and rawdata. + + Raises: + ReaderError: Bag not open. + + """ + query = [ + 'SELECT topics.id,messages.timestamp,messages.data', + 'FROM messages JOIN topics ON messages.topic_id=topics.id', + ] + args: list[Any] = [] + clause = 'WHERE' + + if connections: + topics = {x.topic for x in connections} + query.append(f'{clause} topics.name IN ({",".join("?" for _ in topics)})') + args += topics + clause = 'AND' + + if start is not None: + query.append(f'{clause} messages.timestamp >= ?') + args.append(start) + clause = 'AND' + + if stop is not None: + query.append(f'{clause} messages.timestamp < ?') + args.append(stop) + clause = 'AND' + + query.append('ORDER BY timestamp') + querystr = ' '.join(query) + + for path in self.paths: + conn = sqlite3.connect(f'file:{path}?immutable=1', uri=True) + conn.row_factory = lambda _, x: x + cur = conn.cursor() + cur.execute( + 'SELECT count(*) FROM sqlite_master ' + 'WHERE type="table" AND name IN ("messages", "topics")', + ) + if cur.fetchone()[0] != 2: + raise ReaderError(f'Cannot open database {path} or database missing tables.') + + cur.execute('SELECT name,id FROM topics') + connmap: dict[int, Connection] = { + row[1]: next((x for x in self.connections if x.topic == row[0]), + None) # type: ignore + for row in cur + } + + cur.execute(querystr, args) + + for cid, timestamp, data in cur: + yield connmap[cid], timestamp, data From aa18bec9d1b27b48630aec3a8e0e9d7a9f537fe6 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Fri, 13 Jan 2023 12:16:05 +0100 Subject: [PATCH 107/114] Improve parsing of string types in idl definitions --- src/rosbags/typesys/idl.py | 10 +++++----- tests/test_parse.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rosbags/typesys/idl.py b/src/rosbags/typesys/idl.py index 821ec173..348e2e18 100644 --- a/src/rosbags/typesys/idl.py +++ b/src/rosbags/typesys/idl.py @@ -73,8 +73,8 @@ typedef_dcl = 'typedef' type_declarator type_declarator - = ( simple_type_spec - / template_type_spec + = ( template_type_spec + / simple_type_spec / constr_type_dcl ) any_declarators @@ -169,7 +169,7 @@ octet_type string_type = 'string' '<' expression '>' - / 'string\b' + / r'string\b' scoped_name = identifier '::' scoped_name @@ -491,10 +491,10 @@ class VisitorIDL(Visitor): # pylint: disable=too-many-public-methods def visit_string_type( self, - children: Union[StringNode, tuple[LiteralMatch, LiteralMatch, LiteralNode, LiteralMatch]], + children: Union[str, tuple[LiteralMatch, LiteralMatch, LiteralNode, LiteralMatch]], ) -> Union[StringNode, tuple[Nodetype, str, LiteralNode]]: """Prrocess string type specifier.""" - if len(children) == 2: + if isinstance(children, str): return (Nodetype.BASE, 'string') assert len(children) == 4 diff --git a/tests/test_parse.py b/tests/test_parse.py index 6eeee92c..f0fc7755 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -307,7 +307,7 @@ def test_parse_idl() -> None: assert consts == [] assert len(fields) == 1 assert fields[0][0] == 'values' - assert fields[0][1] == (Nodetype.ARRAY, ((Nodetype.NAME, 'string'), 3)) + assert fields[0][1] == (Nodetype.ARRAY, ((Nodetype.BASE, 'string'), 3)) def test_register_types() -> None: From df9bf80170bbfd4c281ec52dbacab3e34c8c6c3f Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 1 Mar 2023 20:12:57 +0100 Subject: [PATCH 108/114] Update tooling --- .gitlab-ci.yml | 6 +++++- pyproject.toml | 44 +++++++++++++++++++------------------------- setup.cfg | 13 +++++-------- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d102bd4e..fb06de2b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,7 +10,11 @@ test: - python3.8 -m venv venv - venv/bin/python -m pip install -r requirements-dev.txt - venv/bin/python -m pip install -e .[dev] - - venv/bin/python -m pytest + - venv/bin/pytest --cov-report=term --cov-report=xml --junit-xml=report.xml + - venv/bin/flake8 src tests + - venv/bin/mypy --no-error-summary src tests + - venv/bin/pylint --jobs 0 --score n src tests + - venv/bin/yapf -dpr src tests - venv/bin/sphinx-build docs public coverage: '/\d+\%\s*$/' artifacts: diff --git a/pyproject.toml b/pyproject.toml index 1494a21e..252a2f78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,25 +1,25 @@ [build-system] -requires = ["setuptools>=56.2.0", "wheel"] +requires = ["setuptools>=65.4.0", "wheel"] build-backend = "setuptools.build_meta" -[tool.coverage.report] -exclude_lines = [ +[tool.coverage] +report.exclude_lines = [ "pragma: no cover", "if TYPE_CHECKING:", "if __name__ == '__main__':", ] +report.show_missing = true +report.skip_covered = true +run.branch = true +run.source = ["src"] -[tool.coverage.run] -source = [ - "src", -] [tool.flake8] avoid_escape = false docstring_convention = "all" docstring_style = "google" -extend_exclude = ["venv*", ".venv*"] +extend_exclude = ["venv"] ignore = [ # do not require annotation of `self` "ANN101", @@ -40,25 +40,31 @@ max_line_length = 100 strictness = "long" suppress_none_returning = true + [tool.isort] include_trailing_comma = true line_length = 100 multi_line_output = 3 + [tool.mypy] explicit_package_bases = true -mypy_path = "$MYPY_CONFIG_FILE_DIR/src" +fast_module_lookup = true +mypy_path = "src" namespace_packages = true strict = true + [[tool.mypy.overrides]] module = "lz4.frame" ignore_missing_imports = true + [tool.pydocstyle] convention = "google" add_select = ["D204", "D400", "D401", "D404", "D413"] + [tool.pylint.'MESSAGES CONTROL'] enable = "all" disable = [ @@ -72,7 +78,7 @@ disable = [ "too-many-branches", # fixme "fixme", - # pep8-naming (pylint FAQ", keep: invalid-name) + # pep8-naming (pylint FAQ, keep: invalid-name) "bad-classmethod-argument", "bad-mcs-classmethod-argument", "no-self-argument", @@ -95,22 +101,10 @@ disable = [ "unused-variable", ] + [tool.pytest.ini_options] -addopts = [ - "-v", - "--flake8", - "--mypy", - "--pylint", - "--yapf", - "--cov", - "--cov-branch", - "--cov-report=html", - "--cov-report=term", - "--cov-report=xml", - "--no-cov-on-fail", - "--junitxml=report.xml", -] -junit_family = "xunit2" +addopts = ["--cov=src", "--verbose"] + [tool.yapf] based_on_style = "google" diff --git a/setup.cfg b/setup.cfg index cf214f18..0b4ded51 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,7 +44,6 @@ include_package_data = true package_dir = = src packages = find_namespace: -zip_safe = false python_requires = >=3.8.2 install_requires = @@ -79,22 +78,20 @@ dev = flake8-use-fstring mypy pep8-naming + pylint pytest pytest-cov - pytest-flake8 - pytest-mypy - pytest-pylint - pytest-yapf3 sphinx sphinx-autodoc-typehints sphinx-rtd-theme + toml # required by yapf yapf -[options.packages.find] -where = src - [options.package_data] * = py.typed +[options.packages.find] +where = src + [sdist] formats = gztar, zip From 53e3209cd57ad1df49cc204cd708bc79edbd7f89 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 1 Mar 2023 20:13:08 +0100 Subject: [PATCH 109/114] Update dependencies --- requirements-dev.txt | 1163 +++++++++++++++++++++++------------------- requirements.txt | 274 +++++----- 2 files changed, 802 insertions(+), 635 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index bcd0bed2..cde713ce 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,109 +1,206 @@ # -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # pip-compile --extra=dev --generate-hashes --output-file=requirements-dev.txt setup.cfg # -alabaster==0.7.12 \ - --hash=sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359 \ - --hash=sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02 +alabaster==0.7.13 \ + --hash=sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3 \ + --hash=sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2 # via sphinx astor==0.8.1 \ --hash=sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5 \ --hash=sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e - # via flake8-simplify -astroid==2.11.7 \ - --hash=sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b \ - --hash=sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946 + # via + # flake8-simplify + # flake8-type-checking +astroid==2.14.2 \ + --hash=sha256:0e0e3709d64fbffd3037e4ff403580550f14471fd3eaae9fa11cc9a5c7901153 \ + --hash=sha256:a3cf9f02c53dd259144a7e8f3ccd75d67c9a8c716ef183e0c1f291bc5d7bb3cf # via pylint -attrs==21.4.0 \ - --hash=sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4 \ - --hash=sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd +attrs==22.2.0 \ + --hash=sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836 \ + --hash=sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99 # via # flake8-annotations # flake8-bugbear # pytest - # pytest-mypy -babel==2.10.3 \ - --hash=sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51 \ - --hash=sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb +babel==2.12.1 \ + --hash=sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610 \ + --hash=sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455 # via sphinx -certifi==2022.6.15 \ - --hash=sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d \ - --hash=sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412 +certifi==2022.12.7 \ + --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ + --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 # via requests -charset-normalizer==2.1.0 \ - --hash=sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5 \ - --hash=sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413 +charset-normalizer==3.0.1 \ + --hash=sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b \ + --hash=sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42 \ + --hash=sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d \ + --hash=sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b \ + --hash=sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a \ + --hash=sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59 \ + --hash=sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154 \ + --hash=sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1 \ + --hash=sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c \ + --hash=sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a \ + --hash=sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d \ + --hash=sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6 \ + --hash=sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b \ + --hash=sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b \ + --hash=sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783 \ + --hash=sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5 \ + --hash=sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918 \ + --hash=sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555 \ + --hash=sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639 \ + --hash=sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786 \ + --hash=sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e \ + --hash=sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed \ + --hash=sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820 \ + --hash=sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8 \ + --hash=sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3 \ + --hash=sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541 \ + --hash=sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14 \ + --hash=sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be \ + --hash=sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e \ + --hash=sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76 \ + --hash=sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b \ + --hash=sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c \ + --hash=sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b \ + --hash=sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3 \ + --hash=sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc \ + --hash=sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6 \ + --hash=sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59 \ + --hash=sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4 \ + --hash=sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d \ + --hash=sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d \ + --hash=sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3 \ + --hash=sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a \ + --hash=sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea \ + --hash=sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6 \ + --hash=sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e \ + --hash=sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603 \ + --hash=sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24 \ + --hash=sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a \ + --hash=sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58 \ + --hash=sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678 \ + --hash=sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a \ + --hash=sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c \ + --hash=sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6 \ + --hash=sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18 \ + --hash=sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174 \ + --hash=sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317 \ + --hash=sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f \ + --hash=sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc \ + --hash=sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837 \ + --hash=sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41 \ + --hash=sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c \ + --hash=sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579 \ + --hash=sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753 \ + --hash=sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8 \ + --hash=sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291 \ + --hash=sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087 \ + --hash=sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866 \ + --hash=sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3 \ + --hash=sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d \ + --hash=sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1 \ + --hash=sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca \ + --hash=sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e \ + --hash=sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db \ + --hash=sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72 \ + --hash=sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d \ + --hash=sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc \ + --hash=sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539 \ + --hash=sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d \ + --hash=sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af \ + --hash=sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b \ + --hash=sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602 \ + --hash=sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f \ + --hash=sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478 \ + --hash=sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c \ + --hash=sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e \ + --hash=sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479 \ + --hash=sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7 \ + --hash=sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8 # via requests -classify-imports==4.1.0 \ - --hash=sha256:45436d3c4c886ca9092a2c90551b392ba120360e7a782574169ddeb866bbc08a \ - --hash=sha256:69ddc4320690c26aa8baa66bf7e0fa0eecfda49d99cf71a59dee0b57dac82616 +classify-imports==4.2.0 \ + --hash=sha256:7abfb7ea92149b29d046bd34573d247ba6e68cc28100c801eba4af17964fc40e \ + --hash=sha256:dbbc264b70a470ed8c6c95976a11dfb8b7f63df44ed1af87328bbed2663f5161 # via flake8-type-checking -coverage[toml]==6.4.2 \ - --hash=sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32 \ - --hash=sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7 \ - --hash=sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996 \ - --hash=sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55 \ - --hash=sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46 \ - --hash=sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de \ - --hash=sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039 \ - --hash=sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee \ - --hash=sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1 \ - --hash=sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f \ - --hash=sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63 \ - --hash=sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083 \ - --hash=sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe \ - --hash=sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0 \ - --hash=sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6 \ - --hash=sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe \ - --hash=sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933 \ - --hash=sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0 \ - --hash=sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c \ - --hash=sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07 \ - --hash=sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8 \ - --hash=sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b \ - --hash=sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e \ - --hash=sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120 \ - --hash=sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f \ - --hash=sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e \ - --hash=sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd \ - --hash=sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f \ - --hash=sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386 \ - --hash=sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8 \ - --hash=sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae \ - --hash=sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc \ - --hash=sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783 \ - --hash=sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d \ - --hash=sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c \ - --hash=sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97 \ - --hash=sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978 \ - --hash=sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf \ - --hash=sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29 \ - --hash=sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39 \ - --hash=sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452 +coverage[toml]==7.2.1 \ + --hash=sha256:0339dc3237c0d31c3b574f19c57985fcbe494280153bbcad33f2cdf469f4ac3e \ + --hash=sha256:09643fb0df8e29f7417adc3f40aaf379d071ee8f0350ab290517c7004f05360b \ + --hash=sha256:0bd7e628f6c3ec4e7d2d24ec0e50aae4e5ae95ea644e849d92ae4805650b4c4e \ + --hash=sha256:0cf557827be7eca1c38a2480484d706693e7bb1929e129785fe59ec155a59de6 \ + --hash=sha256:0f8318ed0f3c376cfad8d3520f496946977abde080439d6689d7799791457454 \ + --hash=sha256:1b7fb13850ecb29b62a447ac3516c777b0e7a09ecb0f4bb6718a8654c87dfc80 \ + --hash=sha256:22c308bc508372576ffa3d2dbc4824bb70d28eeb4fcd79d4d1aed663a06630d0 \ + --hash=sha256:3004765bca3acd9e015794e5c2f0c9a05587f5e698127ff95e9cfba0d3f29339 \ + --hash=sha256:3a209d512d157379cc9ab697cbdbb4cfd18daa3e7eebaa84c3d20b6af0037384 \ + --hash=sha256:436313d129db7cf5b4ac355dd2bd3f7c7e5294af077b090b85de75f8458b8616 \ + --hash=sha256:49567ec91fc5e0b15356da07a2feabb421d62f52a9fff4b1ec40e9e19772f5f8 \ + --hash=sha256:4dd34a935de268a133e4741827ae951283a28c0125ddcdbcbba41c4b98f2dfef \ + --hash=sha256:570c21a29493b350f591a4b04c158ce1601e8d18bdcd21db136fbb135d75efa6 \ + --hash=sha256:5928b85416a388dd557ddc006425b0c37e8468bd1c3dc118c1a3de42f59e2a54 \ + --hash=sha256:5d2b9b5e70a21474c105a133ba227c61bc95f2ac3b66861143ce39a5ea4b3f84 \ + --hash=sha256:617a94ada56bbfe547aa8d1b1a2b8299e2ec1ba14aac1d4b26a9f7d6158e1273 \ + --hash=sha256:6a034480e9ebd4e83d1aa0453fd78986414b5d237aea89a8fdc35d330aa13bae \ + --hash=sha256:6fce673f79a0e017a4dc35e18dc7bb90bf6d307c67a11ad5e61ca8d42b87cbff \ + --hash=sha256:78d2c3dde4c0b9be4b02067185136b7ee4681978228ad5ec1278fa74f5ca3e99 \ + --hash=sha256:7f099da6958ddfa2ed84bddea7515cb248583292e16bb9231d151cd528eab657 \ + --hash=sha256:80559eaf6c15ce3da10edb7977a1548b393db36cbc6cf417633eca05d84dd1ed \ + --hash=sha256:834c2172edff5a08d78e2f53cf5e7164aacabeb66b369f76e7bb367ca4e2d993 \ + --hash=sha256:861cc85dfbf55a7a768443d90a07e0ac5207704a9f97a8eb753292a7fcbdfcfc \ + --hash=sha256:8649371570551d2fd7dee22cfbf0b61f1747cdfb2b7587bb551e4beaaa44cb97 \ + --hash=sha256:87dc37f16fb5e3a28429e094145bf7c1753e32bb50f662722e378c5851f7fdc6 \ + --hash=sha256:8a6450da4c7afc4534305b2b7d8650131e130610cea448ff240b6ab73d7eab63 \ + --hash=sha256:8d3843ca645f62c426c3d272902b9de90558e9886f15ddf5efe757b12dd376f5 \ + --hash=sha256:8dca3c1706670297851bca1acff9618455122246bdae623be31eca744ade05ec \ + --hash=sha256:97a3189e019d27e914ecf5c5247ea9f13261d22c3bb0cfcfd2a9b179bb36f8b1 \ + --hash=sha256:99f4dd81b2bb8fc67c3da68b1f5ee1650aca06faa585cbc6818dbf67893c6d58 \ + --hash=sha256:9e872b082b32065ac2834149dc0adc2a2e6d8203080501e1e3c3c77851b466f9 \ + --hash=sha256:a81dbcf6c6c877986083d00b834ac1e84b375220207a059ad45d12f6e518a4e3 \ + --hash=sha256:abacd0a738e71b20e224861bc87e819ef46fedba2fb01bc1af83dfd122e9c319 \ + --hash=sha256:ae82c988954722fa07ec5045c57b6d55bc1a0890defb57cf4a712ced65b26ddd \ + --hash=sha256:b0c0d46de5dd97f6c2d1b560bf0fcf0215658097b604f1840365296302a9d1fb \ + --hash=sha256:b1991a6d64231a3e5bbe3099fb0dd7c9aeaa4275ad0e0aeff4cb9ef885c62ba2 \ + --hash=sha256:b2167d116309f564af56f9aa5e75ef710ef871c5f9b313a83050035097b56820 \ + --hash=sha256:bd5a12239c0006252244f94863f1c518ac256160cd316ea5c47fb1a11b25889a \ + --hash=sha256:bdd3f2f285ddcf2e75174248b2406189261a79e7fedee2ceeadc76219b6faa0e \ + --hash=sha256:c77f2a9093ccf329dd523a9b2b3c854c20d2a3d968b6def3b820272ca6732242 \ + --hash=sha256:cb5f152fb14857cbe7f3e8c9a5d98979c4c66319a33cad6e617f0067c9accdc4 \ + --hash=sha256:cca7c0b7f5881dfe0291ef09ba7bb1582cb92ab0aeffd8afb00c700bf692415a \ + --hash=sha256:d2ef6cae70168815ed91388948b5f4fcc69681480a0061114db737f957719f03 \ + --hash=sha256:d9256d4c60c4bbfec92721b51579c50f9e5062c21c12bec56b55292464873508 \ + --hash=sha256:e191a63a05851f8bce77bc875e75457f9b01d42843f8bd7feed2fc26bbe60833 \ + --hash=sha256:e2b50ebc2b6121edf352336d503357321b9d8738bb7a72d06fc56153fd3f4cd8 \ + --hash=sha256:e3ea04b23b114572b98a88c85379e9e9ae031272ba1fb9b532aa934c621626d4 \ + --hash=sha256:e4d70c853f0546855f027890b77854508bdb4d6a81242a9d804482e667fff6e6 \ + --hash=sha256:f29351393eb05e6326f044a7b45ed8e38cb4dcc38570d12791f271399dc41431 \ + --hash=sha256:f3d07edb912a978915576a776756069dede66d012baa503022d3a0adba1b6afa \ + --hash=sha256:fac6343bae03b176e9b58104a9810df3cdccd5cfed19f99adfa807ffbf43cf9b # via pytest-cov darglint==1.8.1 \ --hash=sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da \ --hash=sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d # via rosbags (setup.cfg) -dill==0.3.5.1 \ - --hash=sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302 \ - --hash=sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86 +dill==0.3.6 \ + --hash=sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0 \ + --hash=sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373 # via pylint -docutils==0.17.1 \ - --hash=sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125 \ - --hash=sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61 +docutils==0.18.1 \ + --hash=sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c \ + --hash=sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06 # via # sphinx # sphinx-rtd-theme -filelock==3.7.1 \ - --hash=sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404 \ - --hash=sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04 - # via pytest-mypy -flake8==4.0.1 \ - --hash=sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d \ - --hash=sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d +exceptiongroup==1.1.0 \ + --hash=sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e \ + --hash=sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23 + # via pytest +flake8==6.0.0 \ + --hash=sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7 \ + --hash=sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181 # via # flake8-annotations # flake8-bugbear @@ -119,35 +216,34 @@ flake8==4.0.1 \ # flake8-type-checking # flake8-use-fstring # pep8-naming - # pytest-flake8 # rosbags (setup.cfg) -flake8-annotations==2.9.0 \ - --hash=sha256:63fb3f538970b6a8dfd84125cf5af16f7b22e52d5032acb3b7eb23645ecbda9b \ - --hash=sha256:84f46de2964cb18fccea968d9eafce7cf857e34d913d515120795b9af6498d56 +flake8-annotations==3.0.0 \ + --hash=sha256:88c8b35a0db10b9a92be69ed3f81494509a18db1c3162551e57bc0fc35fab065 \ + --hash=sha256:ea927d31016515e9aa6e256651d74baeeee6fa4ad3f8383715ec5c0460a4c225 # via rosbags (setup.cfg) -flake8-bugbear==22.7.1 \ - --hash=sha256:db5d7a831ef4412a224b26c708967ff816818cabae415e76b8c58df156c4b8e5 \ - --hash=sha256:e450976a07e4f9d6c043d4f72b17ec1baf717fe37f7997009c8ae58064f88305 +flake8-bugbear==23.2.13 \ + --hash=sha256:39259814a83f33c8409417ee12dd4050c9c0bb4c8707c12fc18ae62b2f3ddee1 \ + --hash=sha256:f136bd0ca2684f101168bba2310dec541e11aa6b252260c17dcf58d18069a740 # via rosbags (setup.cfg) flake8-commas==2.1.0 \ --hash=sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263 \ --hash=sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54 # via rosbags (setup.cfg) -flake8-comprehensions==3.10.0 \ - --hash=sha256:181158f7e7aa26a63a0a38e6017cef28c6adee71278ce56ce11f6ec9c4905058 \ - --hash=sha256:dad454fd3d525039121e98fa1dd90c46bc138708196a4ebbc949ad3c859adedb +flake8-comprehensions==3.10.1 \ + --hash=sha256:412052ac4a947f36b891143430fef4859705af11b2572fbb689f90d372cf26ab \ + --hash=sha256:d763de3c74bc18a79c039a7ec732e0a1985b0c79309ceb51e56401ad0a2cd44e # via rosbags (setup.cfg) -flake8-docstrings==1.6.0 \ - --hash=sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde \ - --hash=sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b +flake8-docstrings==1.7.0 \ + --hash=sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af \ + --hash=sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75 # via rosbags (setup.cfg) flake8-fixme==1.1.1 \ --hash=sha256:226a6f2ef916730899f29ac140bed5d4a17e5aba79f00a0e3ae1eff1997cb1ac \ --hash=sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a # via rosbags (setup.cfg) -flake8-isort==4.1.2.post0 \ - --hash=sha256:4f95b40706dbb507cff872b34683283662e945d6028d3c8257e69de5fc6b7446 \ - --hash=sha256:dee69bc3c09f0832df88acf795845db8a6673b79237371a05fa927ce095248e5 +flake8-isort==6.0.0 \ + --hash=sha256:537f453a660d7e903f602ecfa36136b140de279df58d02eb1b6a0c84e83c528c \ + --hash=sha256:aa0cac02a62c7739e370ce6b9c31743edac904bae4b157274511fc8a19c75bbc # via rosbags (setup.cfg) flake8-mutable==1.2.0 \ --hash=sha256:38fd9dadcbcda6550a916197bc40ed76908119dabb37fbcca30873666c31d2d5 \ @@ -163,51 +259,51 @@ flake8-print==5.0.0 \ --hash=sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9 \ --hash=sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8 # via rosbags (setup.cfg) -flake8-pyprojecttoml==0.0.1 \ - --hash=sha256:644c3dcf558209ca246239ca4f56844769c863181eab05177d8906dc77bbdb8f \ - --hash=sha256:654d91135ee7f7429ab56dc39a9fb37a48f3e2412e189ae7a756921cb0ffffe5 +flake8-pyprojecttoml==0.0.2 \ + --hash=sha256:37b9a8e5274d04591fbecc0782f626fb31b8d266e26192ce18ada25dabfc9f1d \ + --hash=sha256:b78dd64254e2d6aa596b5be4e2a41b81147cafec57760de1c7322d025159983c # via rosbags (setup.cfg) -flake8-pytest-style==1.6.0 \ - --hash=sha256:5fedb371a950e9fe0e0e6bfc854be7d99151271208f34cd2cc517681ece27780 \ - --hash=sha256:c1175713e9e11b78cd1a035ed0bca0d1e41d09c4af329a952750b61d4194ddac +flake8-pytest-style==1.7.2 \ + --hash=sha256:b924197c99b951315949920b0e5547f34900b1844348432e67a44ab191582109 \ + --hash=sha256:f5d2aa3219163a052dd92226589d45fab8ea027a3269922f0c4029f548ea5cd1 # via rosbags (setup.cfg) -flake8-quotes==3.3.1 \ - --hash=sha256:633adca6fb8a08131536af0d750b44d6985b9aba46f498871e21588c3e6f525a +flake8-quotes==3.3.2 \ + --hash=sha256:6e26892b632dacba517bf27219c459a8396dcfac0f5e8204904c5a4ba9b480e1 # via rosbags (setup.cfg) -flake8-return==1.1.3 \ - --hash=sha256:13a31edeb0c6157dd4f77166cdaa6141703d2b8b24def5558ae659852a003cb4 \ - --hash=sha256:4a266191f7ed69aa26b835ec90c5a5522fa8f79f5cf6363a877ac499f8eb418b +flake8-return==1.2.0 \ + --hash=sha256:1f07af12954ed03ebe2c2aac2418f78b55374e9929d4956109664588f31582a1 \ + --hash=sha256:68dfa56582cd704febd02ad86dcf5df67e38e0836d62f1ceae7930d76d3dd955 # via rosbags (setup.cfg) -flake8-simplify==0.19.2 \ - --hash=sha256:9c1e96ba738f9057a561697d67df7a9058898ac6313a67eda09942255cba6d38 \ - --hash=sha256:a30ef76bf1c0cb89a52a8a5cee37667688e61a66735e997c08d56cd002f9e3e9 +flake8-simplify==0.19.3 \ + --hash=sha256:1057320e9312d75849541fee822900d27bcad05b2405edc84713affee635629e \ + --hash=sha256:2fb083bf5142a98d9c9554755cf2f56f8926eb4a33eae30c0809041b1546879e # via rosbags (setup.cfg) -flake8-type-checking==2.0.7 \ - --hash=sha256:055d33558938b599cbf754987fa3e11f51c3a2a57e26c575674ba1ea392c93ca \ - --hash=sha256:a326830f9e12fc86b2bf3c5b56b786dc9541b8cd7e9755d73938d6a0e36d7b6d +flake8-type-checking==2.3.0 \ + --hash=sha256:7117b8a22d64db02f9d8c724df5d2517e59c6290b034cfa54496c7ae73c07f51 \ + --hash=sha256:f802c9933b2a98b96fc4a0b3b90ef0f8379625f867cb73633c09fc2bf746333b # via rosbags (setup.cfg) -flake8-use-fstring==1.3 \ - --hash=sha256:1bd4a409adbb93e64e711fdd26b88759c33792e3899f174edc68ddf7307e81b6 +flake8-use-fstring==1.4 \ + --hash=sha256:6550bf722585eb97dffa8343b0f1c372101f5c4ab5b07ebf0edd1c79880cdd39 # via rosbags (setup.cfg) -idna==3.3 \ - --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ - --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 # via requests imagesize==1.4.1 \ --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \ --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a # via sphinx -importlib-metadata==4.12.0 \ - --hash=sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670 \ - --hash=sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23 +importlib-metadata==6.0.0 \ + --hash=sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad \ + --hash=sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d # via sphinx -iniconfig==1.1.1 \ - --hash=sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3 \ - --hash=sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32 +iniconfig==2.0.0 \ + --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ + --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 # via pytest -isort==5.10.1 \ - --hash=sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7 \ - --hash=sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951 +isort==5.12.0 \ + --hash=sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504 \ + --hash=sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6 # via # flake8-isort # pylint @@ -215,287 +311,300 @@ jinja2==3.1.2 \ --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 # via sphinx -lazy-object-proxy==1.7.1 \ - --hash=sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7 \ - --hash=sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a \ - --hash=sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c \ - --hash=sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc \ - --hash=sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f \ - --hash=sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09 \ - --hash=sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442 \ - --hash=sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e \ - --hash=sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029 \ - --hash=sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61 \ - --hash=sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb \ - --hash=sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0 \ - --hash=sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35 \ - --hash=sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42 \ - --hash=sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1 \ - --hash=sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad \ - --hash=sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443 \ - --hash=sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd \ - --hash=sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9 \ - --hash=sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148 \ - --hash=sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38 \ - --hash=sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55 \ - --hash=sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36 \ - --hash=sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a \ - --hash=sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b \ - --hash=sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44 \ - --hash=sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6 \ - --hash=sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69 \ - --hash=sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4 \ - --hash=sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84 \ - --hash=sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de \ - --hash=sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28 \ - --hash=sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c \ - --hash=sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1 \ - --hash=sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8 \ - --hash=sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b \ - --hash=sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb +lazy-object-proxy==1.9.0 \ + --hash=sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382 \ + --hash=sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82 \ + --hash=sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9 \ + --hash=sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494 \ + --hash=sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46 \ + --hash=sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30 \ + --hash=sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63 \ + --hash=sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4 \ + --hash=sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae \ + --hash=sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be \ + --hash=sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701 \ + --hash=sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd \ + --hash=sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006 \ + --hash=sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a \ + --hash=sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586 \ + --hash=sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8 \ + --hash=sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821 \ + --hash=sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07 \ + --hash=sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b \ + --hash=sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171 \ + --hash=sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b \ + --hash=sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2 \ + --hash=sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7 \ + --hash=sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4 \ + --hash=sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8 \ + --hash=sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e \ + --hash=sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f \ + --hash=sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda \ + --hash=sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4 \ + --hash=sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e \ + --hash=sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671 \ + --hash=sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11 \ + --hash=sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455 \ + --hash=sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734 \ + --hash=sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb \ + --hash=sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59 # via astroid -lz4==4.0.2 \ - --hash=sha256:083b7172c2938412ae37c3a090250bfdd9e4a6e855442594f86c3608ed12729b \ - --hash=sha256:154e6e9f58a7bafc4d2a1395160305b78fc82fa708bfa58cf0ad977c443d1f8f \ - --hash=sha256:1bd56282f6993e013ccf7f6edf1530c2a13d1662741e2be072349c7f70bc0682 \ - --hash=sha256:1ed9a1875dc2a489f3b665d0211984689d0e76585e55650b044a64dbd2d22992 \ - --hash=sha256:345608de23b4d68fbdef373f1e53d6c5abd99a062d4ff922e3350f47775ab123 \ - --hash=sha256:35e6caced0229b90151d31d9cf1eaa541e597f8021bf5b70ff9e6374e3e43b23 \ - --hash=sha256:3881573c3db902db370e072eb64b40c7c8289b94b2a731e051858cc198f890e8 \ - --hash=sha256:3fa0f000d8ce39e643e9e5c49fc4d1985156ffb177e3123a0f22551f5864841b \ - --hash=sha256:439898dd4176a724243002003c3f733eb6ce48a5988175f54c8560e0b100b7a6 \ - --hash=sha256:4cfa82f26b4f1835c797bd70e5ce20d5f1ee897b9a0c53e62d607f9029f521ce \ - --hash=sha256:56ea660097fec87f0c6746146b316775037f8dd886a4c5915360e5b32b7112d0 \ - --hash=sha256:5fe9db7627674875e4279c2ed50b1e38fb91ec3093347f871ed996e58edbb488 \ - --hash=sha256:61dbcca64e8e1655e06b588356c4b2515bccc1d7e84065f858a685abd96f0cf2 \ - --hash=sha256:6f3b3670f52f0871885258bcbc746f483760434336f0bc5581f161cc5d4b0c9a \ - --hash=sha256:9d141719d3cbb7933809642a61b68b8f595ddf85657016521756ddcf826b85cd \ - --hash=sha256:a8e02c2477bd704f43113ac8dd966c361187383591388818d74e1b73e4674759 \ - --hash=sha256:d2b18a6d6d9071c03dbf9e30bbe22e4476f24f1a4d73b1e975605ad3ce725e6c \ - --hash=sha256:ea2c2182a5b0ad03f33ac09db0925a1738a1d65751a3e058110bd900c643d359 \ - --hash=sha256:ed86ab22bfe1f4cd4fc983704134a8fdf746c1121a398f8f14cbd014c1a5b0ae \ - --hash=sha256:ee73357412c5505f6ba0ea61ff71455e2e4c1e04d8e60f17f3cd937261d773fa \ - --hash=sha256:fba1730cd2327a9d013192a9878714cc82f4877d2ada556222d03ea6428a80ed +lz4==4.3.2 \ + --hash=sha256:0ca83a623c449295bafad745dcd399cea4c55b16b13ed8cfea30963b004016c9 \ + --hash=sha256:0f5614d8229b33d4a97cb527db2a1ac81308c6e796e7bdb5d1309127289f69d5 \ + --hash=sha256:1c4c100d99eed7c08d4e8852dd11e7d1ec47a3340f49e3a96f8dfbba17ffb300 \ + --hash=sha256:1f25eb322eeb24068bb7647cae2b0732b71e5c639e4e4026db57618dcd8279f0 \ + --hash=sha256:200d05777d61ba1ff8d29cb51c534a162ea0b4fe6d3c28be3571a0a48ff36080 \ + --hash=sha256:31d72731c4ac6ebdce57cd9a5cabe0aecba229c4f31ba3e2c64ae52eee3fdb1c \ + --hash=sha256:3a85b430138882f82f354135b98c320dafb96fc8fe4656573d95ab05de9eb092 \ + --hash=sha256:4931ab28a0d1c133104613e74eec1b8bb1f52403faabe4f47f93008785c0b929 \ + --hash=sha256:4caedeb19e3ede6c7a178968b800f910db6503cb4cb1e9cc9221157572139b49 \ + --hash=sha256:65d5c93f8badacfa0456b660285e394e65023ef8071142e0dcbd4762166e1be0 \ + --hash=sha256:6b50f096a6a25f3b2edca05aa626ce39979d63c3b160687c8c6d50ac3943d0ba \ + --hash=sha256:7211dc8f636ca625abc3d4fb9ab74e5444b92df4f8d58ec83c8868a2b0ff643d \ + --hash=sha256:7a9eec24ec7d8c99aab54de91b4a5a149559ed5b3097cf30249b665689b3d402 \ + --hash=sha256:7c2df117def1589fba1327dceee51c5c2176a2b5a7040b45e84185ce0c08b6a3 \ + --hash=sha256:7e2dc1bd88b60fa09b9b37f08553f45dc2b770c52a5996ea52b2b40f25445676 \ + --hash=sha256:83903fe6db92db0be101acedc677aa41a490b561567fe1b3fe68695b2110326c \ + --hash=sha256:83acfacab3a1a7ab9694333bcb7950fbeb0be21660d236fd09c8337a50817897 \ + --hash=sha256:86480f14a188c37cb1416cdabacfb4e42f7a5eab20a737dac9c4b1c227f3b822 \ + --hash=sha256:867664d9ca9bdfce840ac96d46cd8838c9ae891e859eb98ce82fcdf0e103a947 \ + --hash=sha256:8df16c9a2377bdc01e01e6de5a6e4bbc66ddf007a6b045688e285d7d9d61d1c9 \ + --hash=sha256:8f00a9ba98f6364cadda366ae6469b7b3568c0cced27e16a47ddf6b774169270 \ + --hash=sha256:926b26db87ec8822cf1870efc3d04d06062730ec3279bbbd33ba47a6c0a5c673 \ + --hash=sha256:a6a46889325fd60b8a6b62ffc61588ec500a1883db32cddee9903edfba0b7584 \ + --hash=sha256:a98b61e504fb69f99117b188e60b71e3c94469295571492a6468c1acd63c37ba \ + --hash=sha256:ad38dc6a7eea6f6b8b642aaa0683253288b0460b70cab3216838747163fb774d \ + --hash=sha256:b10b77dc2e6b1daa2f11e241141ab8285c42b4ed13a8642495620416279cc5b2 \ + --hash=sha256:d5ea0e788dc7e2311989b78cae7accf75a580827b4d96bbaf06c7e5a03989bd5 \ + --hash=sha256:e05afefc4529e97c08e65ef92432e5f5225c0bb21ad89dee1e06a882f91d7f5e \ + --hash=sha256:e1431d84a9cfb23e6773e72078ce8e65cad6745816d4cbf9ae67da5ea419acda \ + --hash=sha256:ec6755cacf83f0c5588d28abb40a1ac1643f2ff2115481089264c7630236618a \ + --hash=sha256:edc2fb3463d5d9338ccf13eb512aab61937be50aa70734bcf873f2f493801d3b \ + --hash=sha256:edd8987d8415b5dad25e797043936d91535017237f72fa456601be1479386c92 \ + --hash=sha256:edda4fb109439b7f3f58ed6bede59694bc631c4b69c041112b1b7dc727fffb23 \ + --hash=sha256:f571eab7fec554d3b1db0d666bdc2ad85c81f4b8cb08906c4c59a8cad75e6e22 \ + --hash=sha256:f7c50542b4ddceb74ab4f8b3435327a0861f06257ca501d59067a6a482535a77 # via rosbags (setup.cfg) -markupsafe==2.1.1 \ - --hash=sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003 \ - --hash=sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88 \ - --hash=sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5 \ - --hash=sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7 \ - --hash=sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a \ - --hash=sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603 \ - --hash=sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1 \ - --hash=sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135 \ - --hash=sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247 \ - --hash=sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6 \ - --hash=sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601 \ - --hash=sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77 \ - --hash=sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02 \ - --hash=sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e \ - --hash=sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63 \ - --hash=sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f \ - --hash=sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980 \ - --hash=sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b \ - --hash=sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812 \ - --hash=sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff \ - --hash=sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96 \ - --hash=sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1 \ - --hash=sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925 \ - --hash=sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a \ - --hash=sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6 \ - --hash=sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e \ - --hash=sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f \ - --hash=sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4 \ - --hash=sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f \ - --hash=sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3 \ - --hash=sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c \ - --hash=sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a \ - --hash=sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417 \ - --hash=sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a \ - --hash=sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a \ - --hash=sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37 \ - --hash=sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452 \ - --hash=sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933 \ - --hash=sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a \ - --hash=sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7 +markupsafe==2.1.2 \ + --hash=sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed \ + --hash=sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc \ + --hash=sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2 \ + --hash=sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460 \ + --hash=sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7 \ + --hash=sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0 \ + --hash=sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1 \ + --hash=sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa \ + --hash=sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03 \ + --hash=sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323 \ + --hash=sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65 \ + --hash=sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013 \ + --hash=sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036 \ + --hash=sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f \ + --hash=sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4 \ + --hash=sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419 \ + --hash=sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2 \ + --hash=sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619 \ + --hash=sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a \ + --hash=sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a \ + --hash=sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd \ + --hash=sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7 \ + --hash=sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666 \ + --hash=sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65 \ + --hash=sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859 \ + --hash=sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625 \ + --hash=sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff \ + --hash=sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156 \ + --hash=sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd \ + --hash=sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba \ + --hash=sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f \ + --hash=sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1 \ + --hash=sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094 \ + --hash=sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a \ + --hash=sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513 \ + --hash=sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed \ + --hash=sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d \ + --hash=sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3 \ + --hash=sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147 \ + --hash=sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c \ + --hash=sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603 \ + --hash=sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601 \ + --hash=sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a \ + --hash=sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1 \ + --hash=sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d \ + --hash=sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3 \ + --hash=sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54 \ + --hash=sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2 \ + --hash=sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6 \ + --hash=sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58 # via jinja2 -mccabe==0.6.1 \ - --hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \ - --hash=sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f +mccabe==0.7.0 \ + --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ + --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e # via # flake8 # pylint -mypy==0.961 \ - --hash=sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5 \ - --hash=sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66 \ - --hash=sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e \ - --hash=sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56 \ - --hash=sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e \ - --hash=sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d \ - --hash=sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813 \ - --hash=sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932 \ - --hash=sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569 \ - --hash=sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b \ - --hash=sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0 \ - --hash=sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648 \ - --hash=sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6 \ - --hash=sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950 \ - --hash=sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15 \ - --hash=sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723 \ - --hash=sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a \ - --hash=sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3 \ - --hash=sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6 \ - --hash=sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24 \ - --hash=sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b \ - --hash=sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d \ - --hash=sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492 - # via - # pytest-mypy - # rosbags (setup.cfg) -mypy-extensions==0.4.3 \ - --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ - --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 - # via mypy -numpy==1.23.1 \ - --hash=sha256:1408c3527a74a0209c781ac82bde2182b0f0bf54dea6e6a363fe0cc4488a7ce7 \ - --hash=sha256:173f28921b15d341afadf6c3898a34f20a0569e4ad5435297ba262ee8941e77b \ - --hash=sha256:1865fdf51446839ca3fffaab172461f2b781163f6f395f1aed256b1ddc253622 \ - --hash=sha256:3119daed207e9410eaf57dcf9591fdc68045f60483d94956bee0bfdcba790953 \ - --hash=sha256:35590b9c33c0f1c9732b3231bb6a72d1e4f77872390c47d50a615686ae7ed3fd \ - --hash=sha256:37e5ebebb0eb54c5b4a9b04e6f3018e16b8ef257d26c8945925ba8105008e645 \ - --hash=sha256:37ece2bd095e9781a7156852e43d18044fd0d742934833335599c583618181b9 \ - --hash=sha256:3ab67966c8d45d55a2bdf40701536af6443763907086c0a6d1232688e27e5447 \ - --hash=sha256:47f10ab202fe4d8495ff484b5561c65dd59177949ca07975663f4494f7269e3e \ - --hash=sha256:55df0f7483b822855af67e38fb3a526e787adf189383b4934305565d71c4b148 \ - --hash=sha256:5d732d17b8a9061540a10fda5bfeabca5785700ab5469a5e9b93aca5e2d3a5fb \ - --hash=sha256:68b69f52e6545af010b76516f5daaef6173e73353e3295c5cb9f96c35d755641 \ - --hash=sha256:7e8229f3687cdadba2c4faef39204feb51ef7c1a9b669247d49a24f3e2e1617c \ - --hash=sha256:8002574a6b46ac3b5739a003b5233376aeac5163e5dcd43dd7ad062f3e186129 \ - --hash=sha256:876f60de09734fbcb4e27a97c9a286b51284df1326b1ac5f1bf0ad3678236b22 \ - --hash=sha256:9ce242162015b7e88092dccd0e854548c0926b75c7924a3495e02c6067aba1f5 \ - --hash=sha256:a35c4e64dfca659fe4d0f1421fc0f05b8ed1ca8c46fb73d9e5a7f175f85696bb \ - --hash=sha256:aeba539285dcf0a1ba755945865ec61240ede5432df41d6e29fab305f4384db2 \ - --hash=sha256:b15c3f1ed08df4980e02cc79ee058b788a3d0bef2fb3c9ca90bb8cbd5b8a3a04 \ - --hash=sha256:c2f91f88230042a130ceb1b496932aa717dcbd665350beb821534c5c7e15881c \ - --hash=sha256:d748ef349bfef2e1194b59da37ed5a29c19ea8d7e6342019921ba2ba4fd8b624 \ - --hash=sha256:e0d7447679ae9a7124385ccf0ea990bb85bb869cef217e2ea6c844b6a6855073 +mypy==1.0.1 \ + --hash=sha256:0af4f0e20706aadf4e6f8f8dc5ab739089146b83fd53cb4a7e0e850ef3de0bb6 \ + --hash=sha256:15b5a824b58c7c822c51bc66308e759243c32631896743f030daf449fe3677f3 \ + --hash=sha256:17455cda53eeee0a4adb6371a21dd3dbf465897de82843751cf822605d152c8c \ + --hash=sha256:2013226d17f20468f34feddd6aae4635a55f79626549099354ce641bc7d40262 \ + --hash=sha256:24189f23dc66f83b839bd1cce2dfc356020dfc9a8bae03978477b15be61b062e \ + --hash=sha256:27a0f74a298769d9fdc8498fcb4f2beb86f0564bcdb1a37b58cbbe78e55cf8c0 \ + --hash=sha256:28cea5a6392bb43d266782983b5a4216c25544cd7d80be681a155ddcdafd152d \ + --hash=sha256:448de661536d270ce04f2d7dddaa49b2fdba6e3bd8a83212164d4174ff43aa65 \ + --hash=sha256:48525aec92b47baed9b3380371ab8ab6e63a5aab317347dfe9e55e02aaad22e8 \ + --hash=sha256:5bc8d6bd3b274dd3846597855d96d38d947aedba18776aa998a8d46fabdaed76 \ + --hash=sha256:5deb252fd42a77add936b463033a59b8e48eb2eaec2976d76b6878d031933fe4 \ + --hash=sha256:5f546ac34093c6ce33f6278f7c88f0f147a4849386d3bf3ae193702f4fe31407 \ + --hash=sha256:5fdd63e4f50e3538617887e9aee91855368d9fc1dea30da743837b0df7373bc4 \ + --hash=sha256:65b122a993d9c81ea0bfde7689b3365318a88bde952e4dfa1b3a8b4ac05d168b \ + --hash=sha256:71a808334d3f41ef011faa5a5cd8153606df5fc0b56de5b2e89566c8093a0c9a \ + --hash=sha256:920169f0184215eef19294fa86ea49ffd4635dedfdea2b57e45cb4ee85d5ccaf \ + --hash=sha256:93a85495fb13dc484251b4c1fd7a5ac370cd0d812bbfc3b39c1bafefe95275d5 \ + --hash=sha256:a2948c40a7dd46c1c33765718936669dc1f628f134013b02ff5ac6c7ef6942bf \ + --hash=sha256:c6c2ccb7af7154673c591189c3687b013122c5a891bb5651eca3db8e6c6c55bd \ + --hash=sha256:c96b8a0c019fe29040d520d9257d8c8f122a7343a8307bf8d6d4a43f5c5bfcc8 \ + --hash=sha256:d42a98e76070a365a1d1c220fcac8aa4ada12ae0db679cb4d910fabefc88b994 \ + --hash=sha256:dbeb24514c4acbc78d205f85dd0e800f34062efcc1f4a4857c57e4b4b8712bff \ + --hash=sha256:e60d0b09f62ae97a94605c3f73fd952395286cf3e3b9e7b97f60b01ddfbbda88 \ + --hash=sha256:e64f48c6176e243ad015e995de05af7f22bbe370dbb5b32bd6988438ec873919 \ + --hash=sha256:e831662208055b006eef68392a768ff83596035ffd6d846786578ba1714ba8f6 \ + --hash=sha256:eda5c8b9949ed411ff752b9a01adda31afe7eae1e53e946dbdf9db23865e66c4 # via rosbags (setup.cfg) -packaging==21.3 \ - --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \ - --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522 +mypy-extensions==1.0.0 \ + --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ + --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 + # via mypy +numpy==1.24.2 \ + --hash=sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22 \ + --hash=sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f \ + --hash=sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9 \ + --hash=sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96 \ + --hash=sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0 \ + --hash=sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a \ + --hash=sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281 \ + --hash=sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04 \ + --hash=sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468 \ + --hash=sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253 \ + --hash=sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756 \ + --hash=sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a \ + --hash=sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb \ + --hash=sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d \ + --hash=sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0 \ + --hash=sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910 \ + --hash=sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978 \ + --hash=sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5 \ + --hash=sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f \ + --hash=sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a \ + --hash=sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5 \ + --hash=sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2 \ + --hash=sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d \ + --hash=sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95 \ + --hash=sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5 \ + --hash=sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d \ + --hash=sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780 \ + --hash=sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa + # via rosbags (setup.cfg) +packaging==23.0 \ + --hash=sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2 \ + --hash=sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97 # via # pytest # sphinx -pep8-naming==0.13.1 \ - --hash=sha256:3af77cdaa9c7965f7c85a56cd579354553c9bbd3fdf3078a776f12db54dd6944 \ - --hash=sha256:f7867c1a464fe769be4f972ef7b79d6df1d9aff1b1f04ecf738d471963d3ab9c +pep8-naming==0.13.3 \ + --hash=sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971 \ + --hash=sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80 # via rosbags (setup.cfg) -platformdirs==2.5.2 \ - --hash=sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788 \ - --hash=sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19 +platformdirs==3.0.0 \ + --hash=sha256:8a1228abb1ef82d788f74139988b137e78692984ec7b08eaa6c65f1723af28f9 \ + --hash=sha256:b1d5eb14f221506f50d6604a561f4c5786d9e80355219694a1b244bcd96f4567 # via pylint pluggy==1.0.0 \ --hash=sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159 \ --hash=sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3 # via pytest -py==1.11.0 \ - --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \ - --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378 - # via pytest -pycodestyle==2.8.0 \ - --hash=sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20 \ - --hash=sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f +pycodestyle==2.10.0 \ + --hash=sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053 \ + --hash=sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610 # via # flake8 # flake8-print -pydocstyle==6.1.1 \ - --hash=sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc \ - --hash=sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4 +pydocstyle==6.3.0 \ + --hash=sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019 \ + --hash=sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1 # via flake8-docstrings -pyflakes==2.4.0 \ - --hash=sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c \ - --hash=sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e +pyflakes==3.0.1 \ + --hash=sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf \ + --hash=sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd # via flake8 -pygments==2.12.0 \ - --hash=sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb \ - --hash=sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519 +pygments==2.14.0 \ + --hash=sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297 \ + --hash=sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717 # via sphinx -pylint==2.14.5 \ - --hash=sha256:487ce2192eee48211269a0e976421f334cf94de1806ca9d0a99449adcdf0285e \ - --hash=sha256:fabe30000de7d07636d2e82c9a518ad5ad7908590fe135ace169b44839c15f90 - # via pytest-pylint -pyparsing==3.0.9 \ - --hash=sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb \ - --hash=sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc - # via packaging -pytest==7.1.2 \ - --hash=sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c \ - --hash=sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45 +pylint==2.16.2 \ + --hash=sha256:13b2c805a404a9bf57d002cd5f054ca4d40b0b87542bdaba5e05321ae8262c84 \ + --hash=sha256:ff22dde9c2128cd257c145cfd51adeff0be7df4d80d669055f24a962b351bbe4 + # via rosbags (setup.cfg) +pytest==7.2.1 \ + --hash=sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5 \ + --hash=sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42 # via # pytest-cov - # pytest-flake8 - # pytest-mypy - # pytest-pylint - # pytest-yapf3 # rosbags (setup.cfg) -pytest-cov==3.0.0 \ - --hash=sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6 \ - --hash=sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470 +pytest-cov==4.0.0 \ + --hash=sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b \ + --hash=sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470 # via rosbags (setup.cfg) -pytest-flake8==1.1.1 \ - --hash=sha256:ba4f243de3cb4c2486ed9e70752c80dd4b636f7ccb27d4eba763c35ed0cd316e \ - --hash=sha256:e0661a786f8cbf976c185f706fdaf5d6df0b1667c3bcff8e823ba263618627e7 - # via rosbags (setup.cfg) -pytest-mypy==0.9.1 \ - --hash=sha256:9ffa3bf405c12c5c6be9e92e22bebb6ab2c91b9c32f45b0f0c93af473269ab5c \ - --hash=sha256:a2505fcf61f1c0c51f950d4623ea8ca2daf6fb2101a5603554bad2e130202083 - # via rosbags (setup.cfg) -pytest-pylint==0.18.0 \ - --hash=sha256:790c7a8019fab08e59bd3812db1657a01995a975af8b1c6ce95b9aa39d61da27 \ - --hash=sha256:b63aaf8b80ff33c8ceaa7f68323ed04102c7790093ccf6bdb261a4c2dc6fd564 - # via rosbags (setup.cfg) -pytest-yapf3==0.6.1 \ - --hash=sha256:63093e56f5c7baba4d221050c30e3d4e7132730741f95ec5de7a8c5fa3eb7821 \ - --hash=sha256:92b6b36d3b9435eedfb0072a8fb765496de1198c22d5cd3eb072359d35faba55 - # via rosbags (setup.cfg) -pytz==2022.1 \ - --hash=sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7 \ - --hash=sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c +pytz==2022.7.1 \ + --hash=sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0 \ + --hash=sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a # via babel -requests==2.28.1 \ - --hash=sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983 \ - --hash=sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349 +requests==2.28.2 \ + --hash=sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa \ + --hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf # via sphinx ruamel-yaml==0.17.21 \ --hash=sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7 \ --hash=sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af # via rosbags (setup.cfg) -ruamel-yaml-clib==0.2.6 \ - --hash=sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd \ - --hash=sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee \ - --hash=sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0 \ - --hash=sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7 \ - --hash=sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277 \ - --hash=sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104 \ - --hash=sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd \ - --hash=sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0 \ - --hash=sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78 \ - --hash=sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de \ - --hash=sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99 \ - --hash=sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527 \ - --hash=sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84 \ - --hash=sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7 \ - --hash=sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468 \ - --hash=sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b \ - --hash=sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94 \ - --hash=sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233 \ - --hash=sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb \ - --hash=sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5 \ - --hash=sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe \ - --hash=sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751 \ - --hash=sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502 \ - --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ - --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c +ruamel-yaml-clib==0.2.7 \ + --hash=sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e \ + --hash=sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3 \ + --hash=sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5 \ + --hash=sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497 \ + --hash=sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f \ + --hash=sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac \ + --hash=sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697 \ + --hash=sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763 \ + --hash=sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282 \ + --hash=sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94 \ + --hash=sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1 \ + --hash=sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072 \ + --hash=sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9 \ + --hash=sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5 \ + --hash=sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231 \ + --hash=sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93 \ + --hash=sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b \ + --hash=sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb \ + --hash=sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f \ + --hash=sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307 \ + --hash=sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8 \ + --hash=sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b \ + --hash=sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b \ + --hash=sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640 \ + --hash=sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7 \ + --hash=sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a \ + --hash=sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71 \ + --hash=sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8 \ + --hash=sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122 \ + --hash=sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7 \ + --hash=sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80 \ + --hash=sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e \ + --hash=sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab \ + --hash=sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0 \ + --hash=sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646 \ + --hash=sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38 # via ruamel-yaml snowballstemmer==2.2.0 \ --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \ @@ -503,33 +612,37 @@ snowballstemmer==2.2.0 \ # via # pydocstyle # sphinx -sphinx==5.1.1 \ - --hash=sha256:309a8da80cb6da9f4713438e5b55861877d5d7976b69d87e336733637ea12693 \ - --hash=sha256:ba3224a4e206e1fbdecf98a4fae4992ef9b24b85ebf7b584bb340156eaf08d89 +sphinx==6.1.3 \ + --hash=sha256:0dac3b698538ffef41716cf97ba26c1c7788dba73ce6f150c1ff5b4720786dd2 \ + --hash=sha256:807d1cb3d6be87eb78a381c3e70ebd8d346b9a25f3753e9947e866b2786865fc # via # rosbags (setup.cfg) # sphinx-autodoc-typehints # sphinx-rtd-theme -sphinx-autodoc-typehints==1.18.3 \ - --hash=sha256:20294de2a818bda04953c5cb302ec5af46138c81980ad9efa6d8fc1fc4242518 \ - --hash=sha256:c04d8f8d70e988960e25b206af39a90df84e7e2c085bb24e123bc3684021b313 +sphinx-autodoc-typehints==1.22 \ + --hash=sha256:71fca2d5eee9b034204e4c686ab20b4d8f5eb9409396216bcae6c87c38e18ea6 \ + --hash=sha256:ef4a8b9d52de66065aa7d3adfabf5a436feb8a2eff07c2ddc31625d8807f2b69 # via rosbags (setup.cfg) -sphinx-rtd-theme==1.0.0 \ - --hash=sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8 \ - --hash=sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c +sphinx-rtd-theme==1.2.0 \ + --hash=sha256:a0d8bd1a2ed52e0b338cbe19c4b2eef3c5e7a048769753dac6a9f059c7b641b8 \ + --hash=sha256:f823f7e71890abe0ac6aaa6013361ea2696fc8d3e1fa798f463e82bdb77eeff2 # via rosbags (setup.cfg) -sphinxcontrib-applehelp==1.0.2 \ - --hash=sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a \ - --hash=sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58 +sphinxcontrib-applehelp==1.0.4 \ + --hash=sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228 \ + --hash=sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e # via sphinx sphinxcontrib-devhelp==1.0.2 \ --hash=sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e \ --hash=sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4 # via sphinx -sphinxcontrib-htmlhelp==2.0.0 \ - --hash=sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07 \ - --hash=sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2 +sphinxcontrib-htmlhelp==2.0.1 \ + --hash=sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff \ + --hash=sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903 # via sphinx +sphinxcontrib-jquery==2.0.0 \ + --hash=sha256:8fb65f6dba84bf7bcd1aea1f02ab3955ac34611d838bcc95d4983b805b234daa \ + --hash=sha256:ed47fa425c338ffebe3c37e1cdb56e30eb806116b85f01055b158c7057fdb995 + # via sphinx-rtd-theme sphinxcontrib-jsmath==1.0.1 \ --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \ --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8 @@ -545,7 +658,7 @@ sphinxcontrib-serializinghtml==1.1.5 \ toml==0.10.2 \ --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f - # via pytest-pylint + # via rosbags (setup.cfg) tomli==2.0.1 \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f @@ -555,142 +668,158 @@ tomli==2.0.1 \ # mypy # pylint # pytest -tomlkit==0.11.1 \ - --hash=sha256:1c5bebdf19d5051e2e1de6cf70adfc5948d47221f097fcff7a3ffc91e953eaf5 \ - --hash=sha256:61901f81ff4017951119cd0d1ed9b7af31c821d6845c8c477587bbdcd5e5854e +tomlkit==0.11.6 \ + --hash=sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b \ + --hash=sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73 # via pylint -typing-extensions==4.3.0 \ - --hash=sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02 \ - --hash=sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6 +typing-extensions==4.5.0 \ + --hash=sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb \ + --hash=sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4 # via # astroid # mypy # pylint -urllib3==1.26.11 \ - --hash=sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc \ - --hash=sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a +urllib3==1.26.14 \ + --hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \ + --hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1 # via requests -wrapt==1.14.1 \ - --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \ - --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \ - --hash=sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4 \ - --hash=sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2 \ - --hash=sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656 \ - --hash=sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3 \ - --hash=sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff \ - --hash=sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310 \ - --hash=sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a \ - --hash=sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57 \ - --hash=sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069 \ - --hash=sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383 \ - --hash=sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe \ - --hash=sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87 \ - --hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d \ - --hash=sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b \ - --hash=sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907 \ - --hash=sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f \ - --hash=sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0 \ - --hash=sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28 \ - --hash=sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1 \ - --hash=sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853 \ - --hash=sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc \ - --hash=sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3 \ - --hash=sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3 \ - --hash=sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164 \ - --hash=sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1 \ - --hash=sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c \ - --hash=sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1 \ - --hash=sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7 \ - --hash=sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1 \ - --hash=sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320 \ - --hash=sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed \ - --hash=sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1 \ - --hash=sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248 \ - --hash=sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c \ - --hash=sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456 \ - --hash=sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77 \ - --hash=sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef \ - --hash=sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1 \ - --hash=sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7 \ - --hash=sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86 \ - --hash=sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4 \ - --hash=sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d \ - --hash=sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d \ - --hash=sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8 \ - --hash=sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5 \ - --hash=sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471 \ - --hash=sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00 \ - --hash=sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68 \ - --hash=sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3 \ - --hash=sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d \ - --hash=sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735 \ - --hash=sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d \ - --hash=sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569 \ - --hash=sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7 \ - --hash=sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59 \ - --hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \ - --hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \ - --hash=sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b \ - --hash=sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f \ - --hash=sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462 \ - --hash=sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015 \ - --hash=sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af +wrapt==1.15.0 \ + --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \ + --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \ + --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \ + --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \ + --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \ + --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \ + --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \ + --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \ + --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \ + --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \ + --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \ + --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \ + --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \ + --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \ + --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \ + --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \ + --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \ + --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \ + --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \ + --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \ + --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \ + --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \ + --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \ + --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \ + --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \ + --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \ + --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \ + --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \ + --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \ + --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \ + --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \ + --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \ + --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \ + --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \ + --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \ + --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \ + --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \ + --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \ + --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \ + --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \ + --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \ + --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \ + --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \ + --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \ + --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \ + --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \ + --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \ + --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \ + --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \ + --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \ + --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \ + --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \ + --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \ + --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \ + --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \ + --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \ + --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \ + --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \ + --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \ + --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \ + --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \ + --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \ + --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \ + --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \ + --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \ + --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \ + --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \ + --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \ + --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \ + --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \ + --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \ + --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \ + --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \ + --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \ + --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639 # via astroid yapf==0.32.0 \ --hash=sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32 \ --hash=sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b - # via - # pytest-yapf3 - # rosbags (setup.cfg) -zipp==3.8.1 \ - --hash=sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2 \ - --hash=sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009 + # via rosbags (setup.cfg) +zipp==3.15.0 \ + --hash=sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b \ + --hash=sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556 # via importlib-metadata -zstandard==0.18.0 \ - --hash=sha256:083dc08abf03807af9beeb2b6a91c23ad78add2499f828176a3c7b742c44df02 \ - --hash=sha256:0ac0357a0d985b4ff31a854744040d7b5754385d1f98f7145c30e02c6865cb6f \ - --hash=sha256:19cac7108ff2c342317fad6dc97604b47a41f403c8f19d0bfc396dfadc3638b8 \ - --hash=sha256:1af1268a7dc870eb27515fb8db1f3e6c5a555d2b7bcc476fc3bab8886c7265ab \ - --hash=sha256:1be31e9e3f7607ee0cdd60915410a5968b205d3e7aa83b7fcf3dd76dbbdb39e0 \ - --hash=sha256:1dc2d3809e763055a1a6c1a73f2b677320cc9a5aa1a7c6cfb35aee59bddc42d9 \ - --hash=sha256:266aba27fa9cc5e9091d3d325ebab1fa260f64e83e42516d5e73947c70216a5b \ - --hash=sha256:28723a1d2e4df778573b76b321ebe9f3469ac98988104c2af116dd344802c3f8 \ - --hash=sha256:2dc466207016564805e56d28375f4f533b525ff50d6776946980dff5465566ac \ - --hash=sha256:39e98cf4773234bd9cebf9f9db730e451dfcfe435e220f8921242afda8321887 \ - --hash=sha256:3af8c2383d02feb6650e9255491ec7d0824f6e6dd2bbe3e521c469c985f31fb1 \ - --hash=sha256:46f679bc5dfd938db4fb058218d9dc4db1336ffaf1ea774ff152ecadabd40805 \ - --hash=sha256:490d11b705b8ae9dc845431bacc8dd1cef2408aede176620a5cd0cd411027936 \ - --hash=sha256:49685bf9a55d1ab34bd8423ea22db836ba43a181ac6b045ac4272093d5cb874e \ - --hash=sha256:4a2ee1d4f98447f3e5183ecfce5626f983504a4a0c005fbe92e60fa8e5d547ec \ - --hash=sha256:4cbb85f29a990c2fdbf7bc63246567061a362ddca886d7fae6f780267c0a9e67 \ - --hash=sha256:5228e596eb1554598c872a337bbe4e5afe41cd1f8b1b15f2e35b50d061e35244 \ - --hash=sha256:533db8a6fac6248b2cb2c935e7b92f994efbdeb72e1ffa0b354432e087bb5a3e \ - --hash=sha256:63694a376cde0aa8b1971d06ca28e8f8b5f492779cb6ee1cc46bbc3f019a42a5 \ - --hash=sha256:702a8324cd90c74d9c8780d02bf55e79da3193c870c9665ad3a11647e3ad1435 \ - --hash=sha256:7231543d38d2b7e02ef7cc78ef7ffd86419437e1114ff08709fe25a160e24bd6 \ - --hash=sha256:75479e7c2b3eebf402c59fbe57d21bc400cefa145ca356ee053b0a08908c5784 \ - --hash=sha256:76725d1ee83a8915100a310bbad5d9c1fc6397410259c94033b8318d548d9990 \ - --hash=sha256:8677ffc6a6096cccbd892e558471c901fd821aba12b7fbc63833c7346f549224 \ - --hash=sha256:8b2260c4e07dd0723eadb586de7718b61acca4083a490dda69c5719d79bc715c \ - --hash=sha256:999a4e1768f219826ba3fa2064fab1c86dd72fdd47a42536235478c3bb3ca3e2 \ - --hash=sha256:9df59cd1cf3c62075ee2a4da767089d19d874ac3ad42b04a71a167e91b384722 \ - --hash=sha256:a7fa67cba473623848b6e88acf8d799b1906178fd883fb3a1da24561c779593b \ - --hash=sha256:bd3220d7627fd4d26397211cb3b560ec7cc4a94b75cfce89e847e8ce7fabe32d \ - --hash=sha256:bfa6c8549fa18e6497a738b7033c49f94a8e2e30c5fbe2d14d0b5aa8bbc1695d \ - --hash=sha256:c86befac87445927488f5c8f205d11566f64c11519db223e9d282b945fa60dab \ - --hash=sha256:c990063664c08169c84474acecc9251ee035871589025cac47c060ff4ec4bc1a \ - --hash=sha256:cdb44d7284c8c5dd1b66dfb86dda7f4560fa94bfbbc1d2da749ba44831335e32 \ - --hash=sha256:ce6f59cba9854fd14da5bfe34217a1501143057313966637b7291d1b0267bd1e \ - --hash=sha256:d4a8fd45746a6c31e729f35196e80b8f1e9987c59f5ccb8859d7c6a6fbeb9c63 \ - --hash=sha256:d6c85ca5162049ede475b7ec98e87f9390501d44a3d6776ddd504e872464ec25 \ - --hash=sha256:d716a7694ce1fa60b20bc10f35c4a22be446ef7f514c8dbc8f858b61976de2fb \ - --hash=sha256:d85bfabad444812133a92fc6fbe463e1d07581dba72f041f07a360e63808b23c \ - --hash=sha256:d956e2f03c7200d7e61345e0880c292783ec26618d0d921dcad470cb195bbce2 \ - --hash=sha256:dbb3cb8a082d62b8a73af42291569d266b05605e017a3d8a06a0e5c30b5f10f0 \ - --hash=sha256:dc2a4de9f363b3247d472362a65041fe4c0f59e01a2846b15d13046be866a885 \ - --hash=sha256:e02043297c1832f2666cd2204f381bef43b10d56929e13c42c10c732c6e3b4ed \ - --hash=sha256:eea18c1e7442f2aa9aff1bb84550dbb6a1f711faf6e48e7319de8f2b2e923c2a \ - --hash=sha256:ef7e8a200e4c8ac9102ed3c90ed2aa379f6b880f63032200909c1be21951f556 +zstandard==0.20.0 \ + --hash=sha256:0488f2a238b4560828b3a595f3337daac4d3725c2a1637ffe2a0d187c091da59 \ + --hash=sha256:059316f07e39b7214cd9eed565d26ab239035d2c76835deeff381995f7a27ba8 \ + --hash=sha256:0aa4d178560d7ee32092ddfd415c2cdc6ab5ddce9554985c75f1a019a0ff4c55 \ + --hash=sha256:0b815dec62e2d5a1bf7a373388f2616f21a27047b9b999de328bca7462033708 \ + --hash=sha256:0d213353d58ad37fb5070314b156fb983b4d680ed5f3fce76ab013484cf3cf12 \ + --hash=sha256:0f32a8f3a697ef87e67c0d0c0673b245babee6682b2c95e46eb30208ffb720bd \ + --hash=sha256:29699746fae2760d3963a4ffb603968e77da55150ee0a3326c0569f4e35f319f \ + --hash=sha256:2adf65cfce73ce94ef4c482f6cc01f08ddf5e1ca0c1ec95f2b63840f9e4c226c \ + --hash=sha256:2eeb9e1ecd48ac1d352608bfe0dc1ed78a397698035a1796cf72f0c9d905d219 \ + --hash=sha256:302a31400de0280f17c4ce67a73444a7a069f228db64048e4ce555cd0c02fbc4 \ + --hash=sha256:39ae788dcdc404c07ef7aac9b11925185ea0831b985db0bbc43f95acdbd1c2ce \ + --hash=sha256:39cbaf8fe3fa3515d35fb790465db4dc1ff45e58e1e00cbaf8b714e85437f039 \ + --hash=sha256:40466adfa071f58bfa448d90f9623d6aff67c6d86de6fc60be47a26388f6c74d \ + --hash=sha256:489959e2d52f7f1fe8ea275fecde6911d454df465265bf3ec51b3e755e769a5e \ + --hash=sha256:4a3c36284c219a4d2694e52b2582fe5d5f0ecaf94a22cf0ea959b527dbd8a2a6 \ + --hash=sha256:4abf9a9e0841b844736d1ae8ead2b583d2cd212815eab15391b702bde17477a7 \ + --hash=sha256:4af5d1891eebef430038ea4981957d31b1eb70aca14b906660c3ac1c3e7a8612 \ + --hash=sha256:5499d65d4a1978dccf0a9c2c0d12415e16d4995ffad7a0bc4f72cc66691cf9f2 \ + --hash=sha256:5a3578b182c21b8af3c49619eb4cd0b9127fa60791e621b34217d65209722002 \ + --hash=sha256:613daadd72c71b1488742cafb2c3b381c39d0c9bb8c6cc157aa2d5ea45cc2efc \ + --hash=sha256:6179808ebd1ebc42b1e2f221a23c28a22d3bc8f79209ae4a3cc114693c380bff \ + --hash=sha256:7041efe3a93d0975d2ad16451720932e8a3d164be8521bfd0873b27ac917b77a \ + --hash=sha256:78fb35d07423f25efd0fc90d0d4710ae83cfc86443a32192b0c6cb8475ec79a5 \ + --hash=sha256:79c3058ccbe1fa37356a73c9d3c0475ec935ab528f5b76d56fc002a5a23407c7 \ + --hash=sha256:84c1dae0c0a21eea245b5691286fe6470dc797d5e86e0c26b57a3afd1e750b48 \ + --hash=sha256:862ad0a5c94670f2bd6f64fff671bd2045af5f4ed428a3f2f69fa5e52483f86a \ + --hash=sha256:9aca916724d0802d3e70dc68adeff893efece01dffe7252ee3ae0053f1f1990f \ + --hash=sha256:9aea3c7bab4276212e5ac63d28e6bd72a79ff058d57e06926dfe30a52451d943 \ + --hash=sha256:a56036c08645aa6041d435a50103428f0682effdc67f5038de47cea5e4221d6f \ + --hash=sha256:a5efe366bf0545a1a5a917787659b445ba16442ae4093f102204f42a9da1ecbc \ + --hash=sha256:afbcd2ed0c1145e24dd3df8440a429688a1614b83424bc871371b176bed429f9 \ + --hash=sha256:b07f391fd85e3d07514c05fb40c5573b398d0063ab2bada6eb09949ec6004772 \ + --hash=sha256:b0f556c74c6f0f481b61d917e48c341cdfbb80cc3391511345aed4ce6fb52fdc \ + --hash=sha256:b671b75ae88139b1dd022fa4aa66ba419abd66f98869af55a342cb9257a1831e \ + --hash=sha256:b6d718f1b7cd30adb02c2a46dde0f25a84a9de8865126e0fff7d0162332d6b92 \ + --hash=sha256:ba4bb4c5a0cac802ff485fa1e57f7763df5efa0ad4ee10c2693ecc5a018d2c1a \ + --hash=sha256:ba86f931bf925e9561ccd6cb978acb163e38c425990927feb38be10c894fa937 \ + --hash=sha256:c1929afea64da48ec59eca9055d7ec7e5955801489ac40ac2a19dde19e7edad9 \ + --hash=sha256:c28c7441638c472bfb794f424bd560a22c7afce764cd99196e8d70fbc4d14e85 \ + --hash=sha256:c4efa051799703dc37c072e22af1f0e4c77069a78fb37caf70e26414c738ca1d \ + --hash=sha256:cc98c8bcaa07150d3f5d7c4bd264eaa4fdd4a4dfb8fd3f9d62565ae5c4aba227 \ + --hash=sha256:cd0aa9a043c38901925ae1bba49e1e638f2d9c3cdf1b8000868993c642deb7f2 \ + --hash=sha256:cdd769da7add8498658d881ce0eeb4c35ea1baac62e24c5a030c50f859f29724 \ + --hash=sha256:d08459f7f7748398a6cc65eb7f88aa7ef5731097be2ddfba544be4b558acd900 \ + --hash=sha256:dc47cec184e66953f635254e5381df8a22012a2308168c069230b1a95079ccd0 \ + --hash=sha256:e3f6887d2bdfb5752d5544860bd6b778e53ebfaf4ab6c3f9d7fd388445429d41 \ + --hash=sha256:e6b4de1ba2f3028fafa0d82222d1e91b729334c8d65fbf04290c65c09d7457e1 \ + --hash=sha256:ee2a1510e06dfc7706ea9afad363efe222818a1eafa59abc32d9bbcd8465fba7 \ + --hash=sha256:f199d58f3fd7dfa0d447bc255ff22571f2e4e5e5748bfd1c41370454723cb053 \ + --hash=sha256:f1ba6bbd28ad926d130f0af8016f3a2930baa013c2128cfff46ca76432f50669 \ + --hash=sha256:f847701d77371d90783c0ce6cfdb7ebde4053882c2aaba7255c70ae3c3eb7af0 # via rosbags (setup.cfg) # WARNING: The following packages were not pinned, but pip requires them to be diff --git a/requirements.txt b/requirements.txt index 42a0f1ae..d121d95d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,130 +1,168 @@ # -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # pip-compile --generate-hashes setup.cfg # -lz4==4.0.2 \ - --hash=sha256:083b7172c2938412ae37c3a090250bfdd9e4a6e855442594f86c3608ed12729b \ - --hash=sha256:154e6e9f58a7bafc4d2a1395160305b78fc82fa708bfa58cf0ad977c443d1f8f \ - --hash=sha256:1bd56282f6993e013ccf7f6edf1530c2a13d1662741e2be072349c7f70bc0682 \ - --hash=sha256:1ed9a1875dc2a489f3b665d0211984689d0e76585e55650b044a64dbd2d22992 \ - --hash=sha256:345608de23b4d68fbdef373f1e53d6c5abd99a062d4ff922e3350f47775ab123 \ - --hash=sha256:35e6caced0229b90151d31d9cf1eaa541e597f8021bf5b70ff9e6374e3e43b23 \ - --hash=sha256:3881573c3db902db370e072eb64b40c7c8289b94b2a731e051858cc198f890e8 \ - --hash=sha256:3fa0f000d8ce39e643e9e5c49fc4d1985156ffb177e3123a0f22551f5864841b \ - --hash=sha256:439898dd4176a724243002003c3f733eb6ce48a5988175f54c8560e0b100b7a6 \ - --hash=sha256:4cfa82f26b4f1835c797bd70e5ce20d5f1ee897b9a0c53e62d607f9029f521ce \ - --hash=sha256:56ea660097fec87f0c6746146b316775037f8dd886a4c5915360e5b32b7112d0 \ - --hash=sha256:5fe9db7627674875e4279c2ed50b1e38fb91ec3093347f871ed996e58edbb488 \ - --hash=sha256:61dbcca64e8e1655e06b588356c4b2515bccc1d7e84065f858a685abd96f0cf2 \ - --hash=sha256:6f3b3670f52f0871885258bcbc746f483760434336f0bc5581f161cc5d4b0c9a \ - --hash=sha256:9d141719d3cbb7933809642a61b68b8f595ddf85657016521756ddcf826b85cd \ - --hash=sha256:a8e02c2477bd704f43113ac8dd966c361187383591388818d74e1b73e4674759 \ - --hash=sha256:d2b18a6d6d9071c03dbf9e30bbe22e4476f24f1a4d73b1e975605ad3ce725e6c \ - --hash=sha256:ea2c2182a5b0ad03f33ac09db0925a1738a1d65751a3e058110bd900c643d359 \ - --hash=sha256:ed86ab22bfe1f4cd4fc983704134a8fdf746c1121a398f8f14cbd014c1a5b0ae \ - --hash=sha256:ee73357412c5505f6ba0ea61ff71455e2e4c1e04d8e60f17f3cd937261d773fa \ - --hash=sha256:fba1730cd2327a9d013192a9878714cc82f4877d2ada556222d03ea6428a80ed +lz4==4.3.2 \ + --hash=sha256:0ca83a623c449295bafad745dcd399cea4c55b16b13ed8cfea30963b004016c9 \ + --hash=sha256:0f5614d8229b33d4a97cb527db2a1ac81308c6e796e7bdb5d1309127289f69d5 \ + --hash=sha256:1c4c100d99eed7c08d4e8852dd11e7d1ec47a3340f49e3a96f8dfbba17ffb300 \ + --hash=sha256:1f25eb322eeb24068bb7647cae2b0732b71e5c639e4e4026db57618dcd8279f0 \ + --hash=sha256:200d05777d61ba1ff8d29cb51c534a162ea0b4fe6d3c28be3571a0a48ff36080 \ + --hash=sha256:31d72731c4ac6ebdce57cd9a5cabe0aecba229c4f31ba3e2c64ae52eee3fdb1c \ + --hash=sha256:3a85b430138882f82f354135b98c320dafb96fc8fe4656573d95ab05de9eb092 \ + --hash=sha256:4931ab28a0d1c133104613e74eec1b8bb1f52403faabe4f47f93008785c0b929 \ + --hash=sha256:4caedeb19e3ede6c7a178968b800f910db6503cb4cb1e9cc9221157572139b49 \ + --hash=sha256:65d5c93f8badacfa0456b660285e394e65023ef8071142e0dcbd4762166e1be0 \ + --hash=sha256:6b50f096a6a25f3b2edca05aa626ce39979d63c3b160687c8c6d50ac3943d0ba \ + --hash=sha256:7211dc8f636ca625abc3d4fb9ab74e5444b92df4f8d58ec83c8868a2b0ff643d \ + --hash=sha256:7a9eec24ec7d8c99aab54de91b4a5a149559ed5b3097cf30249b665689b3d402 \ + --hash=sha256:7c2df117def1589fba1327dceee51c5c2176a2b5a7040b45e84185ce0c08b6a3 \ + --hash=sha256:7e2dc1bd88b60fa09b9b37f08553f45dc2b770c52a5996ea52b2b40f25445676 \ + --hash=sha256:83903fe6db92db0be101acedc677aa41a490b561567fe1b3fe68695b2110326c \ + --hash=sha256:83acfacab3a1a7ab9694333bcb7950fbeb0be21660d236fd09c8337a50817897 \ + --hash=sha256:86480f14a188c37cb1416cdabacfb4e42f7a5eab20a737dac9c4b1c227f3b822 \ + --hash=sha256:867664d9ca9bdfce840ac96d46cd8838c9ae891e859eb98ce82fcdf0e103a947 \ + --hash=sha256:8df16c9a2377bdc01e01e6de5a6e4bbc66ddf007a6b045688e285d7d9d61d1c9 \ + --hash=sha256:8f00a9ba98f6364cadda366ae6469b7b3568c0cced27e16a47ddf6b774169270 \ + --hash=sha256:926b26db87ec8822cf1870efc3d04d06062730ec3279bbbd33ba47a6c0a5c673 \ + --hash=sha256:a6a46889325fd60b8a6b62ffc61588ec500a1883db32cddee9903edfba0b7584 \ + --hash=sha256:a98b61e504fb69f99117b188e60b71e3c94469295571492a6468c1acd63c37ba \ + --hash=sha256:ad38dc6a7eea6f6b8b642aaa0683253288b0460b70cab3216838747163fb774d \ + --hash=sha256:b10b77dc2e6b1daa2f11e241141ab8285c42b4ed13a8642495620416279cc5b2 \ + --hash=sha256:d5ea0e788dc7e2311989b78cae7accf75a580827b4d96bbaf06c7e5a03989bd5 \ + --hash=sha256:e05afefc4529e97c08e65ef92432e5f5225c0bb21ad89dee1e06a882f91d7f5e \ + --hash=sha256:e1431d84a9cfb23e6773e72078ce8e65cad6745816d4cbf9ae67da5ea419acda \ + --hash=sha256:ec6755cacf83f0c5588d28abb40a1ac1643f2ff2115481089264c7630236618a \ + --hash=sha256:edc2fb3463d5d9338ccf13eb512aab61937be50aa70734bcf873f2f493801d3b \ + --hash=sha256:edd8987d8415b5dad25e797043936d91535017237f72fa456601be1479386c92 \ + --hash=sha256:edda4fb109439b7f3f58ed6bede59694bc631c4b69c041112b1b7dc727fffb23 \ + --hash=sha256:f571eab7fec554d3b1db0d666bdc2ad85c81f4b8cb08906c4c59a8cad75e6e22 \ + --hash=sha256:f7c50542b4ddceb74ab4f8b3435327a0861f06257ca501d59067a6a482535a77 # via rosbags (setup.cfg) -numpy==1.23.1 \ - --hash=sha256:1408c3527a74a0209c781ac82bde2182b0f0bf54dea6e6a363fe0cc4488a7ce7 \ - --hash=sha256:173f28921b15d341afadf6c3898a34f20a0569e4ad5435297ba262ee8941e77b \ - --hash=sha256:1865fdf51446839ca3fffaab172461f2b781163f6f395f1aed256b1ddc253622 \ - --hash=sha256:3119daed207e9410eaf57dcf9591fdc68045f60483d94956bee0bfdcba790953 \ - --hash=sha256:35590b9c33c0f1c9732b3231bb6a72d1e4f77872390c47d50a615686ae7ed3fd \ - --hash=sha256:37e5ebebb0eb54c5b4a9b04e6f3018e16b8ef257d26c8945925ba8105008e645 \ - --hash=sha256:37ece2bd095e9781a7156852e43d18044fd0d742934833335599c583618181b9 \ - --hash=sha256:3ab67966c8d45d55a2bdf40701536af6443763907086c0a6d1232688e27e5447 \ - --hash=sha256:47f10ab202fe4d8495ff484b5561c65dd59177949ca07975663f4494f7269e3e \ - --hash=sha256:55df0f7483b822855af67e38fb3a526e787adf189383b4934305565d71c4b148 \ - --hash=sha256:5d732d17b8a9061540a10fda5bfeabca5785700ab5469a5e9b93aca5e2d3a5fb \ - --hash=sha256:68b69f52e6545af010b76516f5daaef6173e73353e3295c5cb9f96c35d755641 \ - --hash=sha256:7e8229f3687cdadba2c4faef39204feb51ef7c1a9b669247d49a24f3e2e1617c \ - --hash=sha256:8002574a6b46ac3b5739a003b5233376aeac5163e5dcd43dd7ad062f3e186129 \ - --hash=sha256:876f60de09734fbcb4e27a97c9a286b51284df1326b1ac5f1bf0ad3678236b22 \ - --hash=sha256:9ce242162015b7e88092dccd0e854548c0926b75c7924a3495e02c6067aba1f5 \ - --hash=sha256:a35c4e64dfca659fe4d0f1421fc0f05b8ed1ca8c46fb73d9e5a7f175f85696bb \ - --hash=sha256:aeba539285dcf0a1ba755945865ec61240ede5432df41d6e29fab305f4384db2 \ - --hash=sha256:b15c3f1ed08df4980e02cc79ee058b788a3d0bef2fb3c9ca90bb8cbd5b8a3a04 \ - --hash=sha256:c2f91f88230042a130ceb1b496932aa717dcbd665350beb821534c5c7e15881c \ - --hash=sha256:d748ef349bfef2e1194b59da37ed5a29c19ea8d7e6342019921ba2ba4fd8b624 \ - --hash=sha256:e0d7447679ae9a7124385ccf0ea990bb85bb869cef217e2ea6c844b6a6855073 +numpy==1.24.2 \ + --hash=sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22 \ + --hash=sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f \ + --hash=sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9 \ + --hash=sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96 \ + --hash=sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0 \ + --hash=sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a \ + --hash=sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281 \ + --hash=sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04 \ + --hash=sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468 \ + --hash=sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253 \ + --hash=sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756 \ + --hash=sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a \ + --hash=sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb \ + --hash=sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d \ + --hash=sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0 \ + --hash=sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910 \ + --hash=sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978 \ + --hash=sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5 \ + --hash=sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f \ + --hash=sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a \ + --hash=sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5 \ + --hash=sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2 \ + --hash=sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d \ + --hash=sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95 \ + --hash=sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5 \ + --hash=sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d \ + --hash=sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780 \ + --hash=sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa # via rosbags (setup.cfg) ruamel-yaml==0.17.21 \ --hash=sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7 \ --hash=sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af # via rosbags (setup.cfg) -ruamel-yaml-clib==0.2.6 \ - --hash=sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd \ - --hash=sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee \ - --hash=sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0 \ - --hash=sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7 \ - --hash=sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277 \ - --hash=sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104 \ - --hash=sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd \ - --hash=sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0 \ - --hash=sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78 \ - --hash=sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de \ - --hash=sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99 \ - --hash=sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527 \ - --hash=sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84 \ - --hash=sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7 \ - --hash=sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468 \ - --hash=sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b \ - --hash=sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94 \ - --hash=sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233 \ - --hash=sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb \ - --hash=sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5 \ - --hash=sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe \ - --hash=sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751 \ - --hash=sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502 \ - --hash=sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed \ - --hash=sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c +ruamel-yaml-clib==0.2.7 \ + --hash=sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e \ + --hash=sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3 \ + --hash=sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5 \ + --hash=sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497 \ + --hash=sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f \ + --hash=sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac \ + --hash=sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697 \ + --hash=sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763 \ + --hash=sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282 \ + --hash=sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94 \ + --hash=sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1 \ + --hash=sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072 \ + --hash=sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9 \ + --hash=sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5 \ + --hash=sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231 \ + --hash=sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93 \ + --hash=sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b \ + --hash=sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb \ + --hash=sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f \ + --hash=sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307 \ + --hash=sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8 \ + --hash=sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b \ + --hash=sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b \ + --hash=sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640 \ + --hash=sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7 \ + --hash=sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a \ + --hash=sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71 \ + --hash=sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8 \ + --hash=sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122 \ + --hash=sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7 \ + --hash=sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80 \ + --hash=sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e \ + --hash=sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab \ + --hash=sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0 \ + --hash=sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646 \ + --hash=sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38 # via ruamel-yaml -zstandard==0.18.0 \ - --hash=sha256:083dc08abf03807af9beeb2b6a91c23ad78add2499f828176a3c7b742c44df02 \ - --hash=sha256:0ac0357a0d985b4ff31a854744040d7b5754385d1f98f7145c30e02c6865cb6f \ - --hash=sha256:19cac7108ff2c342317fad6dc97604b47a41f403c8f19d0bfc396dfadc3638b8 \ - --hash=sha256:1af1268a7dc870eb27515fb8db1f3e6c5a555d2b7bcc476fc3bab8886c7265ab \ - --hash=sha256:1be31e9e3f7607ee0cdd60915410a5968b205d3e7aa83b7fcf3dd76dbbdb39e0 \ - --hash=sha256:1dc2d3809e763055a1a6c1a73f2b677320cc9a5aa1a7c6cfb35aee59bddc42d9 \ - --hash=sha256:266aba27fa9cc5e9091d3d325ebab1fa260f64e83e42516d5e73947c70216a5b \ - --hash=sha256:28723a1d2e4df778573b76b321ebe9f3469ac98988104c2af116dd344802c3f8 \ - --hash=sha256:2dc466207016564805e56d28375f4f533b525ff50d6776946980dff5465566ac \ - --hash=sha256:39e98cf4773234bd9cebf9f9db730e451dfcfe435e220f8921242afda8321887 \ - --hash=sha256:3af8c2383d02feb6650e9255491ec7d0824f6e6dd2bbe3e521c469c985f31fb1 \ - --hash=sha256:46f679bc5dfd938db4fb058218d9dc4db1336ffaf1ea774ff152ecadabd40805 \ - --hash=sha256:490d11b705b8ae9dc845431bacc8dd1cef2408aede176620a5cd0cd411027936 \ - --hash=sha256:49685bf9a55d1ab34bd8423ea22db836ba43a181ac6b045ac4272093d5cb874e \ - --hash=sha256:4a2ee1d4f98447f3e5183ecfce5626f983504a4a0c005fbe92e60fa8e5d547ec \ - --hash=sha256:4cbb85f29a990c2fdbf7bc63246567061a362ddca886d7fae6f780267c0a9e67 \ - --hash=sha256:5228e596eb1554598c872a337bbe4e5afe41cd1f8b1b15f2e35b50d061e35244 \ - --hash=sha256:533db8a6fac6248b2cb2c935e7b92f994efbdeb72e1ffa0b354432e087bb5a3e \ - --hash=sha256:63694a376cde0aa8b1971d06ca28e8f8b5f492779cb6ee1cc46bbc3f019a42a5 \ - --hash=sha256:702a8324cd90c74d9c8780d02bf55e79da3193c870c9665ad3a11647e3ad1435 \ - --hash=sha256:7231543d38d2b7e02ef7cc78ef7ffd86419437e1114ff08709fe25a160e24bd6 \ - --hash=sha256:75479e7c2b3eebf402c59fbe57d21bc400cefa145ca356ee053b0a08908c5784 \ - --hash=sha256:76725d1ee83a8915100a310bbad5d9c1fc6397410259c94033b8318d548d9990 \ - --hash=sha256:8677ffc6a6096cccbd892e558471c901fd821aba12b7fbc63833c7346f549224 \ - --hash=sha256:8b2260c4e07dd0723eadb586de7718b61acca4083a490dda69c5719d79bc715c \ - --hash=sha256:999a4e1768f219826ba3fa2064fab1c86dd72fdd47a42536235478c3bb3ca3e2 \ - --hash=sha256:9df59cd1cf3c62075ee2a4da767089d19d874ac3ad42b04a71a167e91b384722 \ - --hash=sha256:a7fa67cba473623848b6e88acf8d799b1906178fd883fb3a1da24561c779593b \ - --hash=sha256:bd3220d7627fd4d26397211cb3b560ec7cc4a94b75cfce89e847e8ce7fabe32d \ - --hash=sha256:bfa6c8549fa18e6497a738b7033c49f94a8e2e30c5fbe2d14d0b5aa8bbc1695d \ - --hash=sha256:c86befac87445927488f5c8f205d11566f64c11519db223e9d282b945fa60dab \ - --hash=sha256:c990063664c08169c84474acecc9251ee035871589025cac47c060ff4ec4bc1a \ - --hash=sha256:cdb44d7284c8c5dd1b66dfb86dda7f4560fa94bfbbc1d2da749ba44831335e32 \ - --hash=sha256:ce6f59cba9854fd14da5bfe34217a1501143057313966637b7291d1b0267bd1e \ - --hash=sha256:d4a8fd45746a6c31e729f35196e80b8f1e9987c59f5ccb8859d7c6a6fbeb9c63 \ - --hash=sha256:d6c85ca5162049ede475b7ec98e87f9390501d44a3d6776ddd504e872464ec25 \ - --hash=sha256:d716a7694ce1fa60b20bc10f35c4a22be446ef7f514c8dbc8f858b61976de2fb \ - --hash=sha256:d85bfabad444812133a92fc6fbe463e1d07581dba72f041f07a360e63808b23c \ - --hash=sha256:d956e2f03c7200d7e61345e0880c292783ec26618d0d921dcad470cb195bbce2 \ - --hash=sha256:dbb3cb8a082d62b8a73af42291569d266b05605e017a3d8a06a0e5c30b5f10f0 \ - --hash=sha256:dc2a4de9f363b3247d472362a65041fe4c0f59e01a2846b15d13046be866a885 \ - --hash=sha256:e02043297c1832f2666cd2204f381bef43b10d56929e13c42c10c732c6e3b4ed \ - --hash=sha256:eea18c1e7442f2aa9aff1bb84550dbb6a1f711faf6e48e7319de8f2b2e923c2a \ - --hash=sha256:ef7e8a200e4c8ac9102ed3c90ed2aa379f6b880f63032200909c1be21951f556 +zstandard==0.20.0 \ + --hash=sha256:0488f2a238b4560828b3a595f3337daac4d3725c2a1637ffe2a0d187c091da59 \ + --hash=sha256:059316f07e39b7214cd9eed565d26ab239035d2c76835deeff381995f7a27ba8 \ + --hash=sha256:0aa4d178560d7ee32092ddfd415c2cdc6ab5ddce9554985c75f1a019a0ff4c55 \ + --hash=sha256:0b815dec62e2d5a1bf7a373388f2616f21a27047b9b999de328bca7462033708 \ + --hash=sha256:0d213353d58ad37fb5070314b156fb983b4d680ed5f3fce76ab013484cf3cf12 \ + --hash=sha256:0f32a8f3a697ef87e67c0d0c0673b245babee6682b2c95e46eb30208ffb720bd \ + --hash=sha256:29699746fae2760d3963a4ffb603968e77da55150ee0a3326c0569f4e35f319f \ + --hash=sha256:2adf65cfce73ce94ef4c482f6cc01f08ddf5e1ca0c1ec95f2b63840f9e4c226c \ + --hash=sha256:2eeb9e1ecd48ac1d352608bfe0dc1ed78a397698035a1796cf72f0c9d905d219 \ + --hash=sha256:302a31400de0280f17c4ce67a73444a7a069f228db64048e4ce555cd0c02fbc4 \ + --hash=sha256:39ae788dcdc404c07ef7aac9b11925185ea0831b985db0bbc43f95acdbd1c2ce \ + --hash=sha256:39cbaf8fe3fa3515d35fb790465db4dc1ff45e58e1e00cbaf8b714e85437f039 \ + --hash=sha256:40466adfa071f58bfa448d90f9623d6aff67c6d86de6fc60be47a26388f6c74d \ + --hash=sha256:489959e2d52f7f1fe8ea275fecde6911d454df465265bf3ec51b3e755e769a5e \ + --hash=sha256:4a3c36284c219a4d2694e52b2582fe5d5f0ecaf94a22cf0ea959b527dbd8a2a6 \ + --hash=sha256:4abf9a9e0841b844736d1ae8ead2b583d2cd212815eab15391b702bde17477a7 \ + --hash=sha256:4af5d1891eebef430038ea4981957d31b1eb70aca14b906660c3ac1c3e7a8612 \ + --hash=sha256:5499d65d4a1978dccf0a9c2c0d12415e16d4995ffad7a0bc4f72cc66691cf9f2 \ + --hash=sha256:5a3578b182c21b8af3c49619eb4cd0b9127fa60791e621b34217d65209722002 \ + --hash=sha256:613daadd72c71b1488742cafb2c3b381c39d0c9bb8c6cc157aa2d5ea45cc2efc \ + --hash=sha256:6179808ebd1ebc42b1e2f221a23c28a22d3bc8f79209ae4a3cc114693c380bff \ + --hash=sha256:7041efe3a93d0975d2ad16451720932e8a3d164be8521bfd0873b27ac917b77a \ + --hash=sha256:78fb35d07423f25efd0fc90d0d4710ae83cfc86443a32192b0c6cb8475ec79a5 \ + --hash=sha256:79c3058ccbe1fa37356a73c9d3c0475ec935ab528f5b76d56fc002a5a23407c7 \ + --hash=sha256:84c1dae0c0a21eea245b5691286fe6470dc797d5e86e0c26b57a3afd1e750b48 \ + --hash=sha256:862ad0a5c94670f2bd6f64fff671bd2045af5f4ed428a3f2f69fa5e52483f86a \ + --hash=sha256:9aca916724d0802d3e70dc68adeff893efece01dffe7252ee3ae0053f1f1990f \ + --hash=sha256:9aea3c7bab4276212e5ac63d28e6bd72a79ff058d57e06926dfe30a52451d943 \ + --hash=sha256:a56036c08645aa6041d435a50103428f0682effdc67f5038de47cea5e4221d6f \ + --hash=sha256:a5efe366bf0545a1a5a917787659b445ba16442ae4093f102204f42a9da1ecbc \ + --hash=sha256:afbcd2ed0c1145e24dd3df8440a429688a1614b83424bc871371b176bed429f9 \ + --hash=sha256:b07f391fd85e3d07514c05fb40c5573b398d0063ab2bada6eb09949ec6004772 \ + --hash=sha256:b0f556c74c6f0f481b61d917e48c341cdfbb80cc3391511345aed4ce6fb52fdc \ + --hash=sha256:b671b75ae88139b1dd022fa4aa66ba419abd66f98869af55a342cb9257a1831e \ + --hash=sha256:b6d718f1b7cd30adb02c2a46dde0f25a84a9de8865126e0fff7d0162332d6b92 \ + --hash=sha256:ba4bb4c5a0cac802ff485fa1e57f7763df5efa0ad4ee10c2693ecc5a018d2c1a \ + --hash=sha256:ba86f931bf925e9561ccd6cb978acb163e38c425990927feb38be10c894fa937 \ + --hash=sha256:c1929afea64da48ec59eca9055d7ec7e5955801489ac40ac2a19dde19e7edad9 \ + --hash=sha256:c28c7441638c472bfb794f424bd560a22c7afce764cd99196e8d70fbc4d14e85 \ + --hash=sha256:c4efa051799703dc37c072e22af1f0e4c77069a78fb37caf70e26414c738ca1d \ + --hash=sha256:cc98c8bcaa07150d3f5d7c4bd264eaa4fdd4a4dfb8fd3f9d62565ae5c4aba227 \ + --hash=sha256:cd0aa9a043c38901925ae1bba49e1e638f2d9c3cdf1b8000868993c642deb7f2 \ + --hash=sha256:cdd769da7add8498658d881ce0eeb4c35ea1baac62e24c5a030c50f859f29724 \ + --hash=sha256:d08459f7f7748398a6cc65eb7f88aa7ef5731097be2ddfba544be4b558acd900 \ + --hash=sha256:dc47cec184e66953f635254e5381df8a22012a2308168c069230b1a95079ccd0 \ + --hash=sha256:e3f6887d2bdfb5752d5544860bd6b778e53ebfaf4ab6c3f9d7fd388445429d41 \ + --hash=sha256:e6b4de1ba2f3028fafa0d82222d1e91b729334c8d65fbf04290c65c09d7457e1 \ + --hash=sha256:ee2a1510e06dfc7706ea9afad363efe222818a1eafa59abc32d9bbcd8465fba7 \ + --hash=sha256:f199d58f3fd7dfa0d447bc255ff22571f2e4e5e5748bfd1c41370454723cb053 \ + --hash=sha256:f1ba6bbd28ad926d130f0af8016f3a2930baa013c2128cfff46ca76432f50669 \ + --hash=sha256:f847701d77371d90783c0ce6cfdb7ebde4053882c2aaba7255c70ae3c3eb7af0 # via rosbags (setup.cfg) From 56df6c40f60d33e8c6439b332c772d005d5f7e3f Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Wed, 1 Mar 2023 20:18:38 +0100 Subject: [PATCH 110/114] Update code for current linter --- src/rosbags/rosbag1/reader.py | 2 +- src/rosbags/rosbag1/writer.py | 2 +- src/rosbags/rosbag2/reader.py | 2 +- src/rosbags/serde/cdr.py | 6 +++--- src/rosbags/serde/ros1.py | 4 ++-- src/rosbags/typesys/register.py | 3 +-- src/rosbags/typesys/types.py | 3 +-- tests/cdr.py | 4 ++-- tests/test_highlevel.py | 2 +- tests/test_reader1.py | 37 +++++++++++++++++---------------- tests/test_writer.py | 10 ++++----- 11 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 60569c91..5950b87f 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -366,7 +366,7 @@ class Reader: def open(self) -> None: """Open rosbag and read metadata.""" try: - self.bio = self.path.open('rb') + self.bio = self.path.open('rb') # pylint: disable=consider-using-with except OSError as err: raise ReaderError(f'Could not open file {str(self.path)!r}: {err.strerror}.') from err diff --git a/src/rosbags/rosbag1/writer.py b/src/rosbags/rosbag1/writer.py index ca480dcb..8b4f687b 100644 --- a/src/rosbags/rosbag1/writer.py +++ b/src/rosbags/rosbag1/writer.py @@ -192,7 +192,7 @@ class Writer: def open(self) -> None: """Open rosbag1 for writing.""" try: - self.bio = self.path.open('xb') + self.bio = self.path.open('xb') # pylint: disable=consider-using-with except FileExistsError: raise WriterError(f'{self.path} exists already, not overwriting.') from None diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 27de18f8..02733a4e 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -138,7 +138,7 @@ class Reader: self.files: list[FileInformation] = self.metadata.get('files', [])[:] self.custom_data: dict[str, str] = self.metadata.get('custom_data', {}) - self.tmpdir: Optional[TemporaryDirectory] = None + self.tmpdir: Optional[TemporaryDirectory[str]] = None self.storage: Optional[StorageProtocol] = None except KeyError as exc: raise ReaderError(f'A metadata key is missing {exc!r}.') from None diff --git a/src/rosbags/serde/cdr.py b/src/rosbags/serde/cdr.py index 369b526e..2d3c983a 100644 --- a/src/rosbags/serde/cdr.py +++ b/src/rosbags/serde/cdr.py @@ -38,7 +38,7 @@ def generate_getsize_cdr(fields: list[Field]) -> tuple[CDRSerSize, int]: aligned = 8 iterators = tee([*fields, None]) - icurr = cast(Iterator[Field], iterators[0]) + icurr = cast('Iterator[Field]', iterators[0]) inext = iterators[1] next(inext) lines = [ @@ -178,7 +178,7 @@ def generate_serialize_cdr(fields: list[Field], endianess: str) -> CDRSer: # pylint: disable=too-many-branches,too-many-locals,too-many-statements aligned = 8 iterators = tee([*fields, None]) - icurr = cast(Iterator[Field], iterators[0]) + icurr = cast('Iterator[Field]', iterators[0]) inext = iterators[1] next(inext) lines = [ @@ -317,7 +317,7 @@ def generate_deserialize_cdr(fields: list[Field], endianess: str) -> CDRDeser: # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements aligned = 8 iterators = tee([*fields, None]) - icurr = cast(Iterator[Field], iterators[0]) + icurr = cast('Iterator[Field]', iterators[0]) inext = iterators[1] next(inext) lines = [ diff --git a/src/rosbags/serde/ros1.py b/src/rosbags/serde/ros1.py index 32a543db..f9cda30a 100644 --- a/src/rosbags/serde/ros1.py +++ b/src/rosbags/serde/ros1.py @@ -43,7 +43,7 @@ def generate_ros1_to_cdr( # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements aligned = 8 iterators = tee([*fields, None]) - icurr = cast(Iterator[Field], iterators[0]) + icurr = cast('Iterator[Field]', iterators[0]) inext = iterators[1] next(inext) funcname = 'ros1_to_cdr' if copy else 'getsize_ros1_to_cdr' @@ -199,7 +199,7 @@ def generate_cdr_to_ros1( # pylint: disable=too-many-branches,too-many-locals,too-many-nested-blocks,too-many-statements aligned = 8 iterators = tee([*fields, None]) - icurr = cast(Iterator[Field], iterators[0]) + icurr = cast('Iterator[Field]', iterators[0]) inext = iterators[1] next(inext) funcname = 'cdr_to_ros1' if copy else 'getsize_cdr_to_ros1' diff --git a/src/rosbags/typesys/register.py b/src/rosbags/typesys/register.py index 299c2bc6..3b562fba 100644 --- a/src/rosbags/typesys/register.py +++ b/src/rosbags/typesys/register.py @@ -46,7 +46,7 @@ def get_typehint(desc: tuple[int, Union[str, tuple[tuple[int, str], Optional[int sub = desc[1][0] if INTLIKE.match(sub[1]): - typ = 'bool8' if sub[1] == 'bool' else sub[1] + typ = 'bool_' if sub[1] == 'bool' else sub[1] return f'numpy.ndarray[Any, numpy.dtype[numpy.{typ}]]' assert isinstance(sub, tuple) return f'list[{get_typehint(sub)}]' @@ -71,7 +71,6 @@ def generate_python_code(typs: Typesdict) -> str: '', '# flake8: noqa N801', '# pylint: disable=invalid-name,too-many-instance-attributes,too-many-lines', - '# pylint: disable=unsubscriptable-object', '', 'from __future__ import annotations', '', diff --git a/src/rosbags/typesys/types.py b/src/rosbags/typesys/types.py index c4f7f175..f969a417 100644 --- a/src/rosbags/typesys/types.py +++ b/src/rosbags/typesys/types.py @@ -6,7 +6,6 @@ # flake8: noqa N801 # pylint: disable=invalid-name,too-many-instance-attributes,too-many-lines -# pylint: disable=unsubscriptable-object from __future__ import annotations @@ -608,7 +607,7 @@ class rcl_interfaces__msg__ParameterValue: double_value: float string_value: str byte_array_value: numpy.ndarray[Any, numpy.dtype[numpy.uint8]] - bool_array_value: numpy.ndarray[Any, numpy.dtype[numpy.bool8]] + bool_array_value: numpy.ndarray[Any, numpy.dtype[numpy.bool_]] integer_array_value: numpy.ndarray[Any, numpy.dtype[numpy.int64]] double_array_value: numpy.ndarray[Any, numpy.dtype[numpy.float64]] string_array_value: list[str] diff --git a/tests/cdr.py b/tests/cdr.py index ee3d7f64..b28c5722 100644 --- a/tests/cdr.py +++ b/tests/cdr.py @@ -274,13 +274,13 @@ def serialize_array( if desc.valtype == Valtype.BASE: if desc.args == 'string': for item in val: - pos = serialize_string(rawdata, bmap, pos, cast(str, item)) + pos = serialize_string(rawdata, bmap, pos, cast('str', item)) return pos size = SIZEMAP[desc.args] pos = (pos + size - 1) & -size size *= len(val) - val = cast(NDArray[numpy.int_], val) + val = cast('NDArray[numpy.int_]', val) if (bmap is BASETYPEMAP_LE) != (sys.byteorder == 'little'): val = val.byteswap() # no inplace on readonly array rawdata[pos:pos + size] = memoryview(val.tobytes()) diff --git a/tests/test_highlevel.py b/tests/test_highlevel.py index 67189f42..defe1b1b 100644 --- a/tests/test_highlevel.py +++ b/tests/test_highlevel.py @@ -87,7 +87,7 @@ def test_anyreader1(bags1: Sequence[Path]) -> None: # pylint: disable=redefined reader = AnyReader(bags1) with pytest.raises(AnyReaderError, match='seems to be empty'): reader.open() - assert all(not x.bio for x in reader.readers) + assert all(not x.bio for x in reader.readers) # type: ignore[union-attr] with AnyReader(bags1[:3]) as reader: assert reader.duration == 15 diff --git a/tests/test_reader1.py b/tests/test_reader1.py index 20cb1e0f..f1d95228 100644 --- a/tests/test_reader1.py +++ b/tests/test_reader1.py @@ -69,8 +69,7 @@ def create_message( }, f'MSGCONTENT{msg}'.encode() -def write_bag( # pylint: disable=too-many-locals,too-many-statements - +def write_bag( # pylint: disable=too-many-locals bag: Path, header: dict[str, bytes], chunks: Sequence[Any] = (), @@ -129,8 +128,8 @@ def write_bag( # pylint: disable=too-many-locals,too-many-statements { 'op': b'\x05', 'compression': b'none', - 'size': pack(' None: x42_2_0 = IndexData(42, 2, 0) x43_3_0 = IndexData(43, 3, 0) - # flake8: noqa - # pylint: disable=unneeded-not assert not x42_1_0 < x42_2_0 assert x42_1_0 <= x42_2_0 assert x42_1_0 == x42_2_0 - assert not x42_1_0 != x42_2_0 + assert not x42_1_0 != x42_2_0 # noqa assert x42_1_0 >= x42_2_0 assert not x42_1_0 > x42_2_0 assert x42_1_0 < x43_3_0 assert x42_1_0 <= x43_3_0 - assert not x42_1_0 == x43_3_0 + assert not x42_1_0 == x43_3_0 # noqa assert x42_1_0 != x43_3_0 assert not x42_1_0 >= x43_3_0 assert not x42_1_0 > x43_3_0 @@ -215,10 +212,12 @@ def test_reader(tmp_path: Path) -> None: # pylint: disable=too-many-statements # single message write_bag( - bag, create_default_header(), chunks=[[ + bag, + create_default_header(), + chunks=[[ create_connection(), create_message(time=42), - ]] + ]], ) with Reader(bag) as reader: assert reader.message_count == 1 @@ -239,8 +238,8 @@ def test_reader(tmp_path: Path) -> None: # pylint: disable=too-many-statements create_connection(), create_message(time=10, msg=10), create_message(time=5, msg=5), - ] - ] + ], + ], ) with Reader(bag) as reader: assert reader.message_count == 2 @@ -266,8 +265,8 @@ def test_reader(tmp_path: Path) -> None: # pylint: disable=too-many-statements create_message(time=10, msg=10), create_connection(cid=2, topic=2), create_message(cid=2, time=5, msg=5), - ] - ] + ], + ], ) with Reader(bag) as reader: assert len(reader.topics.keys()) == 2 @@ -398,11 +397,13 @@ def test_failure_cases(tmp_path: Path) -> None: # pylint: disable=too-many-stat # bad uint8 field write_bag( - bag, create_default_header(), chunks=[[ + bag, + create_default_header(), + chunks=[[ ({}, {}), create_connection(), create_message(), - ]] + ]], ) with Reader(bag) as reader, \ pytest.raises(ReaderError, match='field \'op\''): diff --git a/tests/test_writer.py b/tests/test_writer.py index a39e3f50..551474ff 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: def test_writer(tmp_path: Path) -> None: """Test Writer.""" - path = (tmp_path / 'rosbag2') + path = tmp_path / 'rosbag2' with Writer(path) as bag: connection = bag.add_connection('/test', 'std_msgs/msg/Int8') bag.write(connection, 42, b'\x00') @@ -26,7 +26,7 @@ def test_writer(tmp_path: Path) -> None: assert (path / 'rosbag2.db3').exists() size = (path / 'rosbag2.db3').stat().st_size - path = (tmp_path / 'compress_none') + path = tmp_path / 'compress_none' bag = Writer(path) bag.set_compression(bag.CompressionMode.NONE, bag.CompressionFormat.ZSTD) with bag: @@ -37,7 +37,7 @@ def test_writer(tmp_path: Path) -> None: assert (path / 'compress_none.db3').exists() assert size == (path / 'compress_none.db3').stat().st_size - path = (tmp_path / 'compress_file') + path = tmp_path / 'compress_file' bag = Writer(path) bag.set_compression(bag.CompressionMode.FILE, bag.CompressionFormat.ZSTD) with bag: @@ -48,7 +48,7 @@ def test_writer(tmp_path: Path) -> None: assert not (path / 'compress_file.db3').exists() assert (path / 'compress_file.db3.zstd').exists() - path = (tmp_path / 'compress_message') + path = tmp_path / 'compress_message' bag = Writer(path) bag.set_compression(bag.CompressionMode.MESSAGE, bag.CompressionFormat.ZSTD) with bag: @@ -59,7 +59,7 @@ def test_writer(tmp_path: Path) -> None: assert (path / 'compress_message.db3').exists() assert size > (path / 'compress_message.db3').stat().st_size - path = (tmp_path / 'with_custom_data') + path = tmp_path / 'with_custom_data' bag = Writer(path) bag.open() bag.set_custom_data('key1', 'value1') From 967328627c728326c087b312ec525008cd964f59 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 2 Mar 2023 09:37:52 +0100 Subject: [PATCH 111/114] Advertise AnyReader in documentation --- README.rst | 12 +++++++----- docs/index.rst | 1 + docs/topics/highlevel.rst | 22 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 docs/topics/highlevel.rst diff --git a/README.rst b/README.rst index ab378151..b3c19de8 100644 --- a/README.rst +++ b/README.rst @@ -15,6 +15,7 @@ Rosbags Rosbags is the **pure python** library for everything rosbag. It contains: +- **highlevel** easy-to-use interfaces, - **rosbag2** reader and writer, - **rosbag1** reader and writer, - **extensible** type system with serializers and deserializers, @@ -34,18 +35,19 @@ Rosbags is published on PyPI and does not have any special dependencies. Simply pip install rosbags -Read and deserialize rosbag2 messages: +Read and deserialize messages from rosbag1 or rosbag2 files: .. code-block:: python - from rosbags.rosbag2 import Reader - from rosbags.serde import deserialize_cdr + from pathlib import Path + + from rosbags.highlevel import AnyReader # create reader instance and open for reading - with Reader('/home/ros/rosbag_2020_03_24') as reader: + with AnyReader([Path('/home/ros/rosbag_2020_03_24')]) as reader: connections = [x for x in reader.connections if x.topic == '/imu_raw/Imu'] for connection, timestamp, rawdata in reader.messages(connections=connections): - msg = deserialize_cdr(rawdata, connection.msgtype) + msg = reader.deserialize(rawdata, connection.msgtype) print(msg.header.frame_id) diff --git a/docs/index.rst b/docs/index.rst index d609e6df..81f4668f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,6 +10,7 @@ :maxdepth: 1 :hidden: + topics/highlevel topics/typesys topics/serde topics/rosbag2 diff --git a/docs/topics/highlevel.rst b/docs/topics/highlevel.rst new file mode 100644 index 00000000..2aafd106 --- /dev/null +++ b/docs/topics/highlevel.rst @@ -0,0 +1,22 @@ +Highlevel APIs +============== +The :py:mod:`rosbags.highlevel` package provides classes that abstract the complexity of ROS types, serialization and message access into single easy-to-use interfaces. + +All in one reader +----------------- +Instances of the :py:class:`AnyReader ` class give unified access to ROS1 and ROS2 bag files. If a bag file includes message definitions the reader auto-registers all messages into a blank type store, otherwise it falls back to the default type store. It also exposes appropriate deserialization methods on the reader instance itself. + +.. code-block:: python + + from pathlib import Path + + from rosbags.highlevel import AnyReader + + # create reader instance and open for reading + with AnyReader([Path('/home/ros/rosbag_2020_03_24')]) as reader: + connections = [x for x in reader.connections if x.topic == '/imu_raw/Imu'] + for connection, timestamp, rawdata in reader.messages(connections=connections): + msg = reader.deserialize(rawdata, connection.msgtype) + print(msg.header.frame_id) + +AnyReader takes a list of ``pathlib.Path`` instances as arguments. It can take either one ROS2 bag file or one or more ROS1 bag files belonging to a split bag. The reader will replay ROS1 split bags in correct timestamp order. From 9830c38fc74b2ca8f9dc8e309ca3050637b05a96 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 2 Mar 2023 12:25:05 +0100 Subject: [PATCH 112/114] Do not read index if no chunks in rosbag1 --- src/rosbags/rosbag1/reader.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rosbags/rosbag1/reader.py b/src/rosbags/rosbag1/reader.py index 5950b87f..037b65a5 100644 --- a/src/rosbags/rosbag1/reader.py +++ b/src/rosbags/rosbag1/reader.py @@ -357,7 +357,7 @@ class Reader: self.bio: Optional[BinaryIO] = None self.connections: list[Connection] = [] - self.indexes: dict[int, list[IndexData]] + self.indexes: dict[int, list[IndexData]] = {} self.index_data_header_offsets: Optional[tuple[int, int]] = None self.chunk_infos: list[ChunkInfo] = [] self.chunks: dict[int, Chunk] = {} @@ -397,6 +397,9 @@ class Reader: if index_pos == 0: raise ReaderError('Bag is not indexed, reindex before reading.') + if chunk_count == 0: + return + self.bio.seek(index_pos) try: self.connections = [self.read_connection() for _ in range(conn_count)] From dff38bdb6023909c64105b02eab8bf1161c45591 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 2 Mar 2023 12:55:12 +0100 Subject: [PATCH 113/114] Add rosbag2 mcap storage reader --- docs/topics/rosbag2.rst | 3 +- setup.cfg | 1 + src/rosbags/highlevel/anyreader.py | 41 +- src/rosbags/rosbag2/reader.py | 12 +- src/rosbags/rosbag2/storage_mcap.py | 571 ++++++++++++++++++++++++++++ tests/test_highlevel.py | 60 +++ tests/test_reader.py | 429 +++++++++++++++++++++ 7 files changed, 1096 insertions(+), 21 deletions(-) create mode 100644 src/rosbags/rosbag2/storage_mcap.py diff --git a/docs/topics/rosbag2.rst b/docs/topics/rosbag2.rst index dbee4d06..21fbecf4 100644 --- a/docs/topics/rosbag2.rst +++ b/docs/topics/rosbag2.rst @@ -4,7 +4,7 @@ The :py:mod:`rosbags.rosbag2` package provides a conformant implementation of ro Supported Versions ------------------ -All versions up to the current (ROS2 Foxy) version 4 are supported. +All versions up to the current (ROS2 Humble) version 6 are supported. Supported Features ------------------ @@ -18,6 +18,7 @@ Rosbag2 is a flexible format that supports plugging different serialization meth :Storages: - sqlite3 + - mcap Writing rosbag2 --------------- diff --git a/setup.cfg b/setup.cfg index 0b4ded51..41515ad5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,6 +12,7 @@ keywords = conversion deserialization idl + mcap message msg reader diff --git a/src/rosbags/highlevel/anyreader.py b/src/rosbags/highlevel/anyreader.py index d9562af0..1363fec7 100644 --- a/src/rosbags/highlevel/anyreader.py +++ b/src/rosbags/highlevel/anyreader.py @@ -17,6 +17,7 @@ from rosbags.rosbag2 import Reader as Reader2 from rosbags.rosbag2 import ReaderError as ReaderError2 from rosbags.serde import deserialize_cdr, deserialize_ros1 from rosbags.typesys import get_types_from_msg, register_types, types +from rosbags.typesys.idl import get_types_from_idl if TYPE_CHECKING: import sys @@ -125,24 +126,34 @@ class AnyReader: reader.close() raise AnyReaderError(*err.args) from err + for key in [ + 'builtin_interfaces/msg/Time', + 'builtin_interfaces/msg/Duration', + 'std_msgs/msg/Header', + ]: + self.typestore.FIELDDEFS[key] = types.FIELDDEFS[key] + attr = key.replace('/', '__') + setattr(self.typestore, attr, getattr(types, attr)) + typs: dict[str, Any] = {} if self.is2: - for key, value in types.FIELDDEFS.items(): - self.typestore.FIELDDEFS[key] = value - attr = key.replace('/', '__') - setattr(self.typestore, attr, getattr(types, attr)) + reader = self.readers[0] + assert isinstance(reader, Reader2) + if reader.metadata['storage_identifier'] == 'mcap': + for connection in reader.connections: + if connection.md5sum: + if connection.md5sum == 'idl': + typ = get_types_from_idl(connection.msgdef) + else: + typ = get_types_from_msg(connection.msgdef, connection.msgtype) + typs.update(typ) + register_types(typs, self.typestore) + else: + for key, value in types.FIELDDEFS.items(): + self.typestore.FIELDDEFS[key] = value + attr = key.replace('/', '__') + setattr(self.typestore, attr, getattr(types, attr)) else: - for key in [ - 'builtin_interfaces/msg/Time', - 'builtin_interfaces/msg/Duration', - 'std_msgs/msg/Header', - ]: - self.typestore.FIELDDEFS[key] = types.FIELDDEFS[key] - attr = key.replace('/', '__') - setattr(self.typestore, attr, getattr(types, attr)) - - typs: dict[str, Any] = {} for reader in self.readers: - assert isinstance(reader, Reader1) for connection in reader.connections: typs.update(get_types_from_msg(connection.msgdef, connection.msgtype)) register_types(typs, self.typestore) diff --git a/src/rosbags/rosbag2/reader.py b/src/rosbags/rosbag2/reader.py index 02733a4e..aeb3ceff 100644 --- a/src/rosbags/rosbag2/reader.py +++ b/src/rosbags/rosbag2/reader.py @@ -15,6 +15,7 @@ from ruamel.yaml.error import YAMLError from rosbags.interfaces import Connection, ConnectionExtRosbag2, TopicInfo from .errors import ReaderError +from .storage_mcap import ReaderMcap from .storage_sqlite3 import ReaderSqlite3 if TYPE_CHECKING: @@ -29,19 +30,19 @@ class StorageProtocol(Protocol): def __init__(self, paths: Iterable[Path], connections: Iterable[Connection]): """Initialize.""" - raise NotImplementedError + raise NotImplementedError # pragma: no cover def open(self) -> None: """Open file.""" - raise NotImplementedError + raise NotImplementedError # pragma: no cover def close(self) -> None: """Close file.""" - raise NotImplementedError + raise NotImplementedError # pragma: no cover def get_definitions(self) -> dict[str, tuple[str, str]]: """Get message definitions.""" - raise NotImplementedError + raise NotImplementedError # pragma: no cover def messages( self, @@ -50,7 +51,7 @@ class StorageProtocol(Protocol): stop: Optional[int] = None, ) -> Generator[tuple[Connection, int, bytes], None, None]: """Get messages from file.""" - raise NotImplementedError + raise NotImplementedError # pragma: no cover class Reader: @@ -73,6 +74,7 @@ class Reader: # pylint: disable=too-many-instance-attributes STORAGE_PLUGINS: dict[str, Type[StorageProtocol]] = { + 'mcap': ReaderMcap, 'sqlite3': ReaderSqlite3, } diff --git a/src/rosbags/rosbag2/storage_mcap.py b/src/rosbags/rosbag2/storage_mcap.py new file mode 100644 index 00000000..324ef686 --- /dev/null +++ b/src/rosbags/rosbag2/storage_mcap.py @@ -0,0 +1,571 @@ +# Copyright 2020-2023 Ternaris. +# SPDX-License-Identifier: Apache-2.0 +"""Mcap storage.""" + +from __future__ import annotations + +import heapq +from io import BytesIO +from struct import iter_unpack, unpack_from +from typing import TYPE_CHECKING, NamedTuple + +import zstandard +from lz4.frame import decompress as lz4_decompress + +from .errors import ReaderError + +if TYPE_CHECKING: + from pathlib import Path + from typing import BinaryIO, Callable, Generator, Iterable, Optional + + from rosbags.interfaces import Connection + + +class Schema(NamedTuple): + """Schema.""" + + id: int + name: str + encoding: str + data: str + + +class Channel(NamedTuple): + """Channel.""" + + id: int + schema: str + topic: str + message_encoding: str + metadata: bytes # dict[str, str] + + +class Chunk(NamedTuple): + """Chunk.""" + + start_time: int + end_time: int + size: int + crc: int + compression: str + records: bytes + + +class ChunkInfo(NamedTuple): + """Chunk.""" + + message_start_time: int + message_end_time: int + chunk_start_offset: int + chunk_length: int + message_index_offsets: dict[int, int] + message_index_length: int + compression: str + compressed_size: int + uncompressed_size: int + channel_count: dict[int, int] + + +class Statistics(NamedTuple): + """Statistics.""" + + message_count: int + schema_count: int + channel_count: int + attachement_count: int + metadata_count: int + chunk_count: int + start_time: int + end_time: int + channel_message_counts: bytes + + +class Msg(NamedTuple): + """Message wrapper.""" + + timestamp: int + offset: int + connection: Optional[Connection] + data: Optional[bytes] + + +def read_sized(bio: BinaryIO) -> bytes: + """Read one record.""" + return bio.read(unpack_from(' None: + """Read one record.""" + bio.seek(unpack_from(' bytes: + """Read string.""" + return bio.read(unpack_from(' str: + """Read string.""" + return bio.read(unpack_from(' Generator[Msg, None, None]: + """Yield messages from chunk in time order.""" + yield Msg(chunk.message_start_time, 0, None, None) + + bio.seek(chunk.chunk_start_offset + 9 + 40 + len(chunk.compression)) + compressed_data = bio.read(chunk.compressed_size) + subio = BytesIO(DECOMPRESSORS[chunk.compression](compressed_data, chunk.uncompressed_size)) + + messages = [] + while (offset := subio.tell()) < chunk.uncompressed_size: + op_ = ord(subio.read(1)) + if op_ == 0x05: + recio = BytesIO(read_sized(subio)) + channel_id, _, log_time, _ = unpack_from( + ' None: + """Open MCAP.""" + try: + self.bio = self.path.open('rb') + except OSError as err: + raise ReaderError(f'Could not open file {str(self.path)!r}: {err.strerror}.') from err + + magic = self.bio.read(8) + if not magic: + raise ReaderError(f'File {str(self.path)!r} seems to be empty.') + + if magic != b'\x89MCAP0\r\n': + raise ReaderError('File magic is invalid.') + + op_ = ord(self.bio.read(1)) + if op_ != 0x01: + raise ReaderError('Unexpected record.') + + recio = BytesIO(read_sized(self.bio)) + profile = read_string(recio) + if profile != 'ros2': + raise ReaderError('Profile is not ros2.') + self.data_start = self.bio.tell() + + self.bio.seek(-37, 2) + footer_start = self.bio.tell() + data = self.bio.read() + magic = data[-8:] + if magic != b'\x89MCAP0\r\n': + raise ReaderError('File end magic is invalid.') + + assert len(data) == 37 + assert data[0:9] == b'\x02\x14\x00\x00\x00\x00\x00\x00\x00', data[0:9] + + summary_start, = unpack_from(' None: + """Read index from file.""" + bio = self.bio + assert bio + + schemas = self.schemas + channels = self.channels + chunks = self.chunks + + bio.seek(self.data_end) + while True: + op_ = ord(bio.read(1)) + + if op_ in (0x02, 0x0e): + break + + if op_ == 0x03: + bio.seek(8, 1) + key, = unpack_from(' None: + """Close MCAP.""" + assert self.bio + self.bio.close() + self.bio = None + + def meta_scan(self) -> None: + """Generate metadata by scanning through file.""" + assert self.bio + bio = self.bio + bio_size = self.data_end + bio.seek(self.data_start) + + schemas = self.schemas + channels = self.channels + + while bio.tell() < bio_size: + op_ = ord(bio.read(1)) + + if op_ == 0x03: + bio.seek(8, 1) + key, = unpack_from(' dict[str, tuple[str, str]]: + """Get schema definition.""" + if not self.schemas: + self.meta_scan() + return {schema.name: (schema.encoding[4:], schema.data) for schema in self.schemas.values()} + + def messages_scan( + self, + connections: Iterable[Connection], + start: Optional[int] = None, + stop: Optional[int] = None, + ) -> Generator[tuple[Connection, int, bytes], None, None]: + """Read messages by scanning whole bag.""" + # pylint: disable=too-many-locals + assert self.bio + bio = self.bio + bio_size = self.data_end + bio.seek(self.data_start) + + schemas = self.schemas.copy() + channels = self.channels.copy() + + if channels: + read_meta = False + channel_map = { + cid: conn for conn in connections if ( + cid := next( + ( + cid for cid, x in self.channels.items() + if x.schema == conn.msgtype and x.topic == conn.topic + ), + None, + ) + ) + } + else: + read_meta = True + channel_map = {} + + if start is None: + start = 0 + if stop is None: + stop = 2**63 - 1 + + while bio.tell() < bio_size: + op_ = ord(bio.read(1)) + + if op_ == 0x03 and read_meta: + bio.seek(8, 1) + key, = unpack_from(' Generator[tuple[Connection, int, bytes], None, None]: + """Read messages from bag. + + Args: + connections: Iterable with connections to filter for. + start: Yield only messages at or after this timestamp (ns). + stop: Yield only messages before this timestamp (ns). + + Yields: + tuples of connection, timestamp (ns), and rawdata. + + """ + assert self.bio + + if not self.chunks: + yield from self.messages_scan(connections, start, stop) + return + + channel_map = { + cid: conn for conn in connections if ( + cid := next( + ( + cid for cid, x in self.channels.items() + if x.schema == conn.msgtype and x.topic == conn.topic + ), + None, + ) + ) + } + + chunks = [ + msgsrc( + x, + channel_map, + start or x.message_start_time, + stop or x.message_end_time + 1, + self.bio, + ) + for x in self.chunks + if x.message_start_time != 0 and (start is None or start < x.message_end_time) and + (stop is None or x.message_start_time < stop) and + (any(x.channel_count.get(cid, 0) for cid in channel_map)) + ] + + for timestamp, offset, connection, data in heapq.merge(*chunks): + if not offset: + continue + assert connection + assert data + yield connection, timestamp, data + + +class ReaderMcap: + """Mcap storage reader.""" + + def __init__( + self, + paths: Iterable[Path], + connections: Iterable[Connection], + ): + """Set up storage reader. + + Args: + paths: Paths of storage files. + connections: List of connections. + + """ + self.paths = paths + self.readers: list[MCAPFile] = [] + self.connections = connections + + def open(self) -> None: + """Open rosbag2.""" + self.readers = [MCAPFile(x) for x in self.paths] + for reader in self.readers: + reader.open() + + def close(self) -> None: + """Close rosbag2.""" + assert self.readers + for reader in self.readers: + reader.close() + self.readers = [] + + def get_definitions(self) -> dict[str, tuple[str, str]]: + """Get message definitions.""" + res = {} + for reader in self.readers: + res.update(reader.get_schema_definitions()) + return res + + def messages( + self, + connections: Iterable[Connection] = (), + start: Optional[int] = None, + stop: Optional[int] = None, + ) -> Generator[tuple[Connection, int, bytes], None, None]: + """Read messages from bag. + + Args: + connections: Iterable with connections to filter for. An empty + iterable disables filtering on connections. + start: Yield only messages at or after this timestamp (ns). + stop: Yield only messages before this timestamp (ns). + + Yields: + tuples of connection, timestamp (ns), and rawdata. + + """ + connections = list(connections) or list(self.connections) + + for reader in self.readers: + yield from reader.messages(connections, start, stop) diff --git a/tests/test_highlevel.py b/tests/test_highlevel.py index defe1b1b..9a4d5cb2 100644 --- a/tests/test_highlevel.py +++ b/tests/test_highlevel.py @@ -5,10 +5,12 @@ from __future__ import annotations from typing import TYPE_CHECKING +from unittest.mock import patch import pytest from rosbags.highlevel import AnyReader, AnyReaderError +from rosbags.interfaces import Connection from rosbags.rosbag1 import Writer as Writer1 from rosbags.rosbag2 import Writer as Writer2 @@ -200,3 +202,61 @@ def test_anyreader2(bags2: list[Path]) -> None: # pylint: disable=redefined-out assert nxt[0].topic == '/topic1' with pytest.raises(StopIteration): next(gen) + + +def test_anyreader2_autoregister(bags2: list[Path]) -> None: # pylint: disable=redefined-outer-name + """Test AnyReader on rosbag2.""" + + class MockReader: + """Mock reader.""" + + # pylint: disable=too-few-public-methods + + def __init__(self, paths: list[Path]): + """Initialize mock.""" + _ = paths + self.metadata = {'storage_identifier': 'mcap'} + self.connections = [ + Connection( + 1, + '/foo', + 'test_msg/msg/Foo', + 'string foo', + 'msg', + 0, + None, # type: ignore + self, + ), + Connection( + 2, + '/bar', + 'test_msg/msg/Bar', + 'module test_msgs { module msg { struct Bar {string bar;}; }; };', + 'idl', + 0, + None, # type: ignore + self, + ), + Connection( + 3, + '/baz', + 'test_msg/msg/Baz', + '', + '', + 0, + None, # type: ignore + self, + ), + ] + + def open(self) -> None: + """Unused.""" + + with patch('rosbags.highlevel.anyreader.Reader2', MockReader), \ + patch('rosbags.highlevel.anyreader.register_types') as mock_register_types: + AnyReader([bags2[0]]).open() + mock_register_types.assert_called_once() + assert mock_register_types.call_args[0][0] == { + 'test_msg/msg/Foo': ([], [('foo', (1, 'string'))]), + 'test_msgs/msg/Bar': ([], [('bar', (1, 'string'))]), + } diff --git a/tests/test_reader.py b/tests/test_reader.py index 40583078..cb2a9b30 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -7,6 +7,9 @@ from __future__ import annotations import sqlite3 +import struct +from io import BytesIO +from itertools import groupby from pathlib import Path from typing import TYPE_CHECKING from unittest import mock @@ -19,6 +22,8 @@ from rosbags.rosbag2 import Reader, ReaderError, Writer from .test_serde import MSG_JOINT, MSG_MAGN, MSG_MAGN_BIG, MSG_POLY if TYPE_CHECKING: + from typing import BinaryIO, Iterable + from _pytest.fixtures import SubRequest METADATA = """ @@ -320,3 +325,427 @@ def test_failure_cases(tmp_path: Path) -> None: with pytest.raises(ReaderError, match='not open database'), \ Reader(tmp_path) as reader: next(reader.messages()) + + +def write_record(bio: BinaryIO, opcode: int, records: Iterable[bytes]) -> None: + """Write record.""" + data = b''.join(records) + bio.write(bytes([opcode]) + struct.pack(' bytes: + """Serialize string.""" + data = text.encode() + return struct.pack(' Path: + """Manually contruct mcap bag.""" + # pylint: disable=too-many-locals + # pylint: disable=too-many-statements + (tmp_path / 'metadata.yaml').write_text( + METADATA.format( + extension='.mcap', + compression_format='""', + compression_mode='""', + ).replace('sqlite3', 'mcap'), + ) + + path = tmp_path / 'db.db3.mcap' + bio: BinaryIO + messages: list[tuple[int, int, int]] = [] + chunks = [] + with path.open('wb') as bio: + realbio = bio + bio.write(MCAP_HEADER) + write_record(bio, 0x01, (make_string('ros2'), make_string('test_mcap'))) + + if request.param.startswith('chunked'): + bio = BytesIO() + messages = [] + + write_record(bio, *SCHEMAS[0]) + write_record(bio, *CHANNELS[0]) + messages.append((1, 666, bio.tell())) + write_record( + bio, + 0x05, + ( + struct.pack(' None: + """Test reader and deserializer on simple bag.""" + with Reader(bag_mcap) as reader: + assert reader.duration == 43 + assert reader.start_time == 666 + assert reader.end_time == 709 + assert reader.message_count == 4 + if reader.compression_mode: + assert reader.compression_format == 'zstd' + assert [x.id for x in reader.connections] == [1, 2, 3] + assert [*reader.topics.keys()] == ['/poly', '/magn', '/joint'] + gen = reader.messages() + + connection, timestamp, rawdata = next(gen) + assert connection.topic == '/poly' + assert connection.msgtype == 'geometry_msgs/msg/Polygon' + assert timestamp == 666 + assert rawdata == MSG_POLY[0] + + for idx in range(2): + connection, timestamp, rawdata = next(gen) + assert connection.topic == '/magn' + assert connection.msgtype == 'sensor_msgs/msg/MagneticField' + assert timestamp == 708 + assert rawdata == [MSG_MAGN, MSG_MAGN_BIG][idx][0] + + connection, timestamp, rawdata = next(gen) + assert connection.topic == '/joint' + assert connection.msgtype == 'trajectory_msgs/msg/JointTrajectory' + + with pytest.raises(StopIteration): + next(gen) + + +def test_message_filters_mcap(bag_mcap: Path) -> None: + """Test reader filters messages.""" + with Reader(bag_mcap) as reader: + magn_connections = [x for x in reader.connections if x.topic == '/magn'] + gen = reader.messages(connections=magn_connections) + connection, _, _ = next(gen) + assert connection.topic == '/magn' + connection, _, _ = next(gen) + assert connection.topic == '/magn' + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(start=667) + connection, _, _ = next(gen) + assert connection.topic == '/magn' + connection, _, _ = next(gen) + assert connection.topic == '/magn' + connection, _, _ = next(gen) + assert connection.topic == '/joint' + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(stop=667) + connection, _, _ = next(gen) + assert connection.topic == '/poly' + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(connections=magn_connections, stop=667) + with pytest.raises(StopIteration): + next(gen) + + gen = reader.messages(start=666, stop=666) + with pytest.raises(StopIteration): + next(gen) + + +def test_bag_mcap_files(tmp_path: Path) -> None: + """Test bad mcap files.""" + (tmp_path / 'metadata.yaml').write_text( + METADATA.format( + extension='.mcap', + compression_format='""', + compression_mode='""', + ).replace('sqlite3', 'mcap'), + ) + + path = tmp_path / 'db.db3.mcap' + path.touch() + reader = Reader(tmp_path) + path.unlink() + with pytest.raises(ReaderError, match='Could not open'): + reader.open() + + path.touch() + with pytest.raises(ReaderError, match='seems to be empty'): + Reader(tmp_path).open() + + path.write_bytes(b'xxxxxxxx') + with pytest.raises(ReaderError, match='magic is invalid'): + Reader(tmp_path).open() + + path.write_bytes(b'\x89MCAP0\r\n\xFF') + with pytest.raises(ReaderError, match='Unexpected record'): + Reader(tmp_path).open() + + with path.open('wb') as bio: + bio.write(b'\x89MCAP0\r\n') + write_record(bio, 0x01, (make_string('ros1'), make_string('test_mcap'))) + with pytest.raises(ReaderError, match='Profile is not'): + Reader(tmp_path).open() + + with path.open('wb') as bio: + bio.write(b'\x89MCAP0\r\n') + write_record(bio, 0x01, (make_string('ros2'), make_string('test_mcap'))) + with pytest.raises(ReaderError, match='File end magic is invalid'): + Reader(tmp_path).open() From c80625df279c154c6ec069cbac30faa319755e47 Mon Sep 17 00:00:00 2001 From: Marko Durkovic Date: Thu, 2 Mar 2023 13:35:17 +0100 Subject: [PATCH 114/114] Release 0.9.15 --- CHANGES.rst | 11 +++++++++++ setup.cfg | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index a899f606..60611029 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,17 @@ Changes ======= +0.9.15 - 2023-03-02 +------------------- +- Refactor rosbag2 Reader for multipe storage backends +- Improve parsing of IDL files +- Handle bags contaning only connection records +- Add AnyReader to documentation +- Add initial MCAP reader for rosbag2 `#33`_ + +.. _#33: https://gitlab.com/ternaris/rosbags/issues/33 + + 0.9.14 - 2023-01-12 ------------------- - Fix reader example in README `#40`_ diff --git a/setup.cfg b/setup.cfg index 41515ad5..63a810d1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = rosbags -version = 0.9.14 +version = 0.9.15 author = Ternaris author_email = team@ternaris.com home_page = https://gitlab.com/ternaris/rosbags