Skip to content

Commit 518fbee

Browse files
fix(plugins/forks): Make fork_covariant_parametrize into a marker (#1057)
* fix(plugins/forks): Make `fork_covariant_parametrize` into a marker * fix: changelog * Update docs/writing_tests/test_markers.md Co-authored-by: danceratopz <danceratopz@gmail.com> * refactor(plugins/forks): rename to `parametrize_by_fork` and argument names --------- Co-authored-by: danceratopz <danceratopz@gmail.com>
1 parent f34f19c commit 518fbee

File tree

7 files changed

+61
-118
lines changed

7 files changed

+61
-118
lines changed

docs/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Test fixtures for use by clients are available for each release on the [Github r
6464
- ✨ Generate Transaction Test type ([#933](https://github.com/ethereum/execution-spec-tests/pull/933)).
6565
- ✨ Add a default location for evm logs (`--evm-dump-dir`) when filling tests ([#999](https://github.com/ethereum/execution-spec-tests/pull/999)).
6666
- ✨ Slow tests now have greater timeout when making a request to the T8N server ([#1037](https://github.com/ethereum/execution-spec-tests/pull/1037)).
67-
- ✨ Introduce [`fork_covariant_parametrize`](https://ethereum.github.io/execution-spec-tests/main/writing_tests/test_markers/#custom-fork-covariant-markers) helper function ([#1019](https://github.com/ethereum/execution-spec-tests/pull/1019)).
67+
- ✨ Introduce [`pytest.mark.parametrize_by_fork`](https://ethereum.github.io/execution-spec-tests/main/writing_tests/test_markers/#pytestmarkfork_parametrize) helper marker ([#1019](https://github.com/ethereum/execution-spec-tests/pull/1019), [#1057](https://github.com/ethereum/execution-spec-tests/pull/1057)).
6868
- 🔀 Update EIP-7251 according to [spec updates](https://github.com/ethereum/EIPs/pull/9127) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
6969
- 🔀 Update EIP-7002 according to [spec updates](https://github.com/ethereum/EIPs/pull/9119) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
7070
- 🐞 fix(consume): allow absolute paths with `--evm-bin` ([#1052](https://github.com/ethereum/execution-spec-tests/pull/1052)).

docs/writing_tests/test_markers.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -271,27 +271,32 @@ def test_something_with_all_tx_types_but_skip_type_1(state_test_only, tx_type):
271271

272272
In this example, the test will be skipped if `tx_type` is equal to 1 by returning a `pytest.mark.skip` marker, and return `None` otherwise.
273273

274-
## Custom Fork Covariant Markers
274+
## `@pytest.mark.parametrize_by_fork`
275275

276-
Custom fork covariant markers can be created by using the `fork_covariant_parametrize` decorator.
276+
A test can be dynamically parametrized based on the fork using the `parametrize_by_fork` marker.
277277

278-
This decorator takes three arguments:
278+
This marker takes two positional arguments:
279279

280-
- `parameter_names`: A list of parameter names that will be parametrized using the custom function.
281-
- `fn`: A function that takes the fork as parameter and returns a list of values that will be used to parametrize the test.
282-
- `marks`: A marker, list of markers, or a lambda function that can be used to add additional markers to the test.
280+
- `argnames`: A list of parameter names that will be parametrized using the custom function.
281+
- `fn`: A function that takes the fork as parameter and returns a list of values that will be used to parametrize the test at that specific fork.
282+
283+
And one keyword argument:
284+
285+
- `marks` (optional): A marker, list of markers, or a lambda function that can be used to add additional markers to the generated tests.
286+
287+
The marked test function will be parametrized by the values returned by the `fn` function for each fork.
288+
289+
If the parameters that are being parametrized is only a single parameter, the return value of `fn` should be a list of values for that parameter.
290+
291+
If the parameters that are being parametrized are multiple, the return value of `fn` should be a list of tuples/lists, where each tuple contains the values for each parameter.
283292

284293
```python
285294
import pytest
286295

287-
from pytest_plugins import fork_covariant_parametrize
288-
289296
def covariant_function(fork):
290297
return [[1, 2], [3, 4]] if fork.name() == "Paris" else [[4, 5], [5, 6], [6, 7]]
291298

292-
@fork_covariant_parametrize(parameter_names=[
293-
"test_parameter", "test_parameter_2"
294-
], fn=covariant_function)
299+
@pytest.mark.parametrize_by_fork("test_parameter,test_parameter_2", covariant_function)
295300
@pytest.mark.valid_from("Paris")
296301
@pytest.mark.valid_until("Shanghai")
297302
def test_case(state_test_only, test_parameter, test_parameter_2):

src/pytest_plugins/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
11
"""Package containing pytest plugins related to test filling."""
2-
3-
from .forks import fork_covariant_parametrize
4-
5-
__all__ = ["fork_covariant_parametrize"]

src/pytest_plugins/forks/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,3 @@
33
tests based on the user-provided fork range the tests' specified validity
44
markers.
55
"""
6-
7-
from .forks import fork_covariant_parametrize
8-
9-
__all__ = ["fork_covariant_parametrize"]

src/pytest_plugins/forks/forks.py

Lines changed: 35 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,15 @@ def __init__(
105105
self.fork = fork
106106

107107
@property
108-
def parameter_names(self) -> List[str]:
108+
def argnames(self) -> List[str]:
109109
"""Return the parameter names for the test case."""
110-
parameter_names = []
110+
argnames = []
111111
for p in self.fork_covariant_parameters:
112-
parameter_names.extend(p.names)
113-
return parameter_names
112+
argnames.extend(p.names)
113+
return argnames
114114

115115
@property
116-
def parameter_values(self) -> List[ParameterSet]:
116+
def argvalues(self) -> List[ParameterSet]:
117117
"""Return the parameter values for the test case."""
118118
parameter_set_combinations = itertools.product(
119119
# Add the values for each parameter, all of them are lists of at least one element.
@@ -146,7 +146,7 @@ class CovariantDescriptor:
146146
the parametrized values change depending on the fork.
147147
"""
148148

149-
parameter_names: List[str] = []
149+
argnames: List[str] = []
150150
fn: Callable[[Fork], List[Any] | Iterable[Any]] | None = None
151151

152152
selector: FunctionType | None = None
@@ -156,8 +156,9 @@ class CovariantDescriptor:
156156

157157
def __init__(
158158
self,
159-
parameter_names: List[str] | str,
159+
argnames: List[str] | str,
160160
fn: Callable[[Fork], List[Any] | Iterable[Any]] | None = None,
161+
*,
161162
selector: FunctionType | None = None,
162163
marks: None
163164
| pytest.Mark
@@ -168,17 +169,18 @@ def __init__(
168169
Initialize a new covariant descriptor.
169170
170171
Args:
171-
parameter_names: The names of the parameters that are covariant with the fork.
172+
argnames: The names of the parameters that are covariant with the fork.
172173
fn: A function that takes the fork as the single parameter and returns the values for
173174
the parameter for each fork.
174175
selector: A function that filters the values for the parameter.
175176
marks: A list of pytest marks to apply to the test cases parametrized by the parameter.
176177
177178
"""
178-
if isinstance(parameter_names, str):
179-
self.parameter_names = parameter_names.split(",")
180-
else:
181-
self.parameter_names = parameter_names
179+
self.argnames = (
180+
[argname.strip() for argname in argnames.split(",")]
181+
if isinstance(argnames, str)
182+
else argnames
183+
)
182184
self.fn = fn
183185
self.selector = selector
184186
self.marks = marks
@@ -195,7 +197,7 @@ def process_value(
195197
if isinstance(parameters_values, ParameterSet):
196198
return parameters_values
197199

198-
if len(self.parameter_names) == 1:
200+
if len(self.argnames) == 1:
199201
# Wrap values that are meant for a single parameter in a list
200202
parameters_values = [parameters_values]
201203
marks = self.marks
@@ -237,7 +239,7 @@ def add_values(self, fork_parametrizer: ForkParametrizer) -> None:
237239
values = self.process_values(values)
238240
assert len(values) > 0
239241
fork_parametrizer.fork_covariant_parameters.append(
240-
ForkCovariantParameter(names=self.parameter_names, values=values)
242+
ForkCovariantParameter(names=self.argnames, values=values)
241243
)
242244

243245

@@ -300,7 +302,7 @@ def fn(fork: Fork) -> List[Any]:
300302
return getattr(fork, self.fork_attribute_name)(block_number=0, timestamp=0)
301303

302304
super().__init__(
303-
parameter_names=self.marker_parameter_names,
305+
argnames=self.marker_parameter_names,
304306
fn=fn,
305307
selector=selector,
306308
marks=marks,
@@ -312,7 +314,7 @@ def covariant_decorator(
312314
marker_name: str,
313315
description: str,
314316
fork_attribute_name: str,
315-
parameter_names: List[str],
317+
argnames: List[str],
316318
) -> Type[CovariantDecorator]:
317319
"""Generate a new covariant decorator subclass."""
318320
return type(
@@ -322,7 +324,7 @@ def covariant_decorator(
322324
"marker_name": marker_name,
323325
"description": description,
324326
"fork_attribute_name": fork_attribute_name,
325-
"marker_parameter_names": parameter_names,
327+
"marker_parameter_names": argnames,
326328
},
327329
)
328330

@@ -333,103 +335,53 @@ def covariant_decorator(
333335
description="marks a test to be parametrized for all tx types at parameter named tx_type"
334336
" of type int",
335337
fork_attribute_name="tx_types",
336-
parameter_names=["tx_type"],
338+
argnames=["tx_type"],
337339
),
338340
covariant_decorator(
339341
marker_name="with_all_contract_creating_tx_types",
340342
description="marks a test to be parametrized for all tx types that can create a contract"
341343
" at parameter named tx_type of type int",
342344
fork_attribute_name="contract_creating_tx_types",
343-
parameter_names=["tx_type"],
345+
argnames=["tx_type"],
344346
),
345347
covariant_decorator(
346348
marker_name="with_all_precompiles",
347349
description="marks a test to be parametrized for all precompiles at parameter named"
348350
" precompile of type int",
349351
fork_attribute_name="precompiles",
350-
parameter_names=["precompile"],
352+
argnames=["precompile"],
351353
),
352354
covariant_decorator(
353355
marker_name="with_all_evm_code_types",
354356
description="marks a test to be parametrized for all EVM code types at parameter named"
355357
" `evm_code_type` of type `EVMCodeType`, such as `LEGACY` and `EOF_V1`",
356358
fork_attribute_name="evm_code_types",
357-
parameter_names=["evm_code_type"],
359+
argnames=["evm_code_type"],
358360
),
359361
covariant_decorator(
360362
marker_name="with_all_call_opcodes",
361363
description="marks a test to be parametrized for all *CALL opcodes at parameter named"
362364
" call_opcode, and also the appropriate EVM code type at parameter named evm_code_type",
363365
fork_attribute_name="call_opcodes",
364-
parameter_names=["call_opcode", "evm_code_type"],
366+
argnames=["call_opcode", "evm_code_type"],
365367
),
366368
covariant_decorator(
367369
marker_name="with_all_create_opcodes",
368370
description="marks a test to be parametrized for all *CREATE* opcodes at parameter named"
369371
" create_opcode, and also the appropriate EVM code type at parameter named evm_code_type",
370372
fork_attribute_name="create_opcodes",
371-
parameter_names=["create_opcode", "evm_code_type"],
373+
argnames=["create_opcode", "evm_code_type"],
372374
),
373375
covariant_decorator(
374376
marker_name="with_all_system_contracts",
375377
description="marks a test to be parametrized for all system contracts at parameter named"
376378
" system_contract of type int",
377379
fork_attribute_name="system_contracts",
378-
parameter_names=["system_contract"],
380+
argnames=["system_contract"],
379381
),
380382
]
381383

382384

383-
FORK_COVARIANT_PARAMETRIZE_ATTRIBUTE = "fork_covariant_parametrize"
384-
385-
386-
def fork_covariant_parametrize(
387-
*,
388-
parameter_names: List[str] | str,
389-
fn: Callable[[Fork], List[Any] | Iterable[Any]],
390-
marks: None
391-
| pytest.Mark
392-
| pytest.MarkDecorator
393-
| List[pytest.Mark | pytest.MarkDecorator] = None,
394-
):
395-
"""
396-
Decorate a test function by fork-covariant parameters.
397-
398-
The decorated function will be parametrized by the values returned by the `fn` function
399-
for each fork.
400-
401-
If the parameters that are being parametrized is only a single parameter, the return value
402-
of `fn` should be a list of values for that parameter.
403-
404-
If the parameters that are being parametrized are multiple, the return value of `fn` should
405-
be a list of tuples/lists, where each tuple contains the values for each parameter.
406-
407-
Args:
408-
parameter_names: Names of the parameters to be parametrized and that are covariant with
409-
the fork.
410-
fn: Function that takes the fork as the single parameter and returns the values for
411-
the parameter for each fork.
412-
marks: List of pytest marks to apply to all test cases parametrized.
413-
414-
"""
415-
416-
def decorator(decorated_function: FunctionType) -> FunctionType:
417-
"""Decorate a test function by covariant parameters."""
418-
covariant_descriptor = CovariantDescriptor(
419-
parameter_names=parameter_names,
420-
fn=fn,
421-
marks=marks,
422-
)
423-
covariant_descriptors: List[CovariantDescriptor] = getattr(
424-
decorated_function, FORK_COVARIANT_PARAMETRIZE_ATTRIBUTE, []
425-
)
426-
covariant_descriptors.append(covariant_descriptor)
427-
setattr(decorated_function, FORK_COVARIANT_PARAMETRIZE_ATTRIBUTE, covariant_descriptors)
428-
return decorated_function
429-
430-
return decorator
431-
432-
433385
@pytest.hookimpl(tryfirst=True)
434386
def pytest_configure(config: pytest.Config):
435387
"""
@@ -749,11 +701,12 @@ def add_fork_covariant_parameters(
749701
for fork_parametrizer in fork_parametrizers:
750702
covariant_descriptor(metafunc=metafunc).add_values(fork_parametrizer=fork_parametrizer)
751703

752-
if hasattr(metafunc.function, FORK_COVARIANT_PARAMETRIZE_ATTRIBUTE):
753-
covariant_descriptors: List[CovariantDescriptor] = getattr(
754-
metafunc.function, FORK_COVARIANT_PARAMETRIZE_ATTRIBUTE
755-
)
756-
for descriptor in covariant_descriptors:
704+
for marker in metafunc.definition.iter_markers():
705+
if marker.name == "parametrize_by_fork":
706+
descriptor = CovariantDescriptor(
707+
*marker.args,
708+
**marker.kwargs,
709+
)
757710
for fork_parametrizer in fork_parametrizers:
758711
descriptor.add_values(fork_parametrizer=fork_parametrizer)
759712

@@ -767,10 +720,10 @@ def parameters_from_fork_parametrizer_list(
767720

768721
for fork_parametrizer in fork_parametrizers:
769722
if not param_names:
770-
param_names = fork_parametrizer.parameter_names
723+
param_names = fork_parametrizer.argnames
771724
else:
772-
assert param_names == fork_parametrizer.parameter_names
773-
param_values.extend(fork_parametrizer.parameter_values)
725+
assert param_names == fork_parametrizer.argnames
726+
param_values.extend(fork_parametrizer.argvalues)
774727

775728
# Remove duplicate parameters
776729
param_1 = 0

src/pytest_plugins/forks/tests/test_covariant_markers.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -288,12 +288,13 @@ def test_case(state_test_only, tx_type):
288288
"""
289289
import pytest
290290
291-
from pytest_plugins import fork_covariant_parametrize
292-
293291
def covariant_function(fork):
294292
return [1, 2] if fork.name() == "Paris" else [3, 4, 5]
295293
296-
@fork_covariant_parametrize(parameter_names=["test_parameter"], fn=covariant_function)
294+
@pytest.mark.parametrize_by_fork(
295+
argnames=["test_parameter"],
296+
fn=covariant_function,
297+
)
297298
@pytest.mark.valid_from("Paris")
298299
@pytest.mark.valid_until("Shanghai")
299300
def test_case(state_test_only, test_parameter):
@@ -307,14 +308,10 @@ def test_case(state_test_only, test_parameter):
307308
"""
308309
import pytest
309310
310-
from pytest_plugins import fork_covariant_parametrize
311-
312311
def covariant_function(fork):
313312
return [[1, 2], [3, 4]] if fork.name() == "Paris" else [[4, 5], [5, 6], [6, 7]]
314313
315-
@fork_covariant_parametrize(parameter_names=[
316-
"test_parameter", "test_parameter_2"
317-
], fn=covariant_function)
314+
@pytest.mark.parametrize_by_fork("test_parameter,test_parameter_2", covariant_function)
318315
@pytest.mark.valid_from("Paris")
319316
@pytest.mark.valid_until("Shanghai")
320317
def test_case(state_test_only, test_parameter, test_parameter_2):
@@ -328,8 +325,6 @@ def test_case(state_test_only, test_parameter, test_parameter_2):
328325
"""
329326
import pytest
330327
331-
from pytest_plugins import fork_covariant_parametrize
332-
333328
def covariant_function(fork):
334329
return [
335330
pytest.param(1, id="first_value"),
@@ -340,7 +335,7 @@ def covariant_function(fork):
340335
5,
341336
]
342337
343-
@fork_covariant_parametrize(parameter_names=["test_parameter"], fn=covariant_function)
338+
@pytest.mark.parametrize_by_fork("test_parameter",covariant_function)
344339
@pytest.mark.valid_from("Paris")
345340
@pytest.mark.valid_until("Shanghai")
346341
def test_case(state_test_only, test_parameter):
@@ -354,8 +349,6 @@ def test_case(state_test_only, test_parameter):
354349
"""
355350
import pytest
356351
357-
from pytest_plugins import fork_covariant_parametrize
358-
359352
def covariant_function(fork):
360353
return [
361354
pytest.param(1, 2, id="first_test"),
@@ -366,7 +359,7 @@ def covariant_function(fork):
366359
pytest.param(6, 7, id="sixth_test"),
367360
]
368361
369-
@fork_covariant_parametrize(parameter_names=[
362+
@pytest.mark.parametrize_by_fork(argnames=[
370363
"test_parameter", "test_parameter_2"
371364
], fn=covariant_function)
372365
@pytest.mark.valid_from("Paris")

src/pytest_plugins/forks/tests/test_fork_parametrizer_types.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ def test_fork_parametrizer(
189189
expected_parameter_sets: List[ParameterSet],
190190
):
191191
"""Test that the fork parametrizer correctly parametrizes tests based on the fork name."""
192-
parameter_names, values = parameters_from_fork_parametrizer_list(fork_parametrizers)
193-
assert parameter_names == expected_names
192+
argnames, values = parameters_from_fork_parametrizer_list(fork_parametrizers)
193+
assert argnames == expected_names
194194
assert len(values) == len(expected_parameter_sets)
195195
for i in range(len(values)):
196196
assert len(values[i].values) == len(expected_parameter_sets[i].values)

0 commit comments

Comments
 (0)