Inherits from

PyHasBlock, SourceFile, HasBlock, Usable, File, Expression, Importable, Editable, HasName

Properties


classes

Returns all Classes in the file.

Returns a list of all Classes defined in the file, sorted by position in the file. Use this method to iterate over all classes in a file or to get information about class definitions.

Returns: list[TClass]: A list of Class objects in the file, sorted by position in the file.

def classes(self) -> list[TClass]:
    ...

content

Returns the content of the file as a UTF-8 encoded string.

Gets the content of the file, either from pending changes or by reading from disk. Binary files cannot be read as strings.

Args: None

Returns: str: The content of the file as a UTF-8 encoded string.

Raises: ValueError: If the file is binary. Use content_bytes instead for binary files.

def content(self) -> str:
    ...

decorators

Returns a list of decorators associated with this symbol.

Retrieves all decorator nodes from the symbol’s parent TreeSitter node and converts them into PyDecorator objects.

Args: None

Returns: list[PyDecorator]: A list of PyDecorator objects representing the decorators on the symbol. Returns an empty list if the symbol is not decorated.

Note: This property should be used in conjunction with is_decorated to check if the symbol has any decorators.

def decorators(self) -> list[PyDecorator]:
    ...

dependencies

Returns a list of symbols that this symbol depends on.

Returns a list of symbols (including imports) that this symbol directly depends on. The returned list is sorted by file location for consistent ordering.

Returns: list[Union[Symbol, Import]]: A list of symbols and imports that this symbol directly depends on, sorted by file location.

def dependencies(self) -> list[Union["Symbol", "Import"]]:
    ...

directory

Returns the directory that contains this file.

The file can be housed within a directory in the codebase, and this property will return that directory instance.

Returns: Directory | None: The directory containing this file, or None if the file is not in any directory.

def directory(self) -> Directory | None:
    ...

docstring

Gets the function’s docstring.

Retrieves the docstring of the function as a PyCommentGroup object. If the function has no docstring, returns None.

Returns: PyCommentGroup | None: The docstring of the function as a PyCommentGroup, or None if no docstring exists.

def docstring(self) -> PyCommentGroup | None:
    ...

extended

Returns a SymbolGroup of all extended nodes associated with this element.

Creates a SymbolGroup that provides a common interface for editing all extended nodes, such as decorators, modifiers, and comments associated with the element.

Args: None

Returns: SymbolGroup: A group containing this node and its extended nodes that allows batch modification through a common interface.

def extended(self) -> SymbolGroup:
    ...

extended_source

Returns the source text representation of all extended nodes.

Gets the source text of all extended nodes combined. This property allows reading the source text of all extended nodes (e.g. decorators, export statements) associated with this node.

Returns: str: The combined source text of all extended nodes.

def extended_source(self) -> str:
    ...

extension

Returns the file extension.

Returns: str: The file extension including the dot (e.g., ‘.py’, ‘.ts’, ‘.js’).

def extension(self) -> str:
    ...

file

A property that returns the file object for non-source files.

This is used by Editable.file to work with non-source files, allowing consistent interface usage across both source and non-source files.

Returns: Self: The current file object.

def file(self) -> Self:
    ...

filepath

Retrieves the file path of the file that this Editable instance belongs to.

Returns: str: The file path of the file.

def filepath(self) -> str:
    ...

full_name

Returns the full name of the object, including the namespace path.

Returns the complete qualified name of an object, including any parent class or namespace paths. For class methods, this returns the parent class’s full name followed by the method name. For chained attributes (e.g., ‘a.b’), this returns the full chained name.

Args: None

Returns: str | None: The complete qualified name of the object. Returns None if no name is available. For class methods, returns ‘ParentClass.method_name’. For chained attributes, returns the full chain (e.g., ‘a.b’). For simple names, returns just the name.

def full_name(self) -> str | None:
    ...

function_calls

Returns all function calls within the code block and its decorators.

Args: None

Returns: list[FunctionCall]: A sorted list of FunctionCall objects representing all function calls in the code block and its decorators. The list may contain duplicates.

def function_calls(self) -> list[FunctionCall]:
    ...

functions

Returns all Functions in the file.

Returns a list of all top-level functions defined in the file, sorted by their position in the file. Does not include nested functions (functions defined within other functions or classes).

Returns: list[TFunction]: A list of Function objects representing all top-level functions in the file.

def functions(self) -> list[TFunction]:
    ...

global_vars

Returns all GlobalVars in the file.

Retrieves all global variables (assignments) defined at the top level in the file, sorted by their position in the file.

Returns: list[TGlobalVar]: A list of global variable assignments, where each element is an Assignment representing a global variable.

def global_vars(self) -> list[TGlobalVar]:
    ...

import_module_name

Returns the module name that this file gets imported as.

Gets the module name for this file in the context of imports. This name is used when other files import this file, either directly or when importing symbols from this file.

Returns: str: The module name used when importing this file.

def import_module_name(self) -> str:
    ...

import_statements

Returns all ImportStatements in the file, where each import statement can contain multiple imports.

Retrieves a list of all import statements in the file, sorted by their position. Each ImportStatement can contain multiple individual imports (e.g., ‘from module import a, b, c’).

Returns: list[ImportStatement]: A sorted list of import statements contained in the file.

def import_statements(self) -> list[ImportStatement]:
    ...

importers

Returns all imports that directly imports this file as a module.

This method returns a list of imports where this file is imported directly as a module, not individual symbols from this file.

For example:

  • from a import <this file> will be included
  • from <this file> import a will NOT be included

Args: None

Returns: list[TImport]: List of Import objects that import this file as a module, sorted by file location.

def importers(self) -> list[TImport]:
    ...

imports

List of all Imports in this file.

Retrieves all imports defined in this file. The imports are sorted by their position in the file.

Returns: list[TImport]: A list of Import instances contained in this file, ordered by their position.

def imports(self) -> list[TImport]:
    ...

inbound_imports

Returns all imports that are importing symbols contained in this file.

Retrieves a list of Import objects representing imports that reference symbols or content defined in this file. This includes imports of symbols declared in the file and imports of the file itself.

Returns: list[TImport]: A list of Import objects that reference content from this file.

def inbound_imports(self) -> list[TImport]:
    ...

is_binary

Indicates whether the file contains binary data.

A property that returns True if the file contains binary data, False if it contains text data.

Returns: bool: True if the file contains binary data, False if it contains text data.

def is_binary(self) -> bool:
    ...

is_decorated

Returns whether the symbol is decorated with decorators.

Checks if the symbol has a parent and if that parent’s type is a decorated definition.

Returns: bool: True if the symbol has decorators, False otherwise.

def is_decorated(self) -> bool:
    ...

name

Retrieves the name of the object excluding any namespace prefixes.

Returns the “base” name of the object without any namespace or module prefix. For instance, for an object ‘a.b’, this method returns ‘b’.

Returns: str | None: The base name of the object as a string, or None if there is no associated name node.

def name(self) -> str | None:
    ...

owners

Returns the CODEOWNERS of the file.

Returns all Github CODEOWNERS associated with this file. If there is no CODEOWNERS file in the codebase, returns an empty set.

Returns: set[str]: A set of Github usernames or team names that own this file. Empty if no CODEOWNERS file exists.

def owners(self) -> set[str]:
    ...

resolved_value

Returns the resolved type of an Expression.

Returns the inferred type of the expression. For example a function call’s resolved value will be it’s definition.

Returns: Union[Expression, list[Expression]]: The resolved expression type(s). Returns a single Expression if there is only one resolved type, or a list of Expressions if there are multiple resolved types. Returns self if the expression is not resolvable or has no resolved types.

def resolved_value(self) -> Expression | list[Expression]:
    ...

source

Text representation of the Editable instance.

Returns the source text of the Editable instance. This is the main property used to access the text content of any code element in GraphSitter.

Returns: str: The text content of this Editable instance.

def source(self) -> str:
    ...

start_byte

Returns the starting byte position of a file in its content.

The start byte is always 0 for a file as it represents the beginning of the file’s content.

Returns: int: Always returns 0.

def start_byte(self) -> int:
    ...

symbols_sorted_topologically

Returns all Symbols in the file, sorted topologically (parents first). Robust to dependency loops.

Performs a topological sort of the symbols in the file based on symbol dependencies. This ensures that parent symbols appear before their dependents while handling potential dependency loops gracefully.

Args: None

Returns: list[Symbol]: A list of symbols sorted topologically with parents appearing before their dependents.

def symbols_sorted_topologically(self) -> list[Symbol]:
    ...

variable_usages

Returns Editables for all TreeSitter node instances of variable usages within this node’s scope.

This method finds all variable identifier nodes in the TreeSitter AST, excluding:

  • Function names in function calls
  • Import names in import statements
  • Property access identifiers (except the base object)
  • Keyword argument names (in Python and TypeScript)

This is useful for variable renaming and usage analysis within a scope.

Returns: list[Editable]: A list of Editable nodes representing variable usages. Each Editable corresponds to a TreeSitter node instance where the variable is referenced.

def variable_usages(self) -> list[Editable]:
    ...

Methods


add_decorator

Adds a decorator to a function or method.

Adds a new decorator to the symbol’s definition. The decorator is inserted before the first non-comment extended node with proper indentation.

Args: new_decorator (str): The decorator to add. Should be a complete decorator string including the ’@’ symbol. skip_if_exists (bool, optional): If True, will not add the decorator if it already exists. Defaults to False.

Returns: bool: True if the decorator was added, False if skipped due to existing decorator.

def add_decorator(self, new_decorator: str, skip_if_exists: bool = False) -> bool:
    ...

add_import_from_import_string

Adds an import statement to the file from a string representation.

This method adds a new import statement to the file, handling placement based on existing imports. Future imports are placed at the top of the file, followed by regular imports.

Args: import_string (str): The string representation of the import statement to add (e.g., ‘from module import symbol’).

Returns: None: This function modifies the file in place.

def add_import_from_import_string(self, import_string: str) -> None:
    ...

add_symbol

Adds symbol to the file.

Adds the given symbol to the file, optionally exporting it if applicable. If the symbol already exists in the file, returns the existing symbol.

Args: symbol (Symbol): The symbol to add to the file. should_export (bool, optional): Whether to export the symbol. Defaults to True.

Returns: Symbol: The added symbol, or the existing symbol if it already exists in the file.

Raises: ValueError: If the symbol type cannot be added to this file type.

def add_symbol(self, symbol: Symbol, should_export: bool = True):
    ...

add_symbol_from_source

Adds a symbol to a file from a string representation.

This method adds a new symbol definition to the file by appending its source code string. The symbol will be added after existing symbols if present, otherwise at the beginning of the file.

Args: source (str): String representation of the symbol to be added. This should be valid source code for the file’s programming language.

Returns: None: The symbol is added directly to the file’s content.

def add_symbol_from_source(self, source: str) -> None:
    ...

add_symbol_import

Adds an import to a file for a given symbol.

This method adds an import statement to the file for a specified symbol. If an import for the symbol already exists, it returns the existing import instead of creating a new one.

Args: symbol (Symbol): The symbol to import. alias (str | None): Optional alias for the imported symbol. Defaults to None. import_type (ImportType): The type of import to use. Defaults to ImportType.UNKNOWN. is_type_import (bool): Whether this is a type-only import. Defaults to False.

Returns: Import: The created or existing import for the symbol.

def add_symbol_import(
        self,
        symbol: Symbol,
        alias: str | None = None,
        import_type: ImportType = ImportType.UNKNOWN,
        is_type_import: bool = False,
    ):
    ...

edit

Replace the source of this Editable with new_src.

Replaces the text representation of this Editable instance with new text content. The method handles indentation adjustments and transaction management.

Args: new_src (str): The new source text to replace the current text with. fix_indentation (bool): If True, adjusts the indentation of new_src to match the current text’s indentation level. Defaults to False. priority (int): The priority of the edit transaction. Higher priority edits are applied first. Defaults to 0. dedupe (bool): If True, deduplicates identical transactions. Defaults to True.

Returns: None

def edit(self, new_src: str, fix_indentation: bool = False, priority: int = 0, dedupe: bool = True) -> None:
    ...

find

Find and return matching nodes or substrings within an Editable instance.

This method searches through the extended_nodes of the Editable instance and returns all nodes or substrings that match the given search criteria.

Args: strings_to_match (Union[list[str], str]): One or more strings to search for. exact (bool): If True, only return nodes whose source exactly matches one of the strings_to_match. If False, return nodes that contain any of the strings_to_match as substrings. Defaults to False.

Returns: list[Editable]: A list of Editable instances that match the search criteria.

def find(self, strings_to_match: list[str] | str, *, exact: bool = False) -> list[Editable]:
    ...

find_by_byte_range

Finds all editable objects that overlap with the given byte range in the file.

Uses the file’s range index to efficiently retrieve all editable objects (like functions, classes, variables) that intersect with the specified byte range.

Args: range (Range): The byte range to search within the file.

Returns: list[Editable]: A list of all Editable objects that overlap with the given range.

def find_by_byte_range(self, range: Range) -> list[Editable]:
    ...

find_string_literals

Returns a list of string literals within this node’s source that match any of the given strings.

Args: strings_to_match (list[str]): A list of strings to search for in string literals. fuzzy_match (bool): If True, matches substrings within string literals. If False, only matches exact strings. Defaults to False.

Returns: list[Editable]: A list of Editable objects representing the matching string literals.

def find_string_literals(self, strings_to_match: list[str], fuzzy_match: bool = False) -> list[Editable]:
    ...

flag

Adds a visual flag comment to the end of this Editable’s source text.

Flags this Editable by appending a comment with emoji flags at the end of its source text. This is useful for visually highlighting specific nodes in the source code during development and debugging.

Returns: None

def flag(self, **kwargs: Unpack[FlagKwargs]) -> CodeFlag[Self]:
    ...

get_class

Returns a specific Class by full name. Returns None if not found.

Searches for a class in the file with the specified name. Similar to get_symbol, but specifically for Class types.

Args: name (str): The full name of the class to search for.

Returns: TClass | None: The matching Class object if found, None otherwise.

def get_class(self, name: str) -> TClass | None:
    ...

get_extensions

Returns the file extensions associated with Python files.

Gets the list of file extensions that are considered Python files.

Returns: list[str]: A list containing ‘.py’ as the only Python file extension.

def get_extensions() -> list[str]:
    ...

get_function

Returns a specific Function by name.

Gets a Function object from the file by searching for a function with the given name.

Args: name (str): The name of the function to find.

Returns: TFunction | None: The matching Function object if found, None otherwise.

def get_function(self, name: str) -> TFunction | None:
    ...

get_global_var

Returns a specific global var by name. Returns None if not found.

Args: name (str): The name of the global variable to find.

Returns: TGlobalVar | None: The global variable if found, None otherwise.

def get_global_var(self, name: str) -> TGlobalVar | None:
    ...

get_import

Returns the import with matching alias. Returns None if not found.

Args: symbol_alias (str): The alias name to search for. This can match either the direct import name or the aliased name.

Returns: TImport | None: The import statement with the matching alias if found, None otherwise.

def get_import(self, symbol_alias: str) -> TImport | None:
    ...

get_import_insert_index

Determines the index position where a new import statement should be inserted in a Python file.

The function determines the optimal position for inserting a new import statement, following Python’s import ordering conventions. Future imports are placed at the top of the file, followed by all other imports.

Args: import_string (str): The import statement to be inserted.

Returns: int | None: The index where the import should be inserted. Returns 0 for future imports or if there are no existing imports after future imports. Returns None if there are no imports in the file.

def get_import_insert_index(self, import_string) -> int | None:
    ...

get_import_string

Generates an import string for a symbol.

Constructs a Python import statement based on the provided parameters, handling different import types and module paths.

Args: alias (str | None, optional): Alias to use for the imported symbol. Defaults to None. module (str | None, optional): Module path to import from. If None, uses module name from source. Defaults to None. import_type (ImportType, optional): Type of import statement to generate. Defaults to ImportType.UNKNOWN. is_type_import (bool, optional): Whether this is a type import. Currently unused. Defaults to False.

Returns: str: A formatted import string in the form of ‘from import ’ with optional alias or wildcard syntax.

def get_import_string(self, alias: str | None = None, module: str | None = None, import_type: ImportType = ImportType.UNKNOWN, is_type_import: bool = False) -> str:
    ...

get_name

Returns the Name node of the object.

Retrieves the name node of the object which can be used for modification operations.

Returns: Name | ChainedAttribute | None: The name node of the object. Can be a Name node for simple names, a ChainedAttribute for names with namespaces (e.g., a.b), or None if the object has no name.

def get_name(self) -> Name | ChainedAttribute | None:
    ...

get_symbol

Gets a symbol by its name from the file.

Attempts to resolve the symbol by name using name resolution rules first. If that fails, searches through the file’s symbols list for a direct name match.

Args: name (str): The name of the symbol to find.

Returns: Symbol | None: The found symbol, or None if not found.

def get_symbol(self, name: str) -> Symbol | None:
    ...

get_variable_usages

Returns Editables for all TreeSitter nodes corresponding to instances of variable usage that matches the given variable name.

Retrieves a list of variable usages that match a specified name, with an option for fuzzy matching. By default, excludes property identifiers and argument keywords.

Args: var_name (str): The variable name to search for. fuzzy_match (bool): If True, matches variables where var_name is a substring. If False, requires exact match. Defaults to False.

Returns: list[Editable]: List of Editable objects representing variable usage nodes matching the given name.

def get_variable_usages(self, var_name: str, fuzzy_match: bool = False) -> list[Editable]:
    ...

has_import

Returns True if the file has an import with the given alias.

Checks if the file contains an import statement with a specific alias.

Args: symbol_alias (str): The alias to check for in the import statements.

Returns: bool: True if an import with the given alias exists, False otherwise.

def has_import(self, symbol_alias: str) -> bool:
    ...

insert_after

Inserts code after this node.

Args: new_src (str): The source code to insert after this node. fix_indentation (bool, optional): Whether to adjust the indentation of new_src to match the current node. Defaults to False. newline (bool, optional): Whether to add a newline before the new_src. Defaults to True. priority (int, optional): Priority of the insertion transaction. Defaults to 0. dedupe (bool, optional): Whether to deduplicate identical transactions. Defaults to True.

Returns: None

def insert_after(self, new_src: str, fix_indentation: bool = False, newline: bool = True, priority: int = 0, dedupe: bool = True) -> None:
    ...

insert_before

Inserts text before this node’s source with optional indentation and newline handling.

This method inserts the provided text before the current node’s source code. It can automatically handle indentation and newline placement.

Args: new_src (str): The text to insert before this node. fix_indentation (bool): Whether to fix the indentation of new_src to match the current node. Defaults to False. newline (bool): Whether to add a newline after new_src. Defaults to True. priority (int): Transaction priority for managing multiple edits. Defaults to 0. dedupe (bool): Whether to deduplicate identical transactions. Defaults to True.

Returns: None

def insert_before(self, new_src: str, fix_indentation: bool = False, newline: bool = True, priority: int = 0, dedupe: bool = True) -> None:
    ...

is_wrapped_in

Check if this node is contained another node of the given class

def is_wrapped_in(self, cls: type[Expression]) -> bool:
    ...

json

def json(self, max_depth: int = 2, methods: bool = True) -> JSON:
    ...

reduce_condition

Reduces an editable to the following condition

def reduce_condition(self, bool_condition: bool, node: Editable | None = None) -> None:
    ...

remove

Removes the file from the file system and graph.

Queues the file to be removed during the next commit operation. The file will be removed from the filesystem and its node will be removed from the graph.

Args: None

Returns: None

def remove(self) -> None:
    ...

rename

Renames the symbol and all its references in the codebase.

Renames a symbol to a new name and updates all references to that symbol throughout the codebase, including imports and call sites.

Args: new_name (str): The new name for the symbol. priority (int): Priority of the edit operation. Defaults to 0.

Returns: tuple[NodeId, NodeId]: A tuple containing the file node ID and the new node ID of the renamed symbol.

def rename(self, new_name: str, priority: int = 0):
    ...

replace

Search and replace occurrences of text within this node’s source and its extended nodes.

This method performs string replacement similar to Python’s string.replace(), with support for regex patterns. It operates on both the main node and any extended nodes (e.g. decorators, exports).

Args: old (str): The text or pattern to search for. new (str): The text to replace matches with. count (int, optional): Maximum number of replacements to make. Defaults to -1 (replace all). is_regex (bool, optional): Whether to treat ‘old’ as a regex pattern. Defaults to False. priority (int, optional): Priority of the replacement operation. Defaults to 0.

Returns: int: The total number of replacements made.

Raises: ValueError: If there are multiple occurrences of the substring in a node’s source.

def replace(self, old: str, new: str, count: int = -1, is_regex: bool = False, priority: int = 0) -> int:
    ...

Returns a list of all regex match of regex_pattern, similar to python’s re.search().

Searches for matches of a regular expression pattern within the text of this node and its extended nodes.

Args: regex_pattern (str): The regular expression pattern to search for. include_strings (bool): When False, excludes the contents of string literals from the search. Defaults to True. include_comments (bool): When False, excludes the contents of comments from the search. Defaults to True.

Returns: list[Editable]: A list of Editable objects corresponding to the matches found.

def search(self, regex_pattern: str, include_strings: bool = True, include_comments: bool = True) -> list[Editable]:
    ...

set_docstring

Sets or updates a docstring for a Python function or class.

Updates the existing docstring if one exists, otherwise creates a new docstring. The docstring can be automatically formatted and cleaned before being set.

Args: docstring (str): The docstring content to set. auto_format (bool, optional): Whether to format the text into a proper docstring format. Defaults to True. clean_format (bool, optional): Whether to clean and normalize the docstring format before insertion. Defaults to True. force_multiline (bool, optional): Whether to force single-line comments to be converted to multi-line format. Defaults to False.

Returns: None

def set_docstring(self, docstring: str, auto_format: bool = True, clean_format: bool = True, force_multiline: bool = False) -> None:
    ...

set_name

Sets the name of a code element.

Modifies the name of the object’s underlying name node. Works with both simple names and chained attributes (e.g., ‘a.b’).

Args: name (str): The new name to set for the object.

Returns: None

def set_name(self, name: str) -> None:
    ...

symbol_can_be_added

Checks if a Python symbol can be added to this Python source file.

Verifies whether a given Python symbol is compatible with and can be added to this Python source file. Currently always returns True as Python files can contain any Python symbol type.

Args: symbol (PySymbol): The Python symbol to check for compatibility with this file.

Returns: bool: Always returns True as Python files can contain any Python symbol type.

def symbol_can_be_added(self, symbol: PySymbol) -> bool:
    ...

symbol_usages

Returns a list of symbols that use the exportable object or import it. Returns symbols that use this exportable object, including imports that import this exportable object. By default, returns all usages. This shows where this symbol is imported, but not where it is subsequently used.

Args: usage_types: The types of usages to search for. Defaults to any.

  • DIRECT: Direct uses of the symbol
  • CHAINED: Uses through method/attribute chains
  • INDIRECT: Uses through renamed imports
  • ALIASED: Uses through aliases

Returns: list[Import | Symbol | Export]: A list of symbols that use this exportable object, including imports that import it.

Note: This method can be called as both a property or a method. If used as a property, it is equivalent to invoking it without arguments.

def symbol_usages(self, usage_types: UsageType | None = None) -> list[Import | Symbol | Export]:
    ...

symbols

Returns all Symbols in the file, sorted by position in the file.

Args: nested: Include nested symbols

Returns: list[Symbol | TClass | TFunction | TGlobalVar | TInterface]: A list of all top-level symbols in the file, sorted by their position in the file. Symbols can be one of the following types:

  • Symbol: Base symbol class
  • TClass: Class definition
  • TFunction: Function definition
  • TGlobalVar: Global variable assignment
  • TInterface: Interface definition
def symbols(self, nested: bool = False) -> list[Symbol | TClass | TFunction | TGlobalVar | TInterface]:
    ...

update_filepath

Renames the file and updates all imports to point to the new location.

When a file is renamed, this method does three things:

  1. Creates a new file node in the graph with the new filepath
  2. Moves the file on disk to the new location
  3. Updates all inbound imports to point to the new module location

Args: new_filepath (str): The new filepath to move the file to.

Returns: None

def update_filepath(self, new_filepath: str) -> None:
    ...

usages

Returns a list of usages of the exportable object.

Retrieves a list of all locations where the exportable object is used in the codebase. By default, returns all usages, such as imports or references within the same file.

Args: usage_types: Specifies which types of usages to include in the results. Default is any usages. (graph_sitter.core.dataclasses.usage.UsageType)

Returns: list[Usage]: A sorted list of Usage objects representing where this exportable is used, ordered by source location in reverse.

Raises: ValueError: If no usage types are specified or if only ALIASED and DIRECT types are specified together.

Note: This method can be called as both a property or a method. If used as a property, it is equivalent to invoking it without arguments.

def usages(self, usage_types: UsageType | None = None) -> list[Usage]:
    ...