2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-27 19:16:44 +00:00

fix(backend): enforce docstring args (#9428)

* enforce docstring args

* add more rules that we could add later

* fix missing yields

* add a type

* make 3.9 compat
This commit is contained in:
Matthias Mair 2025-04-04 00:02:55 +02:00 committed by GitHub
parent b48ceb00f2
commit a2370dbe59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 98 additions and 47 deletions

View File

@ -126,17 +126,18 @@ def define_env(env):
"""Define custom environment variables for the documentation build process.""" """Define custom environment variables for the documentation build process."""
@env.macro @env.macro
def sourcedir(dirname, branch=None): def sourcedir(dirname: str, branch=None):
"""Return a link to a directory within the source code repository. """Return a link to a directory within the source code repository.
Arguments: Arguments:
- dirname: The name of the directory to link to (relative to the top-level directory) dirname: The name of the directory to link to (relative to the top-level directory)
branch: The branch of the repository to link to (defaults to the current build environment)
Returns: Returns:
- A fully qualified URL to the source code directory on GitHub A fully qualified URL to the source code directory on GitHub
Raises: Raises:
- FileNotFoundError: If the directory does not exist, or the generated URL is invalid FileNotFoundError: If the directory does not exist, or the generated URL is invalid
""" """
if branch == None: if branch == None:
branch = get_build_environment() branch = get_build_environment()
@ -169,13 +170,15 @@ def define_env(env):
"""Return a link to a file within the source code repository. """Return a link to a file within the source code repository.
Arguments: Arguments:
- filename: The name of the file to link to (relative to the top-level directory) filename: The name of the file to link to (relative to the top-level directory)
branch: The branch of the repository to link to (defaults to the current build environment)
raw: If True, return the raw URL to the file (defaults to False)
Returns: Returns:
- A fully qualified URL to the source code file on GitHub A fully qualified URL to the source code file on GitHub
Raises: Raises:
- FileNotFoundError: If the file does not exist, or the generated URL is invalid FileNotFoundError: If the file does not exist, or the generated URL is invalid
""" """
if branch == None: if branch == None:
branch = get_build_environment() branch = get_build_environment()
@ -247,9 +250,9 @@ def define_env(env):
"""Include a file in the documentation, in a 'collapse' block. """Include a file in the documentation, in a 'collapse' block.
Arguments: Arguments:
- filename: The name of the file to include (relative to the top-level directory) filename: The name of the file to include (relative to the top-level directory)
- title: title: The title of the collapse block in the documentation
- fmt: fmt: The format of the included file (e.g., 'python', 'html', etc.)
""" """
here = os.path.dirname(__file__) here = os.path.dirname(__file__)
path = os.path.join(here, '..', filename) path = os.path.join(here, '..', filename)
@ -299,7 +302,7 @@ def define_env(env):
"""Extract information on a particular global setting. """Extract information on a particular global setting.
Arguments: Arguments:
- key: The name of the global setting to extract information for. key: The name of the global setting to extract information for.
""" """
global GLOBAL_SETTINGS global GLOBAL_SETTINGS
setting = GLOBAL_SETTINGS[key] setting = GLOBAL_SETTINGS[key]
@ -311,7 +314,7 @@ def define_env(env):
"""Extract information on a particular user setting. """Extract information on a particular user setting.
Arguments: Arguments:
- key: The name of the user setting to extract information for. key: The name of the user setting to extract information for.
""" """
global USER_SETTINGS global USER_SETTINGS
setting = USER_SETTINGS[key] setting = USER_SETTINGS[key]

View File

@ -18,11 +18,16 @@ src = ["src/backend/InvenTree"]
"__init__.py" = ["D104"] "__init__.py" = ["D104"]
[tool.ruff.lint] [tool.ruff.lint]
select = ["A", "B", "C", "C4", "D", "F", "I", "N", "SIM", "PIE", "PLE", "PLW", "RUF", "UP", "W"] select = ["A", "B", "C", "C4", "D", "F", "I", "N", "SIM", "PIE", "PLE", "PLW", "RUF", "UP", "W",
#"DOC201", "DOC202", # enforce return docs
"DOC402","DOC403", # enforce yield docs
#"DOC501","DOC502", # enforce raise
]
# Things that should be enabled in the future: # Things that should be enabled in the future:
# - LOG # - LOG
# - DJ # for Django stuff # - DJ # for Django stuff
# - S # for security stuff (bandit) # - S # for security stuff (bandit)
# - D401 - Imperative docstrings
ignore = [ ignore = [
"PLE1205", "PLE1205",
@ -51,8 +56,6 @@ ignore = [
"N806", "N806",
# - N812 - lowercase imported as non-lowercase # - N812 - lowercase imported as non-lowercase
"N812", "N812",
# - D417 Missing argument descriptions in the docstring
"D417",
# - RUF032 - decimal-from-float-literal # - RUF032 - decimal-from-float-literal
"RUF032", "RUF032",
"RUF045", "RUF045",
@ -69,6 +72,7 @@ ignore = [
[tool.ruff.lint.pydocstyle] [tool.ruff.lint.pydocstyle]
convention = "google" convention = "google"
ignore-var-parameters = true
[tool.ruff.lint.isort] [tool.ruff.lint.isort]
split-on-trailing-comma = false split-on-trailing-comma = false

View File

@ -194,6 +194,7 @@ def format_money(
money (Money): The money object to format money (Money): The money object to format
decimal_places (int): Number of decimal places to use decimal_places (int): Number of decimal places to use
fmt (str): Format pattern according LDML / the babel format pattern syntax (https://babel.pocoo.org/en/latest/numbers.html) fmt (str): Format pattern according LDML / the babel format pattern syntax (https://babel.pocoo.org/en/latest/numbers.html)
include_symbol (bool): Whether to include the currency symbol in the formatted output
Returns: Returns:
str: The formatted string str: The formatted string

View File

@ -437,6 +437,7 @@ def increment_serial_number(serial, part=None):
Arguments: Arguments:
serial: The serial number which should be incremented serial: The serial number which should be incremented
part: Optional part object to provide additional context for incrementing the serial number
Returns: Returns:
incremented value, or None if incrementing could not be performed. incremented value, or None if incrementing could not be performed.
@ -491,6 +492,7 @@ def extract_serial_numbers(
input_string: Input string with specified serial numbers (string, or integer) input_string: Input string with specified serial numbers (string, or integer)
expected_quantity: The number of (unique) serial numbers we expect expected_quantity: The number of (unique) serial numbers we expect
starting_value: Provide a starting value for the sequence (or None) starting_value: Provide a starting value for the sequence (or None)
part: Part that should be used as context
""" """
if starting_value is None: if starting_value is None:
starting_value = increment_serial_number(None, part=part) starting_value = increment_serial_number(None, part=part)

View File

@ -132,6 +132,7 @@ def get_shared_class_instance_state_mixin(get_state_key: Callable[[type], str]):
Arguments: Arguments:
key: The key for the shared state key: The key for the shared state
default: The default value to return if the key does not exist
""" """
return cache.get(self._get_key(key)) or default return cache.get(self._get_key(key)) or default

View File

@ -170,11 +170,11 @@ class InvenTreeMetadata(SimpleMetadata):
- model_value is callable, and field_value is not (this indicates that the model value is translated) - model_value is callable, and field_value is not (this indicates that the model value is translated)
- model_value is not a string, and field_value is a string (this indicates that the model value is translated) - model_value is not a string, and field_value is a string (this indicates that the model value is translated)
Arguments: Args:
- field_name: The name of the field field_name (str): The name of the field.
- field_key: The property key to override field_key (str): The property key to override.
- field_value: The value of the field (if available) field_value: The value of the field (if available).
- model_value: The equivalent value of the model (if available) model_value: The equivalent value of the model (if available).
""" """
if field_value is None and model_value is not None: if field_value is None and model_value is not None:
return model_value return model_value

View File

@ -39,6 +39,9 @@ def setup_tracing(
headers: The headers to send with the traces. headers: The headers to send with the traces.
resources_input: The resources to send with the traces. resources_input: The resources to send with the traces.
console: Whether to output the traces to the console. console: Whether to output the traces to the console.
auth: Dict with auth information
is_http: Whether to use HTTP or gRPC for the exporter.
append_http: Whether to append '/v1/traces' to the endpoint.
""" """
if InvenTree.ready.isImportingData() or InvenTree.ready.isRunningMigrations(): if InvenTree.ready.isImportingData() or InvenTree.ready.isRunningMigrations():
return return

View File

@ -1766,15 +1766,15 @@ def after_custom_unit_updated(sender, instance, **kwargs):
reload_unit_registry() reload_unit_registry()
def rename_attachment(instance, filename): def rename_attachment(instance, filename: str):
"""Callback function to rename an uploaded attachment file. """Callback function to rename an uploaded attachment file.
Arguments: Args:
- instance: The Attachment instance instance (Attachment): The Attachment instance for which the file is being renamed.
- filename: The original filename of the uploaded file filename (str): The original filename of the uploaded file.
Returns: Returns:
- The new filename for the uploaded file, e.g. 'attachments/<model_type>/<model_id>/<filename>' str: The new filename for the uploaded file, e.g. 'attachments/<model_type>/<model_id>/<filename>'.
""" """
# Remove any illegal characters from the filename # Remove any illegal characters from the filename
illegal_chars = '\'"\\`~#|!@#$%^&*()[]{}<>?;:+=,' illegal_chars = '\'"\\`~#|!@#$%^&*()[]{}<>?;:+=,'

View File

@ -2,6 +2,7 @@
import functools import functools
import re import re
from datetime import datetime
from django.db.models import Count, F, Q from django.db.models import Count, F, Q
from django.urls import include, path from django.urls import include, path
@ -567,15 +568,21 @@ class PartScheduling(RetrieveAPI):
schedule = [] schedule = []
def add_schedule_entry(date, quantity, title, instance, speculative_quantity=0): def add_schedule_entry(
date: datetime,
quantity: float,
title: str,
instance,
speculative_quantity: float = 0,
):
"""Add a new entry to the schedule list. """Add a new entry to the schedule list.
Arguments: Args:
- date: The date of the scheduled event date (datetime): The date of the scheduled event.
- quantity: The quantity of stock to be added or removed quantity (float): The quantity of stock to be added or removed.
- title: The title of the scheduled event title (str): The title of the scheduled event.
- instance: The associated model instance (e.g. SalesOrder object) instance (Model): The associated model instance (e.g., SalesOrder object).
- speculative_quantity: A speculative quantity to be added or removed speculative_quantity (float, optional): A speculative quantity to be added or removed. Defaults to 0.
""" """
schedule.append({ schedule.append({
'date': date, 'date': date,

View File

@ -112,8 +112,8 @@ def annotate_total_stock(reference: str = '', filter: Q = None):
- Aggregates the 'quantity' of each relevant stock item - Aggregates the 'quantity' of each relevant stock item
Args: Args:
reference: The relationship reference of the part from the current model e.g. 'part' reference (str): The relationship reference of the part from the current model e.g. 'part'
stock_filter: Q object which defines how to filter the stock items filter (Q): Q object which defines how to filter the stock items
""" """
# Stock filter only returns 'in stock' items # Stock filter only returns 'in stock' items
stock_filter = stock.models.StockItem.IN_STOCK_FILTER stock_filter = stock.models.StockItem.IN_STOCK_FILTER

View File

@ -753,6 +753,7 @@ class Part(
Arguments: Arguments:
serial: The proposed serial number serial: The proposed serial number
stock_item: (optional) A StockItem instance which has this serial number assigned (e.g. testing for duplicates) stock_item: (optional) A StockItem instance which has this serial number assigned (e.g. testing for duplicates)
check_duplicates: If True, checks for duplicate serial numbers in the database.
raise_error: If False, and ValidationError(s) will be handled raise_error: If False, and ValidationError(s) will be handled
Returns: Returns:
@ -2344,15 +2345,20 @@ class Part(
parameter.save() parameter.save()
def getTestTemplates( def getTestTemplates(
self, required=None, include_parent=True, enabled=None self, required=None, include_parent: bool = True, enabled=None
) -> QuerySet[PartTestTemplate]: ) -> QuerySet[PartTestTemplate]:
"""Return a list of all test templates associated with this Part. """Return a list of all test templates associated with this Part.
These are used for validation of a StockItem. These are used for validation of a StockItem.
Args: Args:
required: Set to True or False to filter by "required" status required (bool, optional): Filter templates by whether they are required. Defaults to None.
include_parent: Set to True to traverse upwards include_parent (bool, optional): Include templates from parent parts. Defaults to True.
enabled (bool, optional): Filter templates by their enabled status. Defaults to None.
Returns:
QuerySet: A queryset of matching test templates.
""" """
if include_parent: if include_parent:
tests = PartTestTemplate.objects.filter( tests = PartTestTemplate.objects.filter(

View File

@ -37,12 +37,13 @@ class BarcodeView(CreateAPIView):
# Default serializer class (can be overridden) # Default serializer class (can be overridden)
serializer_class = barcode_serializers.BarcodeSerializer serializer_class = barcode_serializers.BarcodeSerializer
def log_scan(self, request, response=None, result=False): def log_scan(self, request, response=None, result: bool = False):
"""Log a barcode scan to the database. """Log a barcode scan to the database.
Arguments: Arguments:
request: HTTP request object request: HTTP request object
response: Optional response data response: Optional response data
result: Boolean indicating success or failure of the scan
""" """
from common.models import BarcodeScanResult from common.models import BarcodeScanResult

View File

@ -73,7 +73,7 @@ class LabelPrintingMixin:
Arguments: Arguments:
label: The LabelTemplate object to render against label: The LabelTemplate object to render against
item: The model instance to render instance: The model instance to render
request: The HTTP request object which triggered this print job request: The HTTP request object which triggered this print job
Keyword Arguments: Keyword Arguments:
pdf_data: The raw PDF data of the rendered label (if already rendered) pdf_data: The raw PDF data of the rendered label (if already rendered)

View File

@ -117,6 +117,7 @@ class UserInterfaceMixin:
Args: Args:
request: HTTPRequest object (including user information) request: HTTPRequest object (including user information)
context: Additional context data provided by the UI (query parameters)
Returns: Returns:
list: A list of custom panels to be injected into the UI list: A list of custom panels to be injected into the UI
@ -131,6 +132,7 @@ class UserInterfaceMixin:
Args: Args:
request: HTTPRequest object (including user information) request: HTTPRequest object (including user information)
context: Additional context data provided by the UI (query parameters)
Returns: Returns:
list: A list of custom dashboard items to be injected into the UI list: A list of custom dashboard items to be injected into the UI
@ -145,6 +147,7 @@ class UserInterfaceMixin:
Args: Args:
request: HTTPRequest object (including user information) request: HTTPRequest object (including user information)
context: Additional context data provided by the UI (query parameters)
Returns: Returns:
list: A list of custom template editors to be injected into the UI list: A list of custom template editors to be injected into the UI
@ -159,6 +162,7 @@ class UserInterfaceMixin:
Args: Args:
request: HTTPRequest object (including user information) request: HTTPRequest object (including user information)
context: Additional context data provided by the UI (query parameters)
Returns: Returns:
list: A list of custom template previews to be injected into the UI list: A list of custom template previews to be injected into the UI

View File

@ -109,10 +109,13 @@ class PluginsRegistry:
def get_plugin(self, slug, active=None, with_mixin=None): def get_plugin(self, slug, active=None, with_mixin=None):
"""Lookup plugin by slug (unique key). """Lookup plugin by slug (unique key).
Arguments: Args:
slug {str}: The slug (unique key) of the plugin slug (str): The slug of the plugin to look up.
active {bool, None}: Filter by 'active' status of plugin. Defaults to None. active (bool, optional): Filter by 'active' status of the plugin. If None, no filtering is applied. Defaults to None.
with_mixin {str, None}: Filter by mixin. Defaults to None. with_mixin (str, optional): Filter by mixin name. If None, no filtering is applied. Defaults to None.
Returns:
InvenTreePlugin or None: The plugin instance if found, otherwise None.
""" """
# Check if the registry needs to be reloaded # Check if the registry needs to be reloaded
self.check_reload() self.check_reload()

View File

@ -343,6 +343,7 @@ class RegistryTests(TestCase):
Arguments: Arguments:
version: The version string to use for the plugin file version: The version string to use for the plugin file
enabled: Whether the plugin should be enabled or not enabled: Whether the plugin should be enabled or not
reload: Whether to reload the plugin registry after creating the file
Returns: Returns:
str: The plugin registry hash str: The plugin registry hash

View File

@ -100,6 +100,7 @@ def barcode(data: str, barcode_class='code128', **kwargs) -> str:
Arguments: Arguments:
data: Data to encode data: Data to encode
barcode_class (str): The type of barcode to generate (default = 'code128')
Keyword Arguments: Keyword Arguments:
format (str): Image format (default = 'PNG') format (str): Image format (default = 'PNG')

View File

@ -5,7 +5,7 @@ import logging
import os import os
from datetime import date, datetime from datetime import date, datetime
from decimal import Decimal from decimal import Decimal
from typing import Any, Optional from typing import Any, Optional, Union
from django import template from django import template
from django.apps.registry import apps from django.apps.registry import apps
@ -451,7 +451,7 @@ def render_html_text(text: str, **kwargs):
@register.simple_tag @register.simple_tag
def format_number( def format_number(
number, number: Union[int, float, Decimal],
decimal_places: Optional[int] = None, decimal_places: Optional[int] = None,
integer: bool = False, integer: bool = False,
leading: int = 0, leading: int = 0,
@ -460,6 +460,7 @@ def format_number(
"""Render a number with optional formatting options. """Render a number with optional formatting options.
Arguments: Arguments:
number: The number to be formatted
decimal_places: Number of decimal places to render decimal_places: Number of decimal places to render
integer: Boolean, whether to render the number as an integer integer: Boolean, whether to render the number as an integer
leading: Number of leading zeros (default = 0) leading: Number of leading zeros (default = 0)

View File

@ -1647,6 +1647,7 @@ class StockItem(
user (User): The user performing this action user (User): The user performing this action
deltas (dict, optional): A map of the changes made to the model. Defaults to None. deltas (dict, optional): A map of the changes made to the model. Defaults to None.
notes (str, optional): URL associated with this tracking entry. Defaults to ''. notes (str, optional): URL associated with this tracking entry. Defaults to ''.
commit (boolm optional): If True, save the entry to the database. Defaults to True.
Returns: Returns:
StockItemTracking: The created tracking entry StockItemTracking: The created tracking entry

View File

@ -214,7 +214,15 @@ class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
return self.get_rule_set(obj, 'return_order') return self.get_rule_set(obj, 'return_order')
def get_formsets_with_inlines(self, request, obj=None): def get_formsets_with_inlines(self, request, obj=None):
"""Return all inline formsets.""" """Retrieve all inline formsets for the given request and object.
Args:
request (HttpRequest): The HTTP request object.
obj (Model, optional): The model instance for which the formsets are being retrieved. Defaults to None.
Yields:
tuple: A tuple containing the formset and the corresponding inline instance.
"""
for inline in self.get_inline_instances(request, obj): for inline in self.get_inline_instances(request, obj):
# Hide RuleSetInline in the 'Add role' view # Hide RuleSetInline in the 'Add role' view
if not isinstance(inline, RuleSetInline) or obj is not None: if not isinstance(inline, RuleSetInline) or obj is not None:

View File

@ -172,6 +172,7 @@ def content_excludes(
"""Returns a list of content types to exclude from import / export. """Returns a list of content types to exclude from import / export.
Arguments: Arguments:
allow_auth (bool): Allow user authentication data to be exported / imported
allow_tokens (bool): Allow tokens to be exported / imported allow_tokens (bool): Allow tokens to be exported / imported
allow_plugins (bool): Allow plugin information to be exported / imported allow_plugins (bool): Allow plugin information to be exported / imported
allow_sso (bool): Allow SSO tokens to be exported / imported allow_sso (bool): Allow SSO tokens to be exported / imported
@ -243,6 +244,7 @@ def run(c, cmd, path: Optional[Path] = None, pty=False, env=None):
cmd: Command to run. cmd: Command to run.
path: Path to run the command in. path: Path to run the command in.
pty (bool, optional): Run an interactive session. Defaults to False. pty (bool, optional): Run an interactive session. Defaults to False.
env (dict, optional): Environment variables to pass to the command. Defaults to None.
""" """
env = env or {} env = env or {}
path = path or localDir() path = path or localDir()
@ -1082,7 +1084,8 @@ def test(
): ):
"""Run unit-tests for InvenTree codebase. """Run unit-tests for InvenTree codebase.
Arguments: Args:
c: Command line context.
disable_pty (bool): Disable PTY (default = False) disable_pty (bool): Disable PTY (default = False)
runtest (str): Specify which tests to run, in format <module>.<file>.<class>.<method> (default = '') runtest (str): Specify which tests to run, in format <module>.<file>.<class>.<method> (default = '')
migrations (bool): Run migration unit tests (default = False) migrations (bool): Run migration unit tests (default = False)
@ -1394,6 +1397,7 @@ def frontend_trans(c, extract: bool = True):
Args: Args:
c: Context variable c: Context variable
extract (bool): Whether to extract translations from source code. Defaults to True.
""" """
info('Compiling frontend translations') info('Compiling frontend translations')
if extract: if extract: