summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorGalen Williamson <galen@vector35.com>2024-04-19 17:11:04 -0400
committerGalen Williamson <galen@vector35.com>2024-04-19 17:11:04 -0400
commitc788961c5a7ff667d0b6237711f7747cac5be610 (patch)
tree2723627873dfbb033b9bb4fbe0ba3d8325d57f8d /python
parentcff2f4df2ad48f6e386e7eeed862dfca6659ab6d (diff)
docstring hygiene maintenance in python API
Diffstat (limited to 'python')
-rw-r--r--python/binaryview.py20
-rw-r--r--python/debuginfo.py1
-rw-r--r--python/filemetadata.py9
-rw-r--r--python/typearchive.py45
-rw-r--r--python/typecontainer.py19
5 files changed, 88 insertions, 6 deletions
diff --git a/python/binaryview.py b/python/binaryview.py
index a281a460..9bb3c7c9 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -7810,6 +7810,7 @@ class BinaryView:
Attach a given type archive to the analysis and try to connect to it.
If attaching was successful, names from that archive will become available to pull,
but no types will actually be associated by calling this.
+
:param archive: New archive
"""
attached = self.attach_type_archive_by_id(archive.id, archive.path)
@@ -7854,6 +7855,7 @@ class BinaryView:
def detach_type_archive(self, archive: 'typearchive.TypeArchive'):
"""
Detach from a type archive, breaking all associations to types within the archive
+
:param archive: Type archive to detach
"""
self.detach_type_archive_by_id(archive.id)
@@ -7861,6 +7863,7 @@ class BinaryView:
def detach_type_archive_by_id(self, id: str):
"""
Detach from a type archive, breaking all associations to types within the archive
+
:param id: Id of archive to detach
"""
if not core.BNBinaryViewDetachTypeArchive(self.handle, id):
@@ -7869,6 +7872,7 @@ class BinaryView:
def get_type_archive(self, id: str) -> Optional['typearchive.TypeArchive']:
"""
Look up a connected archive by its id
+
:param id: Id of archive
:return: Archive, if one exists with that id. Otherwise None
"""
@@ -7880,6 +7884,7 @@ class BinaryView:
def get_type_archive_path(self, id: str) -> Optional[str]:
"""
Look up the path for an attached (but not necessarily connected) type archive by its id
+
:param id: Id of archive
:return: Archive path, if it is attached. Otherwise None.
"""
@@ -7892,6 +7897,7 @@ class BinaryView:
def type_archive_type_names(self) -> Mapping['_types.QualifiedName', List[Tuple['typearchive.TypeArchive', str]]]:
"""
Get a list of all available type names in all connected archives, and their archive/type id pair
+
:return: name <-> [(archive, archive type id)] for all type names
"""
result = {}
@@ -7908,6 +7914,7 @@ class BinaryView:
def get_type_archives_for_type_name(self, name: '_types.QualifiedNameType') -> List[Tuple['typearchive.TypeArchive', str]]:
"""
Get a list of all connected type archives that have a given type name
+
:return: (archive, archive type id) for all archives
"""
name = _types.QualifiedName(name)
@@ -7932,6 +7939,7 @@ class BinaryView:
def associated_type_archive_types(self) -> Mapping['_types.QualifiedName', Tuple[Optional['typearchive.TypeArchive'], str]]:
"""
Get a list of all types in the analysis that are associated with attached type archives
+
:return: Map of all analysis types to their corresponding archive / id. If a type is associated with a disconnected type archive, the archive will be None.
"""
result = {}
@@ -7952,6 +7960,7 @@ class BinaryView:
def associated_type_archive_type_ids(self) -> Mapping[str, Tuple[str, str]]:
"""
Get a list of all types in the analysis that are associated with type archives
+
:return: Map of all analysis types to their corresponding archive / id
"""
@@ -7976,6 +7985,7 @@ class BinaryView:
def get_associated_types_from_archive(self, archive: 'typearchive.TypeArchive') -> Mapping['_types.QualifiedName', str]:
"""
Get a list of all types in the analysis that are associated with a specific type archive
+
:return: Map of all analysis types to their corresponding archive id
"""
result = {}
@@ -8011,6 +8021,7 @@ class BinaryView:
def get_associated_type_archive_type_target(self, name: '_types.QualifiedNameType') -> Optional[Tuple[Optional['typearchive.TypeArchive'], str]]:
"""
Determine the target archive / type id of a given analysis type
+
:param name: Analysis type
:return: (archive, archive type id) if the type is associated. None otherwise.
"""
@@ -8027,6 +8038,7 @@ class BinaryView:
def get_associated_type_archive_type_target_by_id(self, type_id: str) -> Optional[Tuple[str, str]]:
"""
Determine the target archive / type id of a given analysis type
+
:param type_id: Analysis type id
:return: (archive id, archive type id) if the type is associated. None otherwise.
"""
@@ -8042,6 +8054,7 @@ class BinaryView:
def get_associated_type_archive_type_source(self, archive: 'typearchive.TypeArchive', archive_type: '_types.QualifiedNameType') -> Optional['_types.QualifiedName']:
"""
Determine the local source type name for a given archive type
+
:param archive: Target type archive
:param archive_type: Name of target archive type
:return: Name of source analysis type, if this type is associated. None otherwise.
@@ -8057,6 +8070,7 @@ class BinaryView:
def get_associated_type_archive_type_source_by_id(self, archive_id: str, archive_type_id: str) -> Optional[str]:
"""
Determine the local source type id for a given archive type
+
:param archive_id: Id of target type archive
:param archive_type_id: Id of target archive type
:return: Id of source analysis type, if this type is associated. None otherwise.
@@ -8071,6 +8085,7 @@ class BinaryView:
def disassociate_type_archive_type(self, type: '_types.QualifiedNameType') -> bool:
"""
Disassociate an associated type, so that it will no longer receive updates from its connected type archive
+
:param type: Name of type in analysis
:return: True if successful
"""
@@ -8082,6 +8097,7 @@ class BinaryView:
def disassociate_type_archive_type_by_id(self, type_id: str) -> bool:
"""
Disassociate an associated type id, so that it will no longer receive updates from its connected type archive
+
:param type_id: Id of type in analysis
:return: True if successful
"""
@@ -8091,6 +8107,7 @@ class BinaryView:
-> Optional[Mapping['_types.QualifiedName', Tuple['_types.QualifiedName', '_types.Type']]]:
"""
Pull types from a type archive, updating them and any dependencies
+
:param archive: Target type archive
:param names: Names of desired types in type archive
:return: { name: (name, type) } Mapping from archive name to (analysis name, definition), None on error
@@ -8115,6 +8132,7 @@ class BinaryView:
-> Optional[Mapping[str, str]]:
"""
Pull types from a type archive by id, updating them and any dependencies
+
:param archive_id: Target type archive id
:param archive_type_ids: Ids of desired types in type archive
:return: { id: id } Mapping from archive type id to analysis type id, None on error
@@ -8142,6 +8160,7 @@ class BinaryView:
-> Optional[Mapping['_types.QualifiedName', Tuple['_types.QualifiedName', '_types.Type']]]:
"""
Push a collection of types, and all their dependencies, into a type archive
+
:param archive: Target type archive
:param names: Names of types in analysis
:return: { name: (name, type) } Mapping from analysis name to (archive name, definition), None on error
@@ -8166,6 +8185,7 @@ class BinaryView:
-> Optional[Mapping[str, str]]:
"""
Push a collection of types, and all their dependencies, into a type archive
+
:param archive_id: Id of target type archive
:param type_ids: Ids of types in analysis
:return: True if successful
diff --git a/python/debuginfo.py b/python/debuginfo.py
index b1d25503..806e61a8 100644
--- a/python/debuginfo.py
+++ b/python/debuginfo.py
@@ -314,6 +314,7 @@ class DebugInfo(object):
"""
Type Container for all types in the DebugInfo that resulted from the parse of
the given parser.
+
:param parser_name: Name of parser
:return: Type Container for types from that parser
"""
diff --git a/python/filemetadata.py b/python/filemetadata.py
index a5dda467..e0222fd2 100644
--- a/python/filemetadata.py
+++ b/python/filemetadata.py
@@ -120,17 +120,14 @@ class FileMetadata:
"""
``class FileMetadata`` represents the file being analyzed by Binary Ninja. It is responsible for opening,
closing, creating the database (.bndb) files, and is used to keep track of undoable actions.
+
+ :param str filename: The string path to the file to be opened. Defaults to None.
+ :param handle: A handle to the underlying C FileMetadata object. Defaults to None. (Internal use only.)
"""
_associated_data = {}
def __init__(self, filename: Optional[str] = None, handle: Optional[core.BNFileMetadataHandle] = None):
- """
- Instantiates a new FileMetadata class.
-
- :param str filename: The string path to the file to be opened. Defaults to None.
- :param handle: A handle to the underlying C FileMetadata object. Defaults to None.
- """
if handle is not None:
_type = core.BNFileMetadataHandle
_handle = ctypes.cast(handle, _type)
diff --git a/python/typearchive.py b/python/typearchive.py
index 26b15b5f..96e5378d 100644
--- a/python/typearchive.py
+++ b/python/typearchive.py
@@ -70,6 +70,7 @@ class TypeArchive:
def open(path: str) -> Optional['TypeArchive']:
"""
Open the Type Archive at the given path, if it exists.
+
:param path: Path to Type Archive file
:return: Type Archive, or None if it could not be loaded.
"""
@@ -82,6 +83,7 @@ class TypeArchive:
def create(path: str, platform: 'platform.Platform') -> Optional['TypeArchive']:
"""
Create a Type Archive at the given path.
+
:param path: Path to Type Archive file
:param platform: Relevant platform for types in the archive
:return: Type Archive, or None if it could not be created.
@@ -95,6 +97,7 @@ class TypeArchive:
def lookup_by_id(id: str) -> Optional['TypeArchive']:
"""
Get a reference to the Type Archive with the known id, if one exists.
+
:param id: Type Archive id
:return: Type archive, or None if it could not be found.
"""
@@ -107,6 +110,7 @@ class TypeArchive:
def path(self) -> Optional[str]:
"""
Get the path to the Type Archive's file
+
:return: File path
"""
return core.BNGetTypeArchivePath(self.handle)
@@ -115,6 +119,7 @@ class TypeArchive:
def id(self) -> Optional[str]:
"""
Get the guid for a Type Archive
+
:return: Guid string
"""
return core.BNGetTypeArchiveId(self.handle)
@@ -123,6 +128,7 @@ class TypeArchive:
def platform(self) -> 'platform.Platform':
"""
Get the associated Platform for a Type Archive
+
:return: Platform object
"""
handle = core.BNGetTypeArchivePlatform(self.handle)
@@ -133,6 +139,7 @@ class TypeArchive:
def current_snapshot_id(self) -> str:
"""
Get the id of the current snapshot in the type archive
+
:return: Snapshot id
"""
result = core.BNGetTypeArchiveCurrentSnapshotId(self.handle)
@@ -144,6 +151,7 @@ class TypeArchive:
def current_snapshot_id(self, value: str):
"""
Revert the type archive's current snapshot to the given snapshot
+
:param value: Snapshot id
"""
core.BNSetTypeArchiveCurrentSnapshot(self.handle, value)
@@ -152,6 +160,7 @@ class TypeArchive:
def all_snapshot_ids(self) -> List[str]:
"""
Get a list of every snapshot's id
+
:return: All ids (including the empty first snapshot)
"""
count = ctypes.c_ulonglong(0)
@@ -169,6 +178,7 @@ class TypeArchive:
def get_snapshot_parent_ids(self, snapshot: str) -> Optional[List[str]]:
"""
Get the ids of the parents to the given snapshot
+
:param snapshot: Child snapshot id
:return: Parent snapshot ids, or empty list if the snapshot is a root
"""
@@ -187,6 +197,7 @@ class TypeArchive:
def get_snapshot_child_ids(self, snapshot: str) -> Optional[List[str]]:
"""
Get the ids of the children to the given snapshot
+
:param snapshot: Parent snapshot id
:return: Child snapshot ids, or empty list if the snapshot is a leaf
"""
@@ -207,6 +218,7 @@ class TypeArchive:
Add named types to the type archive. Type must have all dependant named types added
prior to being added, or this function will fail.
If the type already exists, it will be overwritten.
+
:param name: Name of new type
:param type: Definition of new type
"""
@@ -217,6 +229,7 @@ class TypeArchive:
Add named types to the type archive. Types must have all dependant named
types prior to being added, or included in the list, or this function will fail.
Types already existing with any added names will be overwritten.
+
:param new_types: Names and definitions of new types
"""
api_types = (core.BNQualifiedNameAndType * len(new_types))()
@@ -237,6 +250,7 @@ class TypeArchive:
def rename_type(self, old_name: '_types.QualifiedNameType', new_name: '_types.QualifiedNameType') -> None:
"""
Change the name of an existing type in the type archive.
+
:param old_name: Old type name in archive
:param new_name: New type name
"""
@@ -246,6 +260,7 @@ class TypeArchive:
def rename_type_by_id(self, id: str, new_name: '_types.QualifiedNameType') -> None:
"""
Change the name of an existing type in the type archive.
+
:param id: Old id of type in archive
:param new_name: New type name
"""
@@ -257,6 +272,7 @@ class TypeArchive:
def delete_type(self, name: '_types.QualifiedNameType') -> None:
"""
Delete an existing type in the type archive.
+
:param name: Type name
"""
id = self.get_type_id(name)
@@ -267,6 +283,7 @@ class TypeArchive:
def delete_type_by_id(self, id: str) -> None:
"""
Delete an existing type in the type archive.
+
:param id: Type id
"""
if not core.BNDeleteTypeArchiveType(self.handle, id):
@@ -275,6 +292,7 @@ class TypeArchive:
def get_type_by_name(self, name: '_types.QualifiedNameType', snapshot: Optional[str] = None) -> Optional[_types.Type]:
"""
Retrieve a stored type in the archive
+
:param name: Type name
:param snapshot: Snapshot id to search for types, or None to search the latest snapshot
:return: Type, if it exists. Otherwise None
@@ -291,6 +309,7 @@ class TypeArchive:
def get_type_by_id(self, id: str, snapshot: Optional[str] = None) -> Optional[_types.Type]:
"""
Retrieve a stored type in the archive by id
+
:param id: Type id
:param snapshot: Snapshot id to search for types, or None to search the latest snapshot
:return: Type, if it exists. Otherwise None
@@ -306,6 +325,7 @@ class TypeArchive:
def get_type_name_by_id(self, id: str, snapshot: Optional[str] = None) -> Optional['_types.QualifiedName']:
"""
Retrieve a type's name by its id
+
:param id: Type id
:param snapshot: Snapshot id to search for types, or None to search the latest snapshot
:return: Type name, if it exists. Otherwise None
@@ -324,6 +344,7 @@ class TypeArchive:
def get_type_id(self, name: '_types.QualifiedNameType', snapshot: Optional[str] = None) -> Optional[str]:
"""
Retrieve a type's id by its name
+
:param name: Type name
:param snapshot: Snapshot id to search for types, or None to search the latest snapshot
:return: Type id, if it exists. Otherwise None
@@ -343,6 +364,7 @@ class TypeArchive:
def types(self) -> Dict[_types.QualifiedName, _types.Type]:
"""
Retrieve all stored types in the archive at the current snapshot
+
:return: Map of all types, by name
"""
return self.get_types()
@@ -351,6 +373,7 @@ class TypeArchive:
def types_and_ids(self) -> Dict[str, Tuple[_types.QualifiedName, _types.Type]]:
"""
Retrieve all stored types in the archive at the current snapshot
+
:return: Map of type id to type name and definition
"""
return self.get_types_and_ids()
@@ -358,6 +381,7 @@ class TypeArchive:
def get_types(self, snapshot: Optional[str] = None) -> Dict[_types.QualifiedName, _types.Type]:
"""
Retrieve all stored types in the archive at a snapshot
+
:param snapshot: Snapshot id to search for types, or None to search the latest snapshot
:return: Map of all types, by name
"""
@@ -369,6 +393,7 @@ class TypeArchive:
def get_types_and_ids(self, snapshot: Optional[str] = None) -> Dict[str, Tuple[_types.QualifiedName, _types.Type]]:
"""
Retrieve all stored types in the archive at a snapshot
+
:param snapshot: Snapshot id to search for types, or None to search the latest snapshot
:return: Map of type id to type name and definition
"""
@@ -391,6 +416,7 @@ class TypeArchive:
def type_ids(self) -> List[str]:
"""
Get a list of all types' ids in the archive at the current snapshot
+
:return: All type ids
"""
return self.get_type_ids()
@@ -398,6 +424,7 @@ class TypeArchive:
def get_type_ids(self, snapshot: Optional[str] = None) -> List[str]:
"""
Get a list of all types' ids in the archive at a snapshot
+
:param snapshot: Snapshot id to search for types, or None to search the latest snapshot
:return: All type ids
"""
@@ -425,6 +452,7 @@ class TypeArchive:
def get_type_names(self, snapshot: Optional[str] = None) -> List['_types.QualifiedName']:
"""
Get a list of all types' names in the archive at a snapshot
+
:param snapshot: Snapshot id to search for types, or None to search the latest snapshot
:return: All type names
"""
@@ -445,6 +473,7 @@ class TypeArchive:
def type_names_and_ids(self) -> Dict[str, '_types.QualifiedName']:
"""
Get a list of all types' names and ids in the archive at the current snapshot
+
:return: Mapping of all type ids to names
"""
return self.get_type_names_and_ids()
@@ -452,6 +481,7 @@ class TypeArchive:
def get_type_names_and_ids(self, snapshot: Optional[str] = None) -> Dict[str, '_types.QualifiedName']:
"""
Get a list of all types' names and ids in the archive at a current snapshot
+
:param snapshot: Snapshot id to search for types, or None to search the latest snapshot
:return: Mapping of all type ids to names
"""
@@ -475,6 +505,7 @@ class TypeArchive:
def get_outgoing_direct_references(self, id: str, snapshot: Optional[str] = None) -> List[str]:
"""
Get all types a given type references directly
+
:param id: Source type id
:param snapshot: Snapshot id to search for types, or empty string to search the latest snapshot
:return: Target type ids
@@ -497,6 +528,7 @@ class TypeArchive:
def get_outgoing_recursive_references(self, id: str, snapshot: Optional[str] = None) -> List[str]:
"""
Get all types a given type references, and any types that the referenced types reference
+
:param id: Source type id
:param snapshot: Snapshot id to search for types, or empty string to search the latest snapshot
:return: Target type ids
@@ -519,6 +551,7 @@ class TypeArchive:
def get_incoming_direct_references(self, id: str, snapshot: Optional[str] = None) -> List[str]:
"""
Get all types that reference a given type
+
:param id: Target type id
:param snapshot: Snapshot id to search for types, or empty string to search the latest snapshot
:return: Source type ids
@@ -541,6 +574,7 @@ class TypeArchive:
def get_incoming_recursive_references(self, id: str, snapshot: Optional[str] = None) -> List[str]:
"""
Get all types that reference a given type, and all types that reference them, recursively
+
:param id: Target type id
:param snapshot: Snapshot id to search for types, or empty string to search the latest snapshot
:return: Source type ids
@@ -563,6 +597,7 @@ class TypeArchive:
def query_metadata(self, key: str) -> Optional['metadata.MetadataValueType']:
"""
Look up a metadata entry in the archive
+
:param string key: key to query
:rtype: Metadata associated with the key, if it exists. Otherwise, None
:Example:
@@ -580,6 +615,7 @@ class TypeArchive:
def store_metadata(self, key: str, md: 'metadata.MetadataValueType') -> None:
"""
Store a key/value pair in the archive's metadata storage
+
:param string key: key value to associate the Metadata object with
:param Varies md: object to store.
:Example:
@@ -597,6 +633,7 @@ class TypeArchive:
def remove_metadata(self, key: str) -> None:
"""
Delete a given metadata entry in the archive
+
:param string key: key associated with metadata
:Example:
@@ -609,6 +646,7 @@ class TypeArchive:
def serialize_snapshot(self, snapshot: str) -> 'databuffer.DataBuffer':
"""
Turn a given snapshot into a data stream
+
:param snapshot: Snapshot id
:return: Buffer containing serialized snapshot data
"""
@@ -620,6 +658,7 @@ class TypeArchive:
def deserialize_snapshot(self, data: 'databuffer.DataBufferInputType') -> str:
"""
Take a serialized snapshot data stream and create a new snapshot from it
+
:param data: Snapshot data
:return: String of created snapshot id
"""
@@ -632,6 +671,7 @@ class TypeArchive:
def register_notification(self, notify: 'TypeArchiveNotification') -> None:
"""
Register a notification listener
+
:param notify: Object to receive notifications
"""
cb = TypeArchiveNotificationCallbacks(self, notify)
@@ -641,6 +681,7 @@ class TypeArchive:
def unregister_notification(self, notify: 'TypeArchiveNotification') -> None:
"""
Unregister a notification listener
+
:param notify: Object to no longer receive notifications
"""
if notify in self._notifications:
@@ -660,6 +701,7 @@ class TypeArchiveNotification:
def type_added(self, archive: 'TypeArchive', id: str, definition: '_types.Type') -> None:
"""
Called when a type is added to the archive
+
:param archive: Source Type archive
:param id: Id of type added
:param definition: Definition of type
@@ -669,6 +711,7 @@ class TypeArchiveNotification:
def type_updated(self, archive: 'TypeArchive', id: str, old_definition: '_types.Type', new_definition: '_types.Type') -> None:
"""
Called when a type in the archive is updated to a new definition
+
:param archive: Source Type archive
:param id: Id of type
:param old_definition: Previous definition
@@ -679,6 +722,7 @@ class TypeArchiveNotification:
def type_renamed(self, archive: 'TypeArchive', id: str, old_name: '_types.QualifiedName', new_name: '_types.QualifiedName') -> None:
"""
Called when a type in the archive is renamed
+
:param archive: Source Type archive
:param id: Type id
:param old_name: Previous name
@@ -689,6 +733,7 @@ class TypeArchiveNotification:
def type_deleted(self, archive: 'TypeArchive', id: str, definition: '_types.Type') -> None:
"""
Called when a type in the archive is deleted from the archive
+
:param archive: Source Type archive
:param id: Id of type deleted
:param definition: Definition of type deleted
diff --git a/python/typecontainer.py b/python/typecontainer.py
index cc1b3288..0b3221b8 100644
--- a/python/typecontainer.py
+++ b/python/typecontainer.py
@@ -37,10 +37,23 @@ class TypeContainer:
"""
A ``TypeContainer`` is a generic interface to access various Binary Ninja models
that contain types. Types are stored with both a unique id and a unique name.
+
+ The ``TypeContainer`` class should not generally be instantiated directly. Instances
+ can be retrieved from the following properties and methods in the API:
+
+ * :py:meth:`.BinaryView.type_container`
+ * :py:meth:`.BinaryView.auto_type_container`
+ * :py:meth:`.BinaryView.user_type_container`
+ * :py:meth:`.Platform.type_container`
+ * :py:meth:`.TypeLibrary.type_container`
+ * :py:meth:`.DebugInfo.get_type_container`
+
+ :param handle: Handle pointer (Internal use only.)
"""
def __init__(self, handle: core.BNTypeContainerHandle):
"""
Construct a Type Container, internal use only
+
:param handle: Handle pointer
"""
binaryninja._init_plugins()
@@ -147,6 +160,7 @@ class TypeContainer:
"""
Rename a type in the Type Container. All references to this type will be updated
(by id) to use the new name.
+
:param type_id: Id of type to update
:param new_name: New name for the type
:return: True if successful
@@ -157,6 +171,7 @@ class TypeContainer:
"""
Delete a type in the Type Container. Behavior of references to this type is
not specified and you may end up with broken references if any still exist.
+
:param type_id: Id of type to delete
:return: True if successful
"""
@@ -166,6 +181,7 @@ class TypeContainer:
"""
Get the unique id of the type in the Type Container with the given name.
If no type with that name exists, returns None.
+
:param type_name: Name of type
:return: Type id, if exists, else, None
"""
@@ -178,6 +194,7 @@ class TypeContainer:
"""
Get the unique name of the type in the Type Container with the given id.
If no type with that id exists, returns None.
+
:param type_id: Id of type
:return: Type name, if exists, else, None
"""
@@ -192,6 +209,7 @@ class TypeContainer:
"""
Get the definition of the type in the Type Container with the given id.
If no type with that id exists, returns None.
+
:param type_id: Id of type
:return: Type object, if exists, else, None
"""
@@ -232,6 +250,7 @@ class TypeContainer:
"""
Get the definition of the type in the Type Container with the given name.
If no type with that name exists, returns None.
+
:param type_name: Name of type
:return: Type object, if exists, else, None
"""