# asyncio extending

**Futures in asyncio**

**What are Futures?**

Futures are like placeholders for values that will be available in the future. They are used to bridge the gap between code that runs asynchronously (in the background) and code that runs synchronously (in the foreground).

**How do Futures work?**

When you create a Future, you can specify a function that will be called when the value is available. This function is called a callback. The callback will be run as soon as the value is available, even if the event loop is busy doing other things.

**Why use Futures?**

Futures are useful because they allow you to write asynchronous code without having to deal with callbacks directly. This can make your code more readable and easier to maintain.

**Future Functions**

There are a number of functions that can be used to create and manage Futures. Here are a few of the most common:

* `asyncio.Future()` - creates a new Future
* `future.set_result(value)` - sets the value of the Future
* `future.set_exception(exception)` - sets an exception on the Future
* `future.add_done_callback(callback)` - adds a callback to the Future
* `future.result()` - blocks until the Future is done and returns its value
* `future.exception()` - blocks until the Future is done and returns its exception (if any)

**Real World Example**

Here is a simple example of how to use Futures:

```python
import asyncio

def get_data():
    # Make an asynchronous HTTP request to get some data
    data = await asyncio.get_data()
    return data

async def main():
    # Create a Future to hold the result of the HTTP request
    future = asyncio.Future()

    # Create a task to run the HTTP request
    task = asyncio.create_task(get_data())

    # Add a callback to the Future to print the result
    task.add_done_callback(lambda f: print(f.result()))

    # Wait for the Future to be done
    await future
```

In this example, the `get_data()` function is run asynchronously in a task. The task is added to the event loop, which will run it as soon as it is able. The `main()` function creates a Future to hold the result of the HTTP request. A callback is added to the Future to print the result. Finally, the `main()` function waits for the Future to be done.

**Potential Applications**

Futures can be used in a variety of applications, including:

* Asynchronous HTTP requests
* Background tasks
* Data fetching
* Error handling

***

**isfuture(obj)**

**Simplified Explanation:**

The `isfuture()` function checks if the given object is a future or task in Python's asyncio framework.

**Detailed Explanation:**

Futures and tasks are used in asyncio to represent the result of asynchronous operations. They are similar to promises in other programming languages.

* **Futures:** Represent the eventual result of an asynchronous operation. You can use them to wait for the result or get notified when it's available.
* **Tasks:** Futures that are managed by asyncio's event loop. They allow you to schedule and cancel asynchronous operations.

The `isfuture()` function checks if the given object is:

* An instance of the `asyncio.Future` class
* An instance of the `asyncio.Task` class
* An object with a `_asyncio_future_blocking` attribute, which is used by some third-party frameworks to implement future-like behavior.

**Code Snippet:**

```python
import asyncio

# Create a future
future = asyncio.Future()

# Check if it's a future
print(asyncio.isfuture(future))  # True

# Create a task
task = asyncio.create_task(future)

# Check if it's a future
print(asyncio.isfuture(task))  # True

# Check if it's a task
print(asyncio.istask(task))  # True
```

**Real-World Applications:**

* **Web servers:** To handle incoming HTTP requests asynchronously.
* **Data processing:** To schedule long-running tasks and process data efficiently.
* **Databases:** To execute database queries and fetch results asynchronously, improving performance.
* **Networking:** To manage multiple network connections and send or receive data without blocking the event loop.

***

#### ensure\_future function

The `ensure_future` function from the Python `asyncio` module ensures that the provided object is scheduled as a task to execute concurrently with other tasks in the event loop.

**Detailed Explanation**

* **Input:** The function takes an object, `obj`, as input, which can be:
  * A future object
  * A coroutine (a function that can be paused and resumed)
  * An awaitable object (an object that can be used in `await` expressions)
* **Output:** The function returns:
  * `obj` itself if it is already a Future, Task, or Future-like object.
  * A Task object wrapping `obj` if `obj` is a coroutine.
  * A Task object that will await on `obj` if `obj` is awaitable.

**Real-world Example**

Consider the following code:

```python
import asyncio

async def my_coroutine():
    await asyncio.sleep(1)
    print("Hello from my coroutine!")

loop = asyncio.get_event_loop()
task = asyncio.ensure_future(my_coroutine())
loop.run_until_complete(task)
```

In this example, `my_coroutine` is a coroutine. When we call `asyncio.ensure_future(my_coroutine())`, it creates a Task object that wraps the coroutine. The Task object is then scheduled to run by the event loop. The `loop.run_until_complete(task)` line waits for the Task to complete before exiting the main program.

**Potential Applications**

`ensure_future` is useful in situations where you need to:

* Schedule a coroutine to run concurrently with other tasks.
* Ensure that a future object is scheduled to run when it becomes ready.
* Avoid losing track of tasks that may complete at any time, ensuring you can wait for their completion.

**Improved Code Sample**

An improved version of the code sample above that uses the preferred `create_task` function instead of `ensure_future` is:

```python
import asyncio

async def my_coroutine():
    await asyncio.sleep(1)
    print("Hello from my coroutine!")

loop = asyncio.get_event_loop()
task = asyncio.create_task(my_coroutine())
loop.run_until_complete(task)
```

***

**asyncio-extending**

* **wrap\_future()** wraps a concurrent.futures.Future in an asyncio.Future.

**Simplified Explanation**

* **Concurrent.futures.Futures** are used in multi-threaded programs to represent the result of an asynchronous operation.
* **asyncio.Futures** are similar to concurrent.futures.Futures but are used in event-driven programs.
* `wrap_future()` allows you to use a concurrent.futures.Future in an asyncio program.

**Future Object**

* A Future is a placeholder for a result that is not yet available.
* When the result is available, the Future is set to the result.
* Other parts of the program can wait for the future to be set before continuing.

**Example**

```python
import asyncio

async def get_data():
    # Imagine this function makes an HTTP request and returns the response.
    return await asyncio.sleep(1, "Hello, world!")

async def main():
    # Get a concurrent.futures.Future
    future = asyncio.ensure_future(get_data())

    # Wrap the future in an asyncio.Future
    asyncio_future = asyncio.wrap_future(future)

    # Wait for the asyncio.Future to be set
    result = await asyncio_future

    # Print the result
    print(result)

# Run the main function
asyncio.run(main())
```

**Output**

```
Hello, world!
```

**Real-World Applications**

* **Web development:** You can use `wrap_future()` to integrate third-party libraries that use concurrent.futures.Futures into your asyncio-based web application.
* **Data processing:** You can use `wrap_future()` to process data in parallel using concurrent.futures.Futures and then wait for the results in your asyncio program.
* **System tasks:** You can use `wrap_future()` to run system tasks, such as reading files or making system calls, in an asyncio program.

***

**What is a Future?**

A Future is like a promise that an asynchronous operation will complete in the future. It represents the eventual result of that operation, whether it succeeds (has a result) or fails (has an exception).

**How do you use a Future?**

You can use a Future to await the result of an asynchronous operation. This means that you can pause your coroutine (a special kind of function that can be suspended and resumed) until the Future is ready.

Here's a simple example:

```python
import asyncio

async def my_async_function():
    result = await asyncio.sleep(1)  # Pause for 1 second
    return result

async def main():
    future = my_async_function()  # Create a Future
    result = await future  # Pause until the Future is ready
    print(result)  # Print the result

asyncio.run(main())
```

**When to use a Future?**

Futures are typically used to enable low-level callback-based code to interoperate with high-level async/await code. For example, you might use a Future if you have a protocol that uses callbacks to receive data.

**Potential applications of Futures in real world:**

* Web applications that need to handle multiple concurrent requests
* Asynchronous file I/O
* Networking protocols
* Data processing pipelines

**Code snippets or examples for each:**

* **Create a Future:**

```python
import asyncio

future = asyncio.Future()
```

* **Set the result of a Future:**

```python
future.set_result(42)
```

* **Set an exception on a Future:**

```python
future.set_exception(ValueError("This is an error"))
```

* **Wait for a Future to complete:**

```python
result = await future
```

* **Cancel a Future:**

```python
future.cancel()
```

* **Check if a Future has been cancelled:**

```python
if future.cancelled():
    print("Future was cancelled")
```

***

### Future.result() Method in asyncio-extending Module

The `result()` method of the `Future` class in the `asyncio-extending` module returns the result of the future.

#### Signature

```python
def result() -> Any:
```

#### Parameters

This method does not take any parameters.

#### Return Value

The result of the future. If the future has not yet completed, this method will block until the future completes. If the future has completed with an exception, this method will raise the exception.

#### Simplified Explanation

When working with asynchronous code in Python, we often use `Future` objects to represent the result of an operation that has not yet completed. The `result()` method allows us to retrieve the result of the future once it is available.

#### Code Snippet

```python
import asyncio

async def my_function():
    return 42

# Create a future object
future = asyncio.Future()

# Schedule the function to run in the event loop
asyncio.ensure_future(my_function(), loop=asyncio.get_event_loop())

# Wait for the future to complete
loop = asyncio.get_event_loop()
loop.run_until_complete(future)

# Get the result of the future
result = future.result()

# Print the result
print(result)  # Output: 42
```

#### Real-World Applications

The `result()` method is useful in any situation where you need to wait for the result of an asynchronous operation. For example, you could use it to:

* Wait for a network request to complete
* Wait for a database query to finish
* Wait for a file to be downloaded

#### Potential Applications

Here are some potential applications of the `result()` method:

* Asynchronous web applications: You can use the `result()` method to wait for the results of asynchronous database queries or network requests.
* Asynchronous data processing: You can use the `result()` method to wait for the results of asynchronous data processing tasks.
* Asynchronous file I/O: You can use the `result()` method to wait for the results of asynchronous file I/O operations.

***

**set\_result() Method**

**Simplified Explanation:**

The `set_result()` method is used to mark a `Future` object as completed and to specify the result of that future.

**Detailed Explanation:**

* **Future Object:** A future is a placeholder for a result that will become available in the future. It allows you to perform asynchronous operations (operations that don't block the program) and then access the result when it's ready.
* **Done:** A future is considered "done" when it has completed its operation and the result is available.
* **Result:** The result of a future is the value that it represents. This could be the output of a function, the data returned from an HTTP request, or any other value.

**Usage:**

To use the `set_result()` method, you first need to create a `Future` object. You can then use `set_result()` to mark the future as done and provide the result:

```python
import asyncio

async def my_async_function():
    # ... Do some asynchronous operations ...
    return "My result"

# Create a future
future = asyncio.Future()

# Run the async function in a separate task
asyncio.create_task(my_async_function())

# Wait for the future to complete
result = await future
print(result)  # Outputs "My result"
```

**Real-World Applications:**

* **Asynchronous I/O:** The `Future` object is commonly used for asynchronous I/O operations, such as HTTP requests, file reads, or database queries. It allows you to start these operations without blocking the program and then get the result when it's ready.
* **Multithreading:** `Future` objects can be used to create a thread pool and execute tasks concurrently. Each task can return a future, and the main thread can wait for all the futures to complete before continuing.

**Improved Code Example:**

Here's an improved version of the above code snippet:

```python
import asyncio

async def my_improved_async_function():
    # ... Do some asynchronous operations ...
    return "Improved result"

# Create a future
future = asyncio.Future()

# Run the async function in a separate coroutine
asyncio.create_task(my_improved_async_function(), name="my_task")

# Wait for the future to complete with a timeout of 10 seconds
try:
    result = await asyncio.wait_for(future, timeout=10)
    print(result)  # Outputs "Improved result"
except asyncio.TimeoutError:
    print("The async function timed out.")
```

In this example, we use `asyncio.wait_for()` to wait for the future to complete with a timeout of 10 seconds. If the future doesn't complete within that time, a `TimeoutError` will be raised.

***

**set\_exception() method in asyncio-extending**

**Explanation:**

The `set_exception()` method is used to prematurely end a `Future` (a way to wait for a result that may not be available yet) with an exception. This means that any code that is waiting for the `Future` to complete (e.g., using `Future.result()` or `Future.add_done_callback()`) will receive the exception instead of the normal result.

**Syntax:**

```python
def set_exception(exception) -> None
```

**Parameters:**

* `exception`: The exception to be set as the result of the `Future`.

**Exceptions:**

* `InvalidStateError`: Raised if the `Future` is already done.

**Usage:**

To use the `set_exception()` method, you first need to create a `Future` object. This can be done using the `asyncio.Future()` function. Once you have created the `Future`, you can set an exception on it using the `set_exception()` method:

```python
import asyncio

async def my_function():
    try:
        # Do something that may raise an exception.
    except Exception as e:
        # Create a Future object.
        future = asyncio.Future()
        # Set the exception on the Future.
        future.set_exception(e)

        # The `Future` can now be passed to other parts of your program,
        # which can use its `result()` method to get the exception.
```

**Real-World Applications:**

The `set_exception()` method can be used in any situation where you need to prematurely end a `Future` with an exception. For example, you could use it to handle errors that occur during the execution of a task.

**Improved Version or Examples:**

The following is an improved example of how to use the `set_exception()` method:

```python
import asyncio

async def my_function():
    try:
        # Do something that may raise an exception.
    except Exception as e:
        # Create a `Future` object.
        future = asyncio.Future()
        # Set the exception on the `Future`.
        future.set_exception(e)

        # Add a callback to the `Future` to handle the exception.
        future.add_done_callback(handle_exception)

        # Return the `Future`.
        return future

async def handle_exception(future):
    # Get the exception from the `Future`.
    exception = future.exception()

    # Handle the exception.
```

In this example, we create a callback function that handles the exception when the `Future` is completed. This allows us to handle the exception in a separate part of our program.

***

**Done() Method in asyncio-extending**

**Simplified Explanation:**

The `done()` method checks if a Future is complete, meaning it has either:

* Been canceled
* Received a result using `set_result()`
* Received an exception using `set_exception()`

**Detailed Explanation:**

A Future is an object that represents the result of an asynchronous operation, which may not be available immediately. The `done()` method lets you check if the Future has completed its operation and has a result or exception.

**Code Snippets:**

```python
import asyncio

# Create a Future and check if it's done initially (it's not)
future = asyncio.Future()
print(future.done())  # False

# Set a result for the Future
future.set_result("Done!")

# Check again if the Future is done (it should be now)
print(future.done())  # True
```

**Real-World Applications:**

* **Managing asynchronous tasks:** You can use `done()` to determine if an asynchronous task has completed, allowing you to take appropriate actions such as processing results or handling exceptions.
* **Creating timeouts:** You can set a timeout for a Future and use `done()` to check if it has completed within that timeframe. If the timeout is exceeded, you can handle the situation accordingly.

**Potential Applications:**

* **Data fetching:** Retrieve data from a server asynchronously and wait for the results to become available using `done()`.
* **File operations:** Perform file operations asynchronously and check when they are complete using `done()`.
* **Event handling:** Wait for specific events to occur asynchronously and be notified when they do using `done()`.

***

**Topic: Cancelling Futures**

**Simplified Explanation:**

Imagine you have a task running, but then you realize you don't need it anymore. You can cancel it, which means it will stop running.

**Detailed Explanation:**

A Future represents a task that might take some time to finish. When you have a Future, you can check if it has been cancelled using the `cancelled()` method. If it has, you should not do anything with it (like setting a result or an exception), because it means the task has been stopped.

**Code Snippet:**

```python
import asyncio

async def my_task():
    # Some code that takes a long time to run
    pass

# Create a Future for the task
fut = asyncio.Future()

# Start the task
asyncio.create_task(my_task())

# Check if the task has been cancelled before setting a result
if not fut.cancelled():
    fut.set_result(42)
```

**Real-World Applications:**

* Cancelling long-running tasks that are no longer needed
* Preventing unnecessary work from being done
* Gracefully handling user input (e.g., cancelling a file download when the user closes the window)

**Potential Applications:**

* User interfaces: Cancelling background tasks when the user closes a window or navigates away from a page.
* Data processing: Cancelling tasks that are no longer needed due to changes in input data.
* Network operations: Cancelling requests that are no longer relevant due to changes in network conditions.

***

#### What is asyncio?

Asyncio is a library for writing asynchronous code in Python. Asynchronous code allows you to write programs that can handle multiple tasks at the same time, even if those tasks are waiting for input or output from external sources. This makes asyncio very useful for writing server applications, network applications, and other I/O-intensive tasks.

#### What is a Future?

A Future is an object that represents the result of an asynchronous operation. When the operation is complete, the Future will be resolved with the result. You can use the `add_done_callback` method to add a callback function that will be called when the Future is resolved.

#### How to use the `add_done_callback` method

The `add_done_callback` method takes two arguments:

* `callback`: The callback function to be called when the Future is resolved.
* `context`: An optional keyword-only argument that allows you to specify a custom context for the callback to run in.

The following code snippet shows how to use the `add_done_callback` method:

```python
import asyncio

async def main():
    future = asyncio.Future()
    future.add_done_callback(lambda f: print(f.result()))
    await future
    future.set_result(42)

asyncio.run(main())
```

The above code will print the following output:

```
42
```

#### Real-world applications

The `add_done_callback` method can be used in a variety of real-world applications, such as:

* Writing server applications that handle multiple client requests at the same time.
* Writing network applications that communicate with multiple hosts at the same time.
* Writing I/O-intensive applications that need to process large amounts of data.

#### Potential applications

The `add_done_callback` method has a wide range of potential applications, including:

* Web servers: Asyncio can be used to write web servers that can handle multiple client requests at the same time. This can improve the performance of web applications by reducing the amount of time that clients spend waiting for responses.
* Network applications: Asyncio can be used to write network applications that can communicate with multiple hosts at the same time. This can improve the performance of network applications by reducing the amount of time that applications spend waiting for responses.
* I/O-intensive applications: Asyncio can be used to write I/O-intensive applications that need to process large amounts of data. This can improve the performance of I/O-intensive applications by reducing the amount of time that applications spend waiting for I/O operations to complete.

***

**Method: remove\_done\_callback(callback)**

**Simplified Explanation:**

Imagine you have a list of callbacks. Each callback is a function that you want to run when a certain event happens. The `remove_done_callback` method allows you to remove a specific callback from this list.

**Detailed Explanation:**

* **Callbacks:** Callbacks are functions that you can register with asyncio. When a specific event occurs (such as a task finishing), asyncio will automatically call these callbacks.
* **Remove Done Callback:** The `remove_done_callback` method takes a callback as an argument and removes it from the list of callbacks. It returns the number of callbacks that were removed. Typically, this will be 1, but it could be more if the callback was added multiple times.

**Real-World Example:**

Suppose you have a program that starts multiple tasks. You want to print a message when each task finishes. You can do this by registering a callback function with each task:

```python
import asyncio

def task_done_callback(task):
    print(f"Task {task} finished.")

async def main():
    task1 = asyncio.create_task(do_something())
    task2 = asyncio.create_task(do_something_else())

    task1.add_done_callback(task_done_callback)
    task2.add_done_callback(task_done_callback)

    await task1
    await task2

asyncio.run(main())
```

Output:

```
Task <Task pending name='Task-1' coro=<do_something() running at do_something.py:15>> finished.
Task <Task pending name='Task-2' coro=<do_something_else() running at do_something_else.py:15>> finished.
```

Now, if you want to stop printing messages for one of the tasks, you can use the `remove_done_callback` method:

```python
task1.remove_done_callback(task_done_callback)
```

After this, the message for `task1` will no longer be printed when it finishes.

**Potential Applications:**

* Managing callbacks for specific events in your program.
* Unregistering callbacks when they are no longer needed to avoid unnecessary function calls.

***

### asyncio.Future.cancel() Method

**What is a Future?**

A Future is like a placeholder for a value that will become available in the future. It allows you to schedule actions to be taken when the value becomes available.

**What does `cancel()` do?**

The `cancel()` method cancels a Future. This means that the Future will never complete, even if the result becomes available. It also schedules any callbacks that were registered to be called when the Future completes.

**When to use `cancel()`?**

You might use `cancel()` if:

* You no longer need the result of the Future.
* The task that will produce the result is taking too long or has failed.

**How to use `cancel()`?**

To cancel a Future, simply call the `cancel()` method:

```python
import asyncio

future = asyncio.Future()
future.cancel()
```

If the Future is already completed or cancelled, `cancel()` will return `False`. Otherwise, it will return `True`.

**Real-world example**

Imagine you have a web scraping task that you want to cancel if it takes more than 10 seconds. You can use the following code:

```python
import asyncio

async def scrape_website(url):
    # Scrape the website
    ...

    return result

async def main():
    future = asyncio.Future()

    # Schedule the task to be cancelled after 10 seconds
    loop.call_later(10, future.cancel)

    try:
        result = await future
    except asyncio.CancelledError:
        print("The task was cancelled.")
    else:
        print("The task completed successfully.")

    # Cancel the task (in case it hasn't been cancelled already)
    future.cancel()

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
```

**Potential applications**

The `cancel()` method can be used in a variety of applications, including:

* Cancelling long-running tasks
* Cancelling tasks that have failed
* Cancelling tasks that are no longer needed

***

**Method: exception()**

**Explanation:**

Imagine you have a Future, which is like a box that will eventually contain a result or an exception. The exception() method allows you to check if an exception was raised inside that box.

**How it works:**

1. Check if the Future is done, meaning it has a result or exception.
2. If it's done:
   * If an exception was raised, return that exception.
   * If no exception was raised, return None.
3. If it's not done, it's still waiting for a result. In this case, you cannot check for an exception yet, so it raises an error.

**Code Example:**

Imagine you have a function that takes a while to run, and it might fail and raise an exception.

```python
async def do_something():
    try:
        # Do some work that might fail
        ...
    except Exception as e:
        return e
```

You can use exception() to check if an exception occurred after calling do\_something():

```python
from asyncio import Future

# Create a Future to store the result
result = Future()

# Run the function and store the result or exception in the Future
asyncio.create_task(do_something(), result=result)

# Wait for the Future to complete
await result

# Check if an exception was raised
exception = result.exception()

# Handle the exception or do something with the result
if exception:
    print(f"An exception occurred: {exception}")
else:
    print("No exception occurred.")
```

**Real-World Applications:**

* **Error handling:** Check for exceptions raised in asynchronous tasks to handle errors gracefully.
* **Concurrency:** Check if multiple tasks have finished successfully or with exceptions.

***

### asyncio.Future.get\_loop()

**Explanation:**

Imagine you have a task you want to complete, like making a phone call. You create a "Future" object that will hold the result of that task (the phone call). The "Event Loop" is like a manager that keeps track of all the tasks you're waiting for. When you create a Future object, it gets registered with the Event Loop.

The `get_loop()` method on the Future object allows you to access the Event Loop that is managing it. This can be useful for keeping track of the progress of the task or for canceling it if necessary.

**Code Snippet:**

```python
import asyncio

async def my_task():
    pass

future = asyncio.Future()
event_loop = future.get_loop()
```

**Real-World Application:**

In real-world applications, you might use `get_loop()` to check the status of a task or to cancel it if it's taking too long. For example, if you have a task that is fetching data from the internet, you could check the Event Loop to see if it's still running or if it has completed. If it's still running, you could decide to cancel it to prevent it from tying up resources.

### Creating and Scheduling Tasks with Futures

**Explanation:**

Sometimes, you might want to create a task that will run in the background while you wait for its result. You can do this by creating a Task object and scheduling it with the Event Loop. The Task object will be responsible for running the task, and the Future object will hold the result of the task.

The following code snippet shows how to create and schedule a Task:

```python
import asyncio

async def my_task():
    result = await some_async_operation()
    return result

event_loop = asyncio.get_event_loop()
task = event_loop.create_task(my_task())
```

In this example, the `my_task()` function is an async function that performs some asynchronous operation (like fetching data from the internet). The `create_task()` method creates a Task object and schedules it with the Event Loop. The Task will run in the background, and when it completes, it will set the result of the Future object.

**Real-World Application:**

In real-world applications, you might use tasks to perform long-running operations that don't need to be synchronous. For example, you could use a task to fetch data from the internet in the background while you continue to work on other things. When the data is fetched, the task will set the result of the Future object, and you can continue to work with the data.

### Waiting for the Result of a Future

**Explanation:**

Once you have created a Future object, you can wait for its result. To do this, you can call the `await` keyword on the Future object. The `await` keyword will pause the execution of the current coroutine until the Future object has a result.

The following code snippet shows how to wait for the result of a Future:

```python
import asyncio

async def my_task():
    result = await some_async_operation()
    return result

event_loop = asyncio.get_event_loop()
task = event_loop.create_task(my_task())

result = await task
```

In this example, the `my_task()` function is an async function that performs some asynchronous operation (like fetching data from the internet). The `await` keyword pauses the execution of the current coroutine until the Future object has a result. Once the result is available, the `await` keyword resumes the execution of the coroutine and returns the result.

**Real-World Application:**

In real-world applications, you might use `await` to wait for the result of a task that is fetching data from the internet or performing some other long-running operation. Once the result is available, you can continue to work with the data.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://a7246c5516ab4c80cdfe21ca2be3e40c.gitbook.io/python-docs/asyncio-extending.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
