Type Hints and Docstrings¶
This document defines the conventions for type hints and docstrings in the Ptera Software codebase.
Table of Contents¶
Type Hints¶
General Principles¶
Type hints should reflect what functions ACCEPT, not just what they store internally
Always include return type hints (use
-> Nonefor functions that don’t return values)Shape information belongs in docstrings, not type hints (Python’s type system can’t express array shapes)
Import Requirements¶
from collections.abc import Sequence
import numpy as np
Type Hint Patterns by Parameter Type¶
Basic Types¶
Parameter Description |
Type Hint |
|---|---|
String |
|
Boolean |
|
Boolean (accepting numpy bools) |
|
Integer |
|
Number (int or float) |
|
Float only |
|
Array and Array-Like Types¶
Parameter Description |
Type Hint |
Notes |
|---|---|---|
Array-like accepting numbers (int or float) |
|
For user-facing parameters that accept tuples/lists/arrays |
Array-like accepting floats only |
|
When only floats are valid |
Array of arrays (2D array-like) |
|
For nested sequences like |
Already a numpy array |
|
For internal functions or returns |
Class Types¶
Parameter Description |
Type Hint |
Notes |
|---|---|---|
Class from same package |
|
Direct reference |
Class from imported module |
|
Use module alias to avoid circular imports |
Optional and Union Types¶
Parameter Description |
Type Hint |
|---|---|
Optional parameter |
|
Union of multiple types |
|
Type Narrowing Patterns¶
When working with attributes that may be None, use these patterns:
Optional Attributes¶
For class attributes initialized to None but populated later:
class Solver:
def __init__(self):
# Attribute that will be populated before use
self.current_airplanes: list[geometry.airplane.Airplane] | None = None
Narrowing with Assertions¶
Use assert when you have a programming invariant (the value should never be None at this point):
def compute_forces(self):
# At this point in the code, these should always be populated
assert self.current_airplanes is not None
for airplane in self.current_airplanes:
# mypy now knows current_airplanes is not None
...
Use assert when:
None represents a bug, not a valid state
You want runtime safety during development
The invariant should always hold
Narrowing with cast()¶
Use cast() sparingly, only when the type checker cannot infer what you know to be true:
from typing import cast
# For dtype=object arrays where we know the element type
panel = cast(_panel.Panel, object_array[i, j])
Use cast() when:
Working around type checker limitations (e.g., numpy dtype=object arrays)
You’re certain of the type but can’t prove it to the type checker
No runtime check is needed
Avoid cast() for Type | None -> Type narrowing - use assert instead for runtime safety.
When the type being cast to lives across a circular import boundary, use the string form (cast("OtherClass", value)). See “Casting Across a Circular Dependency” below.
Module Alias Pattern¶
Import modules with aliases:
from . import airfoil as airfoil_mod
from . import wing as wing_mod
from . import wing_cross_section as wing_cross_section_mod
# In function signature
def mesh_wing(wing: wing_mod.Wing) -> None:
...
def _get_mcl_points(
inner_airfoil: airfoil_mod.Airfoil,
outer_airfoil: airfoil_mod.Airfoil,
...
) -> list[np.ndarray]:
...
Avoiding Circular Imports with Type Hints¶
Preferred Method: from __future__ import annotations¶
To avoid circular import errors when type hinting, use from __future__ import annotations as the first import in your module. This defers evaluation of type hints, treating them as strings automatically:
from __future__ import annotations
from collections.abc import Sequence
import numpy as np
from . import wing_movement as wing_movement_mod
from .. import geometry
# In function signature - no quotes needed!
def __init__(
self,
base_airplane: geometry.airplane.Airplane,
wing_movements: list[wing_movement_mod.WingMovement],
) -> None:
...
This approach:
Keeps all imports at the top of the file
Prevents circular import errors
Requires no string quotes around type hints
Is the default behavior in Python 3.11+
Casting Across a Circular Dependency¶
from __future__ import annotations defers type-hint evaluation but does not help with cast(), which is a runtime call whose first argument is evaluated. When narrowing a type that lives across a circular boundary, use the string form of cast() so the type name does not need to be importable at runtime:
from typing import TYPE_CHECKING, cast
if TYPE_CHECKING:
from .other_module import OtherClass
def narrow(value):
other = cast("OtherClass", value)
...
The TYPE_CHECKING import gives mypy the symbol for static resolution; the string argument keeps the runtime call free of any reference to OtherClass. Prefer this over importing OtherClass inside the function body: in-function imports are reserved for genuine lazy-load or circular cases, and the string-form cast() resolves the circularity without that escape hatch, keeping all imports at the top of the file.
Docstring Format¶
General Principles¶
Use reStructuredText (rST) format
Short description starts on same line as triple quotes
Parameter descriptions are inline (no separate type line)
Begin descriptions with article + shape/type info for arrays (e.g., “A (4,4) ndarray of floats…”)
Use present tense for descriptions (e.g., “Returns…” not “Will return…”)
Avoid starting descriptions with “This…”
Follow the ASCII Only rule in WRITING_STYLE.md, which covers all character substitutions (dashes, math symbols, smart quotes, ellipsis, arrows, emojis, and other typographic Unicode) used across the project’s prose, comments, and docstrings.
Place closing triple-quotes on their own line
Summary line is a single sentence. Any additional description goes in a new paragraph after a blank line. docformatter enforces this: if the first paragraph contains multiple sentences, it moves all but the first into a new paragraph.
No blank line between the closing triple-quotes and the next line of code. docformatter enforces this too: a blank gap after the docstring will be removed.
Module-Level Docstrings¶
Module-level docstrings appear at the very top of each Python file and describe the module’s contents. The style varies based on the type of module.
Public Package init.py Files¶
__init__.py files for a public package list subpackages, directories, and modules:
"""Contains the geometry classes.
**Contains the following subpackages:**
None
**Contains the following directories:**
None
**Contains the following modules:**
airfoil.py: Contains the Airfoil class.
airplane.py: Contains the Airplane class.
wing.py: Contains the Wing class.
wing_cross_section.py: Contains the WingCrossSection class.
"""
Pattern:
Brief description using “Contains” (present tense)
List public subpackages (or “None”)
List public directories (or “None”)
List public modules with one-line descriptions
Use blank lines between subpackages/directories/module entries for readability
Public Modules¶
Public modules (e.g., airfoil.py, wing.py) have structured docstrings listing their contents:
"""Contains the Airfoil class.
**Contains the following classes:**
Airfoil: A class used to contain the Airfoil of a WingCrossSection.
**Contains the following functions:**
None
"""
Pattern:
Brief description using “Contains” (present tense)
List public classes with brief descriptions (use “A class used to…” or similar)
List public functions (or “None”)
Use blank lines between class/function entries if there are multiple
Private Modules¶
Private modules (e.g., _meshing.py, _functions.py) have minimal docstrings:
"""Contains the function for meshing Wings."""
Pattern:
Single brief sentence
No listing of functions or classes
Keep it concise since these are internal implementation details
Function and Method Docstrings¶
def function_name(
param1: Type1,
param2: Type2,
param3: Type3,
) -> ReturnType:
"""Short description of what the function does.
Optional longer description providing more context. This provides detailed
explanations of the function's behavior. It can be one or more paragraphs.
Optional citation block:
**Citation:**
Adapted from (can be more specific if the whole function wasn't adapted): <source>
Author (or "Authors"): <author>
Date of retrieval (don't include if not known): <date>
:param param1: A (shape) dtype description of param1. Additional details about
what it represents, valid ranges, units, etc. Can wrap to multiple lines.
:param param2: Description of param2.
:param param3: Description of param3.
:return: A (shape) dtype description of what is returned. Additional details
about the return value.
"""
Array Parameter Descriptions¶
For numpy arrays, always include:
Shape: e.g., “(4,4)”, “(M,N,3)”, “(N,)”
Dtype: e.g., “ndarray of floats”, “ndarray of ints”, “ndarray of bools”
Coordinate system and reference point (when applicable)
Units (when applicable)
Default value (when applicable)
Pattern for Array Parameters¶
:param parameter_name: A (shape) ndarray of dtype representing <description>.
Additional context about coordinate systems, valid ranges, units, default value, etc.
Pattern for Array-Like Parameters¶
:param parameter_name: An array-like object of numbers (int or float) with shape
(N,M) representing <description>. Can be a tuple, list, or ndarray. Values are
converted to floats internally. The units are <units>. The default is <default>.
Class Docstrings¶
class ClassName:
"""Short description of the class.
**Contains the following methods:**
public_method_1: Short description (identical to method's docstring's short
description.
public_method_2: Short description (identical to method's docstring's short
description.
Optional notes block
**Notes:**
Detailed description of the class's purpose, behavior, or usage. Can be one or more
paragraphs. Avoid numbered or bulleted lists.
Optional citation block:
**Citation:**
Adapted from (can be more specific if the whole class wasn't adapted): <source>
Author (or "Authors"): <author>
Date of retrieval (don't include if not known): <date>
"""
Subclass Docstrings¶
When a class inherits from another class, use a modified pattern that avoids duplicating documentation from the parent class.
Subclass Class Docstring Template¶
class ChildClass(ParentClass):
"""A subclass of ParentClass used to <description>.
**Notes:**
Inherits all parameters and methods from ParentClass without modification.
Additional notes specific to the subclass. Can be one or more paragraphs.
**Contains the following methods:**
new_method_1: Short description of new method (if any).
None (if no new methods are added)
"""
Key points:
Short description explicitly mentions “A subclass of ParentClass”
Notes section states what is inherited from the parent
“Contains the following methods:” lists only NEW methods unique to this subclass
Write “None” if no new methods are added
Subclass __init__ Docstring Template¶
def __init__(
self,
inherited_param1: Type1,
inherited_param2: Type2,
new_param: Type3,
) -> None:
"""The initialization method.
See ParentClass's initialization method for descriptions of inherited
parameters.
:param new_param: Description of the new parameter unique to this subclass.
:return: None
"""
super().__init__(inherited_param1, inherited_param2)
self.new_param = new_param
Key points:
Reference the parent class’s
__init__docstring for inherited parametersOnly document parameters that are NEW to the subclass
Call
super().__init__()with inherited parameters
Public Subclasses of Private Parents¶
When a public class inherits from a private parent (a class in an underscore prefixed module like _core.py), the conventions above are inverted. The public child keeps a self-contained docstring that documents inherited methods and parameters as its own. The private parent’s class docstring and __init__ docstring use minimal descriptions that reference the public child. However, public methods and properties on the private parent must have self-contained docstrings because they appear on the public child’s RTD page via inheritance (see “Private Parent Method and Property Docstrings” below).
This is because:
The ReadTheDocs site is purely public API. Private parents are excluded from the generated documentation, but inherited public methods and properties DO appear on the public child’s page.
Users should never need to navigate to a private module to understand the public API.
Contributors reading the private parent’s source code can easily navigate to the public child for full documentation of the class and
__init__.
Private Parent Class Docstring Template¶
class _CoreClass:
"""A core class used to contain the shared foundation of PublicClass and its
feature variant siblings.
See PublicClass for full documentation of the shared interface.
<Brief description of what the core class provides and why it exists as a
separate class, aimed at contributors who need to understand the internal
architecture.>
"""
Key points:
Reference the public child for full documentation of the shared interface
Include a brief architectural description for contributors
Do not duplicate the full method listing or parameter documentation
Private Parent __init__ Docstring Template¶
def __init__(
self,
param1: Type1,
param2: Type2,
) -> None:
"""The initialization method.
See PublicClass's initialization method for full parameter descriptions.
:param param1: Brief description.
:param param2: Brief description.
:return: None
"""
Key points:
Reference the public child’s
__init__docstring for full parameter descriptionsInclude brief parameter descriptions (enough for contributors to understand the code without navigating away)
Public Child Class Docstring Template¶
class PublicClass(_core.CoreClass):
"""A class used to <description>.
**Contains the following methods:**
inherited_method_1: Short description.
inherited_method_2: Short description.
new_method_1: Short description (if any).
"""
Key points:
Do not mention the private parent in the short description or the methods listing
List all methods (inherited and new) as if they were the child’s own
The class reads as a standalone public API entry point
Public Child __init__ Docstring Template¶
def __init__(
self,
inherited_param1: Type1,
inherited_param2: Type2,
new_param: Type3,
) -> None:
"""The initialization method.
:param inherited_param1: Full description.
:param inherited_param2: Full description.
:param new_param: Full description.
:return: None
"""
super().__init__(inherited_param1, inherited_param2)
self.new_param = new_param
Key points:
Document all parameters fully (inherited and new)
Do not reference the private parent
Private Parent Method and Property Docstrings¶
Public methods and properties defined on a private parent are inherited by all public children and appear on their RTD pages. Their docstrings must therefore be self-contained and written for a public audience, unlike the class and __init__ docstrings which can defer to the public child.
Rules:
No deferral language. Do not write “see child class for full details” or similar, since the docstring IS the documentation the user sees on the child’s page.
No references to specific sibling types. A
CoreWingMovementmethod docstring must not mentionWingCrossSectionMovementorFreeFlightWingCrossSectionMovement, because the docstring appears on all siblings’ RTD pages. Instead, reference the universal geometry class that the movement class manages (e.g.,WingCrossSection), since geometry classes have no feature subclasses and are always correct.Use “each X’s movement class” framing when referring to child movement objects. For example, write “each
WingCrossSection’s movement class” rather than “itsWingCrossSections’ movement classes”. This avoids implying that a movement class owns geometry objects (movement classes own other movement classes; geometry classes own geometry classes).
Example (correct):
# On CoreWingMovement
@property
def wing_cross_section_movements(self) -> tuple:
"""The movement classes for each of this Wing's WingCrossSections.
:return: A tuple of movement classes, one per WingCrossSection.
"""
Example (incorrect):
# References a specific sibling type
@property
def wing_cross_section_movements(self) -> tuple:
"""The WingCrossSectionMovements for this WingMovement.
...
"""
# Uses deferral language
def generate_wing_at_time_step(self, ...) -> Wing:
"""Generates a Wing at a single time step.
See WingMovement for full details.
...
"""
Scope: This rule applies only to public methods and properties on core classes (those that will be inherited and displayed on RTD). Private helper methods (underscore prefixed) on core classes are internal and can use any convenient wording.
Multiple Public Siblings¶
When multiple public classes share the same private parent (e.g., Movement, FreeFlightMovement, and AeroelasticMovement all extending CoreMovement), each sibling maintains its own self-contained docstring. The inherited method descriptions can be tailored to each sibling’s context (e.g., “Movement’s sub movement objects” vs “FreeFlightMovement’s sub movement objects”).
Property Docstring Template¶
@property
def property_name(self) -> ReturnType:
"""Short description of what the property represents.
:return: Description of what is returned, including type, shape, units.
"""
For simple getter properties that just return a stored attribute, the docstring can be omitted if the attribute is already thoroughly documented in __init__’s docstring. This applies broadly to all simple getter properties, not only to cached properties with invalidating setters.
Cached Properties with Invalidating Setters¶
When implementing a caching pattern where computed properties are lazily evaluated and cached, with setters that invalidate dependent caches, follow these conventions:
Property Getters for Cached Attributes¶
For simple getters that return a cached value (e.g., corner point positions that were previously plain attributes), use a brief docstring or omit the docstring entirely if the attribute is already thoroughly documented in __init__:
@property
def Frpp_G_Cg(self) -> np.ndarray:
# No docstring as this attribute is documented in __init__()'s docstring
return self._Frpp_G_Cg
@property
def Frpp_GP1_CgP1(self) -> np.ndarray:
"""The position of the Panel's front right vertex (in the first Airplane's geometry
axes, relative to the first Airplane's CG).
:return: A (3,) ndarray of floats representing the position of the Panel's front
right vertex (in the first Airplane's geometry axes, relative to the first
Airplane's CG). The units are in meters. Returns None if not yet set or if
Frpp_G_Cg has been modified since last set.
"""
return self._Frpp_GP1_CgP1
Property Setters that Invalidate Caches¶
Do not add docstrings to setters. The cache invalidation behavior is an implementation detail.
@Frpp_G_Cg.setter
def Frpp_G_Cg(self, newFrpp_G_Cg: np.ndarray) -> None:
# No docstring as this is a setter method
self._rightLeg_G = None
self._frontLeg_G = None
self._Frbvp_G_Cg = None
self._Cpp_G_Cg = None
self._unitNormal_G = None
self._area = None
self._aspect_ratio = None
self.Frpp_GP1_CgP1 = None
self._Frpp_G_Cg = newFrpp_G_Cg
Cached Computed Properties¶
For computed properties that are now cached (e.g., rightLeg_G, area), the existing docstring remains unchanged. Caching is an implementation detail that does not affect the public interface.
Class Docstring for Classes with Caching¶
When a class uses this caching pattern, add a Notes: section to the class docstring explaining the caching behavior. Don’t add the getters and setter methods for the non-computed properties to the list of methods:
class Panel:
"""A class used to contain the panels of a Wing.
**Contains the following methods:**
rightLeg_G: The Panel's right leg vector (in geometry axes).
area: An estimate of the Panel's area.
[... other methods (don't include Frpp_G_Cg or Frpp_GP1_CgP1 ...]
**Notes:**
Computed geometric properties (leg vectors, bound vortex points, collocation points,
unit normals, area, and aspect ratio) are lazily evaluated and cached. Setting any
corner point position invalidates all dependent cached values, ensuring consistency
while avoiding redundant computation. Setting a corner point's local position
(one of the parameters with a _G_Cg suffix), sets the corresponding global position
(_GP1_CgP1 suffix) to None. It also sets this Panel's bound vortices and the loads
on the Panel to None.
"""
Examples¶
Example 1: Module-Level Docstrings¶
Public Package init.py¶
"""Contains the geometry classes.
**Contains the following subpackages:**
None
**Contains the following directories:**
None
**Contains the following modules:**
airfoil.py: Contains the Airfoil class.
airplane.py: Contains the Airplane class.
wing.py: Contains the Wing class.
wing_cross_section.py: Contains the WingCrossSection class.
"""
Public Module¶
"""Contains the Airfoil class.
**Contains the following classes:**
Airfoil: A class used to contain the Airfoil of a WingCrossSection.
**Contains the following functions:**
None
"""
Private Module¶
"""Contains the function for meshing Wings."""
Example 2: Function with Array Parameters (Internal)¶
def _get_mcl_points(
inner_airfoil: airfoil_mod.Airfoil,
outer_airfoil: airfoil_mod.Airfoil,
chordwise_coordinates: np.ndarray,
) -> list[np.ndarray]:
"""Takes in the inner and outer Airfoils of a wing section and its normalized
chordwise coordinates.
It returns a list of four column vectors containing the normalized components of
the positions of points along the mean camber line (MCL) (in each Airfoil's axes,
relative to each Airfoil's leading point).
:param inner_airfoil: The wing section's inner Airfoil.
:param outer_airfoil: The wing section's outer Airfoil.
:param chordwise_coordinates: A (N,) ndarray of floats for the normalized
chordwise coordinates where we'd like to sample each Airfoil's MCL. The values
are normalized from 0.0 to 1.0 and are unitless.
:return: A list of four (N,1) ndarrays of floats, where N is the number of points
at which we'd like to sample each Airfoil's MCL. The ndarrays contain components
of the positions of points along each Airfoil's MCL. In order, the ndarrays
returned are, (1) the inner Airfoil's MCL points' y-components, (2) the inner
Airfoil's MCL points' x-components (3) the outer Airfoil's MCL points'
y-components, and (4) the outer Airfoil's MCL points' x-components. The values
are normalized from 0.0 to 1.0 and are unitless.
"""
Example 2: Function with Transformation Matrices¶
def _get_mcs_points(
T_pas_Wcsi_Lpi_Wn_Ler: np.ndarray,
T_pas_Wcso_Lpo_Wn_Ler: np.ndarray,
inner_wing_cross_section: wing_cross_section_mod.WingCrossSection,
outer_wing_cross_section: wing_cross_section_mod.WingCrossSection,
inner_mcl_pointsY_Ai_lpAi: np.ndarray,
inner_mcl_pointsX_Ai_lpAi: np.ndarray,
outer_mcl_pointsY_Ao_lpAo: np.ndarray,
outer_mcl_pointsX_Ao_lpAo: np.ndarray,
spanwise_coordinates: np.ndarray,
) -> list[np.ndarray]:
"""Calculates the points on a wing section's mean camber surface (MCS) (in wing
axes, relative to the leading edge root point).
:param T_pas_Wcsi_Lpi_Wn_Ler: A (4,4) ndarray of floats representing a passive
transformation matrix which maps in homogeneous coordinates from the inner
WingCrossSection's axes, relative to its leading point to wing axes relative to
the leading edge root point.
:param T_pas_Wcso_Lpo_Wn_Ler: A (4,4) ndarray of floats representing a passive
transformation matrix which maps in homogeneous coordinates from the outer
WingCrossSection's axes, relative to its leading point to wing axes relative to
the leading edge root point.
:param inner_wing_cross_section: The wing section's inner WingCrossSection.
:param outer_wing_cross_section: The wing section's outer WingCrossSection.
:param inner_mcl_pointsY_Ai_lpAi: A (M,1) ndarray of floats, where M is the
number of chordwise points in the mesh. Each element represents the y-component
of the inner Airfoil's MCL points (in the inner Airfoil's axes, relative to the
inner Airfoil's leading point). The values are normalized from 0.0 to 1.0 and
are unitless.
:return: A list of four (M,N,3) ndarrays of floats, where M is the number of
chordwise points and N is the number of spanwise points. The four ndarrays are,
in order, this wing section's Panel's (1) forward inner, (2) forward outer,
(3) backward inner, and (4) backward outer panel points (in wing axes, relative
to the leading edge root point). The units are in meters.
"""
Example 3: Public Method with Array-Like Parameters¶
def __init__(
self,
name: str = "NACA0012",
outline_A_lp: np.ndarray | Sequence[Sequence[float | int]] | None = None,
resample: bool = True,
n_points_per_side: int = 400,
) -> None:
"""The initialization method.
:param name: The name of the Airfoil. It should correspond to the name of a file
the airfoils directory, or to a valid NACA 4-series airfoil (once converted to
lower-case and stripped of leading and trailing whitespace) unless you are
passing in your own array of points using outline_A_lp. Note that NACA0000 isn't
a valid NACA-series airfoil. The default is "NACA0012".
:param outline_A_lp: An array-like object of numbers (int or float) with shape
(N,2) representing the 2D points making up the Airfoil's outline (in airfoil
axes, relative to the leading point). If you wish to load coordinates from the
airfoils directory, leave this as None, which is the default. Can be a tuple,
list, or ndarray. Values are converted to floats internally. Make sure all
x-component values are in the range [0.0, 1.0]. The default value is None.
:param resample: Determines whether to resample the points defining the Airfoil's
outline. This applies to points passed in by the user or to those from the
airfoils directory. I highly recommended setting this to True. Can be a bool or
a numpy bool and will be converted internally to a bool. The default is True.
:param n_points_per_side: The number of points to use when creating the Airfoil's
MCL and when resampling the upper and lower parts of the Airfoil's outline. It
must be a positive int greater than or equal to 3. The resampled outline will
have a total number of points equal to (2 * n_points_per_side) - 1. I highly
recommend setting this to at least 100. The default value is 400.
:return: None
"""
Example 4: Method Returning Self-Reference¶
def add_control_surface(
self, deflection: float | int, hinge_point: float | int
) -> Airfoil:
"""Returns a version of the Airfoil with a control surface added at a given point.
It is called during meshing.
:param deflection: The control deflection in degrees. Deflection downwards is
positive. It must be a number (int or float) in the range [-5.0, 5.0] degrees.
Values are converted to floats internally.
:param hinge_point: The location of the hinge as a fraction of chord length. It
must be a number (int or float) in the range (0.0, 1.0). Values are converted
to floats internally.
:return: The new Airfoil with the control surface added.
"""
Example 5: Method with Optional Return¶
def get_plottable_data(self, show: bool = False) -> list[np.ndarray] | None:
"""Returns plottable data for this Airfoil's outline and mean camber line.
:param show: Determines whether to display the plot. Can be a bool or a numpy bool,
and will be converted internally to a bool. If True, the method displays the
plot and returns None. If False, the method returns the data without displaying.
The default is False.
:return: A list of two ndarrays containing the outline and MCL data, or None if
show is True.
"""
Example 6: Method Returning Array¶
def get_resampled_mcl(
self, mcl_fractions: np.ndarray | Sequence[float]
) -> np.ndarray:
"""Returns a ndarray of points along the mean camber line (MCL), resampled from the
mcl_A_outline attribute.
It is used to discretize the MCL for meshing.
:param mcl_fractions: A (N,) array-like object of floats representing normalized
distances along the MCL (from the leading to the trailing edge) at which to
return the resampled MCL points. Can be a tuple, list, or ndarray. The first
value must be 0.0, the last must be 1.0, and the remaining must be in the range
[0.0, 1.0]. All values must be non duplicated and in ascending order.
:return: A (N,2) ndarray of floats that contains the positions of the resampled
MCL points (in airfoil axes, relative to the leading point).
"""
Quick Reference¶
Common Type Hint Patterns¶
# Simple types
param: str
param: bool
param: bool | np.bool_ # Accepts both Python and numpy bools
param: int
param: float | int
# Array-like (user input)
param: np.ndarray | Sequence[float | int]
param: np.ndarray | Sequence[float]
param: np.ndarray | Sequence[Sequence[float | int]]
# Already numpy arrays
param: np.ndarray
# Classes
param: ClassName # Same module
param: module_alias.ClassName # Different module
-> "ClassName" # Self-reference
# Optional/Union
param: Type | None
param: Type1 | Type2
# Collections
-> list[np.ndarray]
-> list[ClassName]
Common Docstring Phrases¶
# Module level
"Contains the <description>."
"Contains the following subpackages:"
"Contains the following directories:"
"Contains the following modules:"
"Contains the following classes:"
"Contains the following functions:"
# Array parameters
":param name: A (shape) ndarray of dtype representing..."
":param name: An array-like object of numbers (int or float) with shape..."
# Boolean parameters (accepting numpy bools)
":param name: A bool that... Can be a bool or a numpy bool and will be converted internally to a bool."
# Return values
":return: A (shape) ndarray of dtype that..."
":return: A list of N (shape) ndarrays..."
":return: None"
# Function descriptions (avoid "This function/method")
"Takes in... and returns..."
"Calculates..."
"Returns..."
"Validates..."
# Units and ranges
"The units are meters."
"The values are normalized from 0.0 to 1.0 and are unitless."
"It must be in the range [0.0, 1.0]."
"It must be a positive int."
# Coordinate systems
"(in geometry axes, relative to the CG)"
"(in wing axes, relative to the leading edge root point)"
"(in airfoil axes, relative to the leading point)"
Notes¶
This style guide should be updated as new patterns emerge
All existing code should gradually be updated to match this style
Use
docformatteror similar tools to help maintain consistent formattingShape information is critical and must always be included in docstrings for arrays