最终用户依靠错误消息进行调试。 因此,确保根据触发的错误显示正确的错误消息非常重要。
核心思想是转换这个:
with pytest.raises(klass):
# Some code that raise an error
对此:
with pytest.raises(klass, match=msg):
# Some code that raise an error
如果引发的错误消息是外部错误消息(意味着这不是特定于熊猫的),您应该使用external_error_raised
而不是pytest.raises
。
external_error_raised
的用法是 __exactly__ 就像pytest.raises
唯一的区别是你没有传入match
参数。
例如:
import pandas._testing as tm
def test_foo():
with tm.external_error_raised(ValueError):
raise ValueError("foo")
https://github.com/pandas-dev/pandas/issues/30999
在你的 PR 中。
请评论你打算做什么,所以我们不会做双倍的工作(不用说我,你可以声明你打算做什么,记住检查是否已经采取了某些措施)。
如果应该标记为“完成”(好像没有更多工作要做)的文件未标记为“完成”,请发表评论让我知道(并通过放置@MomIsBestFriend
提及我
要自己生成完整列表,您可以运行:
python scripts/validate_unwanted_patterns.py -vt="bare_pytest_raises" pandas/tests/
您还可以针对单个文件运行它,例如:
python scripts/validate_unwanted_patterns.py -vt="bare_pytest_raises" pandas/tests/PATH/TO/SPECIFIC/FILE.py
如果文件包含裸pytest.raises
,脚本将输出以下内容:
pandas/tests/arithmetic/test_numeric.py:553:Bare pytests raise have been found. Please pass in the argument 'match' as well the exception
这意味着在pandas/tests/arithmetic/test_numeric.py
行的553
有一个裸露的pytest.raises
该列表可能会随着文件的不断移动/重命名而发生变化。
从#23922 中获取了几乎所有内容,最初由@gfyoung 打开。
我会采取:
我将开始致力于:
熊猫/测试/算术/test_numeric.py
熊猫/测试/算术/test_object.py
熊猫/测试/算术/test_period.py
熊猫/测试/算术/test_timedelta64.py
熊猫/测试/数组/间隔/test_interval.py
@gdex1我希望这会帮助你 :)
(数字代表行号)
pandas/tests/arithmetic/test_numeric.py:138
pandas/tests/arithmetic/test_numeric.py:141
pandas/tests/arithmetic/test_numeric.py:190
pandas/tests/arithmetic/test_numeric.py:208
pandas/tests/arithmetic/test_numeric.py:210
pandas/tests/arithmetic/test_numeric.py:212
pandas/tests/arithmetic/test_numeric.py:214
pandas/tests/arithmetic/test_numeric.py:232
pandas/tests/arithmetic/test_numeric.py:234
pandas/tests/arithmetic/test_numeric.py:236
pandas/tests/arithmetic/test_numeric.py:238
pandas/tests/arithmetic/test_numeric.py:519
pandas/tests/arithmetic/test_numeric.py:610
pandas/tests/arithmetic/test_numeric.py:615
pandas/tests/arithmetic/test_numeric.py:617
pandas/tests/arithmetic/test_numeric.py:795
pandas/tests/arithmetic/test_numeric.py:798
pandas/tests/arithmetic/test_numeric.py:819
pandas/tests/arithmetic/test_object.py:140
pandas/tests/arithmetic/test_object.py:152
pandas/tests/arithmetic/test_object.py:154
pandas/tests/arithmetic/test_object.py:278
pandas/tests/arithmetic/test_object.py:280
pandas/tests/arithmetic/test_object.py:282
pandas/tests/arithmetic/test_object.py:284
pandas/tests/arithmetic/test_object.py:298
pandas/tests/arithmetic/test_object.py:301
pandas/tests/arithmetic/test_object.py:315
pandas/tests/arithmetic/test_object.py:318
md5-634e15eb80aa764171dbacd11a06b70b
pandas/tests/arithmetic/test_timedelta64.py:51
pandas/tests/arithmetic/test_timedelta64.py:445
pandas/tests/arithmetic/test_timedelta64.py:607
pandas/tests/arithmetic/test_timedelta64.py:609
pandas/tests/arithmetic/test_timedelta64.py:703
pandas/tests/arithmetic/test_timedelta64.py:705
pandas/tests/arithmetic/test_timedelta64.py:707
pandas/tests/arithmetic/test_timedelta64.py:709
pandas/tests/arithmetic/test_timedelta64.py:741
pandas/tests/arithmetic/test_timedelta64.py:743
pandas/tests/arithmetic/test_timedelta64.py:960
pandas/tests/arithmetic/test_timedelta64.py:972
pandas/tests/arithmetic/test_timedelta64.py:1028
pandas/tests/arithmetic/test_timedelta64.py:1037
pandas/tests/arithmetic/test_timedelta64.py:1039
pandas/tests/arithmetic/test_timedelta64.py:1502
pandas/tests/arithmetic/test_timedelta64.py:1505
pandas/tests/arithmetic/test_timedelta64.py:1508
pandas/tests/arithmetic/test_timedelta64.py:1511
pandas/tests/arithmetic/test_timedelta64.py:1536
pandas/tests/arithmetic/test_timedelta64.py:1591
pandas/tests/arithmetic/test_timedelta64.py:1783
pandas/tests/arithmetic/test_timedelta64.py:1785
pandas/tests/arithmetic/test_timedelta64.py:1911
pandas/tests/arithmetic/test_timedelta64.py:1960
pandas/tests/arithmetic/test_timedelta64.py:1962
pandas/tests/arithmetic/test_timedelta64.py:1968
md5-634e15eb80aa764171dbacd11a06b70b
pandas/tests/arrays/interval/test_interval.py:155
@gfyoung列表实际上不是由grep -r -e "pytest.raises([a-zA-Z]*)" pandas/tests -l
生成的,它是由 #30755 中的脚本生成的(称为bare_pytest_raises
的验证类型),我将在问题正文中放置说明,一旦它合并了 :smile:
@MomIsBestFriend我会帮助:
熊猫/测试/基地/test_constructors.py
熊猫/测试/基地/test_ops.py
我可以处理这些:
@MomIsBestFriend
熊猫/测试/io/test_html.py
熊猫/测试/io/test_parquet.py
熊猫/测试/io/test_sql.py
熊猫/测试/io/test_stata.py
熊猫/测试/绘图/test_backend.py
熊猫/测试/绘图/test_boxplot_method.py
熊猫/测试/绘图/test_frame.py
熊猫/测试/绘图/test_hist_method.py
熊猫/测试/绘图/test_series.py
熊猫/测试/reductions/test_reductions.py
@MomIsBestFriend在https://github.com/pandas-dev/pandas/issues/23922 中有很多关于这个的讨论。 因为重复我在那里所说的:我认为我们不应该“盲目地”断言所有错误消息。
该线程中提到的一些内容:将其限制为内部错误消息,将匹配限制为消息的几个关键字,避免复杂的模式。
此外,我认为断言错误消息应该与实际检查它是否是一个好的、清晰的错误消息并潜在地改进这一点齐头并进。
从另一个问题的讨论中提炼出一系列注意点放在此处可能会很好。
@jorisvandenbossche
@MomIsBestFriend在#
我完全同意,但问题是新手不知道哪些错误消息要断言,哪些错误消息不应该断言,如果我们以某种方式定义关于哪些错误消息要断言和什么不该断言的规则,同时保持这个问题“初学者友好”,它会很棒(IMO)。
此外,如果我们计划在 CI 中强制执行这一点,我们需要以某种方式标记bare pytest raises
是“裸”的(IMO 注释的样式为isort: skip
就足够了),以及其他人们会知道某个特定的bare pytest raise
是故意裸露的。
该线程中提到的一些内容:将其限制为内部错误消息,将匹配限制为消息的几个关键字,避免复杂的模式。
我不明白为什么我们不想测试内部错误消息,你能详细说明一下吗?
我看到了你在https://github.com/pandas-dev/pandas/issues/23922#issuecomment -458551763 中指出的观点,我对此表示+1
,但我是+2
(如果有任何意义)在https://github.com/pandas-dev/pandas/issues/23922#issuecomment -458733117 和https://github.com/pandas-dev/pandas/issues/ 23922#issuecomment -458735169 因为 IMO 的收益大于成本。
此外,我认为断言错误消息应该与实际检查它是否是一个好的、清晰的错误消息并潜在地改进这一点齐头并进。
绝对同意。
从另一个问题的讨论中提炼出一系列注意点放在此处可能会很好。
我已经阅读了 #23922 的对话,但我没有看到IMO值得在问题正文中作为“注释”放置的任何内容,您能指出我遗漏的内容吗?
我已经阅读了 #23922 的对话,但我没有看到 IMO 值得在问题正文中作为“注释”放置的任何内容,您能指出我遗漏的内容吗?
我也没有看到这个问题还有什么要补充的。
我完全同意,但问题是新手不知道哪些错误消息要断言,哪些错误消息不应该断言,如果我们以某种方式定义关于哪些错误消息要断言和什么不该断言的规则,同时保持这个问题“初学者友好”,它会很棒(IMO)。
此外,如果我们计划在 CI 中强制执行这一点,我们需要以某种方式将裸 pytest 引发的内容标记为“裸”(IMO 评论的风格为 isort: skip 就足够了),并且其他人也会知道特定的裸 pytest raise 是故意裸露的。
这些是为什么挑选和选择要测试的和不测试的不是我更喜欢的方向的部分原因。 我还要补充一点,我们有时会检查except
块中的错误消息字符串,因此好的错误消息在开发过程中也对我们有益。
此外,如果这些“内部”消息不那么重要,为什么我们首先会收到错误消息? 然后我将创建一个助手,然后断言消息为空。
我不明白为什么我们不想测试内部错误消息,你能详细说明一下吗?
所以我说“限制到内部错误消息”,而“内部”可能有点模棱两可……我的意思是:源自熊猫本身的错误消息,当然我们想测试它们。 但是我的意思是我们(IMO)不应该测试太多外部错误消息,意思是:来自 numpy 或其他库的消息。 Numpy 可以改变这些,然后我们的测试开始由于 numpy 的外观变化而失败(这不是假设,我认为它就在上周发生)。
现在,我在https://github.com/pandas-dev/pandas/pull/30998#discussion_r366726966的不同上下文中使用了“内部” 永远不应向用户提出。 IMO,这些对于使用错误消息进行准确测试并不重要。
我明白你在 #23922(评论)中指出的观点,我对此持 +1 分,但我对 #23922(评论)和 #23922(评论)持 +2 分(如果有任何意义),因为IMO 收益大于成本。
让我们把你链接到的@simonjayhawkins的评论放在这里:
我正在假设,也许是错误的,这将有益于
- 确定可以参数化的测试
- 确定应该拆分的测试
- 更好地理解测试的故障模式
- 间接向测试添加更多文档
- 确定错误消息可以更一致的地方
- 识别多余的测试
- 帮助改进错误信息
- 识别当前因错误原因通过的测试。
这些都是有用的东西,我完全同意。 但这并不简单,如果我们想从这个问题中得到那些东西,那么这个问题不适合初学者。 当然,初学者不需要同时做所有这些事情,但我仍然觉得那些添加断言的 PR 通常相当接近“盲目地将当前的错误消息添加到 pytest.raises 调用中”而不做任何事情进一步(以上几点)。
此外,如果以上几点是使本练习有用的原因,我认为将更具体的说明放在这个问题的顶部是有用的。
需要明确的是,我完全赞成更好的错误消息和更好的测试,以断言我们拥有并保持这些错误消息良好。 但是我们的时间也有限,每个 PR 都需要时间和精力去做和审查,问题是最好把精力花在什么地方。
IMO,专注于“修复裸 pytest 引发”而不是专注于“改进错误消息”(并且在这样做的同时,更好地测试它们)会更有用。
此外,如果以上几点是使本练习有用的原因,我认为将更具体的说明放在这个问题的顶部是有用的。
创建一个更大的问题来跟踪这些可能是有意义的(其他值得包含在此类问题中的问题是 https://github.com/pandas-dev/pandas/issues/19159 和 https://github.com/pandas-开发/熊猫/问题/21575)。
这部分本身是独立的,对于初学者来说非常容易上手。
@gfyoung您链接的这些问题与本次讨论有何关联?
它们与您从@simonjayhawkins明确引入的评论
stata.py
缺少的一个匹配项正如这里所说的https://github.com/pandas-dev/pandas/pull/31091#issuecomment -575422207 我同意 @jorisvandenbossche的想法,我们不会测试来自外部包的错误消息,任何想法如何标记那些?
如果我们真的不想测试某些错误消息(为了公平起见,我可以对外部消息采取任何一种方式),我认为我们应该创建一个像这样的辅助函数:
~蟒蛇def external_error_raised(expected_exception):返回 pytest.raises(expected_exception, match=None)~
这将使我们未来的自己清楚这是一个非熊猫错误,并且match=None
用于安抚我们为裸pytest
加薪开发的任何 linting 检查。
如果我们真的不想测试某些错误消息(为了公平起见,我可以对外部消息采取任何一种方式),我认为我们应该创建一个像这样的辅助函数:
def external_error_raised(expected_exception): return pytest.raises(expected_exception, match=None)
这将使我们未来的自己清楚这是一个非熊猫错误,并且
match=None
用于安抚我们为裸pytest
加薪开发的任何 linting 检查。
+1
在那。
我真的很喜欢这个想法,我们可以让它成为我们测试的惯例吗?
如果一个函数正在测试一个函数/方法是否引发错误,并且该错误是一个外部错误,我们只需将match=None
放在“pytest.raises```中。
我们可以让它成为我们测试的约定吗?
我的意思是在贡献指南中放置一个部分。
如果一个函数正在测试一个函数/方法是否引发错误,并且该错误是一个外部错误,我们只需将 match=None 放在“pytest.raises``中。
我更喜欢辅助函数,因为这样您就不必考虑添加它了。 此外,助手名称更清楚地说明了我们为什么要这样做。
如果我们真的不想测试某些错误消息(为了公平起见,我可以对外部消息采取任何一种方式),我认为我们应该创建一个像这样的辅助函数:
def external_error_raised(expected_exception): return pytest.raises(expected_exception, match=None)
这将使我们未来的自己清楚这是一个非熊猫错误,并且
match=None
用于安抚我们为裸pytest
加薪开发的任何 linting 检查。
@gfyoung你建议把这个辅助函数放在哪里? (好像在什么文件中?)
pandas._testing
你好,
我想从事:
熊猫/测试/数组/间隔/test_ops.py
熊猫/测试/数组/test_array.py
熊猫/测试/数组/test_boolean.py
你好 - 我想从事:
熊猫/测试/算术/test_period.py
熊猫/测试/算术/test_timedelta64.py
大家好,我会采取以下措施:
熊猫/测试/计算/test_compat.py
熊猫/测试/计算/test_eval.py
熊猫/测试/dtypes/cast/test_upcast.py
熊猫/测试/dtypes/test_dtypes.py
@MomIsBestFriend这个已经完成但没有标记为完成:
熊猫/测试/算术/test_numeric.py
@MomIsBestFriend这些也是:
熊猫/测试/算术/test_period.py
熊猫/测试/数组/test_integer.py
熊猫/测试/数组/test_period.py
这些包含在 #31852 中
熊猫/测试/扩展/十进制/test_decimal.py
熊猫/测试/扩展/json/test_json.py
熊猫/测试/扩展/test_boolean.py
熊猫/测试/扩展/test_categorical.py
熊猫/测试/框架/索引/test_categorical.py
熊猫/测试/框架/索引/test_indexing.py
熊猫/测试/框架/索引/test_where.py
熊猫/测试/框架/方法/test_explode.py
熊猫/测试/框架/方法/test_isin.py
熊猫/测试/框架/方法/test_quantile.py
熊猫/测试/框架/方法/test_round.py
熊猫/测试/框架/方法/test_sort_values.py
熊猫/测试/框架/方法/test_to_dict.py
我会拿
熊猫/测试/io/excel/test_readers.py
熊猫/测试/io/excel/test_writers.py
熊猫/测试/io/excel/test_xlwt.py
熊猫/测试/io/formats/test_format.py
熊猫/测试/io/formats/test_style.py
pandas/tests/io/formats/test_to_latex.py
@MomIsBestFriend
这些是在没有标记的情况下完成的:
pandas/tests/indexes/datetimes/test_tools.py 不存在
我会去做的:
我已经更新了原来的帖子,现在有一个脚本来检测裸pytest raises
我已经包含了如何使用它的说明,如果有人仍然有问题,欢迎你提问:)
我拿,
熊猫/测试/算术/test_timedelta64.py
熊猫/测试/标量/时间戳/test_arithmetic.py
熊猫/测试/标量/时间戳/test_comparisons.py
熊猫/测试/标量/时间戳/test_constructors.py
熊猫/测试/标量/时间戳/test_timezones.py
熊猫/测试/标量/时间戳/test_unary_ops.py
似乎 pandas/tests/scalar/timestamp/ 中的所有测试都已修复。
$ git checkout master
Already on 'master'
$ python scripts/validate_unwanted_patterns.py -vt="bare_pytest_raises" pandas/tests/scalar/timestamp/
$
pandas/tests/arrays/test_boolean.py => 丢失。
我在拿
熊猫/测试/数组/间隔/test_ops.py
熊猫/测试/数组/test_datetimelike.py
熊猫/测试/groupby/test_categorical.py
熊猫/测试/groupby/test_groupby.py
熊猫/测试/groupby/test_timegrouper.py
熊猫/测试/算术/test_timedelta64.py => #33010
pandas/tests/scalar/timestamp/test_arithmetic.py => 没问题
pandas/tests/scalar/timestamp/test_comparisons.py => 没问题
pandas/tests/scalar/timestamp/test_constructors.py => 没问题
pandas/tests/scalar/timestamp/test_timezones.py => 没问题
pandas/tests/scalar/timestamp/test_unary_ops.py => 没问题
pandas/tests/arrays/test_boolean.py => 丢失。
pandas/tests/arrays/interval/test_ops.py => #33010
熊猫/测试/数组/test_datetimelike.py => #33010
pandas/tests/groupby/test_categorical.py => #33144
pandas/tests/groupby/test_groupby.py => 没问题
pandas/tests/groupby/test_timegrouper.py => 没问题
pandas/tests/indexes/categorical/test_category.py => 没问题
熊猫/测试/索引/common.py #33144
熊猫/测试/索引/datetimelike.py #33144
pandas/tests/indexes/interval/test_astype.py => 所有受影响的测试都被标记为 xfailed,我们还需要修复,是这样吗?
pandas/tests/indexes/multi/test_compat.py #33144
pandas/tests/indexes/multi/test_duplicates.py => 没问题
pandas/tests/indexes/multi/test_format.py => 找不到文件。
pandas/tests/indexes/multi/test_reshape.py #33144
pandas/tests/indexes/multi/test_setops.py => 没问题
pandas/tests/indexes/multi/test_sorting.py #33144
@sumanau7您是否列出了您正在使用的文件? 我正在处理我看到您已合并的一些文件。
完成
pandas/tests/indexes/categorical/test_category.py
pandas/tests/indexes/period/test_constructors.py
pandas/tests/indexes/period/test_join.py
pandas/tests/indexes/period/test_partial_slicing.py
pandas/tests/indexes/period/test_setops.py
pandas/tests/indexes/timedeltas/test_delete.py
我正在与
熊猫/测试/索引/范围/test_constructors.py
熊猫/测试/索引/范围/test_range.py
熊猫/测试/索引/multiindex/test_chaining_and_caching.py
熊猫/测试/索引/multiindex/test_partial.py
熊猫/测试/系列/索引/test_alter_index.py
熊猫/测试/数组/布尔/test_function.py
致力于:
pandas/tests/reshape/merge/test_multi.py
我会采取:
熊猫/测试/窗口/时刻/test_moments_ewm.py
熊猫/测试/窗口/时刻/test_moments_rolling.py
熊猫/测试/窗口/test_dtypes.py
熊猫/测试/窗口/test_ewm.py
熊猫/测试/窗口/test_expanding.py
熊猫/测试/窗口/test_timeseries_window.py
生病也采取:
你好,
我是该项目的新开发人员,我想对此提供帮助。 剩下的哪些测试最适合初学者?
谢谢,
凯文
你好,
我是该项目的新开发人员,我想对此提供帮助。 剩下的哪些测试最适合初学者?谢谢,
凯文
欢迎 - 我不认为其中任何一个比其他任何一个更容易或更难,任何一个都是一个很好的起点
我在运行 validate_unwanted_patterns.py 脚本时遇到错误:
Traceback (most recent call last):
File "C:\Users\Kevom\git\pandas\scripts\validate_unwanted_patterns.py", line 397, in <module>
main(
File "C:\Users\Kevom\git\pandas\scripts\validate_unwanted_patterns.py", line 352, in main
for line_number, msg in function(file_obj):
File "C:\Users\Kevom\git\pandas\scripts\validate_unwanted_patterns.py", line 88, in bare_pytest_raises
contents = file_obj.read()
File "C:\Program Files (x86)\Python\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 76843: character maps to <undefined>
它在读取“pandas/tests/test_strings.py”时发生,所有文件都编码为 cp1252。
我想运行脚本来仔细检查哪些测试仍未完成以避免重复工作。
大多数NotImplementedError
没有要匹配的特定消息,我知道这有点违背目的,但是将它们更改为pytest.raises(NotImplementedError, match=None)
只是为了让 linter 静音是个好主意吗?
我会采取:
开始。
-凯文
我是新手,所以将从这个开始:
你好,我是贡献的新手。 感谢您对问题的清晰记录。 我将从pandas/tests/generic/test_duplicate_labels.py
,如果可行,我会解决更多问题。
我将以 pandas/tests/arrays/test_datetimelike.py 作为开始。
另外,如果您无法成功运行python scripts/validate_unwanted_patterns.py -vt="bare_pytest_raises" pandas/tests/
,请尝试
python scripts/validate_unwanted_patterns.py -vt="bare_pytest_raises" pandas/tests/**/*.py
代替
现在运行它的最简单方法是添加
- id: unwanted-patterns-bare-pytest-raises
name: Check for use of bare use of pytest raises
language: python
entry: python scripts/validate_unwanted_patterns.py --validation-type="bare_pytest_raises"
types: [python]
files: ^pandas/tests/
到.pre-commit-config.yaml
- repo: local
部分中的
pre-commit run unwanted-patterns-bare-pytest-raises --all-files.
我已经用剩余的未完成文件更新了问题
我可以接受这些:
这些是截至今天列表中的前 5 名。
@marktgraham如果您还没有完成test_datetime.py
。 请不要管它,因为我即将进行 PR
@liaoaoyuan97不用担心,我还没碰过test_datetimelike.py
。
我将使用 pandas/tests/extension/base/getitem.py 代替。
validate_unwanted_patterns.py
在我这边引发了一个错误
$ python scripts/validate_unwanted_patterns.py -vt="bare_pytest_raises" pandas/tests/
Traceback (most recent call last):
File "scripts/validate_unwanted_patterns.py", line 479, in <module>
output_format=args.format,
File "scripts/validate_unwanted_patterns.py", line 435, in main
with open(file_path, encoding="utf-8") as file_obj:
IsADirectoryError: [Errno 21] Is a directory: 'pandas/tests/'
似乎与#37419有关?
我尝试了@MarcoGorelli提出的方法并且效果很好。
现在运行它的最简单方法是添加
- id: unwanted-patterns-bare-pytest-raises name: Check for use of bare use of pytest raises language: python entry: python scripts/validate_unwanted_patterns.py --validation-type="bare_pytest_raises" types: [python] files: ^pandas/tests/
到
.pre-commit-config.yaml
- repo: local
部分中的pre-commit run unwanted-patterns-bare-pytest-raises --all-files.
将它添加到.pre-commit-config.yaml
然后更新此线程上的说明是否有意义?
将此添加到 .pre-commit-config.yaml 然后更新此线程上的说明是否有意义?
一旦它引发的所有错误都修复了,我们会将它添加到.pre-commit-config.yaml
中,是的
似乎与#37419有关?
不,它与#37379 相关(当我们将此脚本移至预提交时,因此不再需要它在目录上运行)