diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e144934..787fe04 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: # version_file_path: httpfpt/__init__.py lint: runs-on: ubuntu-latest - name: lint + name: lint ${{ matrix.python-version }} strategy: matrix: python-version: ['3.8'] @@ -27,25 +27,24 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up Python + - name: Setup python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + cache: pip - - name: Install Dependencies - run: pip install ruff==0.0.262 pre-commit==3.2.2 - - - name: ruff check - uses: chartboost/ruff-action@v1 - with: - args: '--config .ruff.toml' - - - name: black formatter - uses: psf/black@stable - with: - options: '--skip-string-normalization --line-length 120' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt - name: pre-commit uses: pre-commit/action@v3.0.0 with: extra_args: --all-files --verbose + + - name: pyright + uses: jakebailey/pyright-action@v1 + with: + project: . + verbose: true diff --git a/.gitignore b/.gitignore index 7c641be..2fc523d 100644 --- a/.gitignore +++ b/.gitignore @@ -78,7 +78,7 @@ httpfpt/log/ .idea/ # online case data -httpfpt/data/online/ +httpfpt/data/online*/ # ruff httpfpt/.ruff_cache/ diff --git a/httpfpt/common/env_handler.py b/httpfpt/common/env_handler.py index af0c371..6e1e423 100644 --- a/httpfpt/common/env_handler.py +++ b/httpfpt/common/env_handler.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import os -from typing import NoReturn import dotenv @@ -20,7 +19,7 @@ def get_env_dict(filepath: str) -> dict: return env_dict -def write_env_vars(filepath: str, filename: str, key: str, value: str) -> NoReturn: +def write_env_vars(filepath: str, filename: str, key: str, value: str) -> None: """ 写入 env 信息 diff --git a/httpfpt/common/json_handler.py b/httpfpt/common/json_handler.py index 60d61d2..0ee8afa 100644 --- a/httpfpt/common/json_handler.py +++ b/httpfpt/common/json_handler.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- import json import os -from typing import Optional, NoReturn, Any +from typing import Optional, Any from httpfpt.common.log import log @@ -41,7 +41,7 @@ def write_json_file( encoding: str = 'utf-8', mode: str = 'a', **kwargs, -) -> NoReturn: +) -> None: """ 写入 json 文件 @@ -53,12 +53,15 @@ def write_json_file( :param kwargs: :return: """ - if not os.path.exists(filepath): - os.makedirs(filepath) if filepath is not None: + if not os.path.exists(filepath): + os.makedirs(filepath) _file = os.path.join(filepath, filename) else: _file = filename + if not _file: + log.warning('写入 json 文件失败,文件名为空') + raise ValueError('写入 json 文件失败,文件名为空') try: with open(_file, encoding=encoding, mode=mode) as f: json.dump(data, f, ensure_ascii=False, **kwargs) diff --git a/httpfpt/common/send_request.py b/httpfpt/common/send_request.py index 6336481..bba59fc 100644 --- a/httpfpt/common/send_request.py +++ b/httpfpt/common/send_request.py @@ -3,7 +3,6 @@ import json import time from json import JSONDecodeError -from typing import NoReturn import allure import httpx @@ -91,7 +90,7 @@ def _httpx_engin(**kwargs) -> HttpxResponse: log.info('开始发送请求...') with httpx.Client( verify=verify, - proxies=proxies, + proxies=proxies, # type: ignore follow_redirects=redirects, ) as client: response = client.request(**kwargs) @@ -247,14 +246,14 @@ def send_request( return response_data @staticmethod - def log_request_setup(parsed_data: dict) -> NoReturn: + def log_request_setup(parsed_data: dict) -> None: log.info(f"请求 setup_testcase: {parsed_data['setup_testcase']}") log.info(f"请求 setup_sql: {parsed_data['setup_sql']}") log.info(f"请求 setup_hooks: {parsed_data['setup_hooks']}") log.info(f"请求 setup_wait_time: {parsed_data['setup_wait_time']}") @staticmethod - def log_request_up(parsed_data: dict) -> NoReturn: + def log_request_up(parsed_data: dict) -> None: log.info(f"用例 env: {parsed_data['env']}") log.info(f"用例 module: {parsed_data['module']}") log.info(f"用例 name: {parsed_data['name']}") @@ -272,7 +271,7 @@ def log_request_up(parsed_data: dict) -> NoReturn: log.info(f"请求 files: {parsed_data['files_no_parse']}") @staticmethod - def log_request_teardown(parsed_data: dict) -> NoReturn: + def log_request_teardown(parsed_data: dict) -> None: log.info(f"请求 teardown_sql: {parsed_data['teardown_sql']}") log.info(f"请求 teardown_hooks: {parsed_data['teardown_hooks']}") log.info(f"请求 teardown_extract: {parsed_data['teardown_extract']}") @@ -280,7 +279,7 @@ def log_request_teardown(parsed_data: dict) -> NoReturn: log.info(f"请求 teardown_wait_time: {parsed_data['teardown_wait_time']}") @staticmethod - def log_request_down(response_data: dict) -> NoReturn: + def log_request_down(response_data: dict) -> None: log.info(f"请求发送时间: {response_data['stat']['execute_time']}") str_status_code = str(response_data['status_code']) if str_status_code.startswith('4') or str_status_code.startswith('5'): @@ -290,7 +289,7 @@ def log_request_down(response_data: dict) -> NoReturn: log.info(f"响应时间: {response_data['elapsed']} ms") @staticmethod - def allure_request_setup(parsed_data: dict) -> NoReturn: + def allure_request_setup(parsed_data: dict) -> None: allure_step( '请求前置', { @@ -302,7 +301,7 @@ def allure_request_setup(parsed_data: dict) -> NoReturn: ) @staticmethod - def allure_request_up(parsed_data: dict) -> NoReturn: + def allure_request_up(parsed_data: dict) -> None: allure_step( '请求数据', { @@ -322,7 +321,7 @@ def allure_request_up(parsed_data: dict) -> NoReturn: ) @staticmethod - def allure_request_teardown(parsed_data: dict) -> NoReturn: + def allure_request_teardown(parsed_data: dict) -> None: allure_step( '请求后置', { @@ -335,7 +334,7 @@ def allure_request_teardown(parsed_data: dict) -> NoReturn: ) @staticmethod - def allure_request_down(response_data: dict) -> NoReturn: + def allure_request_down(response_data: dict) -> None: allure_step( '响应数据', { @@ -345,7 +344,7 @@ def allure_request_down(response_data: dict) -> NoReturn: ) @staticmethod - def allure_dynamic_data(parsed_data: dict) -> NoReturn: + def allure_dynamic_data(parsed_data: dict) -> None: allure.dynamic.title(parsed_data['name']) allure.dynamic.description(parsed_data['description']) allure.dynamic.link(parsed_data['url']) diff --git a/httpfpt/common/toml_handler.py b/httpfpt/common/toml_handler.py index fff6ad7..c33238c 100644 --- a/httpfpt/common/toml_handler.py +++ b/httpfpt/common/toml_handler.py @@ -44,7 +44,7 @@ def write_toml(filepath: str, filename: str, data: dict, encoding: str = 'utf-8' _file = os.path.join(filepath, filename) try: with open(_file, encoding=encoding, mode=mode) as f: - result = rtoml.dump(data, f) + result = rtoml.dump(data, f) # type: ignore except ValueError as e: log.error(f'写入文件 "{filename}" 错误 \n {e}') raise e diff --git a/httpfpt/common/yaml_handler.py b/httpfpt/common/yaml_handler.py index 0cc004e..3abb77b 100644 --- a/httpfpt/common/yaml_handler.py +++ b/httpfpt/common/yaml_handler.py @@ -3,7 +3,7 @@ import os from datetime import datetime from pathlib import Path -from typing import List, Dict, Any, Union, Optional, NoReturn +from typing import List, Dict, Any, Union, Optional import yaml @@ -25,6 +25,9 @@ def read_yaml( _file = os.path.join(filepath, filename) else: _file = filename + if not _file: + log.error('读取 yaml 文件失败,文件名为空') + raise ValueError('读取 yaml 文件失败,文件名为空') try: with open(_file, encoding='utf-8') as f: data = yaml.load(f, Loader=yaml.FullLoader) @@ -38,7 +41,7 @@ def read_yaml( raise ValueError(f'数据文件 {filename} 没有数据! 请检查数据文件内容是否正确!') -def write_yaml(filepath: str, filename: str, data: Any = None, *, encoding: str = 'utf-8', mode: str = 'a') -> NoReturn: +def write_yaml(filepath: str, filename: str, data: Any = None, *, encoding: str = 'utf-8', mode: str = 'a') -> None: """ 将数据写入包含 yaml 格式数据的文件 @@ -70,7 +73,7 @@ def write_yaml_report( encoding: str = 'utf-8', mode: str = 'a', status: str, -) -> NoReturn: +) -> None: """ 写入 yaml 测试报告 @@ -97,7 +100,7 @@ def write_yaml_report( log.success(f'写入 {filename} 测试报告成功') -def write_yaml_vars(data: dict) -> NoReturn: +def write_yaml_vars(data: dict) -> None: """ 写入 yaml 全部变量 diff --git a/httpfpt/db/mysql_db.py b/httpfpt/db/mysql_db.py index ab0aa59..9ab1f0a 100644 --- a/httpfpt/db/mysql_db.py +++ b/httpfpt/db/mysql_db.py @@ -2,7 +2,7 @@ # _*_ coding:utf-8 _*_ import datetime import decimal -from typing import NoReturn, Any +from typing import Any, Optional import pymysql from dbutils.pooled_db import PooledDB @@ -59,7 +59,7 @@ def query(self, sql: str, fetch: str = 'all') -> Any: finally: self.close() - def execute(self, sql: str) -> NoReturn: + def execute(self, sql: str) -> None: """ 执行 sql 操作 @@ -76,7 +76,7 @@ def execute(self, sql: str) -> NoReturn: finally: self.close() - def close(self) -> NoReturn: + def close(self) -> None: """ 关闭游标和数据库连接 @@ -85,7 +85,7 @@ def close(self) -> NoReturn: self.cursor.close() self.conn.close() - def exec_case_sql(self, sql: list, env: str = None) -> dict: + def exec_case_sql(self, sql: list, env: Optional[str] = None) -> dict: """ 执行用例 sql @@ -115,9 +115,9 @@ def exec_case_sql(self, sql: list, env: str = None) -> dict: log.info(f'执行变量提取 sql: {s["sql"]}') key = s['key'] set_type = s['type'] - sql = s['sql'] + sql_text = s['sql'] json_path = s['jsonpath'] - query_data = self.query(sql) + query_data = self.query(sql_text) value = jsonpath(query_data, json_path) if value: value = value[0] diff --git a/httpfpt/db/redis_db.py b/httpfpt/db/redis_db.py index 38f878b..a0f43d0 100644 --- a/httpfpt/db/redis_db.py +++ b/httpfpt/db/redis_db.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys -from typing import Any, NoReturn +from typing import Any from redis import Redis, AuthenticationError @@ -50,7 +50,7 @@ def get(self, key: Any) -> Any: log.warning(f'获取 redis 数据 {key} 失败, 此数据不存在') return data - def set(self, key: Any, value: Any, **kwargs) -> NoReturn: + def set(self, key: Any, value: Any, **kwargs) -> None: """ 设置 redis 数据 @@ -61,7 +61,7 @@ def set(self, key: Any, value: Any, **kwargs) -> NoReturn: self.redis.set(key, value, **kwargs) log.info(f'设置 redis 数据 {key} 成功') - def rset(self, key: Any, value: Any, **kwargs) -> NoReturn: + def rset(self, key: Any, value: Any, **kwargs) -> None: """ 重置设置 redis 数据 @@ -74,7 +74,7 @@ def rset(self, key: Any, value: Any, **kwargs) -> NoReturn: self.redis.set(key, value, **kwargs) log.info(f'重置 redis 数据 {key} 成功') - def delete(self, *key: Any) -> NoReturn: + def delete(self, *key: Any) -> None: """ 删除 redis 数据 @@ -99,7 +99,7 @@ def exists(self, *key: Any) -> int: log.warning(f'判断 redis 数据 {key} 不存在') return num - def lpush(self, key: Any, *value: Any) -> NoReturn: + def lpush(self, key: Any, *value: Any) -> None: """ 从左侧插入列表数据 @@ -110,7 +110,7 @@ def lpush(self, key: Any, *value: Any) -> NoReturn: self.redis.lpush(key, *value) log.info(f'从左侧插入 redis 数据 {key} 成功') - def relpush(self, key: Any, *value: Any) -> NoReturn: + def relpush(self, key: Any, *value: Any) -> None: """ 删除原数据并重新从左侧插入列表数据 diff --git a/httpfpt/enums/assert_type.py b/httpfpt/enums/assert_type.py index f4cb9c2..1df38c6 100644 --- a/httpfpt/enums/assert_type.py +++ b/httpfpt/enums/assert_type.py @@ -22,5 +22,5 @@ class AssertType(StrEnum): length_greater_than_or_equal = 'len_ge' contains = 'contains' not_contains = 'not_contains' - startswith = 'startswith' - endswith = 'endswith' + startswith = 'startswith' # type: ignore + endswith = 'endswith' # type: ignore diff --git a/httpfpt/schemas/case_data.py b/httpfpt/schemas/case_data.py index fad2f85..3f48181 100644 --- a/httpfpt/schemas/case_data.py +++ b/httpfpt/schemas/case_data.py @@ -18,7 +18,7 @@ class ConfigRequestData(BaseModel): timeout: Optional[int] = Field(None, ge=0) verify: Optional[bool] = None redirects: Optional[bool] = None - proxies: Optional[Union[Dict[Literal['http', 'https', 'http://', 'https://'], Union[AnyHttpUrl, None]]]] = None + proxies: Optional[Dict[Literal['http', 'https', 'http://', 'https://'], Union[AnyHttpUrl, None]]] = None class Config(BaseModel): @@ -30,11 +30,11 @@ class Config(BaseModel): class StepsRequestData(BaseModel): method: str url: str - params: Union[dict, bytes, None] = ... - headers: Optional[dict] = ... - body_type: Optional[str] = ... - body: Union[dict, bytes, Tuple[list], None] = ... - files: Union[Dict[str, Union[str, List[str]]], None] = ... + params: Union[dict, bytes, None] + headers: Optional[dict] + body_type: Optional[str] + body: Union[dict, bytes, Tuple[list], None] + files: Union[Dict[str, Union[str, List[str]]], None] class SetupTestCaseData(BaseModel): @@ -51,8 +51,8 @@ class SetupSqlData(BaseModel): class StepsSetUpData(BaseModel): - testcase: Optional[Union[List[Union[str, SetupTestCaseData]]]] = None - sql: Optional[Union[List[Union[str, SetupSqlData]]]] = None + testcase: Optional[List[Union[str, SetupTestCaseData]]] = None + sql: Optional[List[Union[str, SetupSqlData]]] = None hooks: Optional[List[str]] = None wait_time: Optional[int] = None @@ -71,7 +71,7 @@ class TeardownAssertData(BaseModel): class StepsTearDownData(BaseModel): - sql: Optional[Union[List[Union[str, SetupSqlData]]]] = None + sql: Optional[List[Union[str, SetupSqlData]]] = None hooks: Optional[List[str]] = None extract: Optional[List[TeardownExtractData]] = None assert_: Optional[Union[str, List[str], List[TeardownAssertData]]] = Field(None, alias='assert') @@ -82,7 +82,7 @@ class Steps(BaseModel): name: str case_id: str description: str - is_run: Union[bool, dict, None] = ... + is_run: Union[bool, dict, None] = None request: StepsRequestData setup: Optional[StepsSetUpData] = None teardown: Optional[StepsTearDownData] = None diff --git a/httpfpt/utils/allure_control.py b/httpfpt/utils/allure_control.py index f67b44f..baeb217 100644 --- a/httpfpt/utils/allure_control.py +++ b/httpfpt/utils/allure_control.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import json -from typing import NoReturn, Union, Any +from typing import Union, Any, Optional import allure from allure_commons.types import AttachmentType @@ -9,7 +9,7 @@ from httpfpt.utils.file_control import get_file_property -def allure_step(step: str, var: Union[str, dict]) -> NoReturn: +def allure_step(step: str, var: Union[str, dict]) -> None: """ allure 操作步骤 @@ -25,7 +25,9 @@ def allure_step(step: str, var: Union[str, dict]) -> NoReturn: ) -def allure_attach(body: Any = None, name: str = None, attachment_type: str = 'JSON', extension: Any = None) -> NoReturn: +def allure_attach( + body: Any = None, name: Optional[str] = None, attachment_type: str = 'JSON', extension: Any = None +) -> None: """ allure 报告上传附件 @@ -43,7 +45,7 @@ def allure_attach(body: Any = None, name: str = None, attachment_type: str = 'JS ) -def allure_attach_file(filepath: str, name: str = None, extension: Any = None) -> NoReturn: +def allure_attach_file(filepath: str, name: Optional[str] = None, extension: Any = None) -> None: """ allure 报告上传附件 diff --git a/httpfpt/utils/assert_control.py b/httpfpt/utils/assert_control.py index 9928b9f..da6d60d 100644 --- a/httpfpt/utils/assert_control.py +++ b/httpfpt/utils/assert_control.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from decimal import Decimal -from typing import Union, NoReturn, Any +from typing import Union, Any from jsonpath import jsonpath @@ -11,7 +11,7 @@ class Asserter: - def _code_asserter(self, response: dict, assert_text: str) -> NoReturn: + def _code_asserter(self, response: dict, assert_text: str) -> None: """ **代码断言器, 像 pytest 断言一样使用它** @@ -60,7 +60,7 @@ def _code_asserter(self, response: dict, assert_text: str) -> NoReturn: log.info(f'执行断言:{assert_text}') self._exec_code_assert(response, assert_text) - def _json_asserter(self, response: dict, assert_text: dict) -> NoReturn: + def _json_asserter(self, response: dict, assert_text: dict) -> None: """ **json 提取断言器** @@ -100,7 +100,7 @@ def _json_asserter(self, response: dict, assert_text: dict) -> NoReturn: else: raise ValueError(f'jsonpath取值失败, 表达式: {assert_jsonpath}') - def _sql_asserter(self, assert_text: dict) -> NoReturn: + def _sql_asserter(self, assert_text: dict) -> None: """ **sql 提取断言器** @@ -129,7 +129,7 @@ def _sql_asserter(self, assert_text: dict) -> NoReturn: raise ValueError(f'jsonpath取值失败, 表达式: {assert_jsonpath}') @staticmethod - def _exec_code_assert(response: dict, assert_text: str) -> NoReturn: + def _exec_code_assert(response: dict, assert_text: str) -> None: """ 执行 code 断言 @@ -232,7 +232,7 @@ def _exec_code_assert(response: dict, assert_text: str) -> NoReturn: @staticmethod def _exec_json_assert( assert_check: Union[str, None], expected_value: Any, assert_type: str, actual_value: Any - ) -> NoReturn: + ) -> None: """ 执行 jsonpath 断言 @@ -332,7 +332,7 @@ def _exec_json_assert( else: raise ValueError(f'断言表达式格式错误, 含有不支持的断言类型: {assert_type}') - def exec_asserter(self, response: dict, assert_text: Union[str, list, dict, None]) -> NoReturn: + def exec_asserter(self, response: dict, assert_text: Union[str, list, dict, None]) -> None: """ 根据断言内容自动选择断言器执行 diff --git a/httpfpt/utils/auth_plugins.py b/httpfpt/utils/auth_plugins.py index c7c3346..cfe3f7c 100644 --- a/httpfpt/utils/auth_plugins.py +++ b/httpfpt/utils/auth_plugins.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- + import requests from jsonpath import jsonpath diff --git a/httpfpt/utils/case_auto_generator.py b/httpfpt/utils/case_auto_generator.py index 3f6de25..c0441d6 100644 --- a/httpfpt/utils/case_auto_generator.py +++ b/httpfpt/utils/case_auto_generator.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- import os.path from pathlib import Path -from typing import NoReturn import typer @@ -11,7 +10,7 @@ from httpfpt.utils.file_control import search_all_case_yaml_files, search_all_testcase_files, get_file_property -def auto_generate_testcases(rewrite: bool = False) -> NoReturn: +def auto_generate_testcases(rewrite: bool = False) -> None: """ 自动创建测试用例 diff --git a/httpfpt/utils/data_manage/openapi.py b/httpfpt/utils/data_manage/openapi.py index 4eedad3..f3850b8 100644 --- a/httpfpt/utils/data_manage/openapi.py +++ b/httpfpt/utils/data_manage/openapi.py @@ -18,7 +18,7 @@ class SwaggerParser: - def __init__(self, version: int = None, data: dict = None): + def __init__(self, version: Optional[int] = None, data: Optional[dict] = None): """ 初始化参数 @@ -349,7 +349,7 @@ def get_swagger_schema_data(self, name: str) -> dict: data = self.data.get('components').get('schemas').get(name) return data - def get_swagger_request_data(self, value: Union[str, dict]) -> Union[dict, None]: + def get_swagger_request_data(self, value: Union[dict, str]) -> Union[dict, None]: """ 获取请求 data @@ -396,7 +396,7 @@ def get_swagger_request_files(self, value: Union[str, dict]) -> Union[dict, None if self.version == 2: for v in value['parameters']: if v.get('type') == 'file': - files[v['name']] = format_value('object') + files[v.get('name')] = format_value('object') return files if len(files) > 0 else None else: if not isinstance(value, dict): diff --git a/httpfpt/utils/enum_control.py b/httpfpt/utils/enum_control.py index c6a9d03..5b6e19b 100644 --- a/httpfpt/utils/enum_control.py +++ b/httpfpt/utils/enum_control.py @@ -1,12 +1,10 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- from enum import Enum -from typing import TypeVar +from typing import Any -SubEnum = TypeVar('SubEnum') - -def get_enum_values(enum_class: SubEnum) -> list: +def get_enum_values(enum_class: Any) -> list: if issubclass(enum_class, Enum): return list(map(lambda ec: ec.value, enum_class)) else: diff --git a/httpfpt/utils/relate_testcase_executor.py b/httpfpt/utils/relate_testcase_executor.py index 0aa3ced..181fb80 100644 --- a/httpfpt/utils/relate_testcase_executor.py +++ b/httpfpt/utils/relate_testcase_executor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys -from typing import NoReturn, List, Dict, Union +from typing import List, Dict, Union from jsonpath import jsonpath from pydantic import ValidationError @@ -202,7 +202,7 @@ def exec_setup_testcase(parsed: RequestDataParse, setup_testcase: list) -> Union return None -def is_circular_relate(current_case_id: str, relate_case_steps: dict) -> NoReturn: +def is_circular_relate(current_case_id: str, relate_case_steps: dict) -> None: """ 判断是否循环关联 @@ -225,7 +225,7 @@ def is_circular_relate(current_case_id: str, relate_case_steps: dict) -> NoRetur raise ValueError('关联测试用例执行失败,因为在关联测试用例中的关联测试用例参数内含有' '当前正在执行的测试用例,导致了循环引用,触发此异常') # noqa: E501 -def relate_testcase_set_var(testcase_data: dict) -> NoReturn: +def relate_testcase_set_var(testcase_data: dict) -> None: """ 关联测试用例设置变量 @@ -245,7 +245,7 @@ def relate_testcase_set_var(testcase_data: dict) -> NoReturn: raise ValueError('jsonpath 取值失败,表达式: {}'.format(testcase_data['set_var_jsonpath'])) -def relate_testcase_exec(testcase_data: dict) -> NoReturn: +def relate_testcase_exec(testcase_data: dict) -> None: """ 关联测试用例执行 diff --git a/httpfpt/utils/request/case_data_file_parse.py b/httpfpt/utils/request/case_data_file_parse.py index 827446c..1ef3965 100644 --- a/httpfpt/utils/request/case_data_file_parse.py +++ b/httpfpt/utils/request/case_data_file_parse.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import copy -from typing import List, Dict, Any +from typing import List, Dict, Any, Union from pydantic import ValidationError @@ -9,12 +9,12 @@ from httpfpt.utils.pydantic_error_parse import parse_error -def get_request_data(*, file_data: dict, use_pydantic_verify: bool = False) -> List[Dict[str, Dict[str, Any]]]: +def get_request_data(*, file_data: dict, use_pydantic_verify: bool = False) -> Union[List[Dict[str, Any]], None]: """ 通过解析读取的测试用例文件数据, 获取用于数据驱动的请求数据 :param file_data: 从测试用例数据文件读取的测试用例数据 - :param use_pydantic_verify: 是否将数据转换为 pydantic 模型 + :param use_pydantic_verify: pydantic 数据验证开关 :return: """ try: diff --git a/httpfpt/utils/request/hooks_executor.py b/httpfpt/utils/request/hooks_executor.py index dcd9f8f..f0e1295 100644 --- a/httpfpt/utils/request/hooks_executor.py +++ b/httpfpt/utils/request/hooks_executor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- import re -from typing import Any, NoReturn, Union +from typing import Any, Union from httpfpt.common.log import log @@ -71,7 +71,7 @@ def hook_func_value_replace(self, target: dict) -> Any: return dict_target - def exec_hook_func(self, func_list: list) -> NoReturn: + def exec_hook_func(self, func_list: list) -> None: """ 执行 hook 函数不返回任何值 diff --git a/httpfpt/utils/request/request_data_parse.py b/httpfpt/utils/request/request_data_parse.py index d447006..0a432d5 100644 --- a/httpfpt/utils/request/request_data_parse.py +++ b/httpfpt/utils/request/request_data_parse.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- import json import os -from typing import Union, NoReturn +from typing import Union import allure from _pytest.outcomes import Skipped @@ -178,7 +178,7 @@ def module(self) -> str: return module @property - def test_steps(self) -> dict: + def test_steps(self) -> Union[dict, list]: try: test_steps = self.request_data['test_steps'] except RequestParamGetError: @@ -224,11 +224,11 @@ def description(self) -> Union[str, None]: description = None return description - def _is_run(self) -> NoReturn: + def _is_run(self) -> None: try: is_run = self.request_data['test_steps']['is_run'] except RequestParamGetError: - ... + pass else: if is_run is not None: if isinstance(is_run, bool): diff --git a/httpfpt/utils/request/vars_extractor.py b/httpfpt/utils/request/vars_extractor.py index d0a1eae..5e91b2e 100644 --- a/httpfpt/utils/request/vars_extractor.py +++ b/httpfpt/utils/request/vars_extractor.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- import os.path import re -from typing import NoReturn from jsonpath import jsonpath @@ -117,7 +116,7 @@ def relate_vars_replace(self, target: dict) -> dict: return dict_target @staticmethod - def teardown_var_extract(response: dict, extract: list, env: str = None) -> NoReturn: + def teardown_var_extract(response: dict, extract: list, env: str) -> None: """ 后置参数提取 diff --git a/httpfpt/utils/send_report/ding_talk.py b/httpfpt/utils/send_report/ding_talk.py index 3129b99..c10b761 100644 --- a/httpfpt/utils/send_report/ding_talk.py +++ b/httpfpt/utils/send_report/ding_talk.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from typing import NoReturn from httpfpt.common.log import log from httpfpt.core import get_conf @@ -10,7 +9,7 @@ class DingTalk: def __init__(self, content: dict): self.content = content - def send(self) -> NoReturn: + def send(self) -> None: try: import requests @@ -32,7 +31,7 @@ def send(self) -> NoReturn: }, } response = requests.session().post( - url=get_conf.DING_TALK_WEBHOOK, json=data, headers=headers, proxies=get_conf.DING_TALK_PROXY + url=get_conf.DING_TALK_WEBHOOK, json=data, headers=headers, proxies=get_conf.DING_TALK_PROXY # type: ignore # noqa: E501 ) response.raise_for_status() except Exception as e: diff --git a/httpfpt/utils/send_report/lark_talk.py b/httpfpt/utils/send_report/lark_talk.py index caed64c..f17b7d6 100644 --- a/httpfpt/utils/send_report/lark_talk.py +++ b/httpfpt/utils/send_report/lark_talk.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from typing import NoReturn from httpfpt.common.log import log from httpfpt.core import get_conf @@ -10,7 +9,7 @@ class LarkTalk: def __init__(self, content: dict): self.content = content - def send(self) -> NoReturn: + def send(self) -> None: # 发送飞书消息 try: import requests @@ -38,7 +37,7 @@ def send(self) -> NoReturn: }, } response = requests.session().post( - url=get_conf.LARK_TALK_WEBHOOK, json=data, headers=headers, proxies=get_conf.LARK_TALK_PROXY + url=get_conf.LARK_TALK_WEBHOOK, json=data, headers=headers, proxies=get_conf.LARK_TALK_PROXY # type: ignore # noqa: E501 ) response.raise_for_status() except Exception as e: diff --git a/httpfpt/utils/send_report/send_email.py b/httpfpt/utils/send_report/send_email.py index 6485687..d316042 100644 --- a/httpfpt/utils/send_report/send_email.py +++ b/httpfpt/utils/send_report/send_email.py @@ -6,7 +6,6 @@ from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText -from typing import NoReturn from jinja2 import Template @@ -51,7 +50,7 @@ def take_messages(self) -> MIMEMultipart: return msg - def send(self) -> NoReturn: + def send(self) -> None: """ 发送邮件 """ diff --git a/pyright.sh b/pyright.sh new file mode 100644 index 0000000..6d4a1a3 --- /dev/null +++ b/pyright.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +pyright -p . --verbose diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..402e2b8 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,17 @@ +{ + "exclude": [ + "**/__pycache__", + "**/.*", + "venv/**", + "httpfpt/testcases/**", + "httpfpt/utils/data_manage/**.py" + ], + "ignore": [ + "httpfpt/utils/auth_plugins.py", + "httpfpt/conftest.py" + ], + "reportUnusedImport": "error", + "reportOptionalMemberAccess": false, + "reportOptionalSubscript": false, + "pythonVersion": "3.8" +} diff --git a/requirements.txt b/requirements.txt index 1617492..136a21e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,7 @@ openpyxl==3.0.7 pre-commit==3.2.2 pydantic==2.0b2 pymysql==0.9.3 +pyright==1.1.315 pytest==7.2.2 pytest-html==3.1.1 pytest-loguru==0.2.0