mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +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:
		
							
								
								
									
										27
									
								
								docs/main.py
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								docs/main.py
									
									
									
									
									
								
							| @@ -126,17 +126,18 @@ def define_env(env): | ||||
|     """Define custom environment variables for the documentation build process.""" | ||||
|  | ||||
|     @env.macro | ||||
|     def sourcedir(dirname, branch=None): | ||||
|     def sourcedir(dirname: str, branch=None): | ||||
|         """Return a link to a directory within the source code repository. | ||||
|  | ||||
|         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: | ||||
|             - A fully qualified URL to the source code directory on GitHub | ||||
|             A fully qualified URL to the source code directory on GitHub | ||||
|  | ||||
|         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: | ||||
|             branch = get_build_environment() | ||||
| @@ -169,13 +170,15 @@ def define_env(env): | ||||
|         """Return a link to a file within the source code repository. | ||||
|  | ||||
|         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: | ||||
|             - A fully qualified URL to the source code file on GitHub | ||||
|             A fully qualified URL to the source code file on GitHub | ||||
|  | ||||
|         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: | ||||
|             branch = get_build_environment() | ||||
| @@ -247,9 +250,9 @@ def define_env(env): | ||||
|         """Include a file in the documentation, in a 'collapse' block. | ||||
|  | ||||
|         Arguments: | ||||
|             - filename: The name of the file to include (relative to the top-level directory) | ||||
|             - title: | ||||
|             - fmt: | ||||
|             filename: The name of the file to include (relative to the top-level directory) | ||||
|             title: The title of the collapse block in the documentation | ||||
|             fmt: The format of the included file (e.g., 'python', 'html', etc.) | ||||
|         """ | ||||
|         here = os.path.dirname(__file__) | ||||
|         path = os.path.join(here, '..', filename) | ||||
| @@ -299,7 +302,7 @@ def define_env(env): | ||||
|         """Extract information on a particular global setting. | ||||
|  | ||||
|         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 | ||||
|         setting = GLOBAL_SETTINGS[key] | ||||
| @@ -311,7 +314,7 @@ def define_env(env): | ||||
|         """Extract information on a particular user setting. | ||||
|  | ||||
|         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 | ||||
|         setting = USER_SETTINGS[key] | ||||
|   | ||||
| @@ -18,11 +18,16 @@ src = ["src/backend/InvenTree"] | ||||
| "__init__.py" = ["D104"] | ||||
|  | ||||
| [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: | ||||
| # - LOG | ||||
| # - DJ # for Django stuff | ||||
| # - S # for security stuff (bandit) | ||||
| # - D401 - Imperative docstrings | ||||
|  | ||||
| ignore = [ | ||||
|     "PLE1205", | ||||
| @@ -51,8 +56,6 @@ ignore = [ | ||||
| 	"N806", | ||||
| 	# - N812 - lowercase imported as non-lowercase | ||||
| 	"N812", | ||||
|     # - D417 Missing argument descriptions in the docstring | ||||
|     "D417", | ||||
|     # - RUF032 - decimal-from-float-literal | ||||
|     "RUF032", | ||||
|     "RUF045", | ||||
| @@ -69,6 +72,7 @@ ignore = [ | ||||
|  | ||||
| [tool.ruff.lint.pydocstyle] | ||||
| convention = "google" | ||||
| ignore-var-parameters = true | ||||
|  | ||||
| [tool.ruff.lint.isort] | ||||
| split-on-trailing-comma = false | ||||
|   | ||||
| @@ -194,6 +194,7 @@ def format_money( | ||||
|         money (Money): The money object to format | ||||
|         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) | ||||
|         include_symbol (bool): Whether to include the currency symbol in the formatted output | ||||
|  | ||||
|     Returns: | ||||
|         str: The formatted string | ||||
|   | ||||
| @@ -437,6 +437,7 @@ def increment_serial_number(serial, part=None): | ||||
|  | ||||
|     Arguments: | ||||
|         serial: The serial number which should be incremented | ||||
|         part: Optional part object to provide additional context for incrementing the serial number | ||||
|  | ||||
|     Returns: | ||||
|         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) | ||||
|         expected_quantity: The number of (unique) serial numbers we expect | ||||
|         starting_value: Provide a starting value for the sequence (or None) | ||||
|         part: Part that should be used as context | ||||
|     """ | ||||
|     if starting_value is None: | ||||
|         starting_value = increment_serial_number(None, part=part) | ||||
|   | ||||
| @@ -132,6 +132,7 @@ def get_shared_class_instance_state_mixin(get_state_key: Callable[[type], str]): | ||||
|  | ||||
|             Arguments: | ||||
|                 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 | ||||
|  | ||||
|   | ||||
| @@ -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 not a string, and field_value is a string (this indicates that the model value is translated) | ||||
|  | ||||
|         Arguments: | ||||
|             - field_name: The name of the field | ||||
|             - field_key: The property key to override | ||||
|             - field_value: The value of the field (if available) | ||||
|             - model_value: The equivalent value of the model (if available) | ||||
|         Args: | ||||
|             field_name (str): The name of the field. | ||||
|             field_key (str): The property key to override. | ||||
|             field_value: The value of the field (if available). | ||||
|             model_value: The equivalent value of the model (if available). | ||||
|         """ | ||||
|         if field_value is None and model_value is not None: | ||||
|             return model_value | ||||
|   | ||||
| @@ -39,6 +39,9 @@ def setup_tracing( | ||||
|         headers: The headers to send with the traces. | ||||
|         resources_input: The resources to send with the traces. | ||||
|         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(): | ||||
|         return | ||||
|   | ||||
| @@ -1766,15 +1766,15 @@ def after_custom_unit_updated(sender, instance, **kwargs): | ||||
|     reload_unit_registry() | ||||
|  | ||||
|  | ||||
| def rename_attachment(instance, filename): | ||||
| def rename_attachment(instance, filename: str): | ||||
|     """Callback function to rename an uploaded attachment file. | ||||
|  | ||||
|     Arguments: | ||||
|         - instance: The Attachment instance | ||||
|         - filename: The original filename of the uploaded file | ||||
|     Args: | ||||
|         instance (Attachment): The Attachment instance for which the file is being renamed. | ||||
|         filename (str): The original filename of the uploaded file. | ||||
|  | ||||
|     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 | ||||
|     illegal_chars = '\'"\\`~#|!@#$%^&*()[]{}<>?;:+=,' | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| import functools | ||||
| import re | ||||
| from datetime import datetime | ||||
|  | ||||
| from django.db.models import Count, F, Q | ||||
| from django.urls import include, path | ||||
| @@ -567,15 +568,21 @@ class PartScheduling(RetrieveAPI): | ||||
|  | ||||
|         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. | ||||
|  | ||||
|             Arguments: | ||||
|                 - date: The date of the scheduled event | ||||
|                 - quantity: The quantity of stock to be added or removed | ||||
|                 - title: The title of the scheduled event | ||||
|                 - instance: The associated model instance (e.g. SalesOrder object) | ||||
|                 - speculative_quantity: A speculative quantity to be added or removed | ||||
|             Args: | ||||
|                 date (datetime): The date of the scheduled event. | ||||
|                 quantity (float): The quantity of stock to be added or removed. | ||||
|                 title (str): The title of the scheduled event. | ||||
|                 instance (Model): The associated model instance (e.g., SalesOrder object). | ||||
|                 speculative_quantity (float, optional): A speculative quantity to be added or removed. Defaults to 0. | ||||
|             """ | ||||
|             schedule.append({ | ||||
|                 'date': date, | ||||
|   | ||||
| @@ -112,8 +112,8 @@ def annotate_total_stock(reference: str = '', filter: Q = None): | ||||
|     - Aggregates the 'quantity' of each relevant stock item | ||||
|  | ||||
|     Args: | ||||
|         reference: 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 | ||||
|         reference (str): The relationship reference of the part from the current model e.g. 'part' | ||||
|         filter (Q): Q object which defines how to filter the stock items | ||||
|     """ | ||||
|     # Stock filter only returns 'in stock' items | ||||
|     stock_filter = stock.models.StockItem.IN_STOCK_FILTER | ||||
|   | ||||
| @@ -753,6 +753,7 @@ class Part( | ||||
|         Arguments: | ||||
|             serial: The proposed serial number | ||||
|             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 | ||||
|  | ||||
|         Returns: | ||||
| @@ -2344,15 +2345,20 @@ class Part( | ||||
|             parameter.save() | ||||
|  | ||||
|     def getTestTemplates( | ||||
|         self, required=None, include_parent=True, enabled=None | ||||
|         self, required=None, include_parent: bool = True, enabled=None | ||||
|     ) -> QuerySet[PartTestTemplate]: | ||||
|         """Return a list of all test templates associated with this Part. | ||||
|  | ||||
|         These are used for validation of a StockItem. | ||||
|  | ||||
|  | ||||
|         Args: | ||||
|             required: Set to True or False to filter by "required" status | ||||
|             include_parent: Set to True to traverse upwards | ||||
|             required (bool, optional): Filter templates by whether they are required. Defaults to None. | ||||
|             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: | ||||
|             tests = PartTestTemplate.objects.filter( | ||||
|   | ||||
| @@ -37,12 +37,13 @@ class BarcodeView(CreateAPIView): | ||||
|     # Default serializer class (can be overridden) | ||||
|     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. | ||||
|  | ||||
|         Arguments: | ||||
|             request: HTTP request object | ||||
|             response: Optional response data | ||||
|             result: Boolean indicating success or failure of the scan | ||||
|         """ | ||||
|         from common.models import BarcodeScanResult | ||||
|  | ||||
|   | ||||
| @@ -73,7 +73,7 @@ class LabelPrintingMixin: | ||||
|  | ||||
|         Arguments: | ||||
|             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 | ||||
|         Keyword Arguments: | ||||
|             pdf_data: The raw PDF data of the rendered label (if already rendered) | ||||
|   | ||||
| @@ -117,6 +117,7 @@ class UserInterfaceMixin: | ||||
|  | ||||
|         Args: | ||||
|             request: HTTPRequest object (including user information) | ||||
|             context: Additional context data provided by the UI (query parameters) | ||||
|  | ||||
|         Returns: | ||||
|             list: A list of custom panels to be injected into the UI | ||||
| @@ -131,6 +132,7 @@ class UserInterfaceMixin: | ||||
|  | ||||
|         Args: | ||||
|             request: HTTPRequest object (including user information) | ||||
|             context: Additional context data provided by the UI (query parameters) | ||||
|  | ||||
|         Returns: | ||||
|             list: A list of custom dashboard items to be injected into the UI | ||||
| @@ -145,6 +147,7 @@ class UserInterfaceMixin: | ||||
|  | ||||
|         Args: | ||||
|             request: HTTPRequest object (including user information) | ||||
|             context: Additional context data provided by the UI (query parameters) | ||||
|  | ||||
|         Returns: | ||||
|             list: A list of custom template editors to be injected into the UI | ||||
| @@ -159,6 +162,7 @@ class UserInterfaceMixin: | ||||
|  | ||||
|         Args: | ||||
|             request: HTTPRequest object (including user information) | ||||
|             context: Additional context data provided by the UI (query parameters) | ||||
|  | ||||
|         Returns: | ||||
|             list: A list of custom template previews to be injected into the UI | ||||
|   | ||||
| @@ -109,10 +109,13 @@ class PluginsRegistry: | ||||
|     def get_plugin(self, slug, active=None, with_mixin=None): | ||||
|         """Lookup plugin by slug (unique key). | ||||
|  | ||||
|         Arguments: | ||||
|             slug {str}: The slug (unique key) of the plugin | ||||
|             active {bool, None}: Filter by 'active' status of plugin. Defaults to None. | ||||
|             with_mixin {str, None}: Filter by mixin. Defaults to None. | ||||
|         Args: | ||||
|             slug (str): The slug of the plugin to look up. | ||||
|             active (bool, optional): Filter by 'active' status of the plugin. If None, no filtering is applied. 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 | ||||
|         self.check_reload() | ||||
|   | ||||
| @@ -343,6 +343,7 @@ class RegistryTests(TestCase): | ||||
|             Arguments: | ||||
|                 version: The version string to use for the plugin file | ||||
|                 enabled: Whether the plugin should be enabled or not | ||||
|                 reload: Whether to reload the plugin registry after creating the file | ||||
|  | ||||
|             Returns: | ||||
|                 str: The plugin registry hash | ||||
|   | ||||
| @@ -100,6 +100,7 @@ def barcode(data: str, barcode_class='code128', **kwargs) -> str: | ||||
|  | ||||
|     Arguments: | ||||
|         data: Data to encode | ||||
|         barcode_class (str): The type of barcode to generate (default = 'code128') | ||||
|  | ||||
|     Keyword Arguments: | ||||
|         format (str): Image format (default = 'PNG') | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import logging | ||||
| import os | ||||
| from datetime import date, datetime | ||||
| from decimal import Decimal | ||||
| from typing import Any, Optional | ||||
| from typing import Any, Optional, Union | ||||
|  | ||||
| from django import template | ||||
| from django.apps.registry import apps | ||||
| @@ -451,7 +451,7 @@ def render_html_text(text: str, **kwargs): | ||||
|  | ||||
| @register.simple_tag | ||||
| def format_number( | ||||
|     number, | ||||
|     number: Union[int, float, Decimal], | ||||
|     decimal_places: Optional[int] = None, | ||||
|     integer: bool = False, | ||||
|     leading: int = 0, | ||||
| @@ -460,6 +460,7 @@ def format_number( | ||||
|     """Render a number with optional formatting options. | ||||
|  | ||||
|     Arguments: | ||||
|         number: The number to be formatted | ||||
|         decimal_places: Number of decimal places to render | ||||
|         integer: Boolean, whether to render the number as an integer | ||||
|         leading: Number of leading zeros (default = 0) | ||||
|   | ||||
| @@ -1647,6 +1647,7 @@ class StockItem( | ||||
|             user (User): The user performing this action | ||||
|             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 ''. | ||||
|             commit (boolm optional): If True, save the entry to the database. Defaults to True. | ||||
|  | ||||
|         Returns: | ||||
|             StockItemTracking: The created tracking entry | ||||
|   | ||||
| @@ -214,7 +214,15 @@ class RoleGroupAdmin(admin.ModelAdmin):  # pragma: no cover | ||||
|         return self.get_rule_set(obj, 'return_order') | ||||
|  | ||||
|     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): | ||||
|             # Hide RuleSetInline in the 'Add role' view | ||||
|             if not isinstance(inline, RuleSetInline) or obj is not None: | ||||
|   | ||||
							
								
								
									
										6
									
								
								tasks.py
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								tasks.py
									
									
									
									
									
								
							| @@ -172,6 +172,7 @@ def content_excludes( | ||||
|     """Returns a list of content types to exclude from import / export. | ||||
|  | ||||
|     Arguments: | ||||
|         allow_auth (bool): Allow user authentication data to be exported / imported | ||||
|         allow_tokens (bool): Allow tokens to be exported / imported | ||||
|         allow_plugins (bool): Allow plugin information 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. | ||||
|         path: Path to run the command in. | ||||
|         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 {} | ||||
|     path = path or localDir() | ||||
| @@ -1082,7 +1084,8 @@ def test( | ||||
| ): | ||||
|     """Run unit-tests for InvenTree codebase. | ||||
|  | ||||
|     Arguments: | ||||
|     Args: | ||||
|         c: Command line context. | ||||
|         disable_pty (bool): Disable PTY (default = False) | ||||
|         runtest (str): Specify which tests to run, in format <module>.<file>.<class>.<method> (default = '') | ||||
|         migrations (bool): Run migration unit tests (default = False) | ||||
| @@ -1394,6 +1397,7 @@ def frontend_trans(c, extract: bool = True): | ||||
|  | ||||
|     Args: | ||||
|         c: Context variable | ||||
|         extract (bool): Whether to extract translations from source code. Defaults to True. | ||||
|     """ | ||||
|     info('Compiling frontend translations') | ||||
|     if extract: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user