Skip to content

Commit

Permalink
[core] Make case insensitive dict public (#23206)
Browse files Browse the repository at this point in the history
  • Loading branch information
iscai-msft authored Mar 1, 2022
1 parent 3f0c457 commit a10c604
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 21 deletions.
4 changes: 3 additions & 1 deletion sdk/core/azure-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Release History

## 1.22.2 (Unreleased)
## 1.23.0 (Unreleased)

### Features Added

- Add a case insensitive dict `case_insensitive_dict` in `azure.core.utils`. #23206

### Breaking Changes

### Bugs Fixed
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/azure-core/azure/core/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
# regenerated.
# --------------------------------------------------------------------------

VERSION = "1.22.2"
VERSION = "1.23.0"
4 changes: 2 additions & 2 deletions sdk/core/azure-core/azure/core/pipeline/policies/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
# --------------------------------------------------------------------------
import datetime
import email.utils
from ...utils._utils import _FixedOffset, _case_insensitive_dict
from ...utils._utils import _FixedOffset, case_insensitive_dict

def _parse_http_date(text):
"""Parse a HTTP date format into datetime."""
Expand Down Expand Up @@ -57,7 +57,7 @@ def get_retry_after(response):
:return: Value of Retry-After in seconds.
:rtype: float or None
"""
headers = _case_insensitive_dict(response.http_response.headers)
headers = case_insensitive_dict(response.http_response.headers)
retry_after = headers.get("retry-after")
if retry_after:
return parse_retry_after(retry_after)
Expand Down
6 changes: 3 additions & 3 deletions sdk/core/azure-core/azure/core/pipeline/transport/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
ABC,
AbstractContextManager,
)
from ...utils._utils import _case_insensitive_dict
from ...utils._utils import case_insensitive_dict
from ...utils._pipeline_transport_rest_shared import (
_format_parameters_helper,
_prepare_multipart_body_helper,
Expand Down Expand Up @@ -170,7 +170,7 @@ def __init__(self, method, url, headers=None, files=None, data=None):
# type: (str, str, Mapping[str, str], Any, Any) -> None
self.method = method
self.url = url
self.headers = _case_insensitive_dict(headers)
self.headers = case_insensitive_dict(headers)
self.files = files
self.data = data
self.multipart_mixed_info = None # type: Optional[Tuple]
Expand Down Expand Up @@ -472,7 +472,7 @@ class _HttpClientTransportResponse(_HttpResponseBase):
def __init__(self, request, httpclient_response):
super(_HttpClientTransportResponse, self).__init__(request, httpclient_response)
self.status_code = httpclient_response.status
self.headers = _case_insensitive_dict(httpclient_response.getheaders())
self.headers = case_insensitive_dict(httpclient_response.getheaders())
self.reason = httpclient_response.reason
self.content_type = self.headers.get("Content-Type")
self.data = None
Expand Down
4 changes: 2 additions & 2 deletions sdk/core/azure-core/azure/core/rest/_http_response_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
HttpResponse as _HttpResponse,
HttpRequest as _HttpRequest
)
from ..utils._utils import _case_insensitive_dict
from ..utils._utils import case_insensitive_dict
from ..utils._pipeline_transport_rest_shared import (
_pad_attr_name,
BytesIOSocket,
Expand Down Expand Up @@ -438,7 +438,7 @@ class _RestHttpClientTransportResponseBase(_HttpResponseBaseImpl, _RestHttpClien

def __init__(self, **kwargs):
internal_response = kwargs.pop("internal_response")
headers = _case_insensitive_dict(internal_response.getheaders())
headers = case_insensitive_dict(internal_response.getheaders())
super(_RestHttpClientTransportResponseBase, self).__init__(
internal_response=internal_response,
status_code=internal_response.status,
Expand Down
4 changes: 2 additions & 2 deletions sdk/core/azure-core/azure/core/rest/_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

from typing import TYPE_CHECKING

from ..utils._utils import _case_insensitive_dict
from ..utils._utils import case_insensitive_dict
from ._helpers import (
set_content_body,
set_json_body,
Expand Down Expand Up @@ -111,7 +111,7 @@ def __init__(self, method, url, **kwargs):
files=kwargs.pop("files", None),
json=kwargs.pop("json", None),
)
self.headers = _case_insensitive_dict(default_headers)
self.headers = case_insensitive_dict(default_headers)
self.headers.update(kwargs.pop("headers", {}))

if kwargs:
Expand Down
4 changes: 2 additions & 2 deletions sdk/core/azure-core/azure/core/rest/_rest_py3.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
MutableMapping,
)

from ..utils._utils import _case_insensitive_dict
from ..utils._utils import case_insensitive_dict

from ._helpers import (
ParamsType,
Expand Down Expand Up @@ -113,7 +113,7 @@ def __init__(
files=files,
json=json,
)
self.headers = _case_insensitive_dict(default_headers)
self.headers = case_insensitive_dict(default_headers)
self.headers.update(headers or {})

if kwargs:
Expand Down
3 changes: 2 additions & 1 deletion sdk/core/azure-core/azure/core/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@
from ._connection_string_parser import (
parse_connection_string
)
from ._utils import case_insensitive_dict

__all__ = ["parse_connection_string"]
__all__ = ["parse_connection_string", "case_insensitive_dict"]
18 changes: 11 additions & 7 deletions sdk/core/azure-core/azure/core/utils/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# license information.
# --------------------------------------------------------------------------
import datetime
from typing import Any, MutableMapping


class _FixedOffset(datetime.tzinfo):
Expand Down Expand Up @@ -80,15 +81,18 @@ def _convert_to_isoformat(date_time):
deserialized = deserialized.replace(tzinfo=tzinfo)
return deserialized

def _case_insensitive_dict(*args, **kwargs):
"""Return a case-insensitive dict from a structure that a dict would have accepted.
def case_insensitive_dict(*args: Any, **kwargs: Any) -> MutableMapping:
"""Return a case-insensitive mutable mapping from an inputted mapping structure.
Rational is I don't want to re-implement this, but I don't want
to assume "requests" or "aiohttp" are installed either.
So I use the one from "requests" or the one from "aiohttp" ("multidict")
If one day this library is used in an HTTP context without "requests" nor "aiohttp" installed,
we can add "multidict" as a dependency or re-implement our own.
:return: A case-insensitive mutable mapping object.
:rtype: ~collections.abc.MutableMapping
"""

# Rational is I don't want to re-implement this, but I don't want
# to assume "requests" or "aiohttp" are installed either.
# So I use the one from "requests" or the one from "aiohttp" ("multidict")
# If one day this library is used in an HTTP context without "requests" nor "aiohttp" installed,
# we can add "multidict" as a dependency or re-implement our own.
try:
from requests.structures import CaseInsensitiveDict

Expand Down
8 changes: 8 additions & 0 deletions sdk/core/azure-core/doc/azure.core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,11 @@ azure.core.rest
:undoc-members:
:inherited-members:

azure.core.utils
-------------------

.. automodule:: azure.core.utils
:members:
:undoc-members:
:inherited-members:

42 changes: 42 additions & 0 deletions sdk/core/azure-core/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# ------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
import pytest
from azure.core.utils import case_insensitive_dict

@pytest.fixture()
def accept_cases():
return ["accept", "Accept", "ACCEPT", "aCCePT"]

def test_case_insensitive_dict_basic(accept_cases):
my_dict = case_insensitive_dict({"accept": "application/json"})
for accept_case in accept_cases:
assert my_dict[accept_case] == "application/json"

def test_case_insensitive_dict_override(accept_cases):
for accept_case in accept_cases:
my_dict = case_insensitive_dict({accept_case: "should-not/be-me"})
my_dict["accept"] = "application/json"
assert my_dict[accept_case] == my_dict["accept"] == "application/json"

def test_case_insensitive_dict_initialization():
dict_response = {
"platformUpdateDomainCount": 5,
"platformFaultDomainCount": 3,
"virtualMachines": []
}
a = case_insensitive_dict(platformUpdateDomainCount=5, platformFaultDomainCount=3, virtualMachines=[])
b = case_insensitive_dict(zip(['platformUpdateDomainCount', 'platformFaultDomainCount', 'virtualMachines'], [5, 3, []]))
c = case_insensitive_dict([('platformFaultDomainCount', 3), ('platformUpdateDomainCount', 5), ('virtualMachines', [])])
d = case_insensitive_dict({'virtualMachines': [], 'platformFaultDomainCount': 3, 'platformUpdateDomainCount': 5})
e = case_insensitive_dict({'platformFaultDomainCount': 3, 'virtualMachines': []}, platformUpdateDomainCount=5)
f = case_insensitive_dict(dict_response)
g = case_insensitive_dict(**dict_response)
assert a == b == c == d == e == f == g
dicts = [a, b, c, d, e, f, g]
for d in dicts:
assert len(d) == 3
assert d['platformUpdateDomainCount'] == d['platformupdatedomaincount'] == d['PLATFORMUPDATEDOMAINCOUNT'] == 5
assert d['platformFaultDomainCount'] == d['platformfaultdomaincount'] == d['PLATFORMFAULTDOMAINCOUNT'] == 3
assert d['virtualMachines'] == d['virtualmachines'] == d['VIRTUALMACHINES'] == []

0 comments on commit a10c604

Please sign in to comment.