Skip to content

[Cosmos] Remove support for Python27, set Python36 as minimum, and update cosmos emulator pipeline #22475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions eng/pipelines/templates/stages/cosmos-sdk-client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,9 @@ stages:
- job: Emulator
strategy:
matrix:
Windows_Python35:
Windows_Python36:
OSVmImage: 'windows-2019'
PythonVersion: '3.5'
Windows_Python27:
OSVmImage: 'windows-2019'
PythonVersion: '2.7'
PythonVersion: '3.6'
pool:
vmImage: $(OSVmImage)

Expand Down
3 changes: 3 additions & 0 deletions sdk/cosmos/azure-cosmos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

### 4.3.0b2 (Unreleased)

This version and all future versions will require Python 3.6+. Python 2.7 is no longer supported.
We will also be removing support for Python 3.6 and will only support Python 3.7+ starting December 2022.

#### Features Added

#### Breaking Changes
Expand Down
6 changes: 2 additions & 4 deletions sdk/cosmos/azure-cosmos/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
include README.md
include CHANGELOG.md
include *.md
include LICENSE
include azure/__init__.py
recursive-include samples *.py
recursive-include samples *.py *.md
recursive-include test *.py
recursive-include doc *.rst
include azure/cosmos/py.typed
5 changes: 4 additions & 1 deletion sdk/cosmos/azure-cosmos/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## _Disclaimer_
_Azure SDK Python packages support for Python 2.7 has ended 01 January 2022. For more information and questions, please refer to https://github.com/Azure/azure-sdk-for-python/issues/20691_

# Azure Cosmos DB SQL API client library for Python

Azure Cosmos DB is a globally distributed, multi-model database service that supports document, key-value, wide-column, and graph databases.
Expand All @@ -23,7 +26,7 @@ New releases of this SDK won't support Python 2.x starting January 1st, 2022. Pl

* Azure subscription - [Create a free account][azure_sub]
* Azure [Cosmos DB account][cosmos_account] - SQL API
* [Python 2.7 or 3.6+][python]
* [Python 3.6+][python]

If you need a Cosmos DB SQL API account, you can create one with this [Azure CLI][azure_cli] command:

Expand Down
6 changes: 1 addition & 5 deletions sdk/cosmos/azure-cosmos/azure/cosmos/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
import binascii
from typing import Dict, Any

import six
from six.moves.urllib.parse import quote as urllib_quote
from urllib.parse import quote as urllib_quote

from azure.core import MatchConditions

Expand Down Expand Up @@ -578,9 +577,6 @@ def IsValidBase64String(string_to_validate):
if len(buffer) != 4:
return False
except Exception as e: # pylint: disable=broad-except
if six.PY2:
e = e.message # pylint: disable=no-member
# (e.message does exist on py2)
if isinstance(e, binascii.Error):
return False
raise e
Expand Down
102 changes: 53 additions & 49 deletions sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"""
# https://github.com/PyCQA/pylint/issues/3112
# Currently pylint is locked to 2.3.3 and this is fixed in 2.4.4
from typing import Dict, Any, Optional # pylint: disable=unused-import
import six
from typing import Dict, Any, Optional # pylint: disable=unused-import
import urllib.parse
from urllib3.util.retry import Retry
from azure.core.paging import ItemPaged # type: ignore
from azure.core import PipelineClient # type: ignore
Expand Down Expand Up @@ -59,6 +59,7 @@
from . import _utils
from .partition_key import _Undefined, _Empty


# pylint: disable=protected-access


Expand Down Expand Up @@ -87,12 +88,12 @@ class _QueryCompatibilityMode:
_DefaultStringRangePrecision = -1

def __init__(
self,
url_connection, # type: str
auth, # type: Dict[str, Any]
connection_policy=None, # type: Optional[ConnectionPolicy]
consistency_level=documents.ConsistencyLevel.Session, # type: str
**kwargs # type: Any
self,
url_connection, # type: str
auth, # type: Dict[str, Any]
connection_policy=None, # type: Optional[ConnectionPolicy]
consistency_level=documents.ConsistencyLevel.Session, # type: str
**kwargs # type: Any
):
# type: (...) -> None
"""
Expand Down Expand Up @@ -177,9 +178,9 @@ def __init__(
proxies = kwargs.pop('proxies', {})
if self.connection_policy.ProxyConfiguration and self.connection_policy.ProxyConfiguration.Host:
host = self.connection_policy.ProxyConfiguration.Host
url = six.moves.urllib.parse.urlparse(host)
url = urllib.parse.urlparse(host)
proxy = host if url.port else host + ":" + str(self.connection_policy.ProxyConfiguration.Port)
proxies.update({url.scheme : proxy})
proxies.update({url.scheme: proxy})

policies = [
HeadersPolicy(**kwargs),
Expand All @@ -191,7 +192,7 @@ def __init__(
NetworkTraceLoggingPolicy(**kwargs),
DistributedTracingPolicy(**kwargs),
HttpLoggingPolicy(**kwargs),
]
]

transport = kwargs.pop("transport", None)
self.pipeline_client = PipelineClient(base_url=url_connection, transport=transport, policies=policies)
Expand Down Expand Up @@ -840,13 +841,13 @@ def ReadItems(self, collection_link, feed_options=None, response_hook=None, **kw
return self.QueryItems(collection_link, None, feed_options, response_hook=response_hook, **kwargs)

def QueryItems(
self,
database_or_container_link,
query,
options=None,
partition_key=None,
response_hook=None,
**kwargs
self,
database_or_container_link,
query,
options=None,
partition_key=None,
response_hook=None,
**kwargs
):
"""Queries documents in a collection.

Expand Down Expand Up @@ -936,7 +937,8 @@ def QueryItemsChangeFeed(self, collection_link, options=None, response_hook=None
)

def _QueryChangeFeed(
self, collection_link, resource_type, options=None, partition_key_range_id=None, response_hook=None, **kwargs
self, collection_link, resource_type, options=None, partition_key_range_id=None, response_hook=None,
**kwargs
):
"""Queries change feed of a resource in a collection.

Expand Down Expand Up @@ -1129,10 +1131,10 @@ def UpsertItem(self, database_or_container_link, document, options=None, **kwarg
return self.Upsert(document, path, "docs", collection_id, None, options, **kwargs)

PartitionResolverErrorMessage = (
"Couldn't find any partition resolvers for the database link provided. "
+ "Ensure that the link you used when registering the partition resolvers "
+ "matches the link provided or you need to register both types of database "
+ "link(self link as well as ID based link)."
"Couldn't find any partition resolvers for the database link provided. "
+ "Ensure that the link you used when registering the partition resolvers "
+ "matches the link provided or you need to register both types of database "
+ "link(self link as well as ID based link)."
)

# Gets the collection id and path for the document
Expand Down Expand Up @@ -2040,7 +2042,7 @@ def GetDatabaseAccount(self, url_connection=None, **kwargs):
]

self._useMultipleWriteLocations = (
self.connection_policy.UseMultipleWriteLocations and database_account._EnableMultipleWritableLocations
self.connection_policy.UseMultipleWriteLocations and database_account._EnableMultipleWritableLocations
)
return database_account

Expand Down Expand Up @@ -2107,7 +2109,8 @@ def Upsert(self, body, path, typ, id, initial_headers, options=None, **kwargs):
self._UpdateSessionIfRequired(headers, result, self.last_response_headers)
return result

def Replace(self, resource, path, typ, id, initial_headers, options=None, **kwargs): # pylint: disable=redefined-builtin
def Replace(self, resource, path, typ, id, initial_headers, options=None,
**kwargs): # pylint: disable=redefined-builtin
"""Replaces a Azure Cosmos resource and returns it.

:param dict resource:
Expand Down Expand Up @@ -2163,7 +2166,8 @@ def Read(self, path, typ, id, initial_headers, options=None, **kwargs): # pylin
result, self.last_response_headers = self.__Get(path, request_params, headers, **kwargs)
return result

def DeleteResource(self, path, typ, id, initial_headers, options=None, **kwargs): # pylint: disable=redefined-builtin
def DeleteResource(self, path, typ, id, initial_headers, options=None,
**kwargs): # pylint: disable=redefined-builtin
"""Deletes a Azure Cosmos resource and returns it.

:param str path:
Expand Down Expand Up @@ -2327,18 +2331,18 @@ def QueryFeed(self, path, collection_id, query, options, partition_key_range_id=
)

def __QueryFeed(
self,
path,
typ,
id_,
result_fn,
create_fn,
query,
options=None,
partition_key_range_id=None,
response_hook=None,
is_query_plan=False,
**kwargs
self,
path,
typ,
id_,
result_fn,
create_fn,
query,
options=None,
partition_key_range_id=None,
response_hook=None,
is_query_plan=False,
**kwargs
):
"""Query for more than one Azure Cosmos resources.

Expand Down Expand Up @@ -2380,8 +2384,8 @@ def __GetBodiesFromQueryResult(result):
# Copy to make sure that default_headers won't be changed.
if query is None:
# Query operations will use ReadEndpoint even though it uses GET(for feed requests)
request_params = _request_object.RequestObject(typ,
documents._OperationType.QueryPlan if is_query_plan else documents._OperationType.ReadFeed)
request_params = _request_object.RequestObject(
typ, documents._OperationType.QueryPlan if is_query_plan else documents._OperationType.ReadFeed)
headers = base.GetHeaders(self, initial_headers, "get", path, id_, typ, options, partition_key_range_id)
result, self.last_response_headers = self.__Get(path, request_params, headers, **kwargs)
if response_hook:
Expand All @@ -2395,8 +2399,8 @@ def __GetBodiesFromQueryResult(result):
initial_headers[http_constants.HttpHeaders.IsQuery] = "true"

if (
self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.Default
or self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.Query
self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.Default
or self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.Query
):
initial_headers[http_constants.HttpHeaders.ContentType] = runtime_constants.MediaTypes.QueryJson
elif self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.SqlQuery:
Expand Down Expand Up @@ -2428,7 +2432,7 @@ def _GetQueryPlanThroughGateway(self, query, resource_link, **kwargs):
"isQueryPlanRequest": True,
"supportedQueryFeatures": supported_query_features,
"queryVersion": http_constants.Versions.QueryVersion
}
}

resource_link = base.TrimBeginningAndEndingSlashes(resource_link)
path = base.GetPathFromLink(resource_link, "docs")
Expand Down Expand Up @@ -2459,18 +2463,18 @@ def __CheckAndUnifyQueryFormat(self, query_body):
dict or string
"""
if (
self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.Default
or self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.Query
self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.Default
or self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.Query
):
if not isinstance(query_body, dict) and not isinstance(query_body, six.string_types):
if not isinstance(query_body, dict) and not isinstance(query_body, str):
raise TypeError("query body must be a dict or string.")
if isinstance(query_body, dict) and not query_body.get("query"):
raise ValueError('query body must have valid query text with key "query".')
if isinstance(query_body, six.string_types):
if isinstance(query_body, str):
return {"query": query_body}
elif (
self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.SqlQuery
and not isinstance(query_body, six.string_types)
self._query_compatibility_mode == CosmosClientConnection._QueryCompatibilityMode.SqlQuery
and not isinstance(query_body, str)
):
raise TypeError("query body must be a string.")
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
import numbers
from collections import deque

import six

from azure.cosmos import _base
from azure.cosmos._execution_context.base_execution_context import _DefaultQueryExecutionContext

Expand Down Expand Up @@ -154,7 +152,7 @@ def getTypeOrd(orderby_item):
return 2
if isinstance(val, numbers.Number):
return 4
if isinstance(val, six.string_types):
if isinstance(val, str):
return 5

raise TypeError("unknown type" + str(val))
Expand All @@ -176,7 +174,7 @@ def getTypeStr(orderby_item):
return "Boolean"
if isinstance(val, numbers.Number):
return "Number"
if isinstance(val, six.string_types):
if isinstance(val, str):
return "String"

raise TypeError("unknown type" + str(val))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import copy
import hashlib
import json
import six

from azure.cosmos._execution_context.aggregators import (
_AverageAggregator,
Expand Down Expand Up @@ -127,17 +126,15 @@ def __next__(self):
res = next(self._execution_context)

json_repr = json.dumps(self.make_hash(res))
if six.PY3:
json_repr = json_repr.encode("utf-8")
json_repr = json_repr.encode("utf-8")

hash_object = hashlib.sha1(json_repr) # nosec
hashed_result = hash_object.hexdigest()

while hashed_result in self.last_result:
res = next(self._execution_context)
json_repr = json.dumps(self.make_hash(res))
if six.PY3:
json_repr = json_repr.encode("utf-8")
json_repr = json_repr.encode("utf-8")

hash_object = hashlib.sha1(json_repr) # nosec
hashed_result = hash_object.hexdigest()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"""Internal class for partitioned query execution info implementation in the Azure Cosmos database service.
"""

import six
from azure.cosmos.documents import _DistinctType


Expand Down Expand Up @@ -121,7 +120,7 @@ def has_rewritten_query(self):
def _extract(self, path):

item = self._query_execution_info
if isinstance(path, six.string_types):
if isinstance(path, str):
return item.get(path)

for p in path:
Expand Down
Loading