Minor code smell refactoring, updating pipelines and description added. (#64)
This commit is contained in:
Родитель
8cab4c1464
Коммит
8ef845cd8f
|
@ -1,38 +1,49 @@
|
|||
trigger:
|
||||
- master
|
||||
- dev
|
||||
- master
|
||||
- dev
|
||||
|
||||
schedules:
|
||||
- cron: "0 8 * * 1,3,5"
|
||||
displayName: Monday, Wednesday and Friday - 1 AM (PDT) build
|
||||
branches:
|
||||
include:
|
||||
- dev
|
||||
- master
|
||||
exclude:
|
||||
- release/*
|
||||
- releases/ancient/*
|
||||
|
||||
jobs:
|
||||
- job: Tests
|
||||
pool:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
strategy:
|
||||
matrix:
|
||||
Python36:
|
||||
pythonVersion: '3.6'
|
||||
Python37:
|
||||
pythonVersion: '3.7'
|
||||
Python38:
|
||||
pythonVersion: '3.8'
|
||||
maxParallel: 3
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '$(pythonVersion)'
|
||||
addToPath: true
|
||||
- task: ShellScript@2
|
||||
inputs:
|
||||
disableAutoCwd: true
|
||||
scriptPath: .ci/build.sh
|
||||
displayName: 'Build'
|
||||
- bash: |
|
||||
chmod +x .ci/run_tests.sh
|
||||
.ci/run_tests.sh
|
||||
displayName: 'Run Tests'
|
||||
- task: PublishCodeCoverageResults@1
|
||||
inputs:
|
||||
codeCoverageTool: cobertura
|
||||
summaryFileLocation: coverage.xml
|
||||
- bash: |
|
||||
rm coverage.xml
|
||||
displayName: 'Clearing coverage.xml file'
|
||||
- job: Tests
|
||||
pool:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
strategy:
|
||||
matrix:
|
||||
Python36:
|
||||
pythonVersion: '3.6'
|
||||
Python37:
|
||||
pythonVersion: '3.7'
|
||||
Python38:
|
||||
pythonVersion: '3.8'
|
||||
maxParallel: 3
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '$(pythonVersion)'
|
||||
addToPath: true
|
||||
- task: ShellScript@2
|
||||
inputs:
|
||||
disableAutoCwd: true
|
||||
scriptPath: .ci/build.sh
|
||||
displayName: 'Build'
|
||||
- bash: |
|
||||
chmod +x .ci/run_tests.sh
|
||||
.ci/run_tests.sh
|
||||
displayName: 'Run Tests'
|
||||
- task: PublishCodeCoverageResults@1
|
||||
inputs:
|
||||
codeCoverageTool: cobertura
|
||||
summaryFileLocation: coverage.xml
|
||||
- bash: |
|
||||
rm coverage.xml
|
||||
displayName: 'Clearing coverage.xml file'
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from pkgutil import extend_path
|
||||
import typing
|
||||
|
||||
__path__: typing.Iterable[str] = extend_path(__path__, __name__)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from ._abc import TimerRequest, InputStream, Context, Out # NoQA
|
||||
from ._eventhub import EventHubEvent # NoQA
|
||||
from ._eventgrid import EventGridEvent, EventGridOutputEvent # NoQA
|
||||
|
@ -12,6 +13,7 @@ from ._queue import QueueMessage # NoQA
|
|||
from ._servicebus import ServiceBusMessage # NoQA
|
||||
from ._durable_functions import OrchestrationContext # NoQA
|
||||
from .meta import get_binding_registry # NoQA
|
||||
|
||||
# Import binding implementations to register them
|
||||
from . import blob # NoQA
|
||||
from . import cosmosdb # NoQA
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import abc
|
||||
import datetime
|
||||
import io
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import collections
|
||||
import json
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from typing import Union
|
||||
from . import _abc
|
||||
from importlib import import_module
|
||||
|
@ -35,12 +36,11 @@ def _serialize_custom_object(obj):
|
|||
"function")
|
||||
# Encode to json using the object's `to_json`
|
||||
obj_type = type(obj)
|
||||
dict_obj = {
|
||||
return {
|
||||
"__class__": obj.__class__.__name__,
|
||||
"__module__": obj.__module__,
|
||||
"__data__": obj_type.to_json(obj)
|
||||
}
|
||||
return dict_obj
|
||||
|
||||
|
||||
def _deserialize_custom_object(obj: dict) -> object:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import datetime
|
||||
import typing
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import datetime
|
||||
import typing
|
||||
|
||||
from azure.functions import _abc as funcabc
|
||||
from azure.functions import _abc as func_abc
|
||||
from azure.functions import meta
|
||||
|
||||
|
||||
class EventHubEvent(funcabc.EventHubEvent):
|
||||
class EventHubEvent(func_abc.EventHubEvent):
|
||||
"""A concrete implementation of Event Hub message type."""
|
||||
|
||||
def __init__(self, *,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import collections.abc
|
||||
import io
|
||||
import json
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from typing import Callable, Dict, List, Optional, Any
|
||||
from io import BytesIO, StringIO
|
||||
from os import linesep
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import abc
|
||||
import typing
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import typing
|
||||
|
|
|
@ -301,7 +301,7 @@ def get_args(tp, evaluate=None):
|
|||
get_args(Callable[[], T][int], evaluate=True) == ([], int,)
|
||||
"""
|
||||
if NEW_TYPING:
|
||||
if evaluate is not None and not evaluate:
|
||||
if not (evaluate is None or evaluate):
|
||||
raise ValueError('evaluate can only be True in Python 3.7')
|
||||
if isinstance(tp, _GenericAlias):
|
||||
res = tp.__args__
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from typing import List, Tuple, Optional
|
||||
from datetime import datetime
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import io
|
||||
from typing import Optional, Union, Any
|
||||
|
||||
|
@ -96,10 +97,7 @@ class BlobConverter(meta.InConverter,
|
|||
trigger_metadata, 'Properties', python_type=dict)
|
||||
if properties:
|
||||
length = properties.get('Length')
|
||||
if length:
|
||||
length = int(length)
|
||||
else:
|
||||
length = None
|
||||
length = int(length) if length else None
|
||||
else:
|
||||
length = None
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import collections.abc
|
||||
import json
|
||||
import typing
|
||||
|
@ -30,15 +31,12 @@ class CosmosDBConverter(meta.InConverter, meta.OutConverter,
|
|||
|
||||
data_type = data.type
|
||||
|
||||
if data_type == 'string':
|
||||
if data_type in ['string', 'json']:
|
||||
body = data.value
|
||||
|
||||
elif data_type == 'bytes':
|
||||
body = data.value.decode('utf-8')
|
||||
|
||||
elif data_type == 'json':
|
||||
body = data.value
|
||||
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'unsupported queue payload type: {data_type}')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import typing
|
||||
import json
|
||||
|
||||
|
@ -61,7 +62,7 @@ class ActivityTriggerConverter(meta.InConverter,
|
|||
|
||||
# Durable functions extension always returns a string of json
|
||||
# See durable functions library's call_activity_task docs
|
||||
if data_type == 'string' or data_type == 'json':
|
||||
if data_type in ['string', 'json']:
|
||||
try:
|
||||
callback = _durable_functions._deserialize_custom_object
|
||||
result = json.loads(data.value, object_hook=callback)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import collections
|
||||
import datetime
|
||||
import json
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import json
|
||||
from typing import Dict, Any, List, Union, Optional, Mapping
|
||||
|
||||
|
@ -33,12 +34,10 @@ class EventHubConverter(meta.InConverter, meta.OutConverter,
|
|||
) -> Union[_eventhub.EventHubEvent, List[_eventhub.EventHubEvent]]:
|
||||
data_type = data.type
|
||||
|
||||
if (data_type == 'string' or data_type == 'bytes'
|
||||
or data_type == 'json'):
|
||||
if data_type in ['string', 'bytes', 'json']:
|
||||
return cls.decode_single_event(data, trigger_metadata)
|
||||
|
||||
elif (data_type == 'collection_bytes'
|
||||
or data_type == 'collection_string'):
|
||||
elif data_type in ['collection_bytes', 'collection_string']:
|
||||
return cls.decode_multiple_events(data, trigger_metadata)
|
||||
|
||||
else:
|
||||
|
@ -48,15 +47,12 @@ class EventHubConverter(meta.InConverter, meta.OutConverter,
|
|||
@classmethod
|
||||
def decode_single_event(cls, data,
|
||||
trigger_metadata) -> _eventhub.EventHubEvent:
|
||||
if data.type == 'string':
|
||||
if data.type in ['string', 'json']:
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
elif data.type == 'bytes':
|
||||
body = data.value
|
||||
|
||||
elif data.type == 'json':
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
return _eventhub.EventHubEvent(body=body)
|
||||
|
||||
@classmethod
|
||||
|
@ -70,8 +66,8 @@ class EventHubConverter(meta.InConverter, meta.OutConverter,
|
|||
parsed_data = data.value.string
|
||||
|
||||
events = []
|
||||
for i in range(len(parsed_data)):
|
||||
event = _eventhub.EventHubEvent(body=parsed_data[i])
|
||||
for parsed_datum in parsed_data:
|
||||
event = _eventhub.EventHubEvent(body=parsed_datum)
|
||||
events.append(event)
|
||||
|
||||
return events
|
||||
|
@ -119,15 +115,12 @@ class EventHubTriggerConverter(EventHubConverter,
|
|||
def decode_single_event(
|
||||
cls, data, trigger_metadata: Mapping[str, meta.Datum]
|
||||
) -> _eventhub.EventHubEvent:
|
||||
if data.type == 'string':
|
||||
if data.type in ['string', 'json']:
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
elif data.type == 'bytes':
|
||||
body = data.value
|
||||
|
||||
elif data.type == 'json':
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
return _eventhub.EventHubEvent(
|
||||
body=body,
|
||||
trigger_metadata=trigger_metadata,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import json
|
||||
import typing
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import typing
|
||||
import json
|
||||
|
||||
|
@ -90,7 +91,6 @@ class KafkaConverter(meta.InConverter, meta.OutConverter, binding='kafka'):
|
|||
meta.is_iterable_type_annotation(pytype, valid_types)
|
||||
or (isinstance(pytype, type) and issubclass(pytype, valid_types))
|
||||
)
|
||||
return issubclass(pytype, KafkaEvent)
|
||||
|
||||
@classmethod
|
||||
def check_output_type_annotation(cls, pytype) -> bool:
|
||||
|
@ -106,12 +106,10 @@ class KafkaConverter(meta.InConverter, meta.OutConverter, binding='kafka'):
|
|||
) -> typing.Union[KafkaEvent, typing.List[KafkaEvent]]:
|
||||
data_type = data.type
|
||||
|
||||
if (data_type == 'string' or data_type == 'bytes'
|
||||
or data_type == 'json'):
|
||||
if data_type in ['string', 'bytes', 'json']:
|
||||
return cls.decode_single_event(data, trigger_metadata)
|
||||
|
||||
elif (data_type == 'collection_bytes'
|
||||
or data_type == 'collection_string'):
|
||||
elif data_type in ['collection_bytes', 'collection_string']:
|
||||
return cls.decode_multiple_events(data, trigger_metadata)
|
||||
|
||||
else:
|
||||
|
@ -123,15 +121,12 @@ class KafkaConverter(meta.InConverter, meta.OutConverter, binding='kafka'):
|
|||
trigger_metadata) -> KafkaEvent:
|
||||
data_type = data.type
|
||||
|
||||
if data_type == 'string':
|
||||
if data_type in ['string', 'json']:
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
elif data_type == 'bytes':
|
||||
body = data.value
|
||||
|
||||
elif data_type == 'json':
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'unsupported event data payload type: {data_type}')
|
||||
|
@ -147,9 +142,7 @@ class KafkaConverter(meta.InConverter, meta.OutConverter, binding='kafka'):
|
|||
elif data.type == 'collection_string':
|
||||
parsed_data = data.value.string
|
||||
|
||||
events = [KafkaEvent(body=pd) for pd in parsed_data]
|
||||
|
||||
return events
|
||||
return [KafkaEvent(body=pd) for pd in parsed_data]
|
||||
|
||||
@classmethod
|
||||
def encode(cls, obj: typing.Any, *,
|
||||
|
@ -168,11 +161,9 @@ class KafkaTriggerConverter(KafkaConverter,
|
|||
|
||||
data_type = data.type
|
||||
|
||||
if (data_type == 'string' or data_type == 'bytes'
|
||||
or data_type == 'json'):
|
||||
if data_type in ['string', 'bytes', 'json']:
|
||||
return cls.decode_single_event(data, trigger_metadata)
|
||||
elif (data_type == 'collection_bytes'
|
||||
or data_type == 'collection_string'):
|
||||
elif data_type in ['collection_bytes', 'collection_string']:
|
||||
return cls.decode_multiple_events(data, trigger_metadata)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
|
@ -183,15 +174,12 @@ class KafkaTriggerConverter(KafkaConverter,
|
|||
trigger_metadata) -> KafkaEvent:
|
||||
data_type = data.type
|
||||
|
||||
if data_type == 'string':
|
||||
if data_type in ['string', 'json']:
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
elif data_type == 'bytes':
|
||||
body = data.value
|
||||
|
||||
elif data_type == 'json':
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'unsupported event data payload type: {data_type}')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import abc
|
||||
import collections.abc
|
||||
import datetime
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import collections.abc
|
||||
import datetime
|
||||
import json
|
||||
|
@ -65,10 +66,7 @@ class QueueMessageInConverter(meta.InConverter,
|
|||
trigger_metadata) -> Any:
|
||||
data_type = data.type
|
||||
|
||||
if data_type == 'string':
|
||||
body = data.value
|
||||
|
||||
elif data_type == 'bytes':
|
||||
if data_type in ['string', 'bytes']:
|
||||
body = data.value
|
||||
|
||||
else:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import datetime
|
||||
import typing
|
||||
|
||||
|
@ -172,15 +173,12 @@ class ServiceBusMessageInConverter(meta.InConverter,
|
|||
# See Azure/azure-functions-python-worker#330
|
||||
body = b''
|
||||
|
||||
elif data.type == 'string':
|
||||
elif data.type in ['string', 'json']:
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
elif data.type == 'bytes':
|
||||
body = data.value
|
||||
|
||||
elif data.type == 'json':
|
||||
body = data.value.encode('utf-8')
|
||||
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f'unsupported queue payload type: {data.type}')
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import json
|
||||
import typing
|
||||
|
||||
|
|
12
setup.py
12
setup.py
|
@ -1,16 +1,18 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from setuptools import setup
|
||||
from azure.functions import __version__
|
||||
|
||||
with open("README.md") as readme:
|
||||
long_description = readme.read()
|
||||
|
||||
setup(
|
||||
name='azure-functions',
|
||||
version=__version__,
|
||||
description='Azure Functions for Python',
|
||||
long_description='Python support for Azure Functions is based on '
|
||||
'Python3.[6|7|8], serverless hosting on Linux and the '
|
||||
'Functions 2.0 and 3.0 runtime. This module provides the '
|
||||
'rich binding definitions for Azure Functions for Python '
|
||||
'apps.',
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/markdown',
|
||||
author='Microsoft Corporation',
|
||||
author_email='azpysdkhelp@microsoft.com',
|
||||
classifiers=[
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
"""Bootstrap for '$ python setup.py test' command."""
|
||||
|
||||
import os.path
|
||||
|
@ -10,9 +11,8 @@ import unittest.runner
|
|||
|
||||
def suite():
|
||||
test_loader = unittest.TestLoader()
|
||||
test_suite = test_loader.discover(
|
||||
return test_loader.discover(
|
||||
os.path.dirname(__file__), pattern='test_*.py')
|
||||
return test_suite
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import unittest
|
||||
|
||||
import azure.functions as func
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import unittest
|
||||
import json
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from datetime import datetime
|
||||
import unittest
|
||||
from typing import List
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from typing import List
|
||||
import unittest
|
||||
import json
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import unittest
|
||||
|
||||
import azure.functions as func
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import unittest
|
||||
from io import StringIO, BytesIO
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from typing import List
|
||||
import unittest
|
||||
import json
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from typing import Mapping, List
|
||||
import unittest
|
||||
import datetime
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from typing import Mapping
|
||||
import unittest
|
||||
from datetime import datetime, timezone
|
||||
|
|
Загрузка…
Ссылка в новой задаче