Write Docstring
Conventions for writing docstrings in src/ethereum/. Docstrings are the primary prose of the specification — they read as a narrative explaining how Ethereum works, not as traditional Python API documentation. They are rendered into HTML by docc, which parses them as Markdown (via mistletoe). Run this skill before writing or modifying docstrings.
General Rules
- Markdown only — no reStructuredText (
.. directives::,:param:, RST section underlines) - 79-character line limit (same as code)
- Imperative mood for summaries ("Obtain" not "Obtains", "Return" not "Returns")
- Summary on the line after opening
""" - Blank line after the summary for multi-line docstrings
- For multi-line docstrings, the closing
"""should be on its own line - No
__init__docstrings (D107 is disabled), only the class itself is documented - Reference link definitions go at the end of the docstring, after a blank line
- Do not include constants/numeric values in the docstring (values in docstrings can easily desync with the code, and no tool will detect it)
- Avoid restating what the code is doing (the code should speak for itself)
- Avoid mentioning the current fork unnecessarily (creates noisy diffs between forks)
- Docstrings should be reserved for meaningful specification, while comments (
# ...) can be used to explain particulars of the Python reference implementation
Module Docstrings
Module docstrings introduce the concepts in the module. They should read as narrative prose — imagine a textbook chapter opening.
Start with a one-line summary, then expand with paragraphs that explain what the module contains and why. Use cross-references to link to the key types and functions defined in the module.
"""
Ethash is a proof-of-work algorithm designed to be [ASIC] resistant through
[memory hardness][mem-hard].
To achieve memory hardness, computing Ethash requires access to subsets of a
large structure. The particular subsets chosen are based on the nonce and block
header, while the set itself is changed every [`epoch`].
At a high level, the Ethash algorithm is as follows:
1. Create a **seed** value, generated with [`generate_seed`] and based on the
preceding block numbers.
1. From the seed, compute a pseudorandom **cache** with [`generate_cache`].
1. From the cache, generate a **dataset** with [`generate_dataset`]. The
dataset grows over time based on [`DATASET_EPOCH_GROWTH_SIZE`].
1. Miners hash slices of the dataset together, which is where the memory
hardness is introduced. Verification of the proof-of-work only requires the
cache to be able to recompute a much smaller subset of the full dataset.
[`DATASET_EPOCH_GROWTH_SIZE`]: ref:ethereum.ethash.DATASET_EPOCH_GROWTH_SIZE
[`generate_dataset`]: ref:ethereum.ethash.generate_dataset
[`generate_cache`]: ref:ethereum.ethash.generate_cache
[`generate_seed`]: ref:ethereum.ethash.generate_seed
[`epoch`]: ref:ethereum.ethash.epoch
[ASIC]: https://en.wikipedia.org/wiki/Application-specific_integrated_circuit
[mem-hard]: https://en.wikipedia.org/wiki/Memory-hard_function
"""
Short modules that need no narrative can use a single-line summary:
"""
Utility functions used in this specification.
"""
Function Docstrings
Function docstrings describe what the function does and why, as part of the specification narrative. Reference parameters inline with backticks — do not use formal Parameters, Returns, or Raises sections.
Short (summary only)
def convert(balance: str) -> U256:
"""
Convert a string in either hexadecimal or base-10 to a `U256`.
"""
Multi-paragraph (with context)
def add_genesis_block(
hardfork: GenesisFork, chain: Any, genesis: GenesisConfiguration
) -> None:
"""
Add the genesis block to an empty blockchain.
The genesis block is an entirely sui generis block (unique) that is not
governed by the general rules applying to all other Ethereum blocks.
Instead, the only consensus requirement is that it must be identical to
the block added by this function.
The initial state is populated with balances based on the Ethereum presale
that happened on the Bitcoin blockchain. Additional ether worth 1.98% of
the presale was given to the foundation.
The `nonce` field is `0x42` referencing Douglas Adams' "HitchHiker's Guide
to the Galaxy".
On testnets the genesis configuration usually allocates 1 wei to addresses
`0x00` to `0xFF` to avoid edge cases around precompiles being created or
cleared (by [EIP-161]).
[EIP-161]: https://eips.ethereum.org/EIPS/eip-161
"""
With cross-references
def cache_size(block_number: Uint) -> Uint:
"""
Obtain the cache size (in bytes) of the epoch to which `block_number`
belongs.
See [`INITIAL_CACHE_SIZE`] and [`CACHE_EPOCH_GROWTH_SIZE`] for the initial
size and linear growth rate, respectively. The cache is generated in
[`generate_cache`].
The actual cache size is smaller than simply multiplying
`CACHE_EPOCH_GROWTH_SIZE` by the epoch number to minimize the risk of
unintended cyclic behavior. It is defined as the highest prime number below
what linear growth would calculate.
[`INITIAL_CACHE_SIZE`]: ref:ethereum.ethash.INITIAL_CACHE_SIZE
[`CACHE_EPOCH_GROWTH_SIZE`]: ref:ethereum.ethash.CACHE_EPOCH_GROWTH_SIZE
[`generate_cache`]: ref:ethereum.ethash.generate_cache
"""
Class Docstrings
Brief summary of what the class represents, with optional narrative and cross-references.
class GenesisConfiguration:
"""
Configuration for the first block of an Ethereum chain.
Specifies the allocation of ether set out in the pre-sale, and some of
the fields of the genesis block.
"""
class EvmTracer(Protocol):
"""
[`Protocol`] that describes tracer functions.
See [`ethereum.trace`] for details about tracing in general, and
[`__call__`] for more on how to implement a tracer.
[`Protocol`]: https://docs.python.org/3/library/typing.html#typing.Protocol
[`ethereum.trace`]: ref:ethereum.trace
[`__call__`]: ref:ethereum.trace.EvmTracer.__call__
"""
Attribute Docstrings
docc documents any assignment that is followed by a bare string literal. This is non-standard Python — normally only modules, classes, and functions can have docstrings. Place a triple-quoted string immediately after the assignment.
This works for constants, class fields, module-level variables, and type aliases.
Constants
EPOCH_SIZE = Uint(30000)
"""
Number of blocks before a dataset needs to be regenerated (known as an
"epoch".) See [`epoch`].
[`epoch`]: ref:ethereum.ethash.epoch
"""
Class fields
class Example:
chain_id: U64
"""
Discriminant between diverged blockchains; `1` for Ethereum's main network.
"""
Module-level variables
_evm_trace: EvmTracer = discard_evm_trace
"""
Active [`EvmTracer`] that is used for generating traces.
[`EvmTracer`]: ref:ethereum.trace.EvmTracer
"""
Type aliases
TraceEvent = (
TransactionStart
| TransactionEnd
| PrecompileStart
| PrecompileEnd
| OpStart
| OpEnd
| OpException
| EvmStop
| GasAndRefund
)
"""
All possible types of events that an [`EvmTracer`] is expected to handle.
[`EvmTracer`]: ref:ethereum.trace.EvmTracer
"""
Cross-References
docc resolves Markdown reference links with the ref: scheme into hyperlinks in the generated documentation.
Internal (to other Python objects)
Use backtick-wrapped names as the link text, with ref: pointing to the fully-qualified path:
[`ForkCriteria`]: ref:ethereum.fork_criteria.ForkCriteria
[`generate_cache`]: ref:ethereum.ethash.generate_cache
Short aliases work when the full name is unwieldy:
[ds]: ref:ethereum.ethash.DATASET_EPOCH_GROWTH_SIZE
External URLs
Standard Markdown reference links:
[ASIC]: https://en.wikipedia.org/wiki/Application-specific_integrated_circuit
[EIP-3155]: https://eips.ethereum.org/EIPS/eip-3155
Bare URLs in angle brackets for inline use:
Available at <https://github.com/ethereum/genesis_block_generator>.
If a URL is too long to include because of the line length limit, you can add # noqa: E501 after the trailing """ to squelch the warning (but this should be a last resort).
Usage in text
Reference links are used inline with brackets:
For these intentional forks to succeed, all participants need to agree on
exactly when to switch rules. The agreed upon criteria are represented by
subclasses of [`ForkCriteria`], like [`ByBlockNumber`] and [`ByTimestamp`].
Markdown Formatting
_italic_to introduce domain terms:_Genesis_ is the term for...**bold**to highlight key concepts:Create a **seed** value- Backticks for code references:
`block_number`,`0x42` - Numbered lists (
1.) for sequential steps - Bullet lists (
-) for unordered items - Markdown headings are rarely needed inside docstrings; use paragraphs instead
Anti-Patterns
- No RST directives:
.. contents::,.. note::,:param:,:returns:— these are outdated - No NumPy/Google sections: no
Parameters\n----------orArgs:blocks - No RST section underlines:
Introduction\n------------is RST, not Markdown - No type repetition in docstrings: types come from annotations, not prose
- No empty boilerplate: don't write
"""Ethereum Specification."""with a.. contents::block — write real narrative or a concise summary - Don't skip attribute docstrings: constants and fields deserve explanations
Reference Files
For examples of well-written docstrings, see:
src/ethereum/ethash.py— narrative module + function docstringssrc/ethereum/genesis.py— class, attribute, and multi-paragraph function docstringssrc/ethereum/trace.py— class, attribute, and protocol docstringssrc/ethereum/fork_criteria.py— narrative module docstring with Markdown formatting
If these files no longer exist or are no longer good examples, abort with an appropriate error message.