Development Guidelines

I’ve developed this coding guidelines for me over the past year (2024). It took a long time for me to be happy with code style, and the codebase doesn’t consistently follow them yet. What’s more, my guidelines are potentially still in flux; I have changed my mind about style things like Component variable annotations (forbid => allow) as I write more Amaranth. Bringing Sentinel up to date with guidelines and/or further refining the below list is a long term effort.

With that in mind, I’ve found the following coding style guidelines useful for keeping Sentinel managable:

Privacy

Technically, all Components except Top should be treated as “not likely to change but still private”. However, I don’t think the majority of objects in the package beginning with underscores looks nice. The compromise I’ve been using is that “non-nested Components should not begin with underscores”. Private top-level objects that are not Components are fine.

Actual private classes, objects, functions, etc (begin with an underscore) should have at least a single-line docstring, and more if Ruff complains (e.g. a “Yields” section).

Public API

The Top and FormalTop Signaturess are part of the public interface. However, Top’s rvfi Signature member is private.

Signatures

Todo

This section of guidelines is still in flux. The source code does not necessarily reflect this section’s guidelines.

Signature objects are not publicly exposed, except maybe the top-level signature in Top. Bindings for Signature objects are allowed, but should be private/local to a Component. Despite being private, local Signature objects should not begin with an underscore if they’re shared between Python modules (to avoid proliferation of a bunch of attributes beginning with underscores in public interfaces).

Signature objects are always from the point-of-view of transfer initiator, even Signature objects without bindings. If a responder signature is required, generate one using Signature.flip.

Variable Annotations

Components And Data Structures

Component class variable annotations for Signatures should be used when possible. There is no way to express “always from initiator point-of-view” in variable annotations. However, variable annotations for Signatures have the benefit of more compact class __init__ and documentation. I use the following rules to limit confusion over directions, and it seems to work fine:

  • In contrast to the previous paragraph, top-level In and Out Flows for Component variable annotations are from the point-of-view of the Component, even if Component is a responder (In) on some of its constituent interfaces.

  • However, if a Component contains nested interface members, their annotations should have Signatures from the transfer initiator point-of-view, even Signature objects without bindings. If a responder Member for a nested Signature is required, generate one using In.

Note that Parametric Components can’t use class variable annotations; use an “Attributes” section in the __init__ docstring for these Components.

Likewise, for prefer Struct, Union, etc over StructLayout, UnionLayout, etc. Their variable annotations make them more compact than Layouts, and they can still be used anywhere a Layout can.

Class Variables

All class variable annotations (enums, Components, Structs, etc) should use doc comments (#:). Try to limit class variables to where they have readily-apparent benefits (e.g. enums, Component Signatures, Structs, etc).

When useful for cross-referencing (on a best-effort basis), doc comments for class variables should start with a type (e.g. (#: type: Docs go here)). This is especially useful for Components, because Sphinx tends to fail to generate useful links from In(MyType)/Out(MyType) annotations by themselves.

Class Nesting

Mild class nesting is allowed at your own discretion. These can either be private or public member class variables. If I think functionality will be useful to others, nested classes/vars are public (e.g. MCause.Cause). If I think it’s purely an implementation detail that a user shouldn’t access, the nested class is private (e.g. Insn._CSR was previously private).

Type Annotations

Type annotations are very much a work-in-progress (and likely out-of-date).