Skip to content

Commit 25be6ef

Browse files
authored
[evaluation] ci,fix: Enable pylint in CI (Azure#37531)
* chore: Add isort config * style,fix(pylint): Resolve C0411(wrong-import-order) and C0412(ungrouped-imports) Resolved with `isort azure/` * fix(pylint): Resolve R1705(no-else-return) * fix(pylint): Resolve W1202(logging-format-interpolation) * fix(pylint): Resolve C4732(specify-parameter-names) From azure-pylint-guidelines-checker * fix(pylint) Resolve C3001(unnecessary-lambda-assignment) * fix(pylint): Resolve W0718(broad-exception-caught) * fix(pylint): Resolve W1514(unspecified-encoding) * fix(pylint): Ignore W0212(protected-access) * fix(pylint): Ignore W0613(unused-argument) for kwargs * fix(pylint): Ignore C4748(client-accepts-api-version-keyword), C4717(missing-client-constructor-parameter-credential), C4718(missing-client-constructor-parameter-kwargs) These aren't clients for azure services * fix(pylint): Resolve C4747(enum-must-inherit-case-insensitive-enum-meta) * refactor: Refactor get_int_env_var to streamline the typing get_int_env_var is always called with a default value, and the docstring suggests that the intent is that the return is always an int. * fix(pylint): Resolve C4742(docstring-missing-rtype) * fix(pylint): Resolve C4722(client-method-missing-type-annotations) * fix(pylint): Resolve C4739(docstring-missing-param) and C4740(docstring-missing-type) * fix(pylint): Resolve C4743(docstring-should-be-keyword) * ci: Enable pylint
1 parent 3a7a934 commit 25be6ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+477
-381
lines changed

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/__init__.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,7 @@
2525
from ._evaluators._rouge import RougeScoreEvaluator, RougeType
2626
from ._evaluators._similarity import SimilarityEvaluator
2727
from ._evaluators._xpia import IndirectAttackEvaluator
28-
from ._model_configurations import (
29-
AzureAIProject,
30-
AzureOpenAIModelConfiguration,
31-
OpenAIModelConfiguration,
32-
)
28+
from ._model_configurations import AzureAIProject, AzureOpenAIModelConfiguration, OpenAIModelConfiguration
3329

3430
__all__ = [
3531
"evaluate",

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_common/rai_service.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111

1212
import jwt
1313
import numpy as np
14-
from azure.core.credentials import TokenCredential
15-
from azure.identity import DefaultAzureCredential
1614

15+
from azure.ai.evaluation._exceptions import ErrorBlame, ErrorCategory, ErrorTarget, EvaluationException
1716
from azure.ai.evaluation._http_utils import get_async_http_client
18-
from azure.ai.evaluation._exceptions import EvaluationException, ErrorBlame, ErrorCategory, ErrorTarget
1917
from azure.ai.evaluation._model_configurations import AzureAIProject
18+
from azure.core.credentials import TokenCredential
19+
from azure.identity import DefaultAzureCredential
2020

2121
from .constants import (
2222
CommonConstants,
@@ -348,7 +348,7 @@ async def _get_service_discovery_url(azure_ai_project: AzureAIProject, token: st
348348
)
349349

350350
if response.status_code != 200:
351-
msg = f"Failed to retrieve the discovery service URL."
351+
msg = "Failed to retrieve the discovery service URL."
352352
raise EvaluationException(
353353
message=msg,
354354
internal_message=msg,

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_common/utils.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,15 @@
22
# Copyright (c) Microsoft Corporation. All rights reserved.
33
# ---------------------------------------------------------
44

5-
from typing import Optional, Union
5+
import threading
6+
from typing import List, Optional, Union
67

7-
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
8+
import nltk
9+
import numpy as np
810

9-
try:
10-
from . import constants
11-
except ImportError:
12-
import constants
11+
from azure.ai.evaluation._model_configurations import AzureOpenAIModelConfiguration, OpenAIModelConfiguration
1312

14-
from typing import List
15-
16-
import threading
17-
import numpy as np
18-
import nltk
13+
from . import constants
1914

2015
_nltk_data_download_lock = threading.Lock()
2116

@@ -46,15 +41,21 @@ def ensure_nltk_data_downloaded():
4641
"""Download NLTK data packages if not already downloaded."""
4742
with _nltk_data_download_lock:
4843
try:
49-
from nltk.tokenize.nist import NISTTokenizer
44+
from nltk.tokenize.nist import NISTTokenizer # pylint: disable=unused-import
5045
except LookupError:
5146
nltk.download("perluniprops")
5247
nltk.download("punkt")
5348
nltk.download("punkt_tab")
5449

5550

5651
def nltk_tokenize(text: str) -> List[str]:
57-
"""Tokenize the input text using the NLTK tokenizer."""
52+
"""Tokenize the input text using the NLTK tokenizer.
53+
54+
:param text: The text to tokenize
55+
:type text: str
56+
:return: A list of tokens
57+
:rtype: list[str]
58+
"""
5859
ensure_nltk_data_downloaded()
5960

6061
if not text.isascii():
@@ -69,15 +70,15 @@ def nltk_tokenize(text: str) -> List[str]:
6970
return list(tokens)
7071

7172

72-
def check_and_add_api_version_for_aoai_model_config(
73+
def ensure_api_version_in_aoai_model_config(
7374
model_config: Union[AzureOpenAIModelConfiguration, OpenAIModelConfiguration],
7475
default_api_version: str,
7576
) -> None:
7677
if "azure_endpoint" in model_config or "azure_deployment" in model_config:
7778
model_config["api_version"] = model_config.get("api_version", default_api_version)
7879

7980

80-
def check_and_add_user_agent_for_aoai_model_config(
81+
def ensure_user_agent_in_aoai_model_config(
8182
model_config: Union[AzureOpenAIModelConfiguration, OpenAIModelConfiguration],
8283
prompty_model_config: dict,
8384
user_agent: Optional[str] = None,

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_constants.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ class Prefixes:
3939
TSG_OUTPUTS = "__outputs."
4040

4141

42+
class DefaultOpenEncoding:
43+
"""Enum that captures SDK's default values for the encoding param of open(...)"""
44+
45+
READ = "utf-8-sig"
46+
"""SDK Default Encoding when reading a file"""
47+
WRITE = "utf-8"
48+
"""SDK Default Encoding when writing a file"""
49+
50+
4251
DEFAULT_EVALUATION_RESULTS_FILE_NAME = "evaluation_results.json"
4352

4453
CONTENT_SAFETY_DEFECT_RATE_THRESHOLD_DEFAULT = 4

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_evaluate/_batch_run_client/batch_run_context.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55

66
from promptflow._sdk._constants import PF_FLOW_ENTRY_IN_TMP, PF_FLOW_META_LOAD_IN_SUBPROCESS
77
from promptflow._utils.user_agent_utils import ClientUserAgentUtil
8+
from promptflow.tracing._integrations._openai_injector import inject_openai_api, recover_openai_api
9+
810
from azure.ai.evaluation._constants import (
911
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
1012
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT_DEFAULT,
1113
PF_BATCH_TIMEOUT_SEC,
1214
PF_BATCH_TIMEOUT_SEC_DEFAULT,
1315
)
14-
from promptflow.tracing._integrations._openai_injector import inject_openai_api, recover_openai_api
1516

1617
from ..._user_agent import USER_AGENT
1718
from .._utils import set_event_loop_policy

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_evaluate/_batch_run_client/code_client.py

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,26 @@
44
import inspect
55
import json
66
import logging
7+
import os
8+
from pathlib import Path
9+
from typing import Callable, Dict, Optional, Union
710

811
import pandas as pd
9-
1012
from promptflow.contracts.types import AttrDict
11-
from azure.ai.evaluation._evaluate._utils import _apply_column_mapping, _has_aggregator, get_int_env_var, load_jsonl
1213
from promptflow.tracing import ThreadPoolExecutorWithContext as ThreadPoolExecutor
13-
from azure.ai.evaluation._exceptions import EvaluationException, ErrorBlame, ErrorCategory, ErrorTarget
14+
15+
from azure.ai.evaluation._evaluate._utils import _apply_column_mapping, _has_aggregator, get_int_env_var, load_jsonl
16+
from azure.ai.evaluation._exceptions import ErrorBlame, ErrorCategory, ErrorTarget, EvaluationException
1417

1518
from ..._constants import PF_BATCH_TIMEOUT_SEC, PF_BATCH_TIMEOUT_SEC_DEFAULT
1619

1720
LOGGER = logging.getLogger(__name__)
1821

1922

2023
class CodeRun:
21-
def __init__(self, run, input_data, evaluator_name=None, aggregated_metrics=None, **kwargs):
24+
def __init__(
25+
self, run, input_data, evaluator_name=None, aggregated_metrics=None, **kwargs # pylint: disable=unused-argument
26+
):
2227
self.run = run
2328
self.evaluator_name = evaluator_name if evaluator_name is not None else ""
2429
self.input_data = input_data
@@ -40,25 +45,29 @@ def get_aggregated_metrics(self):
4045
else None
4146
)
4247
except Exception as ex: # pylint: disable=broad-exception-caught
43-
LOGGER.debug(f"Error calculating metrics for evaluator {self.evaluator_name}, failed with error {str(ex)}")
48+
LOGGER.debug("Error calculating metrics for evaluator %s, failed with error %s", self.evaluator_name, ex)
4449
aggregated_metrics = None
4550

4651
if not isinstance(aggregated_metrics, dict):
4752
LOGGER.warning(
48-
f"Aggregated metrics for evaluator {self.evaluator_name}"
49-
f" is not a dictionary will not be logged as metrics"
53+
"Aggregated metrics for evaluator %s is not a dictionary will not be logged as metrics",
54+
self.evaluator_name,
5055
)
5156

5257
aggregated_metrics = aggregated_metrics if isinstance(aggregated_metrics, dict) else {}
5358

5459
return aggregated_metrics
5560

5661

57-
class CodeClient:
58-
def __init__(self):
62+
class CodeClient: # pylint: disable=client-accepts-api-version-keyword
63+
def __init__( # pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs
64+
self,
65+
) -> None:
5966
self._thread_pool = ThreadPoolExecutor(thread_name_prefix="evaluators_thread")
6067

61-
def _calculate_metric(self, evaluator, input_df, column_mapping, evaluator_name):
68+
def _calculate_metric(
69+
self, evaluator: Callable, input_df: pd.DataFrame, column_mapping: Optional[Dict[str, str]], evaluator_name: str
70+
) -> pd.DataFrame:
6271
row_metric_futures = []
6372
row_metric_results = []
6473
input_df = _apply_column_mapping(input_df, column_mapping)
@@ -110,11 +119,18 @@ def _calculate_aggregations(self, evaluator, run):
110119
return aggregated_output
111120
except Exception as ex: # pylint: disable=broad-exception-caught
112121
LOGGER.warning(
113-
f"Error calculating aggregations for evaluator {run.evaluator_name}," f" failed with error {str(ex)}"
122+
"Error calculating aggregations for evaluator %s, failed with error %s", run.evaluator_name, ex
114123
)
115124
return None
116125

117-
def run(self, flow, data, evaluator_name=None, column_mapping=None, **kwargs):
126+
def run(
127+
self, # pylint: disable=unused-argument
128+
flow: Callable,
129+
data: Union[os.PathLike, Path, pd.DataFrame],
130+
evaluator_name: Optional[str] = None,
131+
column_mapping: Optional[Dict[str, str]] = None,
132+
**kwargs,
133+
) -> CodeRun:
118134
input_df = data
119135
if not isinstance(input_df, pd.DataFrame):
120136
try:
@@ -129,22 +145,28 @@ def run(self, flow, data, evaluator_name=None, column_mapping=None, **kwargs):
129145
) from exc
130146

131147
input_df = pd.DataFrame(json_data)
132-
eval_future = self._thread_pool.submit(self._calculate_metric, flow, input_df, column_mapping, evaluator_name)
148+
eval_future = self._thread_pool.submit(
149+
self._calculate_metric,
150+
evaluator=flow,
151+
input_df=input_df,
152+
column_mapping=column_mapping,
153+
evaluator_name=evaluator_name,
154+
)
133155
run = CodeRun(run=eval_future, input_data=data, evaluator_name=evaluator_name, aggregated_metrics=None)
134156
aggregation_future = self._thread_pool.submit(self._calculate_aggregations, evaluator=flow, run=run)
135157
run.aggregated_metrics = aggregation_future
136158
return run
137159

138-
def get_details(self, run, all_results=False):
160+
def get_details(self, run: CodeRun, all_results: bool = False) -> pd.DataFrame:
139161
result_df = run.get_result_df(exclude_inputs=not all_results)
140162
return result_df
141163

142-
def get_metrics(self, run):
164+
def get_metrics(self, run: CodeRun) -> Optional[None]:
143165
try:
144166
aggregated_metrics = run.get_aggregated_metrics()
145167
print("Aggregated metrics")
146168
print(aggregated_metrics)
147169
except Exception as ex: # pylint: disable=broad-exception-caught
148-
LOGGER.debug(f"Error calculating metrics for evaluator {run.evaluator_name}, failed with error {str(ex)}")
170+
LOGGER.debug("Error calculating metrics for evaluator %s, failed with error %s", run.evaluator_name, ex)
149171
return None
150172
return aggregated_metrics

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_evaluate/_batch_run_client/proxy_client.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,40 @@
44
import inspect
55
import logging
66
import os
7+
from concurrent.futures import Future
8+
from typing import Any, Callable, Dict, Optional, Union
79

810
import numpy as np
9-
11+
import pandas as pd
1012
from promptflow.client import PFClient
13+
from promptflow.entities import Run
1114
from promptflow.tracing import ThreadPoolExecutorWithContext as ThreadPoolExecutor
1215

1316
LOGGER = logging.getLogger(__name__)
1417

1518

1619
class ProxyRun:
17-
def __init__(self, run, **kwargs):
20+
def __init__(self, run: Future, **kwargs) -> None: # pylint: disable=unused-argument
1821
self.run = run
1922

2023

21-
class ProxyClient:
22-
def __init__(self, pf_client: PFClient):
24+
class ProxyClient: # pylint: disable=client-accepts-api-version-keyword
25+
def __init__( # pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs
26+
self, pf_client: PFClient
27+
) -> None:
2328
self._pf_client = pf_client
2429
self._thread_pool = ThreadPoolExecutor(thread_name_prefix="evaluators_thread")
2530

26-
def run(self, flow, data, column_mapping=None, **kwargs):
31+
def run(
32+
self,
33+
flow: Union[str, os.PathLike, Callable],
34+
data: Union[str, os.PathLike],
35+
column_mapping: Optional[Dict[str, str]] = None,
36+
**kwargs
37+
) -> ProxyRun:
2738
flow_to_run = flow
2839
if hasattr(flow, "_to_async"):
29-
flow_to_run = flow._to_async()
40+
flow_to_run = flow._to_async() # pylint: disable=protected-access
3041

3142
batch_use_async = self._should_batch_use_async(flow_to_run)
3243
eval_future = self._thread_pool.submit(
@@ -39,23 +50,22 @@ def run(self, flow, data, column_mapping=None, **kwargs):
3950
)
4051
return ProxyRun(run=eval_future)
4152

42-
def get_details(self, proxy_run, all_results=False):
43-
run = proxy_run.run.result()
53+
def get_details(self, proxy_run: ProxyRun, all_results: bool = False) -> pd.DataFrame:
54+
run: Run = proxy_run.run.result()
4455
result_df = self._pf_client.get_details(run, all_results=all_results)
4556
result_df.replace("(Failed)", np.nan, inplace=True)
4657
return result_df
4758

48-
def get_metrics(self, proxy_run):
49-
run = proxy_run.run.result()
59+
def get_metrics(self, proxy_run: ProxyRun) -> Dict[str, Any]:
60+
run: Run = proxy_run.run.result()
5061
return self._pf_client.get_metrics(run)
5162

5263
@staticmethod
5364
def _should_batch_use_async(flow):
5465
if os.getenv("PF_EVALS_BATCH_USE_ASYNC", "true").lower() == "true":
5566
if hasattr(flow, "__call__") and inspect.iscoroutinefunction(flow.__call__):
5667
return True
57-
elif inspect.iscoroutinefunction(flow):
68+
if inspect.iscoroutinefunction(flow):
5869
return True
59-
else:
60-
return False
70+
return False
6171
return False

0 commit comments

Comments
 (0)