General Rules
- Google Python style, 80 column limit
- Commit messages should have the module name or recipe prefix instead of something like “fix:” or “[fix]”
- Yes: “checkout: Fix spelling error”
- No: “fix: Fix spelling error”
- No: “[checkout] Fix spelling error”
- Use Python type annotations where possible
- Use ‘list’ and ‘dict’ instead of ‘typing.List’ and ‘typing.Dict’
- Use ‘from typing import ...’ and ‘from collections.abc import ...’ instead of ‘import typing’ and ‘import collections.abc’
- Use ‘from future import annotations’
- Function and method arguments should use generic types if possible, like ‘Sequence’
- Return values should use specific types, like ‘list’
- In most cases, don't hide type annotation imports under ‘if TYPE_CHECKING:’
- Yes: from typing import Callable
- No: from typing import TYPE_CHECKING ... if TYPE_CHECKING: from typing import Callable
- Exception: imports used only for annotations that look like “from RECIPE_MODULES.repo.module import ...” where “repo/module” is not in ‘DEPS’ should be hidden behind ‘if TYPE_CHECKING:’—this will allow a reference to a type defined in another module without taking a full dependency on that module
- If “repo/module” is in ‘DEPS’, then don't hide with ‘if TYPE_CHECKING:’
- Make sure any “if TYPE_CHECKING:” lines that do exist have a “# pragma: no cover” comment
- Make sure things pass pylint
- Recipe testing often requires ‘test_data’ or ‘step_test_data’ arguments in what otherwise looks like production code—leave these arguments alone
- Setting properties on a StepPresentation modifies global data, so don't remove apparent no-ops that just modify a ‘properties’ member of an object
- Don't include comments on import lines
- Don't have any trailing whitespace at the end of lines
- The ‘api’ object passed into a recipe's ‘RunSteps’ function is of type ‘recipe_api.RecipeScriptApi’ and it returns ‘RawResult | None’
- The ‘api’ object passed into a recipe's ‘GenTests’ function is of type ‘recipe_test_api.RecipeTestApi’ and it returns ‘Iterator[recipe_test_api.TestData]’
- All recipes must define both ‘RunSteps’ and ‘GenTests’
- Indentation in docstrings should be independent of the length of the variable name
- Yes: Args: very_long_variable_name: Description takes multiple lines.
- No: Args: very_long_variable_name: Description takes multiple lines.
- Use dataclasses instead of attrs
- Exceptions ok for when using attrs features not in dataclasses
- Don't import individual classes or variables—always import modules, and never use ‘from import *’
- Exceptions:
- from pathlib import Path
- from collections.abc import
- from typing import
- from PB. import InputProperties
- All imports should be at top-level, even if they're only used in GenTests()
- When importing a module under “PB”, import “as _pb”
- Yes: from PB.recipe_engine import result as result_pb
- No: from PB.recipe_engine import result
Module Dependencies
- Recipe modules contain a single class that is a subclass of recipe_api.RecipeApi
- Recipes can list their dependencies in a module-level ‘DEPS’ variable
- Dependencies without a “/” refer to the current repo, and if they do have a “/” the part in front is a repository name
- The remote for that repository can be found in “infra/config/recipes.cfg”
- Recipe modules list their dependencies in a ‘DEPS’ variable in ‘init.py’
- Recipes can access an object that is an instance of the subclass of recipe_api.RecipeApi using ‘api.’
- Recipe modules can access that same object using ‘self.m.’
- If necessary, modules themselves can be imported with lines like this: ‘from RECIPE_MODULES.. import api as _api’
- Prefer this structure over other ways to import modules directly
- Prefer type annotations based on the api object over imports
- Yes: def RunTests(api: recipe_api.RecipeScriptApi): checkout: api.checkout.CheckoutContext = ...
- No: from RECIPE_MODULES.pigweed.checkout import api as checkout_api def RunTests(api: recipe_api.RecipeScriptApi): checkout: checkout_api.CheckoutContext = ...
Testing
- After making a change, run ‘./presubmit.py’ to check that everything still works
- Also run ‘shac fmt’ to adjust any formatting
- Run ‘shac check’ to check for lint issues