Python 中用于简单功能化断言的工具
项目描述
该easycheck包提供了一个轻量级工具,用于在 Python 代码中运行功能化的类似断言的检查;它还提供了用于测试的函数——特别是在 doctests 中,但也在 pytests 中,为此目的,一些函数具有专用的别名。这个想法是以easycheck与断言类似的方式使用函数,但具有更多功能且目标略有不同:当满足您定义的条件时,什么也没有发生(实际上,函数返回None);如果违反条件,则会引发异常或发出警告。函数和断言的主要区别easycheck如下:
虽然您不应该在 Python 代码中使用断言,但您可以使用
easycheck函数这样做。虽然断言仅 raise
AssertionError,但您可以选择由 easycheck 函数引发的任何异常。使用时
easycheck,您可以发出警告,而不是引发异常。
主要easycheck功能(名称以 开头check_)的设计方式使其可以用作检查是否满足一个或多个条件的易于理解的代码。它们可以用来代替if-blocks,后者通常用于检查条件并在不满足时引发异常(或发出警告)。因此,您可以执行以下操作:
if not isinstance(x, (float, int)):
raise TypeError('x must be a number')
if x > 10:
raise ValueError('Too high value of x')
或者你可以使用easycheck这个:
check_type(x, (float, int), message='x must be a number')
check_if(x <= 10, ValueError, 'Too high value of x')
与这种经典方法相比,该easycheck方法有两个主要优点:
它节省了一点空间;不多,因为大多数情况下您最终会得到一行代码而不是两行,但并非总是如此,特别是当您提供要引发的异常类型和长消息时。
主要是,它增加了代码的简单性和可读性,因为 easycheck 函数的名称及其参数的设计方式都使得读者可以立即理解正在检查的内容。
您还可以发出警告:
check_if(x <= 10, Warning, 'For stable functioning of the function, x should not be greater than 10.')
该软件包还提供专用于测试的功能,例如,
assert_type(x, (float, int))
assert_if(x <= 10)
该message参数的默认值为None,它执行以下操作。如果 中提供的异常类handle_with是内置的(即可以在 中找到dir(builtins)),则不提供任何消息。但如果它不是内置的异常(或警告)类,则将异常/警告类的文档字符串作为消息。这是提供典型消息的便捷方式。如果您想自定义消息(例如,取决于变量的值),您应该使用自定义字符串(例如,通过 f 字符串)。但是,如果您不想使用任何带有自定义异常/警告的消息,只需提供一个空字符串 ( message='')。
安装
使用 pip 安装和更新:
pip install easycheck
测试
该软件包包含 pytests 和 doctests。后者既包含在所有函数的文档字符串中,也包含在文档文件中。
在代码中使用以引发异常
下面是几个简单使用基本easycheck功能的例子。最基本的用法类似于以下内容:
check_if(a < 10)
这只是检查是否a小于 10;如果是,则什么也不会发生(实际上,check_if(a < 10)返回None)。但是如果违反了条件,函数就会引发AssertionError. AssertionError是返回的默认异常check_if(),但您可以更改:
check_if(a < 10, handle_with=ValueError)
# or shorter and equally readable:
check_if(a < 10, ValueError)
对于内置异常,例如ValueError,默认行为是不打印任何消息。但是,对于自定义异常,异常的文档字符串 ( .__doc__ ) 用作消息。您可以在创建自定义异常时使用它:
class IncorrectNameTypeError(Exception):
"""Argument name must be a string."""
name = 40
check_type(name, IncorrectNameTypeError)
Traceback (most recent call last):
...
IncorrectNameTypeError: Argument name must be a string.
如果您想确保不打印任何消息,即使对于自定义异常,也可以通过传递一个空字符串来覆盖默认行为message=''。您还可以添加自定义消息:
check_if(a < 10, handle_with=ValueError, message='Too high a')
# or shorter and equally readable:
check_if(a < 10, ValueError, 'Too high a')
其他一些函数有不同的默认错误;例如,这个电话
check_type(a, expected_type=str)
# or shorter:
check_type(a, str)
会提高TypeError而这
check_length([1, 2, 3], 1)
将引发LengthError(模块中定义的异常类easycheck)。
以下是easycheck模块提供的功能列表,以及用于测试的别名:
check_if(), 别名assert_if(); 这是最基本的easycheck功能,类似于您将使用的功能if;check_if_not(), 别名assert_if_not(); 的反面check_if(),当您需要确保条件_不_满足时很有帮助;check_if_isclose(), 别名assert_if_isclose(); 比较两个浮点数,基于match.isclose()(见这个文件);check_length(), 别名assert_length(); 比较长度(等于、小于、大于等);check_type(), 别名assert_type(); 检查预期类型,类似于isinstance();check_if_paths_exist(), 别名assert_paths(); 比较存在的路径(或仅一条路径);check_comparison()(用于比较两个项目);就像你会使用的那样与 objectsm 进行比较if obj1 != obj2: raisecheck_all_ifs(); 用于检查多个条件并返回所有检查;check_argument(); 用于对函数的参数进行一项或多项检查。
catch_check()如果您想捕获您使用的函数会引发的异常或警告,您也可以使用函数(请参见此处easycheck的示例)。但是,有时使用块来捕获异常会更好(参见示例)。try-except
> 请注意,一些easycheck函数是内置函数的简单包装器,但它们的行为不同,因为它们具有easycheck函数的典型行为:如果不满足条件,则会引发异常或引发问题。
在代码中使用以发出警告
为了在违反条件时发出警告,只需使用警告类(在handle_with参数中)而不是异常类:
check_if(2 > 1, Warning, 'Too high a value')
check_length([1, 2, 3], 10, Warning, 'Too short list with data')
请记住始终使用带有警告的消息,以使它们有意义。(在use_with_warnings_doctest.rst中查看更多信息)。
当然,您可以使用自定义警告:
class TooSmallSampleSize(Warning):
"""Results for samples size below 100 can be unstable."""
n = 50
check_if(n >= 100, TooSmallSampleSize)
... TooSmallSampleSize: Results for samples size below 100 can be unstable.
warnings.warn(message, error)
在代码中使用,一个例子
想象一下你想连接到一个数据库;如果连接因任何原因失败,您需要读取存档的平面文件。(我们将使用一些未定义的函数,它们的名称将清楚地传达函数的作用。)
from easycheck import check_if, check_if_paths_exist
class DataBaseConnectionError(Exception):
pass
def get_data_from_db(db_details, db_credentials):
try:
connect_to_db(db_details, db_credentials)
except:
return False
data = get_records_from_db()
return data
easycheck代码可能如下所示:
def get_data(db_details, db_credentials):
data = get_data_from_db(db_details, db_credentials)
check_if(
data,
handle_with=DataBaseConnectionError,
message='Cannot communicate with the database'
)
return data
您当然可以处理此异常,例如这里:
def get_data(db_details, db_credentials, archived_data_file):
data = get_data_from_db(db_details, db_credentials)
try:
check_if(
data,
handle_with=DataBaseConnectionError,
message='Cannot communicate with the database'
)
except DataBaseConnectionError:
check_if_paths_exist(archived_data_file)
with open(archived_data_file) as f:
data = f.readlines()
return data
当然,您可以在这里使用专用的上下文管理器。当然,你可以用更短的方式编写它,不使用easycheck,但是信息的流动不会那么顺畅,导致可读性降低:
def get_data(db_details, db_credentials, archived_data_file):
data = get_data_from_db(db_details, db_credentials)
if not data:
with open(archived_data_file) as f:
data = f.readlines()
return data
当然,open()上下文管理器本身会抛出错误,但是当您使用该check_if()函数并显式定义异常类时,您会清楚地向读者表明您正在检查该文件是否存在,如果不存在则引发特定异常。
用于测试
如上所述,大多数easycheck函数都有别名用于测试。当然,您可以使用check_if(),但为了与断言的常用用法保持一致,该easycheck模块提供了这些别名,以便读者立即看到您正在使用这些函数进行测试。考虑这些例子:
# Using assertions
def test_something():
a, b = my_function_1(), my_function_2()
assert a == 2;
assert isinstance(a, int)
assert isinstance(b, tuple)
assert len(b) == 5
# Using easycheck assert-like functions:
def test_something():
a, b = my_function_1(), my_function_2()
assert_if(a == 2)
assert_type(a, int)
assert_type(b, tuple)
assert_length(b, 5)
请注意,只有第一个会引发AssertionError,而其他会引发更有意义的错误(TypeError和LengthError),这可以更好地解释测试未通过的原因。
您将easycheck在use_in_testing_doctest.rst中找到更多关于使用的信息。
其他示例
您会在doctest files中找到许多示例,这些示例也可用作 doctest。
项目详情
下载文件
下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。