The API surface of your client library must have the most thought as it is the primary interaction that the consumer has with your service.

DO support 100% of the features provided by the Azure service the client library represents. Gaps in functionality cause confusion and frustration among developers.

Namespaces

DO implement your library as a subpackage in the azure namespace.

DO pick a package name that allows the consumer to tie the namespace to the service being used. As a default, use the compressed service name at the end of the namespace. The namespace does NOT change when the branding of the product changes. Avoid the use of marketing names that may change.

A compressed service name is the service name without spaces. It may further be shortened if the shortened version is well known in the community. For example, “Azure Media Analytics” would have a compressed service name of mediaanalytics, and “Azure Service Bus” would become servicebus. Separate words using an underscore if necessary. If used, mediaanalytics would become media_analytics

✔️ YOU MAY include a group name segment in your namespace (for example, azure.<group>.<servicename>) if your service or family of services have common behavior (for example, shared authentication types).

If you want to use a group name segment, use one of the following groups:

Namespace Group Functional Area
ai Artificial intelligence, including machine learning
analytics Gathering data for metrics or usage
data Dealing with structured data stores like databases
diagnostics Gathering data for diagnosing issues
digitaltwins Digital Twins, digital representations of physical spaces and IoT devices
identity Authentication and authorization
iot Internet of things
management Control Plane (Azure Resource Manager)
media Audio, video, or mixed reality
messaging Messaging services, like push notifications or pub-sub
search Search technologies
security Security and cryptography
storage Storage of unstructured data

DO place management (Azure Resource Manager) APIs in the mgmt group. Use the grouping azure.mgmt.<servicename> for the namespace. Since more services require control plane APIs than data plane APIs, other namespaces may be used explicitly for control plane only.

DO register the chosen namespace with the Architecture Board. Open an issue to request the namespace. See the registered namespace list for a list of the currently registered namespaces.

DO use an .aio suffix added to the namespace of the sync client for async clients.

Example:

# Yes:
from azure.exampleservice.aio import ExampleServiceClient

# No: Wrong namespace, wrong client name...
from azure.exampleservice import AsyncExampleServiceClient

Clients

Your API surface will consist of one or more service clients that the consumer will instantiate to connect to your service, plus a set of supporting types.

DO name service client types with a Client suffix.

# Yes
class CosmosClient(object) ... 

# No
class CosmosProxy(object) ... 

# No
class CosmosUrl(object) ... 

DO expose the service clients the user is more likely to interact with from the root namespace of your package.

Constructors and factory methods

DO provide a constructor that takes binding parameters (for example, the name of, or a URL pointing to the service instance), a credentials parameter, a transport parameter, and **kwargs for passing settings through to individual HTTP pipeline policies.

Only the minimal information needed to connect and interact with the service should be required. All additional information should be optional.

The constructor must not take a connection string.

DO use a separate factory method ExampleServiceClient.from_connection_string to create a client from a connection string (if the client supports connection strings).

The method should parse the connection string and pass the values to the constructor. Provide a from_connection_string factory method only if the Azure portal exposes a connection string for your service.

Async support

The asyncio library has been available since Python 3.4, and the async/await keywords were introduced in Python 3.5. Despite such availability, most Python developers aren’t familiar with or comfortable using libraries that only provide asynchronous methods.

DO provide both sync and async versions of your APIs

DO use the async/await keywords (requires Python 3.5+). Don’t use the yield from coroutine or asyncio.coroutine syntax.

DO provide two separate client classes for synchronous and asynchronous operations. Don’t combine async and sync operations in the same class.

# Yes
# In module azure.example
class ExampleClient(object):
    def some_service_operation(self, name, size) ...

# In module azure.example.aio
class ExampleClient:
    # Same method name as sync, different client
    async def some_service_operation(self, name, size) ... 

# No
# In module azure.example
class ExampleClient(object):
    def some_service_operation(self, name, size) ...

class AsyncExampleClient: # No async/async pre/postfix.
    async def some_service_operation(self, name, size) ...

# No
# In module azure.example
class ExampleClient(object): # Don't mix'n match with different method names
    def some_service_operation(self, name, size) ...
    async def some_service_operation_async(self, name, size) ...

DO use the same client name for sync and async packages

Example:

Sync/async Namespace Package name Client name
Sync azure.sampleservice azure-sampleservice azure.sampleservice.SampleServiceClient
Async azure.sampleservice.aio azure-sampleservice-aio azure.sampleservice.aio.SampleServiceClient

DO use the same namespace for the synchronous client as the synchronous version of the package with .aio appended.

☑️ YOU SHOULD ship a separate package for async support if the async version requires additional dependencies.

DO use the same name for the asynchronous version of the package as the synchronous version of the package with -aio appended.

DO use aiohttp as the default HTTP stack for async operations. Use azure.core.pipeline.transport.AioHttpTransport as the default transport type for the async client.

Hierarchical services

Many services have resources with nested child (or sub) resources. For example, Azure Storage provides an account that contains zero or more containers, which in turn contains zero or more blobs.

DO create a client type corresponding to each level in the hierarchy except for leaf resource types. You may omit creating a client type for leaf node resources.

DO make it possible to directly create clients for each level in the hierarchy. The constructor can be called directly or via the parent.

class ChildClient:
    # Yes:
    __init__(self, parent, name, credentials, **kwargs) ...

class ChildClient:
    # Yes:
    __init__(self, url, credentials, **kwargs) ...

DO provide a get_<child>_client(self, name, **kwargs) method to retrieve a client for the named child. The method must not make a network call to verify the existence of the child.

DO provide method create_<child>(...) that creates a child resource. The method should return a client for the newly created child resource.

☑️ YOU SHOULD provide method delete_<child>(...) that deletes a child resource.

Service operations

☑️ YOU SHOULD prefer the usage one of the preferred verbs for method names.

Verb Parameters Returns Comments
create_\<noun> key, item, [allow_overwrite=True] Created item Create new item. Fails if item already exists.
upsert_\<noun> key, item item Create new item, or update existing item. Verb is primarily used in database-like services
set_\<noun> key, item item Create new item, or update existing item. Verb is primarily used for dictionary-like properties of a service
update_\<noun> key, partial item item Fails if item doesn’t exist.
replace_\<noun> key, item item Completely replaces an existing item. Fails if the item doesn’t exist.
append_\<noun> item item Add item to a collection. Item will be added last.
add_\<noun> index, item item Add item to a collection. Item will be added at the given index.
get_\<noun> key item Raises an exception if item doesn’t exist
list_\<noun>   azure.core.Pageable[Item] Return an iterable of items. Returns iterable with no items if no items exist (doesn’t return None or throw)
\<noun>\_exists key bool Return True if the item exists. Must raise an exception if the method failed to determine if the item exists (for example, the service returned an HTTP 503 response)
delete_\<noun> key None Delete an existing item. Must succeed even if item didn’t exist.
remove_\<noun> key removed item or None Remove a reference to an item from a collection. This method doesn’t delete the actual item, only the reference.

DO standardize verb prefixes outside the list of preferred verbs for a given service across language SDKs. If a verb is called download in one language, we should avoid naming it fetch in another.

DO prefix methods with begin_ for long running operations. Long running operations must return a Poller object.

DO support the common arguments for service operations:

Name Description Applies to Notes  
timeout Timeout in seconds All service methods    
headers Custom headers to include in the service request All requests Headers are added to all requests made (directly or indirectly) by the method.  
continuation_token Opaque token indicating the first page to retrieve. Retrieved from a previous Paged return value. list operations.    
client_request_id Caller specified identification of the request. Service operations for services that allow the client to send a client-generated correlation ID. Examples of this include x-ms-client-request-id headers. The client library must use this value if provided, or generate a unique value for each request when not specified.
response_hook callable that is called with (response, headers) for each operation. All service methods    

DO accept a Mapping (dict-like) object in the same shape as a serialized model object for parameters.

# Yes:
class Model(object):

    def __init__(self, name, size):
        self.name = name
        self.size = size

def do_something(model: "Model"):
    ...

do_something(Model(name='a', size=17)) # Works
do_something({'name': 'a', 'size', '17'}) # Does the same thing...

DO use “flattened” named arguments for update_ methods. May additionally take the whole model instance as a named parameter. If the caller passes both a model instance and individual key=value parameters, the explicit key=value parameters override whatever was specified in the model instance.

class Model(object):

    def __init__(self, name, size, description):
        self.name = name
        self.size = size
        self.description = description

class Client(object):

    def update_model(self, name=None, size=None, model=None): ...

model = Model(name='hello', size=4711, description='This is a description...')

client.update_model(model=model, size=4712) # Will send a request to the service to update the model's size to 4712
model.description = 'Updated'
model.size = -1
# Will send a request to the service to update the model's size to 4713 and description to 'Updated'
client.update_model(name='hello', size=4713, model=model)  

Response formats

Requests to the service fall into two basic groups - methods that make a single logical request, or a deterministic sequence of requests. An example of a single logical request is a request that may be retried inside the operation. An example of a deterministic sequence of requests is a paged operation.

The logical entity is a protocol neutral representation of a response. For HTTP, the logical entity may combine data from headers, body, and the status line. For example, you may wish to expose an ETag header as a property on the logical entity.

DO optimize for returning the logical entity for a given request. The logical entity MUST represent the information needed in the 99%+ case.

DO return a value that implements the Paged protocol for operations that return collections. The Paged protocol allows the user to iterate through all items in a returned collection, and also provides a method that gives access to individual pages.

DO return a value that implements the Poller protocol for long running operations.

Models

DO implement __repr__ for model types. The representation must include the type name and any key properties (that is, properties that help identify the model instance).

DO truncate the output of __repr__ after 1024 characters.

Authentication

DO use the credentials classes in azure-core whenever possible.

✔️ YOU MAY add additional credential types if required by the service. Contact @adparch for guidance if you believe you have need to do so.

DO support all authentication methods that the service supports.