Skip to content

Commit 97b54c6

Browse files
feat(checklists): add EIPChecklist enum to specify EIP checklist items (#1718)
* fix: move unit test * Create Enums - Incomplete/broken attempt * fix(filler/checklist): Template copying bug * fix: EIPChecklist usage * docs: Update usage * refactor(eip_checklist): Remove new_* from all IDs * fix: tox (minus spellcheck) * fix: typing * fix(docs,checklists): Rename section to be more descriptive * docs: remove references to string EIP checklist notation * docs: backtick `EIPChecklist` instances * docs: update changelog * docs: fix title * fix(checklists): allow ommission of parentheses in checklist markers * style(checklists): add stubs to solve mypy issues using checklist markers * test(checkmarks): add fw tests that apply EIPChecklists in pytest.param * fix(docs): fix bad links to exception tests * fix(checklists): update test markers to use correct checklist IDs Update test files to use the correct checklist IDs that match the template: - Change 'new_precompile' prefix to 'precompile' - This fixes mkdocs build warnings about missing checklist items The checklist template uses 'precompile/test/*' IDs but tests were using 'new_precompile/test/*' IDs, causing template lookup failures during documentation generation. Also add missing docstrings to fix linting errors. --------- Co-authored-by: danceratopz <[email protected]>
1 parent b8da4af commit 97b54c6

File tree

18 files changed

+2891
-290
lines changed

18 files changed

+2891
-290
lines changed

docs/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Users can select any of the artifacts depending on their testing needs for their
5757
- 🐞 Fix bug in ported-from plugin and coverage script that made PRs fail with modified tests that contained no ported tests ([#1661](https://github.com/ethereum/execution-spec-tests/pull/1661)).
5858
- 🔀 Refactor the `click`-based CLI interface used for pytest-based commands (`fill`, `execute`, `consume`) to make them more extensible ([#1654](https://github.com/ethereum/execution-spec-tests/pull/1654)).
5959
- 🔀 Split `src/ethereum_test_types/types.py` into several files to improve code organization ([#1665](https://github.com/ethereum/execution-spec-tests/pull/1665)).
60-
- ✨ Added automatic checklist generation for every EIP inside of the `tests` folder. The checklist is appended to each EIP in the documentation in the "Test Case Reference" section ([#1679](https://github.com/ethereum/execution-spec-tests/pull/1679)).
60+
- ✨ Added automatic checklist generation for every EIP inside of the `tests` folder. The checklist is appended to each EIP in the documentation in the "Test Case Reference" section ([#1679](https://github.com/ethereum/execution-spec-tests/pull/1679), [#1718](https://github.com/ethereum/execution-spec-tests/pull/1718)).
6161

6262
### 🧪 Test Cases
6363

docs/writing_tests/checklist_templates/eip_testing_checklist_template.md

Lines changed: 226 additions & 226 deletions
Large diffs are not rendered by default.

docs/writing_tests/eip_checklist.md

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,44 @@ The EIP checklist feature helps track test coverage for EIP implementations by a
66

77
When implementing tests for an EIP, you can mark specific tests as covering checklist items from the [EIP testing checklist template](../writing_tests/checklist_templates/eip_testing_checklist_template.md). The framework will then generate a filled checklist showing which items have been implemented.
88

9-
## Using the `pytest.mark.eip_checklist` Marker
9+
## Marking Tests as implementing EIP Checklist Items
1010

11-
To mark a test as implementing a specific checklist item:
11+
To mark a test as implementing a specific checklist item, use the structured `EIPChecklist` class:
12+
13+
### The `EIPChecklist` Class
1214

1315
```python
1416
import pytest
1517
from ethereum_test_tools import StateTestFiller
18+
from ethereum_test_checklists import EIPChecklist
1619

17-
@pytest.mark.eip_checklist("new_transaction_type/test/intrinsic_validity/gas_limit/exact")
20+
@EIPChecklist.TransactionType.Test.IntrinsicValidity.GasLimit.Exact()
1821
def test_exact_intrinsic_gas(state_test: StateTestFiller):
1922
"""Test transaction with exact intrinsic gas limit."""
2023
# Test implementation
2124
pass
25+
26+
# You can also use the marker without parentheses
27+
@EIPChecklist.TransactionType.Test.IntrinsicValidity.GasLimit.Insufficient
28+
def test_insufficient_intrinsic_gas(state_test: StateTestFiller):
29+
"""Test transaction with insufficient intrinsic gas limit."""
30+
# Test implementation
31+
pass
2232
```
2333

34+
The `EIPChecklist` class provides type safety and IDE autocompletion, making it easier to find and reference checklist items correctly.
35+
2436
### Marker Parameters
2537

26-
- **First positional parameter** (required): The checklist item ID from the template
38+
- **First positional parameter** (required): The checklist item ID (`EIPChecklist` reference)
2739
- **`eip` keyword parameter** (optional): List of additional EIPs covered by the test
2840

2941
Example with multiple EIPs covered by the same test:
3042

3143
```python
32-
@pytest.mark.eip_checklist("new_transaction_type/test/signature/invalid/v/0", eip=[7702, 2930])
44+
@EIPChecklist.TransactionType.Test.Signature.Invalid.V.Two(
45+
eip=[7702, 2930]
46+
)
3347
def test_invalid_signature(state_test: StateTestFiller):
3448
"""Test invalid signature that affects multiple EIPs."""
3549
pass
@@ -40,8 +54,7 @@ def test_invalid_signature(state_test: StateTestFiller):
4054
You can use partial IDs that will match all checklist items starting with that prefix:
4155

4256
```python
43-
# This will mark all items under "new_transaction_type/test/signature/invalid/" as covered
44-
@pytest.mark.eip_checklist("new_transaction_type/test/signature/invalid/")
57+
@EIPChecklist.TransactionType.Test.Signature.Invalid()
4558
def test_all_invalid_signatures(state_test: StateTestFiller):
4659
"""Test covering all invalid signature scenarios."""
4760
pass
@@ -91,17 +104,44 @@ For checklist items that are not applicable to a specific EIP, create a file nam
91104

92105
```text
93106
# tests/prague/eip7702_set_code_tx/eip_checklist_not_applicable.txt
94-
new_system_contract = EIP-7702 does not introduce a system contract
95-
new_precompile = EIP-7702 does not introduce a precompile
107+
system_contract = EIP-7702 does not introduce a system contract
108+
precompile = EIP-7702 does not introduce a precompile
96109
```
97110

98111
Format: `checklist_item_id = reason`
99112

100113
Both files support partial ID matching, so you can mark entire sections as not applicable:
101114

115+
## MyPy Type Checking Support
116+
117+
The `EIPChecklist` classes are made callable through a companion `.pyi` stub file that provides proper type hints for mypy. This allows you to use both decorator patterns without type checking errors:
118+
119+
```python
120+
# Both of these work with proper mypy support
121+
@EIPChecklist.Opcode.Test.StackComplexOperations() # With parentheses
122+
@EIPChecklist.Opcode.Test.StackComplexOperations # Without parentheses
123+
```
124+
125+
### Regenerating Type Stubs
126+
127+
If you modify the `EIPChecklist` class structure in `src/ethereum_test_checklists/eip_checklist.py`, you need to regenerate the type stub file:
128+
129+
```bash
130+
# Generate the stub file (for maintainers):
131+
uv run generate_checklist_stubs
132+
133+
# Preview what would be generated without writing the file
134+
uv run generate_checklist_stubs --dry-run
135+
136+
# Generate to a custom location
137+
uv run generate_checklist_stubs --output path/to/custom/stubs.pyi
138+
```
139+
140+
The generated stub file (`eip_checklist.pyi`) should be committed to the repository to ensure proper type checking for all developers.
141+
102142
```text
103143
# Mark all system contract items as not applicable
104-
new_system_contract/ = EIP does not introduce system contracts
144+
system_contract/ = EIP does not introduce system contracts
105145
```
106146

107147
## Output Format
@@ -135,28 +175,30 @@ Example output snippet:
135175
| `general/code_coverage/eels` | Run produced tests against EELS... | ✅ | Covered by EELS test suite |
136176
| `general/code_coverage/test_coverage` | Run coverage on the test code itself... | ✅ | `tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_txs` |
137177

138-
## New Transaction Type
178+
## Transaction Type
139179

140180
| ID | Description | Status | Tests |
141181
| -- | ----------- | ------ | ----- |
142-
| `new_transaction_type/test/intrinsic_validity/gas_limit/exact` | Provide the exact intrinsic gas... | ✅ | `tests/prague/eip7702_set_code_tx/test_checklist_example.py::test_exact_intrinsic_gas` |
143-
| `new_transaction_type/test/intrinsic_validity/gas_limit/insufficient` | Provide the exact intrinsic gas minus one... | | |
182+
| `transaction_type/test/intrinsic_validity/gas_limit/exact` | Provide the exact intrinsic gas... | ✅ | `tests/prague/eip7702_set_code_tx/test_checklist_example.py::test_exact_intrinsic_gas` |
183+
| `transaction_type/test/intrinsic_validity/gas_limit/insufficient` | Provide the exact intrinsic gas minus one... | | |
144184

145-
## New System Contract
185+
## System Contract
146186

147187
| ID | Description | Status | Tests |
148188
| -- | ----------- | ------ | ----- |
149-
| `new_system_contract/test/deployment/missing` | Verify block execution behavior... | N/A | EIP-7702 does not introduce a system contract |
189+
| `system_contract/test/deployment/missing` | Verify block execution behavior... | N/A | EIP-7702 does not introduce a system contract |
150190
```
151191

152192
## Best Practices
153193

154194
1. **Start with the checklist**: Review the checklist template before writing tests to ensure comprehensive coverage
155-
2. **Use descriptive test names**: The test name will appear in the checklist, so make it clear what the test covers
156-
3. **Mark items as you go**: Add `eip_checklist` markers while writing tests, not as an afterthought
157-
4. **Document external coverage**: If items are covered by external tools/tests, document this in `eip_checklist_external_coverage.txt`
158-
5. **Be explicit about N/A items**: Document why items are not applicable in `eip_checklist_not_applicable.txt`
159-
6. **Use partial IDs wisely**: When a test covers multiple related items, use partial IDs to mark them all
195+
2. **Use the `EIPChecklist` class**: Use `EIPChecklist.Opcode.Test.GasUsage.Normal` for type safety and IDE autocompletion
196+
3. **Use descriptive test names**: The test name will appear in the checklist, so make it clear what the test covers
197+
4. **Mark items as you go**: Add `eip_checklist` markers while writing tests, not as an afterthought
198+
5. **Document external coverage**: If items are covered by external tools/tests, document this in `eip_checklist_external_coverage.txt`
199+
6. **Be explicit about N/A items**: Document why items are not applicable in `eip_checklist_not_applicable.txt`
200+
7. **Use partial IDs wisely**: When a test covers multiple related items, use partial IDs to mark them all
201+
8. **Verify IDs before using**: Use `str(EIPChecklist.Section.Subsection)` to verify the exact string ID when needed
160202

161203
## Workflow Example
162204

@@ -174,7 +216,9 @@ Example output snippet:
174216
2. **Mark tests as you implement them**:
175217

176218
```python
177-
@pytest.mark.eip_checklist("new_opcode/test/gas_usage/normal")
219+
from ethereum_test_checklists import EIPChecklist
220+
221+
@EIPChecklist.Opcode.Test.GasUsage.Normal()
178222
def test_opcode_gas_consumption(state_test: StateTestFiller):
179223
"""Test normal gas consumption of the new opcode."""
180224
pass
@@ -187,11 +231,23 @@ Example output snippet:
187231
general/code_coverage/eels = Covered by ethereum/execution-specs PR #1234
188232
```
189233

234+
You can verify the correct ID using:
235+
236+
```python
237+
# str(EIPChecklist.General.CodeCoverage.Eels) = "general/code_coverage/eels"
238+
```
239+
190240
4. **Mark non-applicable items**:
191241

192242
```text
193243
# eip_checklist_not_applicable.txt
194-
new_precompile/ = EIP-9999 introduces an opcode, not a precompile
244+
precompile/ = EIP-9999 introduces an opcode, not a precompile
245+
```
246+
247+
You can verify the correct ID using:
248+
249+
```python
250+
# str(EIPChecklist.Precompile) = "precompile"
195251
```
196252

197253
5. **Generate and review checklist**:

docs/writing_tests/exception_tests.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ Exception tests are a special type of test which verify that an invalid transact
66

77
To test for an exception, the test can use either of the following types from `ethereum_test_exceptions` library:
88

9-
1. [`TransactionException`](../running_tests/test_formats/exceptions.md#transactionexception): To be added to the `error` field of the `Transaction` object, and to the `exception` field of the `Block` object that includes the transaction; this exception type is used when a transaction is invalid, and therefore when included in a block, the block is expected to be invalid too. This is different from valid transactions where an exception during EVM execution is expected (e.g. a revert, or out-of-gas), which can be included in valid blocks.
9+
1. [`TransactionException`](../library/ethereum_test_exceptions.md#ethereum_test_exceptions.TransactionException): To be added to the `error` field of the `Transaction` object, and to the `exception` field of the `Block` object that includes the transaction; this exception type is used when a transaction is invalid, and therefore when included in a block, the block is expected to be invalid too. This is different from valid transactions where an exception during EVM execution is expected (e.g. a revert, or out-of-gas), which can be included in valid blocks.
1010

1111
For an example, see [`eip3860_initcode.test_initcode.test_contract_creating_tx`](../tests/shanghai/eip3860_initcode/test_initcode/test_contract_creating_tx.md) which raises `TransactionException.INITCODE_SIZE_EXCEEDED` in the case that the initcode size exceeds the maximum allowed size.
1212

13-
2. [`BlockException`](../running_tests/test_formats/exceptions.md#blockexception): To be added to the `exception` field of the `Block` object; this exception type is used when a block is expected to be invalid, but the exception is related to a block property, e.g. an invalid value of the block header.
13+
2. [`BlockException`](../library/ethereum_test_exceptions.md#ethereum_test_exceptions.BlockException): To be added to the `exception` field of the `Block` object; this exception type is used when a block is expected to be invalid, but the exception is related to a block property, e.g. an invalid value of the block header.
1414

1515
For an example, see [`eip4844_blobs.test_excess_blob_gas.test_invalid_static_excess_blob_gas`](../tests/cancun/eip4844_blobs/test_excess_blob_gas/test_invalid_static_excess_blob_gas.md) which raises `BlockException.INCORRECT_EXCESS_BLOB_GAS` in the case that the `excessBlobGas` remains unchanged
1616
but the parent blobs included are not `TARGET_BLOBS_PER_BLOCK`.
@@ -19,7 +19,7 @@ Although exceptions can be combined with the `|` operator to indicate that a tes
1919

2020
## Adding a new exception
2121

22-
If a test requires a new exception, because none of the existing ones is suitable for the test, a new exception can be added to either [`TransactionException`](../running_tests/test_formats/exceptions.md#transactionexception) or [`BlockException`](../running_tests/test_formats/exceptions.md#blockexception) classes.
22+
If a test requires a new exception, because none of the existing ones is suitable for the test, a new exception can be added to either [`TransactionException`](../library/ethereum_test_exceptions.md#ethereum_test_exceptions.TransactionException) or [`BlockException`](../library/ethereum_test_exceptions.md#ethereum_test_exceptions.BlockException) classes.
2323

2424
The new exception should be added as a new enum value, and the docstring of the attribute should be a string that describes the exception.
2525

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ check_eip_versions = "cli.pytest_commands.check_eip_versions:check_eip_versions"
9191
consume = "cli.pytest_commands.consume:consume"
9292
protec = "cli.pytest_commands.consume:consume"
9393
checklist = "cli.pytest_commands.checklist:checklist"
94+
generate_checklist_stubs = "cli.generate_checklist_stubs:generate_checklist_stubs"
9495
genindex = "cli.gen_index:generate_fixtures_index_cli"
9596
gentest = "cli.gentest:generate"
9697
eofwrap = "cli.eofwrap:eof_wrap"

0 commit comments

Comments
 (0)