1, replace logging with loguru
2, replace string format with f-string
This commit is contained in:
debugtalk
2020-03-07 12:04:43 +08:00
parent 556b6ea367
commit 7213f86f80
18 changed files with 189 additions and 187 deletions

View File

@@ -6,6 +6,7 @@
- replace logging with [loguru](https://github.com/Delgan/loguru)
- remove support for Python 2.7
- replace string format with f-string
## 2.5.7 (2020-02-21)

View File

@@ -1,9 +1,10 @@
import os
import unittest
from loguru import logger
from sentry_sdk import capture_message
from httprunner import (__version__, exceptions, loader, logger, parser,
from httprunner import (__version__, exceptions, loader, parser,
report, runner, utils)
@@ -32,8 +33,6 @@ class HttpRunner(object):
log_file (str): log file path.
"""
logger.setup_logger(log_level, log_file)
self.exception_stage = "initialize HttpRunner()"
kwargs = {
"failfast": failfast,
@@ -99,7 +98,7 @@ class HttpRunner(object):
times = int(times)
except ValueError:
raise exceptions.ParamsError(
"times should be digit, given: {}".format(times))
f"times should be digit, given: {times}")
for times_index in range(times):
# suppose one testcase should not have more than 9999 steps,
@@ -130,7 +129,7 @@ class HttpRunner(object):
for testcase in test_suite:
testcase_name = testcase.config.get("name")
logger.log_info("Start to run testcase: {}".format(testcase_name))
logger.info(f"Start to run testcase: {testcase_name}")
result = self.unittest_runner.run(testcase)
if result.wasSuccessful():
@@ -197,11 +196,11 @@ class HttpRunner(object):
parsed_testcases = parser.parse_tests(tests_mapping)
parse_failed_testfiles = parser.get_parse_failed_testfiles()
if parse_failed_testfiles:
logger.log_warning("parse failures occurred ...")
logger.warning("parse failures occurred ...")
utils.dump_logs(parse_failed_testfiles, project_mapping, "parse_failed")
if len(parsed_testcases) == 0:
logger.log_error("failed to parse all cases, abort.")
logger.error("failed to parse all cases, abort.")
raise exceptions.ParseTestsFailure
if self.save_tests:
@@ -295,7 +294,7 @@ class HttpRunner(object):
dict: result summary
"""
logger.log_info("HttpRunner version: {}".format(__version__))
logger.info(f"HttpRunner version: {__version__}")
if loader.is_test_path(path_or_tests):
return self.run_path(path_or_tests, dot_env_path, mapping)
elif loader.is_test_content(path_or_tests):
@@ -303,4 +302,4 @@ class HttpRunner(object):
loader.init_pwd(project_working_directory)
return self.run_tests(path_or_tests)
else:
raise exceptions.ParamsError("Invalid testcase path or testcases: {}".format(path_or_tests))
raise exceptions.ParamsError(f"Invalid testcase path or testcases: {path_or_tests}")

View File

@@ -3,11 +3,11 @@ import os
import sys
import sentry_sdk
from loguru import logger
from httprunner import __description__, __version__, exceptions
from httprunner.api import HttpRunner
from httprunner.loader import load_cases
from httprunner.logger import color_print, log_error
from httprunner.report import gen_html_report
from httprunner.utils import (create_scaffold,
prettify_json_file, init_sentry_sdk)
@@ -67,19 +67,19 @@ def main():
sys.exit(0)
if args.version:
color_print("{}".format(__version__), "GREEN")
print(f"{__version__}")
sys.exit(0)
if args.validate:
for validate_path in args.validate:
try:
color_print("validate test file: {}".format(validate_path), "GREEN")
logger.info(f"validate test file: {validate_path}")
load_cases(validate_path, args.dot_env_path)
except exceptions.MyBaseError as ex:
log_error(str(ex))
logger.error(str(ex))
continue
color_print("done!", "BLUE")
logger.info("done!")
sys.exit(0)
if args.prettify:
@@ -111,8 +111,7 @@ def main():
)
err_code |= (0 if summary and summary["success"] else 1)
except Exception as ex:
color_print("!!!!!!!!!! exception stage: {} !!!!!!!!!!".format(runner.exception_stage), "YELLOW")
color_print(str(ex), "RED")
logger.error(f"!!!!!!!!!! exception stage: {runner.exception_stage} !!!!!!!!!!\n{str(ex)}")
sentry_sdk.capture_exception(ex)
err_code = 1

View File

@@ -4,11 +4,12 @@ import time
import requests
import urllib3
from loguru import logger
from requests import Request, Response
from requests.exceptions import (InvalidSchema, InvalidURL, MissingSchema,
RequestException)
from httprunner import logger, response
from httprunner import response
from httprunner.utils import lower_dict_keys, omit_long_data
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
@@ -18,10 +19,10 @@ def get_req_resp_record(resp_obj):
""" get request and response info from Response() object.
"""
def log_print(req_resp_dict, r_type):
msg = "\n================== {} details ==================\n".format(r_type)
msg = f"\n================== {r_type} details ==================\n"
for key, value in req_resp_dict[r_type].items():
msg += "{:<16} : {}\n".format(key, repr(value))
logger.log_debug(msg)
logger.debug(msg)
req_resp_dict = {
"request": {},
@@ -214,15 +215,13 @@ class HttpSession(requests.Session):
try:
response.raise_for_status()
except RequestException as e:
logger.log_error(u"{exception}".format(exception=str(e)))
except RequestException as ex:
logger.error(f"{str(ex)}")
else:
logger.log_info(
"""status_code: {}, response_time(ms): {} ms, response_length: {} bytes\n""".format(
response.status_code,
response_time_ms,
content_size
)
logger.info(
f"status_code: {response.status_code}, "
f"response_time(ms): {response_time_ms} ms, "
f"response_length: {content_size} bytes\n"
)
return response
@@ -234,9 +233,9 @@ class HttpSession(requests.Session):
"""
try:
msg = "processed request:\n"
msg += "> {method} {url}\n".format(method=method, url=url)
msg += "> kwargs: {kwargs}".format(kwargs=kwargs)
logger.log_debug(msg)
msg += f"> {method} {url}\n"
msg += f"> kwargs: {kwargs}"
logger.debug(msg)
return requests.Session.request(self, method, url, **kwargs)
except (MissingSchema, InvalidSchema, InvalidURL):
raise

View File

@@ -18,8 +18,9 @@ import multiprocessing
import os
import sys
from loguru import logger
from httprunner import __version__
from httprunner import logger
from httprunner.utils import init_sentry_sdk
init_sentry_sdk()
@@ -31,7 +32,7 @@ def parse_locustfile(file_path):
if file_path is a YAML/JSON file, convert it to locustfile
"""
if not os.path.isfile(file_path):
logger.color_print("file path invalid, exit.", "RED")
logger.error("file path invalid, exit.")
sys.exit(1)
file_suffix = os.path.splitext(file_path)[1]
@@ -41,7 +42,7 @@ def parse_locustfile(file_path):
locustfile_path = gen_locustfile(file_path)
else:
# '' or other suffix
logger.color_print("file type should be YAML/JSON/Python, exit.", "RED")
logger.error("file type should be YAML/JSON/Python, exit.")
sys.exit(1)
return locustfile_path
@@ -105,7 +106,7 @@ def run_locusts_with_processes(sys_argv, processes_count):
def main():
""" Performance test with locust: parse command line options and run commands.
"""
print("HttpRunner version: {}".format(__version__))
print(f"HttpRunner version: {__version__}")
sys.argv[0] = 'locust'
if len(sys.argv) == 1:
sys.argv.extend(["-h"])
@@ -130,7 +131,7 @@ def main():
# default
loglevel = "WARNING"
logger.setup_logger(loglevel)
# logger.setup_logger(loglevel)
# get testcase file path
try:
@@ -147,7 +148,7 @@ def main():
""" locusts -f locustfile.py --processes 4
"""
if "--no-web" in sys.argv:
logger.log_error("conflict parameter args: --processes & --no-web. \nexit.")
logger.error("conflict parameter args: --processes & --no-web. \nexit.")
sys.exit(1)
processes_index = sys.argv.index('--processes')
@@ -157,7 +158,7 @@ def main():
locusts -f locustfile.py --processes
"""
processes_count = multiprocessing.cpu_count()
logger.log_warning("processes count not specified, use {} by default.".format(processes_count))
logger.warning(f"processes count not specified, use {processes_count} by default.")
else:
try:
""" locusts -f locustfile.py --processes 4 """
@@ -166,7 +167,7 @@ def main():
except ValueError:
""" locusts -f locustfile.py --processes -P 8888 """
processes_count = multiprocessing.cpu_count()
logger.log_warning("processes count not specified, use {} by default.".format(processes_count))
logger.warning(f"processes count not specified, use {processes_count} by default.")
sys.argv.pop(processes_index)
run_locusts_with_processes(sys.argv, processes_count)

View File

@@ -85,12 +85,12 @@ def prepare_upload_test(test_dict):
"""
upload_json = test_dict["request"].pop("upload", {})
if not upload_json:
raise ParamsError("invalid upload info: {}".format(upload_json))
raise ParamsError(f"invalid upload info: {upload_json}")
params_list = []
for key, value in upload_json.items():
test_dict["variables"][key] = value
params_list.append("{}=${}".format(key, key))
params_list.append(f"{key}=${key}")
params_str = ", ".join(params_list)
test_dict["variables"]["m_encoder"] = "${multipart_encoder(" + params_str + ")}"

View File

@@ -1,7 +1,9 @@
import importlib
import os
from httprunner import exceptions, logger, utils
from loguru import logger
from httprunner import exceptions, utils
from httprunner.loader.check import JsonSchemaChecker
from httprunner.loader.load import load_module_functions, load_file, load_dot_env_file, \
load_folder_files
@@ -53,7 +55,7 @@ def __extend_with_api_ref(raw_testinfo):
if api_name in tests_def_mapping["api"]:
block = tests_def_mapping["api"][api_name]
elif not os.path.isfile(api_name):
raise exceptions.ApiNotFound("{} not found!".format(api_name))
raise exceptions.ApiNotFound(f"{api_name} not found!")
else:
block = load_file(api_name)
@@ -84,7 +86,7 @@ def __extend_with_testcase_ref(raw_testinfo):
testcase_dict = load_testcase_v2(loaded_testcase)
else:
raise exceptions.FileFormatError(
"Invalid format testcase: {}".format(testcase_path))
f"Invalid format testcase: {testcase_path}")
tests_def_mapping["testcases"][testcase_path] = testcase_dict
else:
@@ -186,8 +188,8 @@ def load_testcase(raw_testcase):
elif key == "test":
tests.append(load_teststep(test_block))
else:
logger.log_warning(
"unexpected block key: {}. block key should only be 'config' or 'test'.".format(key)
logger.warning(
f"unexpected block key: {key}. block key should only be 'config' or 'test'."
)
return {
@@ -485,9 +487,9 @@ def load_cases(path, dot_env_path=None):
try:
loaded_content = load_test_file(path)
except exceptions.ApiNotFound as ex:
logger.log_warning("Invalid api reference in {}: {}".format(path, ex))
logger.warning(f"Invalid api reference in {path}: {ex}")
except exceptions.FileFormatError:
logger.log_warning("Invalid test file format: {}".format(path))
logger.warning(f"Invalid test file format: {path}")
if not loaded_content:
pass

View File

@@ -4,8 +4,9 @@ import os
import platform
import jsonschema
from loguru import logger
from httprunner import exceptions, logger
from httprunner import exceptions
schemas_root_dir = os.path.join(os.path.dirname(__file__), "schemas")
common_schema_path = os.path.join(schemas_root_dir, "common.schema.json")
@@ -50,7 +51,7 @@ class JsonSchemaChecker(object):
try:
jsonschema.validate(content, scheme, resolver=resolver)
except jsonschema.exceptions.ValidationError as ex:
logger.log_error(str(ex))
logger.error(str(ex))
raise exceptions.FileFormatError
return True

View File

@@ -5,9 +5,10 @@ import os
import types
import yaml
from loguru import logger
from httprunner import builtin
from httprunner import exceptions, logger, utils
from httprunner import exceptions, utils
from httprunner.loader.locate import get_project_working_directory
try:
@@ -25,7 +26,7 @@ def _load_yaml_file(yaml_file):
try:
yaml_content = yaml.load(stream)
except yaml.YAMLError as ex:
logger.log_error(str(ex))
logger.error(str(ex))
raise exceptions.FileFormatError
return yaml_content
@@ -38,8 +39,8 @@ def _load_json_file(json_file):
try:
json_content = json.load(data_file)
except exceptions.JSONDecodeError:
err_msg = u"JSONDecodeError: JSON file format error: {}".format(json_file)
logger.log_error(err_msg)
err_msg = f"JSONDecodeError: JSON file format error: {json_file}"
logger.error(err_msg)
raise exceptions.FileFormatError(err_msg)
return json_content
@@ -90,7 +91,7 @@ def load_csv_file(csv_file):
def load_file(file_path):
if not os.path.isfile(file_path):
raise exceptions.FileNotFound("{} does not exist.".format(file_path))
raise exceptions.FileNotFound(f"{file_path} does not exist.")
file_suffix = os.path.splitext(file_path)[1].lower()
if file_suffix == '.json':
@@ -101,8 +102,7 @@ def load_file(file_path):
return load_csv_file(file_path)
else:
# '' or other suffix
err_msg = u"Unsupported file format: {}".format(file_path)
logger.log_warning(err_msg)
logger.warning(f"Unsupported file format: {file_path}")
return []
@@ -169,7 +169,7 @@ def load_dot_env_file(dot_env_path):
if not os.path.isfile(dot_env_path):
return {}
logger.log_info("Loading environment variables from {}".format(dot_env_path))
logger.info(f"Loading environment variables from {dot_env_path}")
env_variables_mapping = {}
with io.open(dot_env_path, 'r', encoding='utf-8') as fp:

View File

@@ -1,7 +1,9 @@
import os
import sys
from httprunner import exceptions, logger
from loguru import logger
from httprunner import exceptions
project_working_directory = None
@@ -26,7 +28,7 @@ def locate_file(start_path, file_name):
elif os.path.isdir(start_path):
start_dir_path = start_path
else:
raise exceptions.FileNotFound("invalid path: {}".format(start_path))
raise exceptions.FileNotFound(f"invalid path: {start_path}")
file_path = os.path.join(start_dir_path, file_name)
if os.path.isfile(file_path):
@@ -34,14 +36,14 @@ def locate_file(start_path, file_name):
# current working directory
if os.path.abspath(start_dir_path) == os.getcwd():
raise exceptions.FileNotFound("{} not found in {}".format(file_name, start_path))
raise exceptions.FileNotFound(f"{file_name} not found in {start_path}")
# system root dir
# Windows, e.g. 'E:\\'
# Linux/Darwin, '/'
parent_dir = os.path.dirname(start_dir_path)
if parent_dir == start_dir_path:
raise exceptions.FileNotFound("{} not found in {}".format(file_name, start_path))
raise exceptions.FileNotFound(f"{file_name} not found in {start_path}")
# locate recursive upward
return locate_file(parent_dir, file_name)
@@ -85,8 +87,8 @@ def init_project_working_directory(test_path):
def prepare_path(path):
if not os.path.exists(path):
err_msg = "path not exist: {}".format(path)
logger.log_error(err_msg)
err_msg = f"path not exist: {path}"
logger.error(err_msg)
raise exceptions.FileNotFound(err_msg)
if not os.path.isabs(path):

View File

@@ -6,8 +6,9 @@ import collections
import json
import re
from loguru import logger
from httprunner import exceptions, utils, loader
from httprunner import logger
from httprunner.compat import basestring, numeric_types, str
# use $$ to escape $ notation
@@ -286,7 +287,7 @@ def uniform_validator(validator):
"""
if not isinstance(validator, dict):
raise exceptions.ParamsError("invalid validator: {}".format(validator))
raise exceptions.ParamsError(f"invalid validator: {validator}")
if "check" in validator and "expect" in validator:
# format1
@@ -300,12 +301,12 @@ def uniform_validator(validator):
compare_values = validator[comparator]
if not isinstance(compare_values, list) or len(compare_values) != 2:
raise exceptions.ParamsError("invalid validator: {}".format(validator))
raise exceptions.ParamsError(f"invalid validator: {validator}")
check_item, expect_value = compare_values
else:
raise exceptions.ParamsError("invalid validator: {}".format(validator))
raise exceptions.ParamsError(f"invalid validator: {validator}")
# uniform comparator, e.g. lt => less_than, eq => equals
comparator = get_uniform_comparator(comparator)
@@ -410,7 +411,7 @@ def get_mapping_variable(variable_name, variables_mapping):
try:
return variables_mapping[variable_name]
except KeyError:
raise exceptions.VariableNotFound("{} is not found.".format(variable_name))
raise exceptions.VariableNotFound(f"{variable_name} is not found.")
def get_mapping_function(function_name, functions_mapping):
@@ -455,7 +456,7 @@ def get_mapping_function(function_name, functions_mapping):
except AttributeError:
pass
raise exceptions.FunctionNotFound("{} is not found.".format(function_name))
raise exceptions.FunctionNotFound(f"{function_name} is not found.")
def parse_function_params(params):
@@ -578,12 +579,12 @@ class LazyFunction(object):
if self._kwargs:
args_string += ", "
str_kwargs = [
"{}={}".format(key, str(value))
f"{key}={str(value)}"
for key, value in self._kwargs.items()
]
args_string += ", ".join(str_kwargs)
return "LazyFunction({}({}))".format(self.func_name, args_string)
return f"LazyFunction({self.func_name}({args_string}))"
def __prepare_cache_key(self, args, kwargs):
return self.func_name, repr(args), repr(kwargs)
@@ -698,7 +699,7 @@ class LazyString(object):
self._string += escape_braces(remain_string)
def __repr__(self):
return "LazyString({})".format(self.raw_string)
return f"LazyString({self.raw_string})"
def to_value(self, variables_mapping=None):
""" parse lazy data with evaluated variables mapping.
@@ -1247,7 +1248,7 @@ def _parse_testcase(testcase, project_mapping, session_variables_set=None):
testcase_type = testcase["type"]
testcase_path = testcase.get("path")
logger.log_error("failed to parse testcase: {}, error: {}".format(testcase_path, ex))
logger.error(f"failed to parse testcase: {testcase_path}, error: {ex}")
global parse_failed_testfiles
if testcase_type not in parse_failed_testfiles:

View File

@@ -3,8 +3,8 @@ import os
from datetime import datetime
from jinja2 import Template
from loguru import logger
from httprunner import logger
from httprunner.exceptions import SummaryEmpty
@@ -19,7 +19,7 @@ def gen_html_report(summary, report_template=None, report_dir=None, report_file=
"""
if not summary["time"] or summary["stat"]["testcases"]["total"] == 0:
logger.log_error("test result summary is empty ! {}".format(summary))
logger.error(f"test result summary is empty ! {summary}")
raise SummaryEmpty
if not report_template:
@@ -27,11 +27,11 @@ def gen_html_report(summary, report_template=None, report_dir=None, report_file=
os.path.abspath(os.path.dirname(__file__)),
"template.html"
)
logger.log_debug("No html report template specified, use default.")
logger.debug("No html report template specified, use default.")
else:
logger.log_info("render with html report template: {}".format(report_template))
logger.info(f"render with html report template: {report_template}")
logger.log_info("Start to render Html report ...")
logger.info("Start to render Html report ...")
start_at_timestamp = summary["time"]["start_at"]
utc_time_iso_8601_str = datetime.utcfromtimestamp(start_at_timestamp).isoformat()
@@ -58,7 +58,7 @@ def gen_html_report(summary, report_template=None, report_dir=None, report_file=
).render(summary)
fp_w.write(rendered_content)
logger.log_info("Generated Html report: {}".format(report_path))
logger.info(f"Generated Html report: {report_path}")
return report_path

View File

@@ -1,7 +1,7 @@
import time
import unittest
from httprunner import logger
from loguru import logger
class HtmlTestResult(unittest.TextTestResult):
@@ -27,7 +27,7 @@ class HtmlTestResult(unittest.TextTestResult):
def startTest(self, test):
""" add start test time """
super(HtmlTestResult, self).startTest(test)
logger.color_print(test.shortDescription(), "yellow")
logger.info(test.shortDescription())
def addSuccess(self, test):
super(HtmlTestResult, self).addSuccess(test)

View File

@@ -205,7 +205,7 @@ def stringify_summary(summary):
for index, suite_summary in enumerate(summary["details"]):
if not suite_summary.get("name"):
suite_summary["name"] = "testcase {}".format(index)
suite_summary["name"] = f"testcase {index}"
for record in suite_summary.get("records"):
meta_datas = record['meta_datas']

View File

@@ -2,8 +2,9 @@ import re
from collections import OrderedDict
import jsonpath
from loguru import logger
from httprunner import exceptions, logger, utils
from httprunner import exceptions, utils
from httprunner.compat import basestring, is_py2
text_extractor_regexp_compile = re.compile(r".*\(.*\).*")
@@ -32,8 +33,8 @@ class ResponseObject(object):
self.__dict__[key] = value
return value
except AttributeError:
err_msg = "ResponseObject does not have attribute: {}".format(key)
logger.log_error(err_msg)
err_msg = f"ResponseObject does not have attribute: {key}"
logger.error(err_msg)
raise exceptions.ParamsError(err_msg)
def _extract_field_with_jsonpath(self, field: str) -> list:
@@ -81,9 +82,9 @@ class ResponseObject(object):
assert result
return result
except (AssertionError, exceptions.JSONDecodeError):
err_msg = u"Failed to extract data with jsonpath! => {}\n".format(field)
err_msg += u"response body: {}\n".format(self.text)
logger.log_error(err_msg)
err_msg = f"Failed to extract data with jsonpath! => {field}\n"
err_msg += f"response body: {self.text}\n"
logger.error(err_msg)
raise exceptions.ExtractFailure(err_msg)
def _extract_field_with_regex(self, field):
@@ -108,9 +109,9 @@ class ResponseObject(object):
"""
matched = re.search(field, self.text)
if not matched:
err_msg = u"Failed to extract data with regex! => {}\n".format(field)
err_msg += u"response body: {}\n".format(self.text)
logger.log_error(err_msg)
err_msg = f"Failed to extract data with regex! => {field}\n"
err_msg += f"response body: {self.text}\n"
logger.error(err_msg)
raise exceptions.ExtractFailure(err_msg)
return matched.group(1)
@@ -141,8 +142,8 @@ class ResponseObject(object):
if top_query in ["status_code", "encoding", "ok", "reason", "url"]:
if sub_query:
# status_code.XX
err_msg = u"Failed to extract: {}\n".format(field)
logger.log_error(err_msg)
err_msg = f"Failed to extract: {field}\n"
logger.error(err_msg)
raise exceptions.ParamsError(err_msg)
return getattr(self, top_query)
@@ -157,27 +158,27 @@ class ResponseObject(object):
try:
return cookies[sub_query]
except KeyError:
err_msg = u"Failed to extract cookie! => {}\n".format(field)
err_msg += u"response cookies: {}\n".format(cookies)
logger.log_error(err_msg)
err_msg = f"Failed to extract cookie! => {field}\n"
err_msg += f"response cookies: {cookies}\n"
logger.error(err_msg)
raise exceptions.ExtractFailure(err_msg)
# elapsed
elif top_query == "elapsed":
available_attributes = u"available attributes: days, seconds, microseconds, total_seconds"
if not sub_query:
err_msg = u"elapsed is datetime.timedelta instance, attribute should also be specified!\n"
err_msg = "elapsed is datetime.timedelta instance, attribute should also be specified!\n"
err_msg += available_attributes
logger.log_error(err_msg)
logger.error(err_msg)
raise exceptions.ParamsError(err_msg)
elif sub_query in ["days", "seconds", "microseconds"]:
return getattr(self.elapsed, sub_query)
elif sub_query == "total_seconds":
return self.elapsed.total_seconds()
else:
err_msg = "{} is not valid datetime.timedelta attribute.\n".format(sub_query)
err_msg = f"{sub_query} is not valid datetime.timedelta attribute.\n"
err_msg += available_attributes
logger.log_error(err_msg)
logger.error(err_msg)
raise exceptions.ParamsError(err_msg)
# headers
@@ -190,9 +191,9 @@ class ResponseObject(object):
try:
return headers[sub_query]
except KeyError:
err_msg = u"Failed to extract header! => {}\n".format(field)
err_msg += u"response headers: {}\n".format(headers)
logger.log_error(err_msg)
err_msg = f"Failed to extract header! => {field}\n"
err_msg += f"response headers: {headers}\n"
logger.error(err_msg)
raise exceptions.ExtractFailure(err_msg)
# response body
@@ -214,9 +215,9 @@ class ResponseObject(object):
return utils.query_json(body, sub_query)
else:
# content = "<html>abcdefg</html>", content.xxx
err_msg = u"Failed to extract attribute from response body! => {}\n".format(field)
err_msg += u"response body: {}\n".format(body)
logger.log_error(err_msg)
err_msg = f"Failed to extract attribute from response body! => {field}\n"
err_msg += f"response body: {body}\n"
logger.error(err_msg)
raise exceptions.ExtractFailure(err_msg)
# new set response attributes in teardown_hooks
@@ -235,30 +236,30 @@ class ResponseObject(object):
return utils.query_json(attributes, sub_query)
else:
# content = "attributes.new_attribute_not_exist"
err_msg = u"Failed to extract cumstom set attribute from teardown hooks! => {}\n".format(field)
err_msg += u"response set attributes: {}\n".format(attributes)
logger.log_error(err_msg)
err_msg = f"Failed to extract cumstom set attribute from teardown hooks! => {field}\n"
err_msg += f"response set attributes: {attributes}\n"
logger.error(err_msg)
raise exceptions.TeardownHooksFailure(err_msg)
# others
else:
err_msg = u"Failed to extract attribute from response! => {}\n".format(field)
err_msg += u"available response attributes: status_code, cookies, elapsed, headers, content, " \
u"text, json, encoding, ok, reason, url.\n\n"
err_msg += u"If you want to set attribute in teardown_hooks, take the following example as reference:\n"
err_msg += u"response.new_attribute = 'new_attribute_value'\n"
logger.log_error(err_msg)
err_msg = f"Failed to extract attribute from response! => {field}\n"
err_msg += "available response attributes: status_code, cookies, elapsed, headers, content, " \
"text, json, encoding, ok, reason, url.\n\n"
err_msg += "If you want to set attribute in teardown_hooks, take the following example as reference:\n"
err_msg += "response.new_attribute = 'new_attribute_value'\n"
logger.error(err_msg)
raise exceptions.ParamsError(err_msg)
def extract_field(self, field):
""" extract value from requests.Response.
"""
if not isinstance(field, basestring):
err_msg = u"Invalid extractor! => {}\n".format(field)
logger.log_error(err_msg)
err_msg = f"Invalid extractor! => {field}\n"
logger.error(err_msg)
raise exceptions.ParamsError(err_msg)
msg = "extract: {}".format(field)
msg = f"extract: {field}"
if field.startswith("$"):
value = self._extract_field_with_jsonpath(field)
@@ -270,8 +271,8 @@ class ResponseObject(object):
if is_py2 and isinstance(value, unicode):
value = value.encode("utf-8")
msg += "\t=> {}".format(value)
logger.log_debug(msg)
msg += f"\t=> {value}"
logger.debug(msg)
return value
@@ -295,7 +296,7 @@ class ResponseObject(object):
if not extractors:
return {}
logger.log_debug("start to extract from response object.")
logger.debug("start to extract from response object.")
extracted_variables_mapping = OrderedDict()
extract_binds_order_dict = utils.ensure_mapping_format(extractors)

View File

@@ -3,7 +3,9 @@
from enum import Enum
from unittest.case import SkipTest
from httprunner import exceptions, logger, response, utils
from loguru import logger
from httprunner import exceptions, response, utils
from httprunner.client import HttpSession
from httprunner.context import SessionContext
from httprunner.validator import Validator
@@ -116,12 +118,12 @@ class Runner(object):
elif "skipIf" in test_dict:
skip_if_condition = test_dict["skipIf"]
if self.session_context.eval_content(skip_if_condition):
skip_reason = "{} evaluate to True".format(skip_if_condition)
skip_reason = f"{skip_if_condition} evaluate to True"
elif "skipUnless" in test_dict:
skip_unless_condition = test_dict["skipUnless"]
if not self.session_context.eval_content(skip_unless_condition):
skip_reason = "{} evaluate to False".format(skip_unless_condition)
skip_reason = f"{skip_unless_condition} evaluate to False"
if skip_reason:
raise SkipTest(skip_reason)
@@ -140,7 +142,7 @@ class Runner(object):
hook_type (HookTypeEnum): setup/teardown
"""
logger.log_debug("call {} hook actions.".format(hook_type.name))
logger.debug(f"call {hook_type.name} hook actions.")
for action in actions:
if isinstance(action, dict) and len(action) == 1:
@@ -148,17 +150,14 @@ class Runner(object):
# {"var": "${func()}"}
var_name, hook_content = list(action.items())[0]
hook_content_eval = self.session_context.eval_content(hook_content)
logger.log_debug(
"assignment with hook: {} = {} => {}".format(
var_name, hook_content, hook_content_eval
)
)
logger.debug(
f"assignment with hook: {var_name} = {hook_content} => {hook_content_eval}")
self.session_context.update_test_variables(
var_name, hook_content_eval
)
else:
# format 2
logger.log_debug("call hook function: {}".format(action))
logger.debug(f"call hook function: {action}")
# TODO: check hook function if valid
self.session_context.eval_content(action)
@@ -230,9 +229,8 @@ class Runner(object):
except KeyError:
raise exceptions.ParamsError("URL or METHOD missed!")
logger.log_info("{method} {url}".format(method=method, url=parsed_url))
logger.log_debug(
"request kwargs(raw): {kwargs}".format(kwargs=parsed_test_request))
logger.info(f"{method} {parsed_url}")
logger.debug(f"request kwargs(raw): {parsed_test_request}")
# request
resp = self.http_client_session.request(
@@ -248,21 +246,22 @@ class Runner(object):
# log request
err_msg += "====== request details ======\n"
err_msg += "url: {}\n".format(parsed_url)
err_msg += "method: {}\n".format(method)
err_msg += "headers: {}\n".format(parsed_test_request.pop("headers", {}))
err_msg += f"url: {parsed_url}\n"
err_msg += f"method: {method}\n"
headers = parsed_test_request.pop("headers", {})
err_msg += f"headers: {headers}\n"
for k, v in parsed_test_request.items():
v = utils.omit_long_data(v)
err_msg += "{}: {}\n".format(k, repr(v))
err_msg += f"{k}: {repr(v)}\n"
err_msg += "\n"
# log response
err_msg += "====== response details ======\n"
err_msg += "status_code: {}\n".format(resp_obj.status_code)
err_msg += "headers: {}\n".format(resp_obj.headers)
err_msg += "body: {}\n".format(repr(resp_obj.text))
logger.log_error(err_msg)
err_msg += f"status_code: {resp_obj.status_code}\n"
err_msg += f"headers: {resp_obj.headers}\n"
err_msg += f"body: {repr(resp_obj.text)}\n"
logger.error(err_msg)
# teardown hooks
teardown_hooks = test_dict.get("teardown_hooks", [])
@@ -395,9 +394,9 @@ class Runner(object):
output = {}
for variable in output_variables_list:
if variable not in variables_mapping:
logger.log_warning(
"variable '{}' can not be found in variables mapping, "
"failed to export!".format(variable)
logger.warning(
f"variable '{variable}' can not be found in variables mapping, "
"failed to export!"
)
continue

View File

@@ -22,7 +22,7 @@ absolute_http_url_regexp = re.compile(r"^https?://", re.I)
def init_sentry_sdk():
sentry_sdk.init(
dsn="https://cc6dd86fbe9f4e7fbd95248cfcff114d@sentry.io/1862849",
release="httprunner@{}".format(__version__)
release=f"httprunner@{__version__}"
)
with sentry_sdk.configure_scope() as scope:
@@ -34,7 +34,7 @@ def set_os_environ(variables_mapping):
"""
for variable in variables_mapping:
os.environ[variable] = variables_mapping[variable]
logger.debug("Set OS environment variable: {}".format(variable))
logger.debug(f"Set OS environment variable: {variable}")
def unset_os_environ(variables_mapping):
@@ -42,7 +42,7 @@ def unset_os_environ(variables_mapping):
"""
for variable in variables_mapping:
os.environ.pop(variable)
logger.debug("Unset OS environment variable: {}".format(variable))
logger.debug(f"Unset OS environment variable: {variable}")
def get_os_environ(variable_name):
@@ -109,7 +109,7 @@ def query_json(json_content, query, delimiter='.'):
"""
raise_flag = False
response_body = u"response body: {}\n".format(json_content)
response_body = f"response body: {json_content}\n"
try:
for key in query.split(delimiter):
if isinstance(json_content, (list, basestring)):
@@ -118,13 +118,13 @@ def query_json(json_content, query, delimiter='.'):
json_content = json_content[key]
else:
logger.error(
"invalid type value: {}({})".format(json_content, type(json_content)))
f"invalid type value: {json_content}({type(json_content)})")
raise_flag = True
except (KeyError, ValueError, IndexError):
raise_flag = True
if raise_flag:
err_msg = u"Failed to extract! => {}\n".format(query)
err_msg = f"Failed to extract! => {query}\n"
err_msg += response_body
logger.error(err_msg)
raise exceptions.ExtractFailure(err_msg)
@@ -371,21 +371,21 @@ def create_scaffold(project_name):
""" create scaffold with specified project name.
"""
if os.path.isdir(project_name):
logger.warning(u"Folder {} exists, please specify a new folder name.".format(project_name))
logger.warning(f"Folder {project_name} exists, please specify a new folder name.")
return
logger.info("Start to create new project: {}".format(project_name))
logger.info("CWD: {}".format(os.getcwd()))
logger.info(f"Start to create new project: {project_name}")
logger.info(f"CWD: {os.getcwd()}")
def create_folder(path):
os.makedirs(path)
msg = "created folder: {}".format(path)
msg = f"created folder: {path}"
logger.info(msg)
def create_file(path, file_content=""):
with open(path, 'w') as f:
f.write(file_content)
msg = "created file: {}".format(path)
msg = f"created file: {path}"
logger.info(msg)
demo_api_content = """
@@ -526,14 +526,14 @@ def prettify_json_file(file_list):
"""
for json_file in set(file_list):
if not json_file.endswith(".json"):
logger.warning("Only JSON file format can be prettified, skip: {}".format(json_file))
logger.warning(f"Only JSON file format can be prettified, skip: {json_file}")
continue
logger.info("Start to prettify JSON file: {}".format(json_file))
logger.info(f"Start to prettify JSON file: {json_file}")
dir_path = os.path.dirname(json_file)
file_name, file_suffix = os.path.splitext(os.path.basename(json_file))
outfile = os.path.join(dir_path, "{}.pretty.json".format(file_name))
outfile = os.path.join(dir_path, f"{file_name}.pretty.json")
with io.open(json_file, 'r', encoding='utf-8') as stream:
try:
@@ -545,7 +545,7 @@ def prettify_json_file(file_list):
json.dump(obj, out, indent=4, separators=(',', ': '))
out.write('\n')
print("success: {}".format(outfile))
print(f"success: {outfile}")
def omit_long_data(body, omit_len=512):
@@ -560,7 +560,7 @@ def omit_long_data(body, omit_len=512):
omitted_body = body[0:omit_len]
appendix_str = " ... OMITTED {} CHARACTORS ...".format(body_len - omit_len)
appendix_str = f" ... OMITTED {body_len - omit_len} CHARACTORS ..."
if isinstance(body, bytes):
appendix_str = appendix_str.encode("utf-8")
@@ -604,11 +604,11 @@ def dump_json_file(json_data, json_file_abs_path):
cls=PythonObjectEncoder
)
msg = "dump file: {}".format(json_file_abs_path)
msg = f"dump file: {json_file_abs_path}"
logger.info(msg)
except TypeError as ex:
msg = "Failed to dump json file: {}\nReason: {}".format(json_file_abs_path, ex)
msg = f"Failed to dump json file: {json_file_abs_path}\nReason: {ex}"
logger.error(msg)
@@ -620,7 +620,7 @@ def prepare_dump_json_file_abs_path(project_mapping, tag_name):
if not test_path:
# running passed in testcase/testsuite data structure
dump_file_name = "tests_mapping.{}.json".format(tag_name)
dump_file_name = f"tests_mapping.{tag_name}.json"
dumped_json_file_abs_path = os.path.join(pwd_dir_path, "logs", dump_file_name)
return dumped_json_file_abs_path
@@ -630,12 +630,12 @@ def prepare_dump_json_file_abs_path(project_mapping, tag_name):
if os.path.isdir(test_path):
file_foder_path = os.path.join(logs_dir_path, test_path_relative_path)
dump_file_name = "all.{}.json".format(tag_name)
dump_file_name = f"all.{tag_name}.json"
else:
file_relative_folder_path, test_file = os.path.split(test_path_relative_path)
file_foder_path = os.path.join(logs_dir_path, file_relative_folder_path)
test_file_name, _file_suffix = os.path.splitext(test_file)
dump_file_name = "{}.{}.json".format(test_file_name, tag_name)
dump_file_name = f"{test_file_name}.{tag_name}.json"
dumped_json_file_abs_path = os.path.join(file_foder_path, dump_file_name)
return dumped_json_file_abs_path

View File

@@ -3,7 +3,9 @@
import sys
import traceback
from httprunner import exceptions, logger, parser
from loguru import logger
from httprunner import exceptions, parser
class Validator(object):
@@ -70,12 +72,12 @@ class Validator(object):
}
script = "\n ".join(script)
code = """
code = f"""
# encoding: utf-8
def run_validate_script():
{}
""".format(script)
{script}
"""
variables = {
"status_code": self.resp_obj.status_code,
@@ -88,12 +90,12 @@ def run_validate_script():
try:
exec(code, variables)
except SyntaxError as ex:
logger.log_warning("SyntaxError in python validate script: {}".format(ex))
logger.warning(f"SyntaxError in python validate script: {ex}")
result["check_result"] = "fail"
result["output"] = "<br/>".join([
"ErrorMessage: {}".format(ex.msg),
"ErrorLine: {}".format(ex.lineno),
"ErrorText: {}".format(ex.text)
f"ErrorMessage: {ex.msg}",
f"ErrorLine: {ex.lineno}",
f"ErrorText: {ex.text}"
])
return result
@@ -101,7 +103,7 @@ def run_validate_script():
# run python validate script
variables["run_validate_script"]()
except Exception as ex:
logger.log_warning("run python validate script failed: {}".format(ex))
logger.warning(f"run python validate script failed: {ex}")
result["check_result"] = "fail"
_type, _value, _tb = sys.exc_info()
@@ -118,8 +120,8 @@ def run_validate_script():
line_no = "N/A"
result["output"] = "<br/>".join([
"ErrorType: {}".format(_type.__name__),
"ErrorLine: {}".format(line_no)
f"ErrorType: {_type.__name__}",
f"ErrorLine: {line_no}"
])
return result
@@ -131,7 +133,7 @@ def run_validate_script():
if not validators:
return
logger.log_debug("start to validate.")
logger.debug("start to validate.")
validate_pass = True
failures = []
@@ -154,7 +156,7 @@ def run_validate_script():
# validator should be LazyFunction object
if not isinstance(validator, parser.LazyFunction):
raise exceptions.ValidationFailure(
"validator should be parsed first: {}".format(validators))
f"validator should be parsed first: {validators}")
# evaluate validator args with context variable mapping.
validator_args = validator.get_args()
@@ -171,18 +173,13 @@ def run_validate_script():
"expect": expect_item,
"expect_value": expect_value
}
validate_msg = "\nvalidate: {} {} {}({})".format(
check_item,
comparator,
expect_value,
type(expect_value).__name__
)
validate_msg = f"\nvalidate: {check_item} {comparator} {expect_value}({type(expect_value).__name__})"
try:
validator.to_value(self.session_context.test_variables_mapping)
validator_dict["check_result"] = "pass"
validate_msg += "\t==> pass"
logger.log_debug(validate_msg)
logger.debug(validate_msg)
except (AssertionError, TypeError):
validate_pass = False
validator_dict["check_result"] = "fail"
@@ -194,7 +191,7 @@ def run_validate_script():
expect_value,
type(expect_value).__name__
)
logger.log_error(validate_msg)
logger.error(validate_msg)
failures.append(validate_msg)
self.validation_results["validate_extractor"].append(validator_dict)