Macaron Style Guide
Macaron makes use of different linters. These linters are managed using pre-commit hooks (see the .pre-commit-config.yaml file). Most styling issues should be caught by pre-commit hooks. However, there are some additional styling rules that we follow.
Python Modules
We ban the use of __all__ in __init__.py files due to known issues with the sphinx-apidoc docstring generator.
Naming
We avoid using the same name for two or more classes in all cases (including cases where the two classes are in different modules), due to known issues with the sphinx-apidoc docstring generator.
Docstrings
We use sphinx-apidoc to generate API Reference automatically from Python docstrings written in the code.
We follow the numpydoc style for Python docstrings (see example) with some exceptions.
Docstrings for classes
Each class should have a docstring written in a triple-quoted block describing the purpose of the class.
For variables of a class: we do not use the Attribute section as per the numpydoc style for class docstring, due to some known issues with Sphinx. Instead, to document class variables, we follow the following convention:
For simple Python classes having an
__init__constructor: We document the__init__constructor like any other regular method, i.e. all parameters should be documented. We do not document instance variables and consider them to be private to the class. To expose an instance variable of a class in the documentation, we use Python property and document the property methods (getter,setter, anddeletermethods) instead. Example:class Point2D: """A point in the 2D coordinate system.""" def __init__(self, coordinate: tuple[float, float]) -> None: """Construct a point in the 2D coordinate system. Parameters ---------- coordinate : tuple[float, float] A pair of x and y coordinates. """ self._x = tuple[0] self._y = tuple[1] @property def x(self) -> float: """Get the x coordinate of the point.""" return self._x
For Python classes that declare instance variables in class attributes (e.g.
typing.NamedTuple,dataclasses.dataclass(), or SQLAlchemy ORM Mapped classes): We document each instance variable with a comment above the variable prefixed with#:(if the comment spans more than one line, all lines must be prefixed with#:). Example:from typing import NamedTuple class Point2D(NamedTuple): """A point in the 2D coordinate system.""" #: The x coordinate of the point. x: float #: The y coordinate of the point. y: float
Logging and Console Output
Macaron uses the rich Python library to provide enhanced and well-formatted logging output in the terminal. All logging should be performed using the macaron.console module.
Why use rich for logging?
Provides visually appealing, color-coded, and structured terminal UI.
Supports custom components like table, progress bar, and panel for complex output.
Makes it easier to spot errors, progress, failed checks, etc.
How to use the logging handler
Import the access_handler from macaron.console and use it to set/get the custom rich handler. For simple logging, you can use the handler’s methods for info, debug, and error messages.
from macaron.console import access_handler # Get the RichConsoleHandler rich_handler = access_handler.get_handler() # Log an info message console.info("<info message>") # Log an debug message console.debug("<debug message>") # Log an error message console.error("<error message>")
To modify the console UI, create a function call which takes necessary information and converts them into Rich RenderableType. Also modify the make_layout() function to use the newly created rich component.
def update_find_source_table(self, key: str, value: str | Status) -> None: self.find_source_content[key] = value find_source_table = Table(show_header=False, box=None) find_source_table.add_column("Details", justify="left") find_source_table.add_column("Value", justify="left") for field, content in self.find_source_content.items(): find_source_table.add_row(field, content) self.find_source_table = find_source_table def make_layout(self) -> Group: layout: list[RenderableType] = [] if self.command == "find-source": if self.find_source_table.row_count > 0: layout = layout + [self.find_source_table] return Group(*layout) rich_handler.update_find_source_table("<Detail>", "<Value>")
For more advance formatting, refer to the rich documentation: https://rich.readthedocs.io/en/stable/