-
Notifications
You must be signed in to change notification settings - Fork 92
Description
PROBLEM
Query is a child to executable and a parent of queries in the hiero python sdk.
this means all queries use Query, which uses Executable.
Methods from executable are inherited to Query and that is inherited to the query request being done
Query is a really important file but we lack explanation of what it does and offers
SOLUTION
create docs/sdk_developers/training/query.md
based from:
src/hiero_sdk_python/query/query.py
Extra bonus points for examples created at examples/query
This requires a self starter with motivation to pull Query apart to understand it!
Some ideas:
The Query class is the base class for all Hedera network queries. Queries allow you to request data from the Hedera network, such as account balances, transaction information, or token info. Unlike transactions, queries do not change the network state, but some may require a payment to execute.
Query inherits from _Executable, meaning it benefits from the unified execution engine, including:
- Automatic retries
- Node selection and rotation
- gRPC network call orchestration
- Logging and error handling
- Child query classes implement query-specific behavior while _Executable handles the underlying execution mechanics.
Key Concepts
- Execution Flow
- When a Query is executed, the following steps happen:
- Pre-execution setup (_before_execute):
- Assigns nodes and operator
- Determines query payment if required
- Request building (_make_request):
- Constructs the Protobuf request for the network
- Includes payment transaction if required
- gRPC call (_get_method + _execute_method):
- Sends the request to a selected node
- Handles retries and backoff on temporary failures
- Response mapping (_map_response):
- Converts raw network response into a usable SDK object
- Retry handling (_should_retry):
- Automatically retries queries with retryable statuses like BUSY or PLATFORM_NOT_ACTIVE
- Error mapping (_map_status_error):
- Converts response errors into Python exceptions like PrecheckError or ReceiptStatusError
- Query Payment
Some queries require a payment. Payment is handled via a small CryptoTransfer transaction attached to the query request.
Use set_query_payment(amount: Hbar) to override the default payment
If no payment is set, the SDK can automatically query the cost using get_cost(client) before executing the query
from hiero_sdk_python.hbar import Hbar
query = AccountBalanceQuery(account_id)
query.set_query_payment(Hbar(1)) # Set custom payment to 1 Hbar
The SDK constructs a signed payment transaction using the operator’s private key.
- Abstract Methods
Subclasses must implement several methods:
| Method | Purpose |
|---|---|
| _get_query_response(response) | Extracts the specific query response from the full network response |
| _make_request() | Builds the Protobuf request for the specific query |
| _get_method(channel) | Returns the gRPC method wrapper (_Method) to call for this query |
| Method | Purpose |
|---|---|
| _map_response(response, node_id, proto_request) | Customize how the response is returned to the user |
| _should_retry(response) | Customize retry logic if needed |
| _map_status_error(response) | Customize error mapping |
- Retry Logic
Queries automatically handle retries for certain network issues:
Retryable statuses:
- BUSY
- PLATFORM_TRANSACTION_NOT_CREATED
- PLATFORM_NOT_ACTIVE
- The base _should_retry implementation handles these, but subclasses can extend it if necessary.
- Cost Queries
If a query requires payment but no amount is set, Query automatically fetches the cost from the network:
cost = query.get_cost(client)
print(f"Query cost: {cost} Hbar")
The SDK performs a COST_ANSWER query, returning the required Hbars to pay for the actual query.
- Building a Child Query
eg
from hiero_sdk_python.query.query import Query
from hiero_sdk_python.hapi.services import query_pb2, crypto_get_account_balance_pb2
class AccountBalanceQuery(Query):
def __init__(self, account_id):
super().__init__()
self.account_id = account_id
def _make_request(self):
header = self._make_request_header()
body = crypto_get_account_balance_pb2.CryptoGetAccountBalanceQuery(
header=header,
accountID=self.account_id._to_proto()
)
return query_pb2.Query(cryptoGetAccountBalance=body)
def _get_query_response(self, response):
return response.cryptoGetAccountBalance
def _get_method(self, channel):
return _Method(query_func=channel.crypto.get_account_balance)query = AccountBalanceQuery(my_account_id)
balance = query.execute(client)
print(f"Account balance: {balance.balance} Hbar")- Key Takeaways
Query handles all network-level logic; subclasses only implement query-specific behavior.
Queries may require payment, which is handled transparently via _build_query_payment_transaction.
Automatic retry and error handling ensures queries are robust against network issues.
Subclasses must implement:
_make_request()
_get_query_response()
_get_method()
You can override retry logic and response mapping if needed.