Skip to content

Commit 0c5374e

Browse files
authoredMar 19, 2024
Add Python stubs for rosbag2_py (ros2#1459) (ros2#1569)
* Add Python stubs for rosbag2_py Signed-off-by: Roman Sokolkov <[email protected]> * Add note to DEVELOPING.md Signed-off-by: Roman Sokolkov <[email protected]> * Add note to py.typed Signed-off-by: Roman Sokolkov <[email protected]> --------- Signed-off-by: Roman Sokolkov <[email protected]>
1 parent fc3b55b commit 0c5374e

12 files changed

+418
-0
lines changed
 

‎.github/workflows/test.yml

+11
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,17 @@ jobs:
8888
source /opt/ros/rolling/setup.sh && colcon test --mixin linters-skip --packages-select ${rosbag2_packages} --packages-skip rosbag2_performance_benchmarking --event-handlers console_cohesion+ --return-code-on-test-failure --ctest-args "-L xfail" --pytest-args "-m xfail"
8989
working-directory: ${{ steps.action-ros-ci.outputs.ros-workspace-directory-name }}
9090
shell: bash
91+
- name: Is regeneration of Python stubs required?
92+
run: |
93+
rosbag2_path=$(colcon list -p --packages-select rosbag2)/..
94+
sudo pip uninstall -y mypy
95+
sudo apt update && sudo apt -y install mypy=0.942-1ubuntu1
96+
source install/setup.sh
97+
stubgen -p rosbag2_py -o ${rosbag2_path}/rosbag2_py
98+
cd ${rosbag2_path}
99+
git diff --exit-code
100+
working-directory: ${{ steps.action-ros-ci.outputs.ros-workspace-directory-name }}
101+
shell: bash
91102
- uses: actions/upload-artifact@v1
92103
with:
93104
name: colcon-logs

‎DEVELOPING.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ colcon build --packages-up-to rosbag2
1111

1212
Note: building Rosbag2 from source, overlaid on a debian installation of `ros-$ROS_DISTRO-rosbag2` has undefined behavior (but should work in most cases, just beware the build may find headers from the binaries instead of the local workspace.)
1313

14+
Note: make sure to [regenerate stub files](rosbag2_py/README.md), when making changes to pybind11-related files in `rosbag2_py`.
1415

1516
## Executing tests
1617

‎rosbag2_py/README.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# rosbag2_py
2+
3+
## Regenerating Python stub files (.pyi)
4+
5+
Python stub files allow to supply type-hinting information for binary Python modules (e.g. pybind-based).
6+
7+
In rosbag2_py stub files are generated with utility called `stubgen`.
8+
9+
To regenerate stub files
10+
```
11+
cd <workspace>
12+
colcon build --packages-up-to rosbag2_py
13+
source install/setup.sh
14+
# Make sure rosbag2_py can be imported
15+
python3 -c 'import rosbag2_py'
16+
17+
sudo apt update && sudo apt install mypy=0.942-1ubuntu1
18+
cd <rosbag2 git repo>
19+
stubgen -p rosbag2_py -o rosbag2_py
20+
```

‎rosbag2_py/rosbag2_py/__init__.pyi

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from rosbag2_py._compression_options import CompressionMode as CompressionMode, CompressionOptions as CompressionOptions, compression_mode_from_string as compression_mode_from_string, compression_mode_to_string as compression_mode_to_string
2+
from rosbag2_py._info import Info as Info
3+
from rosbag2_py._reader import SequentialCompressionReader as SequentialCompressionReader, SequentialReader as SequentialReader, get_registered_readers as get_registered_readers
4+
from rosbag2_py._reindexer import Reindexer as Reindexer
5+
from rosbag2_py._storage import BagMetadata as BagMetadata, ConverterOptions as ConverterOptions, FileInformation as FileInformation, MessageDefinition as MessageDefinition, MetadataIo as MetadataIo, ReadOrder as ReadOrder, ReadOrderSortBy as ReadOrderSortBy, StorageFilter as StorageFilter, StorageOptions as StorageOptions, TopicInformation as TopicInformation, TopicMetadata as TopicMetadata, get_default_storage_id as get_default_storage_id
6+
from rosbag2_py._transport import PlayOptions as PlayOptions, Player as Player, RecordOptions as RecordOptions, Recorder as Recorder, bag_rewrite as bag_rewrite
7+
from rosbag2_py._writer import SequentialCompressionWriter as SequentialCompressionWriter, SequentialWriter as SequentialWriter, get_registered_compressors as get_registered_compressors, get_registered_serializers as get_registered_serializers, get_registered_writers as get_registered_writers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import ClassVar
2+
3+
FILE: CompressionMode
4+
MESSAGE: CompressionMode
5+
NONE: CompressionMode
6+
7+
class CompressionMode:
8+
__doc__: ClassVar[str] = ... # read-only
9+
__members__: ClassVar[dict] = ... # read-only
10+
FILE: ClassVar[CompressionMode] = ...
11+
MESSAGE: ClassVar[CompressionMode] = ...
12+
NONE: ClassVar[CompressionMode] = ...
13+
__entries: ClassVar[dict] = ...
14+
def __init__(self, value: int) -> None: ...
15+
def __eq__(self, other: object) -> bool: ...
16+
def __getstate__(self) -> int: ...
17+
def __hash__(self) -> int: ...
18+
def __index__(self) -> int: ...
19+
def __int__(self) -> int: ...
20+
def __ne__(self, other: object) -> bool: ...
21+
def __setstate__(self, state: int) -> None: ...
22+
@property
23+
def name(self) -> str: ...
24+
@property
25+
def value(self) -> int: ...
26+
27+
class CompressionOptions:
28+
compression_format: str
29+
compression_mode: CompressionMode
30+
compression_queue_size: int
31+
compression_threads: int
32+
def __init__(self, compression_format: str = ..., compression_mode: CompressionMode = ..., compression_queue_size: int = ..., compression_threads: int = ...) -> None: ...
33+
34+
def compression_mode_from_string(arg0: str) -> CompressionMode: ...
35+
def compression_mode_to_string(arg0: CompressionMode) -> str: ...

‎rosbag2_py/rosbag2_py/_info.pyi

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import rosbag2_py._storage
2+
3+
class Info:
4+
def __init__(self) -> None: ...
5+
def read_metadata(self, arg0: str, arg1: str) -> rosbag2_py._storage.BagMetadata: ...
6+
def read_metadata_and_output_service_verbose(self, arg0: str, arg1: str) -> None: ...

‎rosbag2_py/rosbag2_py/_reader.pyi

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Any
2+
3+
class SequentialCompressionReader:
4+
def __init__(self) -> None: ...
5+
def get_all_message_definitions(self, *args, **kwargs) -> Any: ...
6+
def get_all_topics_and_types(self, *args, **kwargs) -> Any: ...
7+
def get_metadata(self, *args, **kwargs) -> Any: ...
8+
def has_next(self) -> bool: ...
9+
def open(self, arg0, arg1) -> None: ...
10+
def open_uri(self, arg0: str) -> None: ...
11+
def read_next(self) -> tuple: ...
12+
def reset_filter(self) -> None: ...
13+
def seek(self, arg0: int) -> None: ...
14+
def set_filter(self, arg0) -> None: ...
15+
def set_read_order(self, arg0) -> bool: ...
16+
17+
class SequentialReader:
18+
def __init__(self) -> None: ...
19+
def get_all_message_definitions(self, *args, **kwargs) -> Any: ...
20+
def get_all_topics_and_types(self, *args, **kwargs) -> Any: ...
21+
def get_metadata(self, *args, **kwargs) -> Any: ...
22+
def has_next(self) -> bool: ...
23+
def open(self, arg0, arg1) -> None: ...
24+
def open_uri(self, arg0: str) -> None: ...
25+
def read_next(self) -> tuple: ...
26+
def reset_filter(self) -> None: ...
27+
def seek(self, arg0: int) -> None: ...
28+
def set_filter(self, arg0) -> None: ...
29+
def set_read_order(self, arg0) -> bool: ...
30+
31+
def get_registered_readers() -> Set[str]: ...

‎rosbag2_py/rosbag2_py/_reindexer.pyi

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import rosbag2_py._storage
2+
3+
class Reindexer:
4+
def __init__(self) -> None: ...
5+
def reindex(self, arg0: rosbag2_py._storage.StorageOptions) -> None: ...

‎rosbag2_py/rosbag2_py/_storage.pyi

+212
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
from typing import ClassVar, Dict, List
2+
3+
import datetime
4+
5+
class BagMetadata:
6+
bag_size: int
7+
compression_format: str
8+
compression_mode: str
9+
custom_data: Dict[str,str]
10+
duration: object
11+
files: List[FileInformation]
12+
message_count: int
13+
relative_file_paths: List[str]
14+
ros_distro: str
15+
starting_time: object
16+
storage_identifier: str
17+
topics_with_message_count: List[TopicInformation]
18+
version: int
19+
def __init__(self, version: int = ..., bag_size: int = ..., storage_identifier: str = ..., relative_file_paths: List[str] = ..., files: List[FileInformation] = ..., duration: object = ..., starting_time: object = ..., message_count: int = ..., topics_with_message_count: List[TopicInformation] = ..., compression_format: str = ..., compression_mode: str = ..., custom_data: Dict[str,str] = ..., ros_distro: str = ...) -> None: ...
20+
21+
class ConverterOptions:
22+
input_serialization_format: str
23+
output_serialization_format: str
24+
def __init__(self, input_serialization_format: str = ..., output_serialization_format: str = ...) -> None: ...
25+
26+
class Duration:
27+
def __init__(self, seconds: int, nanoseconds: int) -> None: ...
28+
29+
class FileInformation:
30+
duration: datetime.timedelta
31+
message_count: int
32+
path: str
33+
starting_time: object
34+
def __init__(self, path: str, starting_time: object, duration: object, message_count: int) -> None: ...
35+
36+
class MessageDefinition:
37+
encoded_message_definition: str
38+
encoding: str
39+
topic_type: str
40+
type_hash: str
41+
def __init__(self, topic_type: str, encoding: str, encoded_message_definition: str, type_hash: str) -> None: ...
42+
43+
class MetadataIo:
44+
def __init__(self) -> None: ...
45+
def deserialize_metadata(self, arg0: str) -> BagMetadata: ...
46+
def metadata_file_exists(self, arg0: str) -> bool: ...
47+
def read_metadata(self, arg0: str) -> BagMetadata: ...
48+
def serialize_metadata(self, arg0: BagMetadata) -> str: ...
49+
def write_metadata(self, arg0: str, arg1: BagMetadata) -> None: ...
50+
51+
class QoS:
52+
def __init__(self, history_depth: int) -> None: ...
53+
def avoid_ros_namespace_conventions(self, arg0: bool) -> QoS: ...
54+
def best_effort(self) -> QoS: ...
55+
def deadline(self, arg0: Duration) -> QoS: ...
56+
def durability(self, arg0: rmw_qos_durability_policy_t) -> QoS: ...
57+
def durability_volatile(self) -> QoS: ...
58+
def history(self, arg0: rmw_qos_history_policy_t) -> QoS: ...
59+
def keep_all(self) -> QoS: ...
60+
def keep_last(self, arg0: int) -> QoS: ...
61+
def lifespan(self, arg0: Duration) -> QoS: ...
62+
def liveliness(self, arg0: rmw_qos_liveliness_policy_t) -> QoS: ...
63+
def liveliness_lease_duration(self, arg0: Duration) -> QoS: ...
64+
def reliability(self, arg0: rmw_qos_reliability_policy_t) -> QoS: ...
65+
def reliable(self) -> QoS: ...
66+
def transient_local(self) -> QoS: ...
67+
68+
class ReadOrder:
69+
reverse: bool
70+
sort_by: ReadOrderSortBy
71+
def __init__(self, sort_by: ReadOrderSortBy = ..., reverse: bool = ...) -> None: ...
72+
73+
class ReadOrderSortBy:
74+
__doc__: ClassVar[str] = ... # read-only
75+
__members__: ClassVar[dict] = ... # read-only
76+
File: ClassVar[ReadOrderSortBy] = ...
77+
PublishedTimestamp: ClassVar[ReadOrderSortBy] = ...
78+
ReceivedTimestamp: ClassVar[ReadOrderSortBy] = ...
79+
__entries: ClassVar[dict] = ...
80+
def __init__(self, value: int) -> None: ...
81+
def __eq__(self, other: object) -> bool: ...
82+
def __getstate__(self) -> int: ...
83+
def __hash__(self) -> int: ...
84+
def __index__(self) -> int: ...
85+
def __int__(self) -> int: ...
86+
def __ne__(self, other: object) -> bool: ...
87+
def __setstate__(self, state: int) -> None: ...
88+
@property
89+
def name(self) -> str: ...
90+
@property
91+
def value(self) -> int: ...
92+
93+
class StorageFilter:
94+
topics: List[str]
95+
topics_regex: str
96+
topics_regex_to_exclude: str
97+
def __init__(self, topics: List[str] = ..., topics_regex: str = ..., topics_regex_to_exclude: str = ...) -> None: ...
98+
99+
class StorageOptions:
100+
custom_data: Dict[str,str]
101+
end_time_ns: int
102+
max_bagfile_duration: int
103+
max_bagfile_size: int
104+
max_cache_size: int
105+
snapshot_mode: bool
106+
start_time_ns: int
107+
storage_config_uri: str
108+
storage_id: str
109+
storage_preset_profile: str
110+
uri: str
111+
def __init__(self, uri: str, storage_id: str = ..., max_bagfile_size: int = ..., max_bagfile_duration: int = ..., max_cache_size: int = ..., storage_preset_profile: str = ..., storage_config_uri: str = ..., snapshot_mode: bool = ..., start_time_ns: int = ..., end_time_ns: int = ..., custom_data: Dict[str,str] = ...) -> None: ...
112+
113+
class TopicInformation:
114+
message_count: int
115+
topic_metadata: TopicMetadata
116+
def __init__(self, topic_metadata: TopicMetadata, message_count: int) -> None: ...
117+
118+
class TopicMetadata:
119+
id: int
120+
name: str
121+
offered_qos_profiles: List[QoS]
122+
serialization_format: str
123+
type: str
124+
type_description_hash: str
125+
def __init__(self, id: int, name: str, type: str, serialization_format: str, offered_qos_profiles: List[QoS] = ..., type_description_hash: str = ...) -> None: ...
126+
def equals(self, arg0: TopicMetadata) -> bool: ...
127+
128+
class rmw_qos_durability_policy_t:
129+
__doc__: ClassVar[str] = ... # read-only
130+
__members__: ClassVar[dict] = ... # read-only
131+
RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT: ClassVar[rmw_qos_durability_policy_t] = ...
132+
RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL: ClassVar[rmw_qos_durability_policy_t] = ...
133+
RMW_QOS_POLICY_DURABILITY_UNKNOWN: ClassVar[rmw_qos_durability_policy_t] = ...
134+
RMW_QOS_POLICY_DURABILITY_VOLATILE: ClassVar[rmw_qos_durability_policy_t] = ...
135+
__entries: ClassVar[dict] = ...
136+
def __init__(self, value: int) -> None: ...
137+
def __eq__(self, other: object) -> bool: ...
138+
def __getstate__(self) -> int: ...
139+
def __hash__(self) -> int: ...
140+
def __index__(self) -> int: ...
141+
def __int__(self) -> int: ...
142+
def __ne__(self, other: object) -> bool: ...
143+
def __setstate__(self, state: int) -> None: ...
144+
@property
145+
def name(self) -> str: ...
146+
@property
147+
def value(self) -> int: ...
148+
149+
class rmw_qos_history_policy_t:
150+
__doc__: ClassVar[str] = ... # read-only
151+
__members__: ClassVar[dict] = ... # read-only
152+
RMW_QOS_POLICY_HISTORY_KEEP_ALL: ClassVar[rmw_qos_history_policy_t] = ...
153+
RMW_QOS_POLICY_HISTORY_KEEP_LAST: ClassVar[rmw_qos_history_policy_t] = ...
154+
RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT: ClassVar[rmw_qos_history_policy_t] = ...
155+
RMW_QOS_POLICY_HISTORY_UNKNOWN: ClassVar[rmw_qos_history_policy_t] = ...
156+
__entries: ClassVar[dict] = ...
157+
def __init__(self, value: int) -> None: ...
158+
def __eq__(self, other: object) -> bool: ...
159+
def __getstate__(self) -> int: ...
160+
def __hash__(self) -> int: ...
161+
def __index__(self) -> int: ...
162+
def __int__(self) -> int: ...
163+
def __ne__(self, other: object) -> bool: ...
164+
def __setstate__(self, state: int) -> None: ...
165+
@property
166+
def name(self) -> str: ...
167+
@property
168+
def value(self) -> int: ...
169+
170+
class rmw_qos_liveliness_policy_t:
171+
__doc__: ClassVar[str] = ... # read-only
172+
__members__: ClassVar[dict] = ... # read-only
173+
RMW_QOS_POLICY_LIVELINESS_AUTOMATIC: ClassVar[rmw_qos_liveliness_policy_t] = ...
174+
RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC: ClassVar[rmw_qos_liveliness_policy_t] = ...
175+
RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT: ClassVar[rmw_qos_liveliness_policy_t] = ...
176+
RMW_QOS_POLICY_LIVELINESS_UNKNOWN: ClassVar[rmw_qos_liveliness_policy_t] = ...
177+
__entries: ClassVar[dict] = ...
178+
def __init__(self, value: int) -> None: ...
179+
def __eq__(self, other: object) -> bool: ...
180+
def __getstate__(self) -> int: ...
181+
def __hash__(self) -> int: ...
182+
def __index__(self) -> int: ...
183+
def __int__(self) -> int: ...
184+
def __ne__(self, other: object) -> bool: ...
185+
def __setstate__(self, state: int) -> None: ...
186+
@property
187+
def name(self) -> str: ...
188+
@property
189+
def value(self) -> int: ...
190+
191+
class rmw_qos_reliability_policy_t:
192+
__doc__: ClassVar[str] = ... # read-only
193+
__members__: ClassVar[dict] = ... # read-only
194+
RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT: ClassVar[rmw_qos_reliability_policy_t] = ...
195+
RMW_QOS_POLICY_RELIABILITY_RELIABLE: ClassVar[rmw_qos_reliability_policy_t] = ...
196+
RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT: ClassVar[rmw_qos_reliability_policy_t] = ...
197+
RMW_QOS_POLICY_RELIABILITY_UNKNOWN: ClassVar[rmw_qos_reliability_policy_t] = ...
198+
__entries: ClassVar[dict] = ...
199+
def __init__(self, value: int) -> None: ...
200+
def __eq__(self, other: object) -> bool: ...
201+
def __getstate__(self) -> int: ...
202+
def __hash__(self) -> int: ...
203+
def __index__(self) -> int: ...
204+
def __int__(self) -> int: ...
205+
def __ne__(self, other: object) -> bool: ...
206+
def __setstate__(self, state: int) -> None: ...
207+
@property
208+
def name(self) -> str: ...
209+
@property
210+
def value(self) -> int: ...
211+
212+
def get_default_storage_id() -> str: ...

‎rosbag2_py/rosbag2_py/_transport.pyi

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from typing import Any, List
2+
3+
import datetime
4+
import rosbag2_py._storage
5+
6+
class PlayOptions:
7+
clock_publish_frequency: float
8+
clock_publish_on_topic_publish: bool
9+
clock_topics: List[str]
10+
delay: float
11+
disable_keyboard_controls: bool
12+
disable_loan_message: bool
13+
loop: bool
14+
node_prefix: str
15+
playback_duration: float
16+
playback_until_timestamp: int
17+
rate: float
18+
read_ahead_queue_size: int
19+
start_offset: float
20+
start_paused: bool
21+
topic_qos_profile_overrides: dict
22+
topic_remapping_options: List[str]
23+
topics_regex_to_exclude: str
24+
topics_regex_to_filter: str
25+
topics_to_filter: List[str]
26+
wait_acked_timeout: int
27+
def __init__(self) -> None: ...
28+
29+
class Player:
30+
def __init__(self) -> None: ...
31+
def burst(self, arg0: rosbag2_py._storage.StorageOptions, arg1: PlayOptions, arg2: int) -> None: ...
32+
def play(self, storage_options: rosbag2_py._storage.StorageOptions, play_options: PlayOptions) -> None: ...
33+
34+
class RecordOptions:
35+
all_services: bool
36+
all_topics: bool
37+
compression_format: str
38+
compression_mode: str
39+
compression_queue_size: int
40+
compression_threads: int
41+
exclude_regex: str
42+
exclude_service_events: List[str]
43+
exclude_topics: List[str]
44+
ignore_leaf_topics: bool
45+
include_hidden_topics: bool
46+
include_unpublished_topics: bool
47+
is_discovery_disabled: bool
48+
node_prefix: str
49+
regex: str
50+
rmw_serialization_format: str
51+
services: List[str]
52+
start_paused: bool
53+
topic_polling_interval: datetime.timedelta
54+
topic_qos_profile_overrides: dict
55+
topics: List[str]
56+
use_sim_time: bool
57+
def __init__(self) -> None: ...
58+
59+
class Recorder:
60+
def __init__(self) -> None: ...
61+
def cancel(self, *args, **kwargs) -> Any: ...
62+
def record(self, storage_options: rosbag2_py._storage.StorageOptions, record_options: RecordOptions, node_name: str = ...) -> None: ...
63+
64+
def bag_rewrite(arg0: List[rosbag2_py._storage.StorageOptions], arg1: str) -> None: ...

‎rosbag2_py/rosbag2_py/_writer.pyi

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import rosbag2_py._compression_options
2+
import rosbag2_py._storage
3+
4+
class SequentialCompressionWriter:
5+
def __init__(self, arg0: rosbag2_py._compression_options.CompressionOptions) -> None: ...
6+
def create_topic(self, arg0: rosbag2_py._storage.TopicMetadata) -> None: ...
7+
def open(self, arg0: rosbag2_py._storage.StorageOptions, arg1: rosbag2_py._storage.ConverterOptions) -> None: ...
8+
def remove_topic(self, arg0: rosbag2_py._storage.TopicMetadata) -> None: ...
9+
def split_bagfile(self) -> None: ...
10+
def take_snapshot(self) -> bool: ...
11+
def write(self, arg0: str, arg1: str, arg2: int) -> None: ...
12+
13+
class SequentialWriter:
14+
def __init__(self) -> None: ...
15+
def close(self) -> None: ...
16+
def create_topic(self, arg0: rosbag2_py._storage.TopicMetadata) -> None: ...
17+
def open(self, arg0: rosbag2_py._storage.StorageOptions, arg1: rosbag2_py._storage.ConverterOptions) -> None: ...
18+
def remove_topic(self, arg0: rosbag2_py._storage.TopicMetadata) -> None: ...
19+
def split_bagfile(self) -> None: ...
20+
def take_snapshot(self) -> bool: ...
21+
def write(self, arg0: str, arg1: str, arg2: int) -> None: ...
22+
23+
def get_registered_compressors() -> Set[str]: ...
24+
def get_registered_serializers() -> Set[str]: ...
25+
def get_registered_writers() -> Set[str]: ...

‎rosbag2_py/rosbag2_py/py.typed

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
See https://peps.python.org/pep-0561/ for details about py.typed files.

0 commit comments

Comments
 (0)
Please sign in to comment.