Fix settings typing and use generics from standard collection (#6487)

* Fix settings typing and use generics from standard collection

* Fix docstring
This commit is contained in:
Lukas 2024-02-15 12:44:32 +01:00 committed by GitHub
parent 35577fad41
commit 8807492db6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 66 additions and 53 deletions

View File

@ -8,7 +8,7 @@ import os
import os.path import os.path
import re import re
from decimal import Decimal, InvalidOperation from decimal import Decimal, InvalidOperation
from typing import Set, Type, TypeVar from typing import TypeVar
from wsgiref.util import FileWrapper from wsgiref.util import FileWrapper
from django.conf import settings from django.conf import settings
@ -889,7 +889,7 @@ def get_objectreference(
Inheritors_T = TypeVar('Inheritors_T') Inheritors_T = TypeVar('Inheritors_T')
def inheritors(cls: Type[Inheritors_T]) -> Set[Type[Inheritors_T]]: def inheritors(cls: type[Inheritors_T]) -> set[type[Inheritors_T]]:
"""Return all classes that are subclasses from the supplied cls.""" """Return all classes that are subclasses from the supplied cls."""
subcls = set() subcls = set()
work = [cls] work = [cls]

View File

@ -9,7 +9,7 @@ import time
import warnings import warnings
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Callable, List from typing import Callable
from django.conf import settings from django.conf import settings
from django.core.exceptions import AppRegistryNotReady from django.core.exceptions import AppRegistryNotReady
@ -291,7 +291,7 @@ class ScheduledTask:
class TaskRegister: class TaskRegister:
"""Registry for periodic tasks.""" """Registry for periodic tasks."""
task_list: List[ScheduledTask] = [] task_list: list[ScheduledTask] = []
def register(self, task, schedule, minutes: int = None): def register(self, task, schedule, minutes: int = None):
"""Register a task with the que.""" """Register a task with the que."""

View File

@ -16,7 +16,7 @@ import uuid
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from enum import Enum from enum import Enum
from secrets import compare_digest from secrets import compare_digest
from typing import Any, Callable, Dict, List, Tuple, TypedDict, Union from typing import Any, Callable, TypedDict, Union
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
@ -157,7 +157,7 @@ class SettingsKeyType(TypedDict, total=False):
units: Units of the particular setting (optional) units: Units of the particular setting (optional)
validator: Validation function/list of functions for the setting (optional, default: None, e.g: bool, int, str, MinValueValidator, ...) validator: Validation function/list of functions for the setting (optional, default: None, e.g: bool, int, str, MinValueValidator, ...)
default: Default value or function that returns default value (optional) default: Default value or function that returns default value (optional)
choices: (Function that returns) Tuple[str: key, str: display value] (optional) choices: Function that returns or value of list[tuple[str: key, str: display value]] (optional)
hidden: Hide this setting from settings page (optional) hidden: Hide this setting from settings page (optional)
before_save: Function that gets called after save with *args, **kwargs (optional) before_save: Function that gets called after save with *args, **kwargs (optional)
after_save: Function that gets called after save with *args, **kwargs (optional) after_save: Function that gets called after save with *args, **kwargs (optional)
@ -169,9 +169,9 @@ class SettingsKeyType(TypedDict, total=False):
name: str name: str
description: str description: str
units: str units: str
validator: Union[Callable, List[Callable], Tuple[Callable]] validator: Union[Callable, list[Callable], tuple[Callable]]
default: Union[Callable, Any] default: Union[Callable, Any]
choices: Union[Tuple[str, str], Callable[[], Tuple[str, str]]] choices: Union[list[tuple[str, str]], Callable[[], list[tuple[str, str]]]]
hidden: bool hidden: bool
before_save: Callable[..., None] before_save: Callable[..., None]
after_save: Callable[..., None] after_save: Callable[..., None]
@ -188,9 +188,9 @@ class BaseInvenTreeSetting(models.Model):
extra_unique_fields: List of extra fields used to be unique, e.g. for PluginConfig -> plugin extra_unique_fields: List of extra fields used to be unique, e.g. for PluginConfig -> plugin
""" """
SETTINGS: Dict[str, SettingsKeyType] = {} SETTINGS: dict[str, SettingsKeyType] = {}
extra_unique_fields: List[str] = [] extra_unique_fields: list[str] = []
class Meta: class Meta:
"""Meta options for BaseInvenTreeSetting -> abstract stops creation of database entry.""" """Meta options for BaseInvenTreeSetting -> abstract stops creation of database entry."""
@ -332,7 +332,7 @@ class BaseInvenTreeSetting(models.Model):
cls, cls,
*, *,
exclude_hidden=False, exclude_hidden=False,
settings_definition: Union[Dict[str, SettingsKeyType], None] = None, settings_definition: Union[dict[str, SettingsKeyType], None] = None,
**kwargs, **kwargs,
): ):
"""Return a list of "all" defined settings. """Return a list of "all" defined settings.
@ -352,7 +352,7 @@ class BaseInvenTreeSetting(models.Model):
# Optionally filter by other keys # Optionally filter by other keys
results = results.filter(**filters) results = results.filter(**filters)
settings: Dict[str, BaseInvenTreeSetting] = {} settings: dict[str, BaseInvenTreeSetting] = {}
# Query the database # Query the database
for setting in results: for setting in results:
@ -394,7 +394,7 @@ class BaseInvenTreeSetting(models.Model):
cls, cls,
*, *,
exclude_hidden=False, exclude_hidden=False,
settings_definition: Union[Dict[str, SettingsKeyType], None] = None, settings_definition: Union[dict[str, SettingsKeyType], None] = None,
**kwargs, **kwargs,
): ):
"""Return a dict of "all" defined global settings. """Return a dict of "all" defined global settings.
@ -409,7 +409,7 @@ class BaseInvenTreeSetting(models.Model):
**kwargs, **kwargs,
) )
settings: Dict[str, Any] = {} settings: dict[str, Any] = {}
for key, setting in all_settings.items(): for key, setting in all_settings.items():
settings[key] = setting.value settings[key] = setting.value
@ -421,7 +421,7 @@ class BaseInvenTreeSetting(models.Model):
cls, cls,
*, *,
exclude_hidden=False, exclude_hidden=False,
settings_definition: Union[Dict[str, SettingsKeyType], None] = None, settings_definition: Union[dict[str, SettingsKeyType], None] = None,
**kwargs, **kwargs,
): ):
"""Check if all required settings are set by definition. """Check if all required settings are set by definition.
@ -436,7 +436,7 @@ class BaseInvenTreeSetting(models.Model):
**kwargs, **kwargs,
) )
missing_settings: List[str] = [] missing_settings: list[str] = []
for setting in all_settings.values(): for setting in all_settings.values():
if setting.required: if setting.required:
@ -1171,6 +1171,16 @@ def reload_plugin_registry(setting):
registry.reload_plugins(full_reload=True, force_reload=True, collect=True) registry.reload_plugins(full_reload=True, force_reload=True, collect=True)
class InvenTreeSettingsKeyType(SettingsKeyType):
"""InvenTreeSettingsKeyType has additional properties only global settings support.
Attributes:
requires_restart: If True, a server restart is required after changing the setting
"""
requires_restart: bool
class InvenTreeSetting(BaseInvenTreeSetting): class InvenTreeSetting(BaseInvenTreeSetting):
"""An InvenTreeSetting object is a key:value pair used for storing single values (e.g. one-off settings values). """An InvenTreeSetting object is a key:value pair used for storing single values (e.g. one-off settings values).
@ -1178,6 +1188,8 @@ class InvenTreeSetting(BaseInvenTreeSetting):
even if that key does not exist. even if that key does not exist.
""" """
SETTINGS: dict[str, InvenTreeSettingsKeyType]
class Meta: class Meta:
"""Meta options for InvenTreeSetting.""" """Meta options for InvenTreeSetting."""

View File

@ -1,6 +1,6 @@
"""Base machine type/base driver.""" """Base machine type/base driver."""
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Tuple, Type, Union from typing import TYPE_CHECKING, Any, Literal, Union
from generic.states import StatusCode from generic.states import StatusCode
from InvenTree.helpers_mixin import ClassProviderMixin, ClassValidationMixin from InvenTree.helpers_mixin import ClassProviderMixin, ClassValidationMixin
@ -59,7 +59,7 @@ class BaseDriver(ClassValidationMixin, ClassProviderMixin):
NAME: str NAME: str
DESCRIPTION: str DESCRIPTION: str
MACHINE_SETTINGS: Dict[str, SettingsKeyType] MACHINE_SETTINGS: dict[str, SettingsKeyType]
machine_type: str machine_type: str
@ -89,7 +89,7 @@ class BaseDriver(ClassValidationMixin, ClassProviderMixin):
""" """
def update_machine( def update_machine(
self, old_machine_state: Dict[str, Any], machine: 'BaseMachineType' self, old_machine_state: dict[str, Any], machine: 'BaseMachineType'
): ):
"""This method gets called for each update of a machine. """This method gets called for each update of a machine.
@ -156,11 +156,11 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
NAME: str NAME: str
DESCRIPTION: str DESCRIPTION: str
base_driver: Type[BaseDriver] base_driver: type[BaseDriver]
MACHINE_SETTINGS: Dict[str, SettingsKeyType] MACHINE_SETTINGS: dict[str, SettingsKeyType]
MACHINE_STATUS: Type[MachineStatus] MACHINE_STATUS: type[MachineStatus]
default_machine_status: MachineStatus default_machine_status: MachineStatus
# used by the ClassValidationMixin # used by the ClassValidationMixin
@ -194,15 +194,15 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
f"'{self.driver.NAME}' is incompatible with machine type '{self.NAME}'" f"'{self.driver.NAME}' is incompatible with machine type '{self.NAME}'"
) )
self.machine_settings: Dict[str, SettingsKeyType] = getattr( self.machine_settings: dict[str, SettingsKeyType] = getattr(
self, 'MACHINE_SETTINGS', {} self, 'MACHINE_SETTINGS', {}
) )
self.driver_settings: Dict[str, SettingsKeyType] = getattr( self.driver_settings: dict[str, SettingsKeyType] = getattr(
self.driver, 'MACHINE_SETTINGS', {} self.driver, 'MACHINE_SETTINGS', {}
) )
self.setting_types: List[ self.setting_types: list[
Tuple[Dict[str, SettingsKeyType], MachineSetting.ConfigType] tuple[dict[str, SettingsKeyType], MachineSetting.ConfigType]
] = [ ] = [
(self.machine_settings, MachineSetting.ConfigType.MACHINE), (self.machine_settings, MachineSetting.ConfigType.MACHINE),
(self.driver_settings, MachineSetting.ConfigType.DRIVER), (self.driver_settings, MachineSetting.ConfigType.DRIVER),
@ -339,11 +339,11 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
Returns: Returns:
is_valid: Are all required settings defined is_valid: Are all required settings defined
missing_settings: Dict[ConfigType, List[str]] of all settings that are missing (empty if is_valid is 'True') missing_settings: dict[ConfigType, list[str]] of all settings that are missing (empty if is_valid is 'True')
""" """
from machine.models import MachineSetting from machine.models import MachineSetting
missing_settings: Dict[MachineSetting.ConfigType, List[str]] = {} missing_settings: dict[MachineSetting.ConfigType, list[str]] = {}
for settings, config_type in self.setting_types: for settings, config_type in self.setting_types:
is_valid, missing = MachineSetting.check_all_settings( is_valid, missing = MachineSetting.check_all_settings(
settings_definition=settings, settings_definition=settings,

View File

@ -1,7 +1,7 @@
"""Machine registry.""" """Machine registry."""
import logging import logging
from typing import Dict, List, Set, Type, Union from typing import Union
from uuid import UUID from uuid import UUID
from machine.machine_type import BaseDriver, BaseMachineType from machine.machine_type import BaseDriver, BaseMachineType
@ -17,12 +17,12 @@ class MachineRegistry:
Set up all needed references for internal and external states. Set up all needed references for internal and external states.
""" """
self.machine_types: Dict[str, Type[BaseMachineType]] = {} self.machine_types: dict[str, type[BaseMachineType]] = {}
self.drivers: Dict[str, Type[BaseDriver]] = {} self.drivers: dict[str, type[BaseDriver]] = {}
self.driver_instances: Dict[str, BaseDriver] = {} self.driver_instances: dict[str, BaseDriver] = {}
self.machines: Dict[str, BaseMachineType] = {} self.machines: dict[str, BaseMachineType] = {}
self.base_drivers: List[Type[BaseDriver]] = [] self.base_drivers: list[type[BaseDriver]] = []
self.errors: list[Union[str, Exception]] = [] self.errors: list[Union[str, Exception]] = []
def handle_error(self, error: Union[Exception, str]): def handle_error(self, error: Union[Exception, str]):
@ -41,10 +41,10 @@ class MachineRegistry:
logger.debug('Collecting machine types') logger.debug('Collecting machine types')
machine_types: Dict[str, Type[BaseMachineType]] = {} machine_types: dict[str, type[BaseMachineType]] = {}
base_drivers: List[Type[BaseDriver]] = [] base_drivers: list[type[BaseDriver]] = []
discovered_machine_types: Set[Type[BaseMachineType]] = ( discovered_machine_types: set[type[BaseMachineType]] = (
InvenTree.helpers.inheritors(BaseMachineType) InvenTree.helpers.inheritors(BaseMachineType)
) )
for machine_type in discovered_machine_types: for machine_type in discovered_machine_types:
@ -74,9 +74,9 @@ class MachineRegistry:
logger.debug('Collecting machine drivers') logger.debug('Collecting machine drivers')
drivers: Dict[str, Type[BaseDriver]] = {} drivers: dict[str, type[BaseDriver]] = {}
discovered_drivers: Set[Type[BaseDriver]] = InvenTree.helpers.inheritors( discovered_drivers: set[type[BaseDriver]] = InvenTree.helpers.inheritors(
BaseDriver BaseDriver
) )
for driver in discovered_drivers: for driver in discovered_drivers:

View File

@ -1,6 +1,6 @@
"""Serializers for the machine app.""" """Serializers for the machine app."""
from typing import List, Union from typing import Union
from rest_framework import serializers from rest_framework import serializers
@ -62,7 +62,7 @@ class MachineConfigSerializer(serializers.ModelSerializer):
"""Serializer method for the status text field.""" """Serializer method for the status text field."""
return getattr(obj.machine, 'status_text', '') return getattr(obj.machine, 'status_text', '')
def get_errors(self, obj: MachineConfig) -> List[str]: def get_errors(self, obj: MachineConfig) -> list[str]:
"""Serializer method for the errors field.""" """Serializer method for the errors field."""
return [str(err) for err in obj.errors] return [str(err) for err in obj.errors]
@ -165,7 +165,7 @@ class MachineDriverSerializer(BaseMachineClassSerializer):
driver_errors = serializers.SerializerMethodField('get_errors') driver_errors = serializers.SerializerMethodField('get_errors')
def get_errors(self, obj) -> List[str]: def get_errors(self, obj) -> list[str]:
"""Serializer method for the errors field.""" """Serializer method for the errors field."""
driver_instance = registry.driver_instances.get(obj.SLUG, None) driver_instance = registry.driver_instances.get(obj.SLUG, None)
if driver_instance is None: if driver_instance is None:

View File

@ -1,7 +1,7 @@
"""Plugin mixin class for SettingsMixin.""" """Plugin mixin class for SettingsMixin."""
import logging import logging
from typing import TYPE_CHECKING, Dict from typing import TYPE_CHECKING
from django.db.utils import OperationalError, ProgrammingError from django.db.utils import OperationalError, ProgrammingError
@ -21,7 +21,7 @@ else:
class SettingsMixin: class SettingsMixin:
"""Mixin that enables global settings for the plugin.""" """Mixin that enables global settings for the plugin."""
SETTINGS: Dict[str, SettingsKeyType] = {} SETTINGS: dict[str, SettingsKeyType] = {}
class MixinMeta: class MixinMeta:
"""Meta for mixin.""" """Meta for mixin."""

View File

@ -9,9 +9,10 @@ import importlib
import logging import logging
import os import os
import time import time
from collections import OrderedDict
from pathlib import Path from pathlib import Path
from threading import Lock from threading import Lock
from typing import Any, Dict, List, OrderedDict from typing import Any
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
@ -52,19 +53,19 @@ class PluginsRegistry:
Set up all needed references for internal and external states. Set up all needed references for internal and external states.
""" """
# plugin registry # plugin registry
self.plugins: Dict[str, InvenTreePlugin] = {} # List of active instances self.plugins: dict[str, InvenTreePlugin] = {} # List of active instances
self.plugins_inactive: Dict[ self.plugins_inactive: dict[
str, InvenTreePlugin str, InvenTreePlugin
] = {} # List of inactive instances ] = {} # List of inactive instances
self.plugins_full: Dict[ self.plugins_full: dict[
str, InvenTreePlugin str, InvenTreePlugin
] = {} # List of all plugin instances ] = {} # List of all plugin instances
# Keep an internal hash of the plugin registry state # Keep an internal hash of the plugin registry state
self.registry_hash = None self.registry_hash = None
self.plugin_modules: List[InvenTreePlugin] = [] # Holds all discovered plugins self.plugin_modules: list[InvenTreePlugin] = [] # Holds all discovered plugins
self.mixin_modules: Dict[str, Any] = {} # Holds all discovered mixins self.mixin_modules: dict[str, Any] = {} # Holds all discovered mixins
self.errors = {} # Holds discovering errors self.errors = {} # Holds discovering errors
@ -682,9 +683,9 @@ class PluginsRegistry:
def _clean_registry(self): def _clean_registry(self):
"""Remove all plugins from registry.""" """Remove all plugins from registry."""
self.plugins: Dict[str, InvenTreePlugin] = {} self.plugins: dict[str, InvenTreePlugin] = {}
self.plugins_inactive: Dict[str, InvenTreePlugin] = {} self.plugins_inactive: dict[str, InvenTreePlugin] = {}
self.plugins_full: Dict[str, InvenTreePlugin] = {} self.plugins_full: dict[str, InvenTreePlugin] = {}
def _update_urls(self): def _update_urls(self):
"""Due to the order in which plugins are loaded, the patterns in urls.py may be out of date. """Due to the order in which plugins are loaded, the patterns in urls.py may be out of date.