Skip to content
iscai-msft edited this page Mar 12, 2021 · 27 revisions

Python Low-Level Client

Code Snippets

If you want to jump straight to sample code, go see our examples. In the following sections, we break down each section in the example code.

What is a Low-Level Client

Our low-level clients provide simple, reliable connection to raw HTTP for previews and advanced usage. We provide caller send_request on the client to send requests to the service. Calls through this method fully harness the power of azure-core and azure-identity. We provide both synchronous and asynchronous low-level clients.

The basic structure of calls with low-level clients is:

  1. Initialize your client
  2. Create a request
  3. Send the request
  4. Handle the response

We will go into each step in the following sections

1. Initialize Your Client

First you import your client from the namespace of your package. For example, let's say your namespace is azure.pets and your client's name is PetsClient. Your import would look like

from azure.pets import PetsClient

Most clients require authenticating through their credential parameter. Depending on what authentication support your library is using, you can either authenticate-with-aad or authenticate with an AzureKeyCredential.

Additionally, most of our clients accept an endpoint parameter at initialization, usually a link to your own resource.

Authenticating with AAD

Depending on your library, our clients support authenticating with an Azure Active Directory (AAD) token credential. We always recommend using a credential type obtained from the azure-identity library for AAD authentication. For this example, we use the most common DefaultAzureCredential.

As an installation note, the azure-identity library is not a dependency of this library. Please pip install azure-identity before using AAD authentication

pip install azure-identity

The following code snippet shows you how to authenticate with a DefaultAzureCredential.

from azure.identity import DefaultAzureCredential
from azure.pets import PetsClient

client = PetsClient(
    endpoint="https://myPets",
    credential=DefaultAzureCredential()
)

Authenticating with AzureKeyCredential

Some libraries support authenticating with an AzureKeyCredential. The following code snippet shows you how to authenticate with an AzureKeyCredential

from azure.core.credentials import AzureKeyCredential
from azure.pets import PetsClient

credential: str = "myCredential"
client = PetsClient(
    endpoint="https://myPets",
    credential=AzureKeyCredential(credential)
)

2. Create a Request

Next, you need to create the request you want to be sent to the service.

You have two options here: create the HttpRequest fully by yourself, or enlist the help of our request preparers.

When creating your HttpRequest, always refer to your service's REST API docs to see what they should look like.

Create Your Own HttpRequest

Creating an HttpRequest by yourself offers you the most flexibility, and is always a great fallback option.

One thing to note for the url you pass to your request: it can either by absolute, or you can pass in a relative url. Relative urls will be relative to the endpoint parameter you pass your client.

Below is a code example for how to create an HttpRequest. Here we POST a json body to a service's 3.0 endpoint.

from azure.core.protocol import HttpRequest

# Can use any of the following urls, the request will end up the same
# Relative urls are relative to the `endpoint` param you pass your client

absolute_url = "https://myPets/cutePets"
relative_url = "/cutePets"

request = HttpRequest("POST", relative_url,
    json={
        "pets": [
            {
                "name": "Fido",
                "age": 42,
                "favoriteFood": "meat"
            },
            {
                "name": "Bessie",
                "age": 1,
                "favoriteFood": "fish"
            }
        ]
    },
    params={"api-version": "v3.0"}
)

Use our Request Preparers

We also offer request preparers to help you create requests for each of the documented endpoints. These preparers are included in the protocol folder of our libraries. With the request preparers, we handle things like the URL path and formatting query and header parameters. We also provide better intellisense, so you know just what to pass to our preparers.

API versions are an important part of our request preparers, so they are always tied to our request preparers. With different API versions, the arguments to your method calls may also change, so please be aware of this behavior.

We allow two ways to assert the API version you would like when using our request preparers. You can see our following code snippets and choose which one works best for you.

Let's make the same call as in the previous example.

  1. Directly import the API version of the request preparers you want to use

    from azure.pets.protocol.v3_0 import prepare_cute_pets
    from azure.core.protocol import HttpRequest
    
    request: HttpRequest = prepare_cute_pets(
        body={
            "pets": [
                {
                    "name": "Fido",
                    "age": 42,
                    "favoriteFood": "meat"
                },
                {
                    "name": "Bessie",
                    "age": 1,
                    "favoriteFood": "fish"
                }
            ]
        },
    )
  2. Import our multi-API request preparers, and pass the API version through the kwarg api_version

    from azure.pets.protocol import prepare_cute_pets
    from azure.core.protocol import HttpRequest
    
    request: HttpRequest = prepare_cute_pets(
        body={
            "pets": [
                {
                    "name": "Fido",
                    "age": 42,
                    "favoriteFood": "meat"
                },
                {
                    "name": "Bessie",
                    "age": 1,
                    "favoriteFood": "fish"
                }
            ]
        },
        api_version="v3.0",
    )

3. Send the Request

Now, we pass this request to your client's send_request method.

from azure.pets import PetsClient
from azure.core.protocol import HttpResponse

# Create your request. See above examples for how to.

response: HttpResponse = client.send_request(request)

4. Handle the Response

Our send_request call returns an HttpResponse.

Error handling

The response you get back from send_request will not automatically raise if your response is an error. If you wish to raise an error if your response is bad, call .raise_for_status() on your returned response. This will raise an exception defined in azure-core if the response is bad. Otherwise, nothing will happen.

JSON response

If the response you get back should be a json object, you can call .json() on your response to get it json-deserialized.

Putting this all together, see our code snippets for how you can deal with your response object

response: HttpResponse = client.send_request(request)

response.raise_for_status()  # raises an error if your response is not good

json_response = response.json()  # get your response as a json object

# Now play with your JSON response!

Stream Downloading

We also offer a way to stream downloading using context managers. To stream, you have to pass stream_response=True to your send_request call and context manage the resultant stream to avoid unclosed sockets and memory leaks.

# iterate over the chunks in your stream
with client.send_request(request, stream_response=True) as response:
  response.raise_for_status()
  bytes = [d for d in response.iter_bytes()]

Async Clients

All of our low-level client libraries also include async clients. A lot of the code you write using sync and async clients will be the same (i.e. request preparation is completely sansio, so this portion of your code will look the same). This section will only include example code for the sections that are different

Initializing Your Async Client

Initializing and authenticating an async client is largely the same as doing so for a sync client, what changes is the import location. Async clients reside in the aio folder of the namespace.

Authenticating with Azure Active Directory (AAD) token credential also differs, as you will have to use the async version of each credential.

Below, we create an async client and authenticate with async DefaultAzureCredential

from azure.identity.aio import DefaultAzureCredential
from azure.pets.aio import PetsClient


client = PetsClient(
    endpoint="https://myPets",
    credential=DefaultAzureCredential()
)

Make sure to close your clients and credentials after using to avoid unclosed client sessions!

Async send_request

As you are making an async network call, you need to await the response of .send_request().

from azure.pets.aio import PetsClient
from azure.core.protocol import AsyncHttpResponse

# Create your request. See above examples for how to.

response: AsyncHttpResponse = await client.send_request(request)

Async Response Handling

The main difference between sync and async response handling is the need to first load the whole response so following sync methods can access the response's body.

response: AsyncHttpResponse = await client.send_request(request)

response.raise_for_status()  # raises an error if your response is not good

await response.load_body()  # loads the memory into the body, so the following sync methods can access it

json_response = response.json()  # get your response as a json object

# Now play with your JSON response!

Async Stream Downloading

Async stream downloading differs from sync stream downloading, as users have to use async context management and iteration to accommodate network calls.

# iterate over the chunks in your stream
async with client.send_request(request) as response:
  bytes = [d async for d in response.iter_bytes()]

Examples

See our code examples for the full end-to-end scenario of our low-level client using both raw requests and our request preparers.

Using Raw Requests

from azure.core.credentials import AzureKeyCredential
from azure.pets import PetsClient
from azure.core.protocol import HttpRequest, HttpResponse
from azure.core.exceptions import HttpResponseError

credential: str = "myCredential"
client = PetsClient(
    endpoint="https://myPets",
    credential=AzureKeyCredential(credential)
)

request = HttpRequest("POST", "/cutePets",
    json={
        "pets": [
            {
                "name": "Fido",
                "age": 42,
                "favoriteFood": "meat"
            },
            {
                "name": "Bessie",
                "age": 1,
                "favoriteFood": "fish"
            }
        ]
    },
    query={"api-version": "v3.0"}
)

response: HttpResponse = client.send_request(request)

try:
    response.raise_for_status()
    json_response = response.json()
    # Play with your response!
except HttpResponseError:
    print(f"Failed with status code {response.status_code}")
    print(json.dumps(response.json(), indent=True))

Using Our Request Preparers

from azure.core.credentials import AzureKeyCredential
from azure.pets import PetsClient
from azure.pets.protocol import prepare_cute_pets
from azure.core.protocol import HttpRequest, HttpResponse
from azure.core.exceptions import HttpResponseError

credential: str = "myCredential"
client = PetsClient(
    endpoint="https://myPets",
    credential=AzureKeyCredential(credential)
)

request: HttpRequest = prepare_cute_pets(
    body={
        "pets": [
            {
                "name": "Fido",
                "age": 42,
                "favoriteFood": "meat"
            },
            {
                "name": "Bessie",
                "age": 1,
                "favoriteFood": "fish"
            }
        ]
    },
    api_version="v3.0",
)

response: HttpResponse = client.send_request(request)

try:
    response.raise_for_status()
    json_response = response.json()
    # Play with your response!
except HttpResponseError:
    print(f"Failed with status code {response.status_code}")
    print(json.dumps(response.json(), indent=True))

Troubleshooting

Errors

All errors thrown by .raise_for_error() are exceptions defined in azure-core.

Logging

Our low-level clients also have logging support. They use the standard logging library for logging. Basic information about HTTP sessions (URLs, headers, etc.) is logged at INFO level.

Detailed DEBUG level logging, including request/response bodies and un-redacted headers, can be enabled on a client with the logging_enable keyword argument.

from azure.identity import DefaultAzureCredential
from azure.pets import PetsClient

client = PetsClient(
    endpoint="https://myPets",
    credential=DefaultAzureCredential(),
    logging_enable=True
)
Clone this wiki locally