diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/azure/ai/language/questionanswering/authoring/_utils/model_base.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/azure/ai/language/questionanswering/authoring/_utils/model_base.py index 45d922b21ccc..097f8197cfd9 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/azure/ai/language/questionanswering/authoring/_utils/model_base.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/azure/ai/language/questionanswering/authoring/_utils/model_base.py @@ -1,4 +1,4 @@ -# pylint: disable=line-too-long,useless-suppression,too-many-lines,duplicate-code +# pylint: disable=line-too-long,useless-suppression,too-many-lines # coding=utf-8 # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. @@ -37,6 +37,7 @@ TZ_UTC = timezone.utc _T = typing.TypeVar("_T") +_NONE_TYPE = type(None) def _timedelta_as_isostr(td: timedelta) -> str: @@ -171,6 +172,21 @@ def default(self, o): # pylint: disable=too-many-return-statements r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT" ) +_ARRAY_ENCODE_MAPPING = { + "pipeDelimited": "|", + "spaceDelimited": " ", + "commaDelimited": ",", + "newlineDelimited": "\n", +} + + +def _deserialize_array_encoded(delimit: str, attr): + if isinstance(attr, str): + if attr == "": + return [] + return attr.split(delimit) + return attr + def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime: """Deserialize ISO-8601 formatted string into Datetime object. @@ -202,7 +218,7 @@ def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime: test_utc = date_obj.utctimetuple() if test_utc.tm_year > 9999 or test_utc.tm_year < 1: raise OverflowError("Hit max or min date") - return date_obj + return date_obj # type: ignore[no-any-return] def _deserialize_datetime_rfc7231(attr: typing.Union[str, datetime]) -> datetime: @@ -256,7 +272,7 @@ def _deserialize_time(attr: typing.Union[str, time]) -> time: """ if isinstance(attr, time): return attr - return isodate.parse_time(attr) + return isodate.parse_time(attr) # type: ignore[no-any-return] def _deserialize_bytes(attr): @@ -315,6 +331,8 @@ def _deserialize_int_as_str(attr): def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None): if annotation is int and rf and rf._format == "str": return _deserialize_int_as_str + if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING: + return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format]) if rf and rf._format: return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format) return _DESERIALIZE_MAPPING.get(annotation) # pyright: ignore @@ -353,9 +371,39 @@ def __contains__(self, key: typing.Any) -> bool: return key in self._data def __getitem__(self, key: str) -> typing.Any: + # If this key has been deserialized (for mutable types), we need to handle serialization + if hasattr(self, "_attr_to_rest_field"): + cache_attr = f"_deserialized_{key}" + if hasattr(self, cache_attr): + rf = _get_rest_field(getattr(self, "_attr_to_rest_field"), key) + if rf: + value = self._data.get(key) + if isinstance(value, (dict, list, set)): + # For mutable types, serialize and return + # But also update _data with serialized form and clear flag + # so mutations via this returned value affect _data + serialized = _serialize(value, rf._format) + # If serialized form is same type (no transformation needed), + # return _data directly so mutations work + if isinstance(serialized, type(value)) and serialized == value: + return self._data.get(key) + # Otherwise return serialized copy and clear flag + try: + object.__delattr__(self, cache_attr) + except AttributeError: + pass + # Store serialized form back + self._data[key] = serialized + return serialized return self._data.__getitem__(key) def __setitem__(self, key: str, value: typing.Any) -> None: + # Clear any cached deserialized value when setting through dictionary access + cache_attr = f"_deserialized_{key}" + try: + object.__delattr__(self, cache_attr) + except AttributeError: + pass self._data.__setitem__(key, value) def __delitem__(self, key: str) -> None: @@ -483,6 +531,8 @@ def _is_model(obj: typing.Any) -> bool: def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements if isinstance(o, list): + if format in _ARRAY_ENCODE_MAPPING and all(isinstance(x, str) for x in o): + return _ARRAY_ENCODE_MAPPING[format].join(o) return [_serialize(x, format) for x in o] if isinstance(o, dict): return {k: _serialize(v, format) for k, v in o.items()} @@ -638,6 +688,10 @@ def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self: if not rf._rest_name_input: rf._rest_name_input = attr cls._attr_to_rest_field: dict[str, _RestField] = dict(attr_to_rest_field.items()) + cls._backcompat_attr_to_rest_field: dict[str, _RestField] = { + Model._get_backcompat_attribute_name(cls._attr_to_rest_field, attr): rf + for attr, rf in cls._attr_to_rest_field.items() + } cls._calculated.add(f"{cls.__module__}.{cls.__qualname__}") return super().__new__(cls) @@ -647,6 +701,16 @@ def __init_subclass__(cls, discriminator: typing.Optional[str] = None) -> None: if hasattr(base, "__mapping__"): base.__mapping__[discriminator or cls.__name__] = cls # type: ignore + @classmethod + def _get_backcompat_attribute_name(cls, attr_to_rest_field: dict[str, "_RestField"], attr_name: str) -> str: + rest_field_obj = attr_to_rest_field.get(attr_name) # pylint: disable=protected-access + if rest_field_obj is None: + return attr_name + original_tsp_name = getattr(rest_field_obj, "_original_tsp_name", None) # pylint: disable=protected-access + if original_tsp_name: + return original_tsp_name + return attr_name + @classmethod def _get_discriminator(cls, exist_discriminators) -> typing.Optional["_RestField"]: for v in cls.__dict__.values(): @@ -767,6 +831,17 @@ def _deserialize_sequence( return obj if isinstance(obj, ET.Element): obj = list(obj) + try: + if ( + isinstance(obj, str) + and isinstance(deserializer, functools.partial) + and isinstance(deserializer.args[0], functools.partial) + and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable + ): + # encoded string may be deserialized to sequence + return deserializer(obj) + except: # pylint: disable=bare-except + pass return type(obj)(_deserialize(deserializer, entry, module) for entry in obj) @@ -817,16 +892,16 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-retur # is it optional? try: - if any(a for a in annotation.__args__ if a == type(None)): # pyright: ignore + if any(a is _NONE_TYPE for a in annotation.__args__): # pyright: ignore if len(annotation.__args__) <= 2: # pyright: ignore if_obj_deserializer = _get_deserialize_callable_from_annotation( - next(a for a in annotation.__args__ if a != type(None)), module, rf # pyright: ignore + next(a for a in annotation.__args__ if a is not _NONE_TYPE), module, rf # pyright: ignore ) return functools.partial(_deserialize_with_optional, if_obj_deserializer) # the type is Optional[Union[...]], we need to remove the None type from the Union annotation_copy = copy.copy(annotation) - annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a != type(None)] # pyright: ignore + annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a is not _NONE_TYPE] # pyright: ignore return _get_deserialize_callable_from_annotation(annotation_copy, module, rf) except AttributeError: pass @@ -972,6 +1047,7 @@ def _failsafe_deserialize_xml( return None +# pylint: disable=too-many-instance-attributes class _RestField: def __init__( self, @@ -984,6 +1060,7 @@ def __init__( format: typing.Optional[str] = None, is_multipart_file_input: bool = False, xml: typing.Optional[dict[str, typing.Any]] = None, + original_tsp_name: typing.Optional[str] = None, ): self._type = type self._rest_name_input = name @@ -995,10 +1072,15 @@ def __init__( self._format = format self._is_multipart_file_input = is_multipart_file_input self._xml = xml if xml is not None else {} + self._original_tsp_name = original_tsp_name @property def _class_type(self) -> typing.Any: - return getattr(self._type, "args", [None])[0] + result = getattr(self._type, "args", [None])[0] + # type may be wrapped by nested functools.partial so we need to check for that + if isinstance(result, functools.partial): + return getattr(result, "args", [None])[0] + return result @property def _rest_name(self) -> str: @@ -1009,14 +1091,37 @@ def _rest_name(self) -> str: def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin # by this point, type and rest_name will have a value bc we default # them in __new__ of the Model class - item = obj.get(self._rest_name) + # Use _data.get() directly to avoid triggering __getitem__ which clears the cache + item = obj._data.get(self._rest_name) if item is None: return item if self._is_model: return item - return _deserialize(self._type, _serialize(item, self._format), rf=self) + + # For mutable types, we want mutations to directly affect _data + # Check if we've already deserialized this value + cache_attr = f"_deserialized_{self._rest_name}" + if hasattr(obj, cache_attr): + # Return the value from _data directly (it's been deserialized in place) + return obj._data.get(self._rest_name) + + deserialized = _deserialize(self._type, _serialize(item, self._format), rf=self) + + # For mutable types, store the deserialized value back in _data + # so mutations directly affect _data + if isinstance(deserialized, (dict, list, set)): + obj._data[self._rest_name] = deserialized + object.__setattr__(obj, cache_attr, True) # Mark as deserialized + return deserialized + + return deserialized def __set__(self, obj: Model, value) -> None: + # Clear the cached deserialized object when setting a new value + cache_attr = f"_deserialized_{self._rest_name}" + if hasattr(obj, cache_attr): + object.__delattr__(obj, cache_attr) + if value is None: # we want to wipe out entries if users set attr to None try: @@ -1046,6 +1151,7 @@ def rest_field( format: typing.Optional[str] = None, is_multipart_file_input: bool = False, xml: typing.Optional[dict[str, typing.Any]] = None, + original_tsp_name: typing.Optional[str] = None, ) -> typing.Any: return _RestField( name=name, @@ -1055,6 +1161,7 @@ def rest_field( format=format, is_multipart_file_input=is_multipart_file_input, xml=xml, + original_tsp_name=original_tsp_name, ) @@ -1184,7 +1291,7 @@ def _get_wrapped_element( _get_element(v, exclude_readonly, meta, wrapped_element) else: wrapped_element.text = _get_primitive_type_value(v) - return wrapped_element + return wrapped_element # type: ignore[no-any-return] def _get_primitive_type_value(v) -> str: @@ -1197,7 +1304,9 @@ def _get_primitive_type_value(v) -> str: return str(v) -def _create_xml_element(tag, prefix=None, ns=None): +def _create_xml_element( + tag: typing.Any, prefix: typing.Optional[str] = None, ns: typing.Optional[str] = None +) -> ET.Element: if prefix and ns: ET.register_namespace(prefix, ns) if ns: diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/azure/ai/language/questionanswering/authoring/_utils/serialization.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/azure/ai/language/questionanswering/authoring/_utils/serialization.py index cca8513e0e67..81ec1de5922b 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/azure/ai/language/questionanswering/authoring/_utils/serialization.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/azure/ai/language/questionanswering/authoring/_utils/serialization.py @@ -1,3 +1,4 @@ +# pylint: disable=line-too-long,useless-suppression,too-many-lines # coding=utf-8 # -------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. @@ -5,7 +6,6 @@ # Code generated by Microsoft (R) Python Code Generator. # Changes may cause incorrect behavior and will be lost if the code is regenerated. # -------------------------------------------------------------------------- -# pylint: disable=line-too-long,useless-suppression,too-many-lines,duplicate-code,missing-module-docstring,missing-class-docstring,missing-function-docstring,consider-using-f-string,invalid-name,too-many-locals,too-many-branches # pyright: reportUnnecessaryTypeIgnoreComment=false @@ -787,7 +787,7 @@ def serialize_data(self, data, data_type, **kwargs): # If dependencies is empty, try with current data class # It has to be a subclass of Enum anyway - enum_type = self.dependencies.get(data_type, data.__class__) + enum_type = self.dependencies.get(data_type, cast(type, data.__class__)) if issubclass(enum_type, Enum): return Serializer.serialize_enum(data, enum_obj=enum_type) @@ -821,13 +821,20 @@ def serialize_basic(cls, data, data_type, **kwargs): :param str data_type: Type of object in the iterable. :rtype: str, int, float, bool :return: serialized object + :raises TypeError: raise if data_type is not one of str, int, float, bool. """ custom_serializer = cls._get_custom_serializers(data_type, **kwargs) if custom_serializer: return custom_serializer(data) if data_type == "str": return cls.serialize_unicode(data) - return eval(data_type)(data) # nosec # pylint: disable=eval-used + if data_type == "int": + return int(data) + if data_type == "float": + return float(data) + if data_type == "bool": + return bool(data) + raise TypeError("Unknown basic data type: {}".format(data_type)) @classmethod def serialize_unicode(cls, data): @@ -1757,7 +1764,7 @@ def deserialize_basic(self, attr, data_type): # pylint: disable=too-many-return :param str data_type: deserialization data type. :return: Deserialized basic type. :rtype: str, int, float or bool - :raises TypeError: if string format is not valid. + :raises TypeError: if string format is not valid or data_type is not one of str, int, float, bool. """ # If we're here, data is supposed to be a basic type. # If it's still an XML node, take the text @@ -1783,7 +1790,11 @@ def deserialize_basic(self, attr, data_type): # pylint: disable=too-many-return if data_type == "str": return self.deserialize_unicode(attr) - return eval(data_type)(attr) # nosec # pylint: disable=eval-used + if data_type == "int": + return int(attr) + if data_type == "float": + return float(attr) + raise TypeError("Unknown basic data type: {}".format(data_type)) @staticmethod def deserialize_unicode(data): diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_create_and_deploy_project_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_create_and_deploy_project_async.py index 788f800fc8d0..deec3d6852c1 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_create_and_deploy_project_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_create_and_deploy_project_async.py @@ -1,3 +1,9 @@ +# coding=utf-8 +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + """Async sample - Create and deploy a Question Answering authoring project.""" import os @@ -15,7 +21,7 @@ async def sample_create_and_deploy_project_async(): client = QuestionAnsweringAuthoringClient(endpoint, AzureKeyCredential(key)) async with client: project_name = "IsaacNewton" - project = await client.create_project( + project = await client.create_project( # pylint: disable=no-value-for-parameter project_name=project_name, options={ "description": "Biography of Sir Isaac Newton", @@ -31,7 +37,7 @@ async def sample_create_and_deploy_project_async(): if p["projectName"] == project_name: print(f"Found project {p['projectName']}") - update_sources_poller = await client.begin_update_sources( + update_sources_poller = await client.begin_update_sources( # pylint: disable=no-value-for-parameter project_name=project_name, sources=[ _models.UpdateSourceRecord( diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_export_import_project_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_export_import_project_async.py index a1f3258484af..c8bf92acfff7 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_export_import_project_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_export_import_project_async.py @@ -1,3 +1,9 @@ +# coding=utf-8 +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + """Async sample - Export and import a Question Answering authoring project.""" import os diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_update_knowledge_sources_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_update_knowledge_sources_async.py index 5fbc34812413..58f48a738a8c 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_update_knowledge_sources_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/async_samples/sample_update_knowledge_sources_async.py @@ -1,3 +1,9 @@ +# coding=utf-8 +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + """Async sample - Update project knowledge sources and add QnAs & synonyms.""" import os @@ -15,7 +21,7 @@ async def sample_update_knowledge_sources_async(): client = QuestionAnsweringAuthoringClient(endpoint, AzureKeyCredential(key)) async with client: project_name = "MicrosoftFAQProject" - await client.create_project( + await client.create_project( # pylint: disable=no-value-for-parameter project_name=project_name, options={ "description": "Test project for some Microsoft QnAs", @@ -25,7 +31,7 @@ async def sample_update_knowledge_sources_async(): }, ) - sources_poller = await client.begin_update_sources( + sources_poller = await client.begin_update_sources( # pylint: disable=no-value-for-parameter project_name=project_name, sources=[ _models.UpdateSourceRecord( @@ -44,7 +50,7 @@ async def sample_update_knowledge_sources_async(): await sources_poller.result() print("Knowledge source added (MicrosoftFAQ)") - qna_poller = await client.begin_update_qnas( + qna_poller = await client.begin_update_qnas( # pylint: disable=no-value-for-parameter project_name=project_name, qnas=[ _models.UpdateQnaRecord( @@ -61,7 +67,7 @@ async def sample_update_knowledge_sources_async(): await qna_poller.result() print("QnA added (1 record)") - await client.update_synonyms( + await client.update_synonyms( # pylint: disable=no-value-for-parameter project_name=project_name, synonyms=_models.SynonymAssets( value=[ diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_create_and_deploy_project.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_create_and_deploy_project.py index de1a97d75277..d90276188291 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_create_and_deploy_project.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_create_and_deploy_project.py @@ -1,3 +1,9 @@ +# coding=utf-8 +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + """Sample - Create and deploy a Question Answering authoring project. This sample demonstrates how to: @@ -26,7 +32,7 @@ def sample_create_and_deploy_project(): client = QuestionAnsweringAuthoringClient(endpoint, AzureKeyCredential(key)) with client: project_name = "IsaacNewton" - project = client.create_project( + project = client.create_project( # pylint: disable=no-value-for-parameter project_name=project_name, options={ "description": "Biography of Sir Isaac Newton", @@ -46,7 +52,7 @@ def sample_create_and_deploy_project(): if p["projectName"] == project_name: print(f"Found project {p['projectName']}") - update_sources_poller = client.begin_update_sources( + update_sources_poller = client.begin_update_sources( # pylint: disable=no-value-for-parameter project_name=project_name, sources=[ _models.UpdateSourceRecord( diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_export_import_project.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_export_import_project.py index da92154cc985..df6402121b51 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_export_import_project.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_export_import_project.py @@ -1,3 +1,9 @@ +# coding=utf-8 +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + """Sample - Export and import a Question Answering authoring project. Shows how to: diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_update_knowledge_sources.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_update_knowledge_sources.py index cdeec7abca36..38dd1fd1e764 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_update_knowledge_sources.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/samples/sample_update_knowledge_sources.py @@ -1,3 +1,9 @@ +# coding=utf-8 +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ + """Sample - Update project knowledge sources and add QnAs & synonyms. Demonstrates: @@ -26,7 +32,7 @@ def sample_update_knowledge_sources(): client = QuestionAnsweringAuthoringClient(endpoint, AzureKeyCredential(key)) with client: project_name = "MicrosoftFAQProject" - client.create_project( + client.create_project( # pylint: disable=no-value-for-parameter project_name=project_name, options={ "description": "Test project for some Microsoft QnAs", @@ -36,7 +42,7 @@ def sample_update_knowledge_sources(): }, ) - sources_poller = client.begin_update_sources( + sources_poller = client.begin_update_sources( # pylint: disable=no-value-for-parameter project_name=project_name, sources=[ _models.UpdateSourceRecord( @@ -55,7 +61,7 @@ def sample_update_knowledge_sources(): sources_poller.result() print("Knowledge source added (MicrosoftFAQ)") - qna_poller = client.begin_update_qnas( + qna_poller = client.begin_update_qnas( # pylint: disable=no-value-for-parameter project_name=project_name, qnas=[ _models.UpdateQnaRecord( @@ -72,7 +78,7 @@ def sample_update_knowledge_sources(): qna_poller.result() print("QnA added (1 record)") - client.update_synonyms( + client.update_synonyms( # pylint: disable=no-value-for-parameter project_name=project_name, synonyms=_models.SynonymAssets( value=[ diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/helpers.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/helpers.py index 9806e69dad4f..cec72f5947e1 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/helpers.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/helpers.py @@ -29,6 +29,7 @@ def create_test_project( return AuthoringTestHelper.export_project( client, project_name, delete_project=delete_old_project, **source_kwargs ) + return None @staticmethod def add_sources(client, project_name, **kwargs): @@ -49,7 +50,7 @@ def add_sources(client, project_name, **kwargs): poller.result() @staticmethod - def export_project(client, project_name, delete_project=True, **kwargs): + def export_project(client, project_name, delete_project=True, **kwargs): # pylint: disable=useless-return # begin_export poller is typed as LROPoller[None]; generator currently discards # the final body so result() returns None. We only validate successful completion. export_poller = client.begin_export(project_name=project_name, file_format="json", **kwargs) @@ -57,10 +58,8 @@ def export_project(client, project_name, delete_project=True, **kwargs): if delete_project: delete_poller = client.begin_delete_project(project_name=project_name, **kwargs) delete_poller.result() - # No export URL available due to None payload; caller should not depend on return value. return None - class AuthoringAsyncTestHelper: """Async utility helper for creating and exporting authoring test projects.""" diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_create_and_deploy_project.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_create_and_deploy_project.py index 6a22ab86837c..31378f529966 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_create_and_deploy_project.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_create_and_deploy_project.py @@ -1,11 +1,10 @@ # pylint: disable=line-too-long,useless-suppression -import pytest -from typing import Any, Dict, cast +from helpers import AuthoringTestHelper +from testcase import QuestionAnsweringAuthoringTestCase + from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering.authoring import QuestionAnsweringAuthoringClient -from helpers import AuthoringTestHelper -from testcase import QuestionAnsweringAuthoringTestCase class TestCreateAndDeploy(QuestionAnsweringAuthoringTestCase): @@ -20,12 +19,12 @@ def test_polling_interval(self, qna_authoring_creds): ) assert client._config.polling_interval == 1 - def test_create_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + def test_create_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) project_name = "IsaacNewton" - client.create_project( + client.create_project( # pylint: disable=no-value-for-parameter project_name=project_name, options={ "description": "Biography of Sir Isaac Newton", @@ -37,7 +36,7 @@ def test_create_project(self, recorded_test, qna_authoring_creds): # type: igno found = any(p.get("projectName") == project_name for p in client.list_projects()) assert found - def test_deploy_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + def test_deploy_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) @@ -46,12 +45,12 @@ def test_deploy_project(self, recorded_test, qna_authoring_creds): # type: igno client, project_name=project_name, is_deployable=True, - polling_interval=0 if self.is_playback else None, + polling_interval=0 if self.is_playback else None, # pylint: disable=using-constant-test ) deployment_poller = client.begin_deploy_project( project_name=project_name, deployment_name="production", - polling_interval=0 if self.is_playback else None, + polling_interval=0 if self.is_playback else None, # pylint: disable=using-constant-test ) # Preview LRO returns None; just ensure it completes without error deployment_poller.result() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_create_and_deploy_project_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_create_and_deploy_project_async.py index 4385d4d3c4e9..b9f1111c44f1 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_create_and_deploy_project_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_create_and_deploy_project_async.py @@ -1,21 +1,21 @@ import pytest -from typing import Any, Dict, cast +from helpers import AuthoringAsyncTestHelper +from testcase import QuestionAnsweringAuthoringTestCase + from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering.authoring.aio import QuestionAnsweringAuthoringClient -from helpers import AuthoringAsyncTestHelper -from testcase import QuestionAnsweringAuthoringTestCase class TestCreateAndDeployAsync(QuestionAnsweringAuthoringTestCase): @pytest.mark.asyncio - async def test_create_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + async def test_create_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) project_name = "IsaacNewton" async with client: - await client.create_project( + await client.create_project( # pylint: disable=no-value-for-parameter project_name=project_name, options={ "description": "Biography of Sir Isaac Newton", @@ -31,7 +31,7 @@ async def test_create_project(self, recorded_test, qna_authoring_creds): # type assert found @pytest.mark.asyncio - async def test_deploy_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + async def test_deploy_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) @@ -41,12 +41,12 @@ async def test_deploy_project(self, recorded_test, qna_authoring_creds): # type client, project_name=project_name, is_deployable=True, - polling_interval=0 if self.is_playback else None, + polling_interval=0 if self.is_playback else None, # pylint: disable=using-constant-test ) deployment_poller = await client.begin_deploy_project( project_name=project_name, deployment_name="production", - polling_interval=0 if self.is_playback else None, + polling_interval=0 if self.is_playback else None, # pylint: disable=using-constant-test ) # Preview LRO returns None; just await completion await deployment_poller.result() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_export_import_project.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_export_import_project.py index 69d2912feed5..d1b374223ad7 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_export_import_project.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_export_import_project.py @@ -1,28 +1,29 @@ +from helpers import AuthoringTestHelper +from testcase import QuestionAnsweringAuthoringTestCase + from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering.authoring import QuestionAnsweringAuthoringClient, models as _models -from helpers import AuthoringTestHelper -from testcase import QuestionAnsweringAuthoringTestCase class TestExportAndImport(QuestionAnsweringAuthoringTestCase): - def test_export_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + def test_export_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) project_name = "IsaacNewton" AuthoringTestHelper.create_test_project( - client, project_name=project_name, polling_interval=0 if self.is_playback else None + client, project_name=project_name, polling_interval=0 if self.is_playback else None # pylint: disable=using-constant-test ) export_poller = client.begin_export( project_name=project_name, file_format="json", - polling_interval=0 if self.is_playback else None, + polling_interval=0 if self.is_playback else None, # pylint: disable=using-constant-test ) export_poller.result() # LROPoller[None]; ensure no exception assert export_poller.done() - def test_import_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + def test_import_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) @@ -33,7 +34,7 @@ def test_import_project(self, recorded_test, qna_authoring_creds): # type: igno project_name=project_name, get_export_url=False, delete_old_project=False, - polling_interval=0 if self.is_playback else None, + polling_interval=0 if self.is_playback else None, # pylint: disable=using-constant-test ) # Wait briefly until project is visible (eventual consistency safeguard) visible = any(p.get("projectName") == project_name for p in client.list_projects()) @@ -63,7 +64,7 @@ def test_import_project(self, recorded_test, qna_authoring_creds): # type: igno project_name=project_name, body=project_payload, file_format="json", - polling_interval=0 if self.is_playback else None, + polling_interval=0 if self.is_playback else None, # pylint: disable=using-constant-test ) import_poller.result() # LROPoller[None]; ensure completion assert import_poller.done() diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_export_import_project_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_export_import_project_async.py index eb6836402720..dafe287b8a41 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_export_import_project_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_export_import_project_async.py @@ -1,17 +1,17 @@ -import pytest import asyncio -from typing import Any, Dict, cast -from azure.ai.language.questionanswering.authoring import models as _models -from azure.core.credentials import AzureKeyCredential -from azure.ai.language.questionanswering.authoring.aio import QuestionAnsweringAuthoringClient +import pytest from helpers import AuthoringAsyncTestHelper from testcase import QuestionAnsweringAuthoringTestCase +from azure.ai.language.questionanswering.authoring import models as _models +from azure.core.credentials import AzureKeyCredential +from azure.ai.language.questionanswering.authoring.aio import QuestionAnsweringAuthoringClient + class TestExportAndImportAsync(QuestionAnsweringAuthoringTestCase): @pytest.mark.asyncio - async def test_export_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + async def test_export_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) @@ -28,7 +28,7 @@ async def test_export_project(self, recorded_test, qna_authoring_creds): # type assert poller.done() @pytest.mark.asyncio - async def test_import_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + async def test_import_project(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_update_knowledge_sources.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_update_knowledge_sources.py index 9c8e324e8288..f104a87f739e 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_update_knowledge_sources.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_update_knowledge_sources.py @@ -1,14 +1,15 @@ -from azure.core.credentials import AzureKeyCredential -from azure.ai.language.questionanswering.authoring import QuestionAnsweringAuthoringClient -from azure.ai.language.questionanswering.authoring import models as _models from typing import cast from helpers import AuthoringTestHelper from testcase import QuestionAnsweringAuthoringTestCase +from azure.core.credentials import AzureKeyCredential +from azure.ai.language.questionanswering.authoring import QuestionAnsweringAuthoringClient +from azure.ai.language.questionanswering.authoring import models as _models + class TestSourcesQnasSynonyms(QuestionAnsweringAuthoringTestCase): - def test_add_source(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + def test_add_source(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) @@ -30,16 +31,16 @@ def test_add_source(self, recorded_test, qna_authoring_creds): # type: ignore[n } ) ] - poller = client.begin_update_sources( + poller = client.begin_update_sources( # pylint: disable=no-value-for-parameter project_name=project_name, sources=cast(list[_models.UpdateSourceRecord], update_source_ops), content_type="application/json", - polling_interval=0 if self.is_playback else None, # type: ignore[arg-type] + polling_interval=0 if self.is_playback else None, # type: ignore[arg-type] # pylint: disable=using-constant-test ) poller.result() assert any(s.get("displayName") == source_display_name for s in client.list_sources(project_name=project_name)) - def test_add_qna(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + def test_add_qna(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) @@ -59,11 +60,11 @@ def test_add_qna(self, recorded_test, qna_authoring_creds): # type: ignore[name } ) ] - poller = client.begin_update_qnas( + poller = client.begin_update_qnas( # pylint: disable=no-value-for-parameter project_name=project_name, qnas=cast(list[_models.UpdateQnaRecord], update_qna_ops), content_type="application/json", - polling_interval=0 if self.is_playback else None, # type: ignore[arg-type] + polling_interval=0 if self.is_playback else None, # type: ignore[arg-type] # pylint: disable=using-constant-test ) poller.result() assert any( @@ -71,7 +72,7 @@ def test_add_qna(self, recorded_test, qna_authoring_creds): # type: ignore[name for q in client.list_qnas(project_name=project_name) ) - def test_add_synonym(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + def test_add_synonym(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) @@ -82,7 +83,7 @@ def test_add_synonym(self, recorded_test, qna_authoring_creds): # type: ignore[ _models.WordAlterations(alterations=["qnamaker", "qna maker"]), ] ) - client.update_synonyms( + client.update_synonyms( # pylint: disable=no-value-for-parameter project_name=project_name, synonyms=synonyms_model, content_type="application/json", diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_update_knowledge_sources_async.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_update_knowledge_sources_async.py index a565ef5ffcb7..39a485f54a79 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_update_knowledge_sources_async.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/test_update_knowledge_sources_async.py @@ -1,23 +1,24 @@ -import pytest from typing import cast +import pytest +from helpers import AuthoringAsyncTestHelper +from testcase import QuestionAnsweringAuthoringTestCase + from azure.core.credentials import AzureKeyCredential from azure.ai.language.questionanswering.authoring.aio import QuestionAnsweringAuthoringClient from azure.ai.language.questionanswering.authoring import models as _models -from helpers import AuthoringAsyncTestHelper -from testcase import QuestionAnsweringAuthoringTestCase class TestSourcesQnasSynonymsAsync(QuestionAnsweringAuthoringTestCase): @pytest.mark.asyncio - async def test_add_source(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + async def test_add_source(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) project_name = "IsaacNewton" async with client: await AuthoringAsyncTestHelper.create_test_project( - client, project_name=project_name, polling_interval=0 if self.is_playback else None + client, project_name=project_name, polling_interval=0 if self.is_playback else None # pylint: disable=using-constant-test ) update_source_ops = [ _models.UpdateSourceRecord( @@ -34,11 +35,11 @@ async def test_add_source(self, recorded_test, qna_authoring_creds): # type: ig } ) ] - poller = await client.begin_update_sources( + poller = await client.begin_update_sources( # pylint: disable=no-value-for-parameter project_name=project_name, sources=cast(list[_models.UpdateSourceRecord], update_source_ops), content_type="application/json", - polling_interval=0 if self.is_playback else None, # type: ignore[arg-type] + polling_interval=0 if self.is_playback else None, # type: ignore[arg-type] # pylint: disable=using-constant-test ) await poller.result() found = False @@ -48,14 +49,14 @@ async def test_add_source(self, recorded_test, qna_authoring_creds): # type: ig assert found @pytest.mark.asyncio - async def test_add_qna(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + async def test_add_qna(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) project_name = "IsaacNewton" async with client: await AuthoringAsyncTestHelper.create_test_project( - client, project_name=project_name, polling_interval=0 if self.is_playback else None + client, project_name=project_name, polling_interval=0 if self.is_playback else None # pylint: disable=using-constant-test ) question = "What is the easiest way to use azure services in my .NET project?" answer = "Using Microsoft's Azure SDKs" @@ -71,11 +72,11 @@ async def test_add_qna(self, recorded_test, qna_authoring_creds): # type: ignor } ) ] - poller = await client.begin_update_qnas( + poller = await client.begin_update_qnas( # pylint: disable=no-value-for-parameter project_name=project_name, qnas=cast(list[_models.UpdateQnaRecord], update_qna_ops), content_type="application/json", - polling_interval=0 if self.is_playback else None, # type: ignore[arg-type] + polling_interval=0 if self.is_playback else None, # type: ignore[arg-type] # pylint: disable=using-constant-test ) await poller.result() found = False @@ -85,21 +86,21 @@ async def test_add_qna(self, recorded_test, qna_authoring_creds): # type: ignor assert found @pytest.mark.asyncio - async def test_add_synonym(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] + async def test_add_synonym(self, recorded_test, qna_authoring_creds): # type: ignore[name-defined] # pylint: disable=unused-argument client = QuestionAnsweringAuthoringClient( qna_authoring_creds["endpoint"], AzureKeyCredential(qna_authoring_creds["key"]) ) project_name = "IsaacNewton" async with client: await AuthoringAsyncTestHelper.create_test_project( - client, project_name=project_name, polling_interval=0 if self.is_playback else None + client, project_name=project_name, polling_interval=0 if self.is_playback else None # pylint: disable=using-constant-test ) synonyms_model = _models.SynonymAssets( value=[ _models.WordAlterations(alterations=["qnamaker", "qna maker"]), ] ) - await client.update_synonyms( + await client.update_synonyms( # pylint: disable=no-value-for-parameter project_name=project_name, synonyms=synonyms_model, content_type="application/json", diff --git a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/testcase.py b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/testcase.py index a8fb6a4a8d00..3a7239e403a9 100644 --- a/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/testcase.py +++ b/sdk/cognitivelanguage/azure-ai-language-questionanswering-authoring/tests/testcase.py @@ -4,6 +4,6 @@ class QuestionAnsweringAuthoringTestCase(AzureRecordedTestCase): @property def kwargs_for_polling(self): - if self.is_playback: + if self.is_playback: # pylint: disable=using-constant-test return {"polling_interval": 0} return {}