mirror of
https://github.com/inventree/InvenTree.git
synced 2026-04-02 09:31:02 +00:00
chore(backend): Bump ty (#11537)
* bump ty - there is better django support now * more fixes * fix usage of types * add missing type * fix access * ensure itteration is safe * fix uncombat decimal usage * ?potential breaking: change access key * remove now obsolete igtnore * ignore errors on StdImageField * remove usage of unkonw parameter * fix diff error * fix schema creation * fix coverage quirk * those are unneeded now? * this seem to have been an issue with 3.12; not occuring on 3.14 * ignore pydantiics * ignore edge cases for now * include isGenerating fix * make typing python 3.11 compatible * fix more errors
This commit is contained in:
@@ -108,13 +108,13 @@ root = ["src/backend/InvenTree"]
|
|||||||
unresolved-reference="ignore" # 21 # see https://github.com/astral-sh/ty/issues/220
|
unresolved-reference="ignore" # 21 # see https://github.com/astral-sh/ty/issues/220
|
||||||
unresolved-attribute="ignore" # 505 # need Plugin Mixin typing
|
unresolved-attribute="ignore" # 505 # need Plugin Mixin typing
|
||||||
call-non-callable="ignore" # 8 ##
|
call-non-callable="ignore" # 8 ##
|
||||||
invalid-argument-type="ignore" # 49
|
|
||||||
invalid-assignment="ignore" # 17 # need to wait for better django field stubs
|
invalid-assignment="ignore" # 17 # need to wait for better django field stubs
|
||||||
invalid-method-override="ignore"
|
invalid-method-override="ignore" # 99
|
||||||
invalid-return-type="ignore" # 22 ##
|
invalid-return-type="ignore" # 22 ##
|
||||||
possibly-missing-attribute="ignore" # 25 # https://github.com/astral-sh/ty/issues/164
|
possibly-missing-attribute="ignore" # 25 # https://github.com/astral-sh/ty/issues/164
|
||||||
unknown-argument="ignore" # 3 # need to wait for better django field stubs
|
unknown-argument="ignore" # 3 # need to wait for better django field stubs
|
||||||
no-matching-overload="ignore" # 3 # need to wait for better django field stubs
|
invalid-argument-type="ignore" # 49
|
||||||
|
no-matching-overload="ignore" # 3 # need to wait for betterdjango field stubs
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
source = ["src/backend/InvenTree", "InvenTree"]
|
source = ["src/backend/InvenTree", "InvenTree"]
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ def regenerate_imagefile(_file, _name: str):
|
|||||||
_name: Name of the variation (e.g. 'thumbnail', 'preview')
|
_name: Name of the variation (e.g. 'thumbnail', 'preview')
|
||||||
"""
|
"""
|
||||||
name = _file.field.attr_class.get_variation_name(_file.name, _name)
|
name = _file.field.attr_class.get_variation_name(_file.name, _name)
|
||||||
return ImageFieldFile(_file.instance, _file, name) # type: ignore
|
return ImageFieldFile(_file.instance, _file, name) # ty:ignore[too-many-positional-arguments]
|
||||||
|
|
||||||
|
|
||||||
def image2name(img_obj: StdImageField, do_preview: bool, do_thumbnail: bool):
|
def image2name(img_obj: StdImageField, do_preview: bool, do_thumbnail: bool):
|
||||||
@@ -311,7 +311,7 @@ def TestIfImageURL(url):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def str2bool(text, test=True):
|
def str2bool(text, test=True) -> bool:
|
||||||
"""Test if a string 'looks' like a boolean value.
|
"""Test if a string 'looks' like a boolean value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import structlog
|
|||||||
from allauth.account.models import EmailAddress
|
from allauth.account.models import EmailAddress
|
||||||
|
|
||||||
import InvenTree.ready
|
import InvenTree.ready
|
||||||
|
import InvenTree.tasks as tasks
|
||||||
from common.models import Priority, issue_mail
|
from common.models import Priority, issue_mail
|
||||||
|
|
||||||
logger = structlog.get_logger('inventree')
|
logger = structlog.get_logger('inventree')
|
||||||
@@ -98,7 +99,7 @@ def send_email(
|
|||||||
)
|
)
|
||||||
return False, 'INVE-W7: no from_email or DEFAULT_FROM_EMAIL specified'
|
return False, 'INVE-W7: no from_email or DEFAULT_FROM_EMAIL specified'
|
||||||
|
|
||||||
InvenTree.tasks.offload_task(
|
tasks.offload_task(
|
||||||
issue_mail,
|
issue_mail,
|
||||||
subject=subject,
|
subject=subject,
|
||||||
body=body,
|
body=body,
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class Command(BaseCommand):
|
|||||||
if kwargs['include_items']:
|
if kwargs['include_items']:
|
||||||
icons[icon]['items'].append({
|
icons[icon]['items'].append({
|
||||||
'model': model.__name__.lower(),
|
'model': model.__name__.lower(),
|
||||||
'id': item.id, # type: ignore
|
'id': item.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
self.stdout.write(f'Writing icon map for {len(icons.keys())} icons')
|
self.stdout.write(f'Writing icon map for {len(icons.keys())} icons')
|
||||||
|
|||||||
@@ -1329,7 +1329,7 @@ class InvenTreeBarcodeMixin(models.Model):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def lookup_barcode(cls, barcode_hash: str) -> models.Model:
|
def lookup_barcode(cls, barcode_hash: str) -> models.Model:
|
||||||
"""Check if a model instance exists with the specified third-party barcode hash."""
|
"""Check if a model instance exists with the specified third-party barcode hash."""
|
||||||
return cls.objects.filter(barcode_hash=barcode_hash).first()
|
return cls.objects.filter(barcode_hash=barcode_hash).first() # ty:ignore[invalid-return-type]
|
||||||
|
|
||||||
def assign_barcode(
|
def assign_barcode(
|
||||||
self,
|
self,
|
||||||
@@ -1485,7 +1485,7 @@ class InvenTreeImageMixin(models.Model):
|
|||||||
|
|
||||||
def rename_image(self, filename):
|
def rename_image(self, filename):
|
||||||
"""Rename the uploaded image file using the IMAGE_RENAME function."""
|
"""Rename the uploaded image file using the IMAGE_RENAME function."""
|
||||||
return self.IMAGE_RENAME(filename) # type: ignore
|
return self.IMAGE_RENAME(filename)
|
||||||
|
|
||||||
image = StdImageField(
|
image = StdImageField(
|
||||||
upload_to=rename_image,
|
upload_to=rename_image,
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ def auth_exempt(view_func):
|
|||||||
def wrapped_view(*args, **kwargs):
|
def wrapped_view(*args, **kwargs):
|
||||||
return view_func(*args, **kwargs)
|
return view_func(*args, **kwargs)
|
||||||
|
|
||||||
wrapped_view.auth_exempt = True # type:ignore[unresolved-attribute]
|
wrapped_view.auth_exempt = True
|
||||||
return wraps(view_func)(wrapped_view)
|
return wraps(view_func)(wrapped_view)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -483,7 +483,7 @@ if LDAP_AUTH: # pragma: no cover
|
|||||||
)
|
)
|
||||||
AUTH_LDAP_USER_SEARCH = django_auth_ldap.config.LDAPSearch(
|
AUTH_LDAP_USER_SEARCH = django_auth_ldap.config.LDAPSearch(
|
||||||
get_setting('INVENTREE_LDAP_SEARCH_BASE_DN', 'ldap.search_base_dn'),
|
get_setting('INVENTREE_LDAP_SEARCH_BASE_DN', 'ldap.search_base_dn'),
|
||||||
ldap.SCOPE_SUBTREE, # type: ignore[unresolved-attribute]
|
ldap.SCOPE_SUBTREE,
|
||||||
str(
|
str(
|
||||||
get_setting(
|
get_setting(
|
||||||
'INVENTREE_LDAP_SEARCH_FILTER_STR',
|
'INVENTREE_LDAP_SEARCH_FILTER_STR',
|
||||||
@@ -519,7 +519,7 @@ if LDAP_AUTH: # pragma: no cover
|
|||||||
)
|
)
|
||||||
AUTH_LDAP_GROUP_SEARCH = django_auth_ldap.config.LDAPSearch(
|
AUTH_LDAP_GROUP_SEARCH = django_auth_ldap.config.LDAPSearch(
|
||||||
get_setting('INVENTREE_LDAP_GROUP_SEARCH', 'ldap.group_search'),
|
get_setting('INVENTREE_LDAP_GROUP_SEARCH', 'ldap.group_search'),
|
||||||
ldap.SCOPE_SUBTREE, # type: ignore[unresolved-attribute]
|
ldap.SCOPE_SUBTREE,
|
||||||
f'(objectClass={AUTH_LDAP_GROUP_OBJECT_CLASS})',
|
f'(objectClass={AUTH_LDAP_GROUP_OBJECT_CLASS})',
|
||||||
)
|
)
|
||||||
AUTH_LDAP_GROUP_TYPE_CLASS = get_setting(
|
AUTH_LDAP_GROUP_TYPE_CLASS = get_setting(
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ def check_provider(provider):
|
|||||||
if not app:
|
if not app:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if allauth.app_settings.SITES_ENABLED: # type: ignore[unresolved-attribute]
|
if allauth.app_settings.SITES_ENABLED:
|
||||||
# At least one matching site must be specified
|
# At least one matching site must be specified
|
||||||
if not app.sites.exists():
|
if not app.sites.exists():
|
||||||
logger.error('SocialApp %s has no sites configured', app)
|
logger.error('SocialApp %s has no sites configured', app)
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ class ScheduledTask:
|
|||||||
QUARTERLY: str = 'Q'
|
QUARTERLY: str = 'Q'
|
||||||
YEARLY: str = 'Y'
|
YEARLY: str = 'Y'
|
||||||
|
|
||||||
TYPE: tuple[str] = (MINUTES, HOURLY, DAILY, WEEKLY, MONTHLY, QUARTERLY, YEARLY) # type: ignore[invalid-assignment]
|
TYPE: tuple[str] = (MINUTES, HOURLY, DAILY, WEEKLY, MONTHLY, QUARTERLY, YEARLY)
|
||||||
|
|
||||||
|
|
||||||
class TaskRegister:
|
class TaskRegister:
|
||||||
|
|||||||
@@ -574,7 +574,7 @@ class GeneralApiTests(InvenTreeAPITestCase):
|
|||||||
|
|
||||||
self.assertIn('License file not found at', str(log.output))
|
self.assertIn('License file not found at', str(log.output))
|
||||||
|
|
||||||
with TemporaryDirectory() as tmp: # type: ignore[no-matching-overload]
|
with TemporaryDirectory() as tmp:
|
||||||
sample_file = Path(tmp, 'temp.txt')
|
sample_file = Path(tmp, 'temp.txt')
|
||||||
sample_file.write_text('abc', 'utf-8')
|
sample_file.write_text('abc', 'utf-8')
|
||||||
|
|
||||||
|
|||||||
@@ -703,12 +703,12 @@ class TestHelpers(TestCase):
|
|||||||
"""Test getMediaUrl."""
|
"""Test getMediaUrl."""
|
||||||
# Str should not work
|
# Str should not work
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
helpers.getMediaUrl('xx/yy.png') # type: ignore
|
helpers.getMediaUrl('xx/yy.png')
|
||||||
|
|
||||||
# Correct usage
|
# Correct usage
|
||||||
part = Part().image
|
part = Part().image
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
helpers.getMediaUrl(StdImageFieldFile(part, part, 'xx/yy.png')), # type: ignore
|
helpers.getMediaUrl(StdImageFieldFile(part, part, 'xx/yy.png')), # ty:ignore[too-many-positional-arguments]
|
||||||
'/media/xx/yy.png',
|
'/media/xx/yy.png',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import base64
|
|||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from opentelemetry import metrics, trace # type: ignore[import]
|
from opentelemetry import metrics, trace
|
||||||
from opentelemetry.instrumentation.django import DjangoInstrumentor
|
from opentelemetry.instrumentation.django import DjangoInstrumentor
|
||||||
from opentelemetry.instrumentation.redis import RedisInstrumentor
|
from opentelemetry.instrumentation.redis import RedisInstrumentor
|
||||||
from opentelemetry.instrumentation.requests import RequestsInstrumentor
|
from opentelemetry.instrumentation.requests import RequestsInstrumentor
|
||||||
|
|||||||
@@ -289,8 +289,10 @@ def inventreeBranch():
|
|||||||
return ' '.join(branch.splitlines())
|
return ' '.join(branch.splitlines())
|
||||||
|
|
||||||
if main_branch is None:
|
if main_branch is None:
|
||||||
return None
|
return None # pragma: no cover - branch information may not be available in all environments
|
||||||
return main_branch.decode('utf-8')
|
return main_branch.decode(
|
||||||
|
'utf-8'
|
||||||
|
) # pragma: no cover - branch information may not be available in all environments
|
||||||
|
|
||||||
|
|
||||||
def inventreeTarget():
|
def inventreeTarget():
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from rest_framework.response import Response
|
|||||||
import build.models as build_models
|
import build.models as build_models
|
||||||
import build.serializers
|
import build.serializers
|
||||||
import common.models
|
import common.models
|
||||||
|
import common.serializers
|
||||||
import part.models as part_models
|
import part.models as part_models
|
||||||
import stock.models as stock_models
|
import stock.models as stock_models
|
||||||
import stock.serializers
|
import stock.serializers
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Build database model definitions."""
|
"""Build database model definitions."""
|
||||||
|
|
||||||
import decimal
|
import decimal
|
||||||
from typing import Optional
|
from typing import Optional, TypedDict
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
@@ -50,7 +50,7 @@ from stock.status_codes import StockHistoryCode, StockStatus
|
|||||||
logger = structlog.get_logger('inventree')
|
logger = structlog.get_logger('inventree')
|
||||||
|
|
||||||
|
|
||||||
class BuildReportContext(report.mixins.BaseReportContext):
|
class BuildReportContext(report.mixins.BaseReportContext, TypedDict):
|
||||||
"""Context for the Build model.
|
"""Context for the Build model.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -1039,7 +1039,7 @@ class Build(
|
|||||||
lines = lines.exclude(bom_item__consumable=True)
|
lines = lines.exclude(bom_item__consumable=True)
|
||||||
lines = lines.annotate(allocated=annotate_allocated_quantity())
|
lines = lines.annotate(allocated=annotate_allocated_quantity())
|
||||||
|
|
||||||
for build_line in lines: # type: ignore[non-iterable]
|
for build_line in lines:
|
||||||
reduce_by = build_line.allocated - build_line.quantity
|
reduce_by = build_line.allocated - build_line.quantity
|
||||||
|
|
||||||
if reduce_by <= 0:
|
if reduce_by <= 0:
|
||||||
@@ -1512,10 +1512,10 @@ class Build(
|
|||||||
unallocated_quantity -= quantity
|
unallocated_quantity -= quantity
|
||||||
|
|
||||||
except (ValidationError, serializers.ValidationError) as exc:
|
except (ValidationError, serializers.ValidationError) as exc:
|
||||||
# Catch model errors and re-throw as DRF errors
|
# Re-raise with a Django-compatible validation payload
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
exc.message, detail=serializers.as_serializer_error(exc)
|
serializers.as_serializer_error(exc)
|
||||||
)
|
) from exc
|
||||||
|
|
||||||
if unallocated_quantity <= 0:
|
if unallocated_quantity <= 0:
|
||||||
# We have now fully-allocated this BomItem - no need to continue!
|
# We have now fully-allocated this BomItem - no need to continue!
|
||||||
@@ -1695,7 +1695,7 @@ def after_save_build(sender, instance: Build, created: bool, **kwargs):
|
|||||||
instance.update_build_line_items()
|
instance.update_build_line_items()
|
||||||
|
|
||||||
|
|
||||||
class BuildLineReportContext(report.mixins.BaseReportContext):
|
class BuildLineReportContext(report.mixins.BaseReportContext, TypedDict):
|
||||||
"""Context for the BuildLine model.
|
"""Context for the BuildLine model.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -1745,6 +1745,7 @@ class BuildLine(report.mixins.InvenTreeReportMixin, InvenTree.models.InvenTreeMo
|
|||||||
"""Return the API URL used to access this model."""
|
"""Return the API URL used to access this model."""
|
||||||
return reverse('api-build-line-list')
|
return reverse('api-build-line-list')
|
||||||
|
|
||||||
|
# type
|
||||||
def report_context(self) -> BuildLineReportContext:
|
def report_context(self) -> BuildLineReportContext:
|
||||||
"""Generate custom report context for this BuildLine object."""
|
"""Generate custom report context for this BuildLine object."""
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -230,6 +230,6 @@ def get_price(
|
|||||||
quantity = decimal.Decimal(f'{quantity}')
|
quantity = decimal.Decimal(f'{quantity}')
|
||||||
|
|
||||||
if pb_found:
|
if pb_found:
|
||||||
cost = pb_cost * quantity
|
cost = decimal.Decimal(pb_cost) * quantity
|
||||||
return InvenTree.helpers.normalize(cost + instance.base_cost)
|
return InvenTree.helpers.normalize(cost + instance.base_cost)
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ class RenderMeta(enums.ChoicesType):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
class RenderChoices(models.TextChoices, metaclass=RenderMeta): # type: ignore
|
class RenderChoices(models.TextChoices, metaclass=RenderMeta):
|
||||||
"""Class for creating enumerated string choices for schema rendering."""
|
"""Class for creating enumerated string choices for schema rendering.""" # ty:ignore[conflicting-metaclass]
|
||||||
|
|
||||||
|
|
||||||
class MetaMixin(models.Model):
|
class MetaMixin(models.Model):
|
||||||
@@ -1084,7 +1084,7 @@ class BaseInvenTreeSetting(models.Model):
|
|||||||
|
|
||||||
return self.__class__.validator_is_bool(validator)
|
return self.__class__.validator_is_bool(validator)
|
||||||
|
|
||||||
def as_bool(self):
|
def as_bool(self) -> bool:
|
||||||
"""Return the value of this setting converted to a boolean value.
|
"""Return the value of this setting converted to a boolean value.
|
||||||
|
|
||||||
Warning: Only use on values where is_bool evaluates to true!
|
Warning: Only use on values where is_bool evaluates to true!
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class SettingsValueField(serializers.Field):
|
|||||||
"""Return the object instance, not the attribute value."""
|
"""Return the object instance, not the attribute value."""
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def to_representation(self, instance: common_models.InvenTreeSetting) -> str:
|
def to_representation(self, instance: common_models.InvenTreeSetting):
|
||||||
"""Return the value of the setting.
|
"""Return the value of the setting.
|
||||||
|
|
||||||
Protected settings are returned as '***'
|
Protected settings are returned as '***'
|
||||||
@@ -381,7 +381,7 @@ class ConfigSerializer(serializers.Serializer):
|
|||||||
"""Return the configuration data as a dictionary."""
|
"""Return the configuration data as a dictionary."""
|
||||||
if not isinstance(instance, str):
|
if not isinstance(instance, str):
|
||||||
instance = list(instance.keys())[0]
|
instance = list(instance.keys())[0]
|
||||||
return {'key': instance, **self.instance[instance]}
|
return {'key': instance, **self.instance.get(instance)}
|
||||||
|
|
||||||
|
|
||||||
class NotesImageSerializer(InvenTreeModelSerializer):
|
class NotesImageSerializer(InvenTreeModelSerializer):
|
||||||
@@ -457,7 +457,7 @@ class FlagSerializer(serializers.Serializer):
|
|||||||
data = {'key': instance, 'state': flag_state(instance, request=request)}
|
data = {'key': instance, 'state': flag_state(instance, request=request)}
|
||||||
|
|
||||||
if request and request.user.is_superuser:
|
if request and request.user.is_superuser:
|
||||||
data['conditions'] = self.instance[instance]
|
data['conditions'] = self.instance.get(instance)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class SettingsKeyType(TypedDict, total=False):
|
|||||||
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 or value of list[tuple[str: key, str: display value]] (optional)
|
choices: Function that returns or value of list[tuple[str: key, str: display value]] (optional)
|
||||||
|
model_filters: Filters to apply when querying the associated model (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)
|
||||||
@@ -42,6 +43,7 @@ class SettingsKeyType(TypedDict, total=False):
|
|||||||
validator: Callable | list[Callable] | tuple[Callable]
|
validator: Callable | list[Callable] | tuple[Callable]
|
||||||
default: Callable | Any
|
default: Callable | Any
|
||||||
choices: list[tuple[str, str]] | Callable[[], list[tuple[str, str]]]
|
choices: list[tuple[str, str]] | Callable[[], list[tuple[str, str]]]
|
||||||
|
model_filters: dict[str, Any]
|
||||||
hidden: bool
|
hidden: bool
|
||||||
before_save: Callable[..., None]
|
before_save: Callable[..., None]
|
||||||
after_save: Callable[..., None]
|
after_save: Callable[..., None]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -53,7 +54,7 @@ def rename_company_image(instance, filename):
|
|||||||
return os.path.join(base, fn)
|
return os.path.join(base, fn)
|
||||||
|
|
||||||
|
|
||||||
class CompanyReportContext(report.mixins.BaseReportContext):
|
class CompanyReportContext(report.mixins.BaseReportContext, TypedDict):
|
||||||
"""Report context for the Company model.
|
"""Report context for the Company model.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -243,7 +244,11 @@ class Company(
|
|||||||
# We may have a pre-fetched primary address list
|
# We may have a pre-fetched primary address list
|
||||||
if hasattr(self, 'primary_address_list'):
|
if hasattr(self, 'primary_address_list'):
|
||||||
addresses = self.primary_address_list
|
addresses = self.primary_address_list
|
||||||
return addresses[0] if len(addresses) > 0 else None
|
return (
|
||||||
|
addresses[0]
|
||||||
|
if len(addresses) > 0 and isinstance(addresses, list)
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
# Otherwise, query the database
|
# Otherwise, query the database
|
||||||
return self.addresses.filter(primary=True).first()
|
return self.addresses.filter(primary=True).first()
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ class InvenTreeCustomStatusSerializerMixin:
|
|||||||
"""Ensure the custom field is updated if the leader was changed."""
|
"""Ensure the custom field is updated if the leader was changed."""
|
||||||
self.gather_custom_fields()
|
self.gather_custom_fields()
|
||||||
# Mirror values from leader to follower
|
# Mirror values from leader to follower
|
||||||
for field in self._custom_fields_leader:
|
for field in self._custom_fields_leader or []:
|
||||||
follower_field_name = f'{field}_custom_key'
|
follower_field_name = f'{field}_custom_key'
|
||||||
if (
|
if (
|
||||||
field in self.initial_data
|
field in self.initial_data
|
||||||
@@ -205,7 +205,7 @@ class InvenTreeCustomStatusSerializerMixin:
|
|||||||
setattr(self.instance, follower_field_name, self.initial_data[field])
|
setattr(self.instance, follower_field_name, self.initial_data[field])
|
||||||
|
|
||||||
# Mirror values from follower to leader
|
# Mirror values from follower to leader
|
||||||
for field in self._custom_fields_follower:
|
for field in self._custom_fields_follower or []:
|
||||||
leader_field_name = field.replace('_custom_key', '')
|
leader_field_name = field.replace('_custom_key', '')
|
||||||
if field in validated_data and leader_field_name not in self.initial_data:
|
if field in validated_data and leader_field_name not in self.initial_data:
|
||||||
try:
|
try:
|
||||||
@@ -276,7 +276,7 @@ class InvenTreeCustomStatusSerializerMixin:
|
|||||||
|
|
||||||
# Inherit choices from leader
|
# Inherit choices from leader
|
||||||
self.gather_custom_fields()
|
self.gather_custom_fields()
|
||||||
if field_name in self._custom_fields:
|
if self._custom_fields and field_name in self._custom_fields:
|
||||||
leader_field_name = field_name.replace('_custom_key', '')
|
leader_field_name = field_name.replace('_custom_key', '')
|
||||||
leader_field = self.fields[leader_field_name]
|
leader_field = self.fields[leader_field_name]
|
||||||
if hasattr(leader_field, 'choices'):
|
if hasattr(leader_field, 'choices'):
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class DataImportSession(models.Model):
|
|||||||
|
|
||||||
return supported_models().get(self.model_type, None)
|
return supported_models().get(self.model_type, None)
|
||||||
|
|
||||||
def get_related_model(self, field_name: str) -> models.Model:
|
def get_related_model(self, field_name: str) -> Optional[models.Model]:
|
||||||
"""Return the related model for a given field name.
|
"""Return the related model for a given field name.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
@@ -699,7 +699,7 @@ class DataImportRow(models.Model):
|
|||||||
if commit:
|
if commit:
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def convert_date_field(self, value: str) -> str:
|
def convert_date_field(self, value: str) -> Optional[str]:
|
||||||
"""Convert an incoming date field to the correct format for the database."""
|
"""Convert an incoming date field to the correct format for the database."""
|
||||||
if value in [None, '']:
|
if value in [None, '']:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ class LabelPrinterBaseDriver(BaseDriver):
|
|||||||
Returns:
|
Returns:
|
||||||
A class instance of a DRF serializer class, by default this an instance of self.PrintingOptionsSerializer using the *args, **kwargs if existing for this driver
|
A class instance of a DRF serializer class, by default this an instance of self.PrintingOptionsSerializer using the *args, **kwargs if existing for this driver
|
||||||
"""
|
"""
|
||||||
return self.PrintingOptionsSerializer(*args, **kwargs) # type: ignore
|
return self.PrintingOptionsSerializer(*args, **kwargs)
|
||||||
|
|
||||||
# --- helper functions
|
# --- helper functions
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class MachineSettingSerializer(GenericReferencedSettingSerializer):
|
|||||||
"""Custom init method to make the config_type field read only."""
|
"""Custom init method to make the config_type field read only."""
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.Meta.read_only_fields = ['config_type'] # type: ignore
|
self.Meta.read_only_fields = ['config_type']
|
||||||
|
|
||||||
|
|
||||||
class BaseMachineClassSerializer(serializers.Serializer):
|
class BaseMachineClassSerializer(serializers.Serializer):
|
||||||
|
|||||||
@@ -232,10 +232,10 @@ class TestLabelPrinterMachineType(InvenTreeAPITestCase):
|
|||||||
machine = self.create_machine()
|
machine = self.create_machine()
|
||||||
|
|
||||||
# setup the label app
|
# setup the label app
|
||||||
apps.get_app_config('report').create_default_labels() # type: ignore
|
apps.get_app_config('report').create_default_labels()
|
||||||
plg_registry.reload_plugins()
|
plg_registry.reload_plugins()
|
||||||
|
|
||||||
config = cast(PluginConfig, plg_registry.get_plugin(plugin_ref).plugin_config()) # type: ignore
|
config = cast(PluginConfig, plg_registry.get_plugin(plugin_ref).plugin_config())
|
||||||
config.active = True
|
config.active = True
|
||||||
config.save()
|
config.save()
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Order model definitions."""
|
"""Order model definitions."""
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional, TypedDict
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
@@ -179,7 +179,7 @@ class TotalPriceMixin(models.Model):
|
|||||||
return total
|
return total
|
||||||
|
|
||||||
|
|
||||||
class BaseOrderReportContext(report.mixins.BaseReportContext):
|
class BaseOrderReportContext(report.mixins.BaseReportContext, TypedDict):
|
||||||
"""Base context for all order models.
|
"""Base context for all order models.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -199,7 +199,7 @@ class BaseOrderReportContext(report.mixins.BaseReportContext):
|
|||||||
title: str
|
title: str
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderReportContext(report.mixins.BaseReportContext):
|
class PurchaseOrderReportContext(report.mixins.BaseReportContext, TypedDict):
|
||||||
"""Context for the purchase order model.
|
"""Context for the purchase order model.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -221,7 +221,7 @@ class PurchaseOrderReportContext(report.mixins.BaseReportContext):
|
|||||||
supplier: Optional[Company]
|
supplier: Optional[Company]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderReportContext(report.mixins.BaseReportContext):
|
class SalesOrderReportContext(report.mixins.BaseReportContext, TypedDict):
|
||||||
"""Context for the sales order model.
|
"""Context for the sales order model.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -243,7 +243,7 @@ class SalesOrderReportContext(report.mixins.BaseReportContext):
|
|||||||
customer: Optional[Company]
|
customer: Optional[Company]
|
||||||
|
|
||||||
|
|
||||||
class ReturnOrderReportContext(report.mixins.BaseReportContext):
|
class ReturnOrderReportContext(report.mixins.BaseReportContext, TypedDict):
|
||||||
"""Context for the return order model.
|
"""Context for the return order model.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -570,7 +570,9 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
|||||||
|
|
||||||
def report_context(self) -> PurchaseOrderReportContext:
|
def report_context(self) -> PurchaseOrderReportContext:
|
||||||
"""Return report context data for this PurchaseOrder."""
|
"""Return report context data for this PurchaseOrder."""
|
||||||
return {**super().report_context(), 'supplier': self.supplier}
|
return_ctx = super().report_context()
|
||||||
|
return_ctx.update({'supplier': self.supplier})
|
||||||
|
return return_ctx
|
||||||
|
|
||||||
def get_absolute_url(self) -> str:
|
def get_absolute_url(self) -> str:
|
||||||
"""Get the 'web' URL for this order."""
|
"""Get the 'web' URL for this order."""
|
||||||
@@ -1269,7 +1271,9 @@ class SalesOrder(TotalPriceMixin, Order):
|
|||||||
|
|
||||||
def report_context(self) -> SalesOrderReportContext:
|
def report_context(self) -> SalesOrderReportContext:
|
||||||
"""Generate report context data for this SalesOrder."""
|
"""Generate report context data for this SalesOrder."""
|
||||||
return {**super().report_context(), 'customer': self.customer}
|
return_ctx = super().report_context()
|
||||||
|
return_ctx.update({'customer': self.customer})
|
||||||
|
return return_ctx
|
||||||
|
|
||||||
def get_absolute_url(self) -> str:
|
def get_absolute_url(self) -> str:
|
||||||
"""Get the 'web' URL for this order."""
|
"""Get the 'web' URL for this order."""
|
||||||
@@ -2197,7 +2201,7 @@ class SalesOrderLineItem(OrderLineItem):
|
|||||||
return self.shipped >= self.quantity
|
return self.shipped >= self.quantity
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentReportContext(report.mixins.BaseReportContext):
|
class SalesOrderShipmentReportContext(report.mixins.BaseReportContext, TypedDict):
|
||||||
"""Context for the SalesOrderShipment model.
|
"""Context for the SalesOrderShipment model.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -2662,7 +2666,9 @@ class ReturnOrder(TotalPriceMixin, Order):
|
|||||||
|
|
||||||
def report_context(self) -> ReturnOrderReportContext:
|
def report_context(self) -> ReturnOrderReportContext:
|
||||||
"""Generate report context data for this ReturnOrder."""
|
"""Generate report context data for this ReturnOrder."""
|
||||||
return {**super().report_context(), 'customer': self.customer}
|
return_ctx = super().report_context()
|
||||||
|
return_ctx.update({'customer': self.customer})
|
||||||
|
return return_ctx
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
"""Get the 'web' URL for this order."""
|
"""Get the 'web' URL for this order."""
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from decimal import Decimal, InvalidOperation
|
from decimal import Decimal, InvalidOperation
|
||||||
from typing import cast
|
from typing import TypedDict, cast
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
@@ -427,7 +427,7 @@ class PartCategoryParameterTemplate(InvenTree.models.InvenTreeMetadataModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PartReportContext(report.mixins.BaseReportContext):
|
class PartReportContext(report.mixins.BaseReportContext, TypedDict):
|
||||||
"""Report context for the Part model.
|
"""Report context for the Part model.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -4216,7 +4216,7 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel):
|
|||||||
if n <= 0:
|
if n <= 0:
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
return int(available_stock / n)
|
return int(Decimal(available_stock) / n)
|
||||||
|
|
||||||
def get_required_quantity(self, build_quantity: float) -> float:
|
def get_required_quantity(self, build_quantity: float) -> float:
|
||||||
"""Calculate the required part quantity, based on the supplied build_quantity.
|
"""Calculate the required part quantity, based on the supplied build_quantity.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from opentelemetry import trace
|
|||||||
|
|
||||||
import common.currency
|
import common.currency
|
||||||
import common.notifications
|
import common.notifications
|
||||||
|
import InvenTree.helpers
|
||||||
import InvenTree.helpers_model
|
import InvenTree.helpers_model
|
||||||
from common.settings import get_global_setting
|
from common.settings import get_global_setting
|
||||||
from InvenTree.tasks import (
|
from InvenTree.tasks import (
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ class StaleStockNotificationTests(InvenTreeTestCase):
|
|||||||
set_global_setting('STOCK_STALE_DAYS', 7, self.user)
|
set_global_setting('STOCK_STALE_DAYS', 7, self.user)
|
||||||
|
|
||||||
# Clear notifications
|
# Clear notifications
|
||||||
NotificationEntry.objects.all().delete() # type: ignore[attr-defined]
|
NotificationEntry.objects.all().delete()
|
||||||
NotificationMessage.objects.all().delete() # type: ignore[attr-defined]
|
NotificationMessage.objects.all().delete()
|
||||||
|
|
||||||
def create_stock_items_with_expiry(self):
|
def create_stock_items_with_expiry(self):
|
||||||
"""Create stock items with various expiry dates for testing."""
|
"""Create stock items with various expiry dates for testing."""
|
||||||
@@ -101,7 +101,7 @@ class StaleStockNotificationTests(InvenTreeTestCase):
|
|||||||
part.tasks.notify_stale_stock(self.user, [])
|
part.tasks.notify_stale_stock(self.user, [])
|
||||||
|
|
||||||
# No notifications should be created
|
# No notifications should be created
|
||||||
self.assertEqual(NotificationMessage.objects.count(), 0) # type: ignore[attr-defined]
|
self.assertEqual(NotificationMessage.objects.count(), 0)
|
||||||
|
|
||||||
def test_notify_stale_stock_single_item(self):
|
def test_notify_stale_stock_single_item(self):
|
||||||
"""Test notify_stale_stock with a single stale item."""
|
"""Test notify_stale_stock with a single stale item."""
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class BarcodeMixin:
|
|||||||
"""Does this plugin support barcode generation."""
|
"""Does this plugin support barcode generation."""
|
||||||
try:
|
try:
|
||||||
# Attempt to call the generate method
|
# Attempt to call the generate method
|
||||||
self.generate(None) # type: ignore
|
self.generate(None)
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
# If a NotImplementedError is raised, then barcode generation is not supported
|
# If a NotImplementedError is raised, then barcode generation is not supported
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ class SupplierMixin(SettingsMixin, Generic[PartData]):
|
|||||||
|
|
||||||
# assign parent_part to root_part if root_part has no variant of already
|
# assign parent_part to root_part if root_part has no variant of already
|
||||||
if root_part and not root_part.is_template and not root_part.variant_of:
|
if root_part and not root_part.is_template and not root_part.variant_of:
|
||||||
root_part.variant_of = parent_part # type: ignore
|
root_part.variant_of = parent_part
|
||||||
root_part.save()
|
root_part.save()
|
||||||
|
|
||||||
return parent_part
|
return parent_part
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class UserInterfaceMixin:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Register mixin."""
|
"""Register mixin."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.add_mixin(PluginMixinEnum.USER_INTERFACE, True, __class__) # type: ignore
|
self.add_mixin(PluginMixinEnum.USER_INTERFACE, True, __class__)
|
||||||
|
|
||||||
def get_ui_features(
|
def get_ui_features(
|
||||||
self, feature_type: FeatureType, context: dict, request: Request, **kwargs
|
self, feature_type: FeatureType, context: dict, request: Request, **kwargs
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ class BrokenFileIntegrationPlugin(InvenTreePlugin):
|
|||||||
"""An very broken plugin."""
|
"""An very broken plugin."""
|
||||||
|
|
||||||
|
|
||||||
aaa = bb # noqa: F821 # type: ignore[unresolved-reference]
|
aaa = bb # noqa: F821
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ def get_modules(pkg, path=None):
|
|||||||
if sys.version_info < (3, 12):
|
if sys.version_info < (3, 12):
|
||||||
module = finder.find_module(name).load_module(name)
|
module = finder.find_module(name).load_module(name)
|
||||||
else:
|
else:
|
||||||
spec = finder.find_spec(name)
|
spec = finder.find_spec(name) # type: ignore[missing-argument]
|
||||||
module = module_from_spec(spec)
|
module = module_from_spec(spec)
|
||||||
sys.modules[name] = module
|
sys.modules[name] = module
|
||||||
spec.loader.exec_module(module)
|
spec.loader.exec_module(module)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from distutils.sysconfig import get_python_lib # type: ignore[import]
|
from distutils.sysconfig import get_python_lib # type: ignore[unresolved-import]
|
||||||
from importlib.metadata import PackageNotFoundError, metadata
|
from importlib.metadata import PackageNotFoundError, metadata
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -92,16 +92,16 @@ class MetaBase:
|
|||||||
TITLE = None
|
TITLE = None
|
||||||
|
|
||||||
@mark_final
|
@mark_final
|
||||||
def get_meta_value(self, key: str, old_key: Optional[str] = None, __default=None):
|
def get_meta_value(self, key: str, old_key: Optional[str] = None, default=None):
|
||||||
"""Reference a meta item with a key.
|
"""Reference a meta item with a key.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
key (str): key for the value
|
key (str): key for the value
|
||||||
old_key (str, optional): deprecated key - will throw warning
|
old_key (str, optional): deprecated key - will throw warning
|
||||||
__default (optional): Value if nothing with key can be found. Defaults to None.
|
default (optional): Value if nothing with key can be found. Defaults to None.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Value referenced with key, old_key or __default if set and not value found
|
Value referenced with key, old_key or default if set and not value found
|
||||||
"""
|
"""
|
||||||
value = getattr(self, key, None)
|
value = getattr(self, key, None)
|
||||||
|
|
||||||
@@ -117,9 +117,9 @@ class MetaBase:
|
|||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use __default if still nothing set
|
# Use default if still nothing set
|
||||||
if (value is None) and __default:
|
if value is None and default is not None:
|
||||||
return __default
|
return default
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@mark_final
|
@mark_final
|
||||||
|
|||||||
@@ -781,9 +781,9 @@ class PluginsRegistry:
|
|||||||
f"Plugin '{p}' is not compatible with the current InvenTree version {v}"
|
f"Plugin '{p}' is not compatible with the current InvenTree version {v}"
|
||||||
)
|
)
|
||||||
if v := plg_i.MIN_VERSION:
|
if v := plg_i.MIN_VERSION:
|
||||||
_msg += _(f'Plugin requires at least version {v}') # type: ignore[unsupported-operator]
|
_msg += _(f'Plugin requires at least version {v}') # ty:ignore[unsupported-operator]
|
||||||
if v := plg_i.MAX_VERSION:
|
if v := plg_i.MAX_VERSION:
|
||||||
_msg += _(f'Plugin requires at most version {v}') # type: ignore[unsupported-operator]
|
_msg += _(f'Plugin requires at most version {v}') # ty:ignore[unsupported-operator]
|
||||||
# Log to error stack
|
# Log to error stack
|
||||||
log_registry_error(_msg, reference=f'{p}:init_plugin')
|
log_registry_error(_msg, reference=f'{p}:init_plugin')
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class SampleSupplierPlugin(SupplierMixin, InvenTreePlugin):
|
|||||||
# after the template part was created, we need to refresh the part from the db because its tree id may have changed
|
# after the template part was created, we need to refresh the part from the db because its tree id may have changed
|
||||||
# which results in an error if saved directly
|
# which results in an error if saved directly
|
||||||
part.refresh_from_db()
|
part.refresh_from_db()
|
||||||
part.variant_of = parent_part # type: ignore
|
part.variant_of = parent_part
|
||||||
part.save()
|
part.save()
|
||||||
|
|
||||||
return part
|
return part
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase):
|
|||||||
def test_folder_loading(self):
|
def test_folder_loading(self):
|
||||||
"""Test that plugins in folders outside of BASE_DIR get loaded."""
|
"""Test that plugins in folders outside of BASE_DIR get loaded."""
|
||||||
# Run in temporary directory -> always a new random name
|
# Run in temporary directory -> always a new random name
|
||||||
with tempfile.TemporaryDirectory() as tmp: # type: ignore[no-matching-overload]
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
# Fill directory with sample data
|
# Fill directory with sample data
|
||||||
new_dir = Path(tmp).joinpath('mock')
|
new_dir = Path(tmp).joinpath('mock')
|
||||||
shutil.copytree(self.mockDir(), new_dir)
|
shutil.copytree(self.mockDir(), new_dir)
|
||||||
|
|||||||
@@ -704,7 +704,7 @@ class LabelTemplate(TemplateUploadMixin, ReportTemplateBase):
|
|||||||
def get_context(self, instance, request=None, **kwargs):
|
def get_context(self, instance, request=None, **kwargs):
|
||||||
"""Supply context data to the label template for rendering."""
|
"""Supply context data to the label template for rendering."""
|
||||||
base_context = super().get_context(instance, request, **kwargs)
|
base_context = super().get_context(instance, request, **kwargs)
|
||||||
label_context: LabelContextExtension = { # type: ignore[invalid-assignment]
|
label_context: LabelContextExtension = {
|
||||||
'width': self.width,
|
'width': self.width,
|
||||||
'height': self.height,
|
'height': self.height,
|
||||||
'page_style': None,
|
'page_style': None,
|
||||||
|
|||||||
@@ -710,25 +710,24 @@ tomli==2.4.0 \
|
|||||||
--hash=sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa \
|
--hash=sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa \
|
||||||
--hash=sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087
|
--hash=sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087
|
||||||
# via coverage
|
# via coverage
|
||||||
ty==0.0.1a21 \
|
ty==0.0.21 \
|
||||||
--hash=sha256:0efba2e52b58f536f4198ba5c4a36cac2ba67d83ec6f429ebc7704233bcda4c3 \
|
--hash=sha256:210e7568c9f886c4d01308d751949ee714ad7ad9d7d928d2ba90d329dd880367 \
|
||||||
--hash=sha256:1474d883129bb63da3b2380fc7ead824cd3baf6a9551e6aa476ffefc58057af3 \
|
--hash=sha256:53508e345b11569f78b21ba8e2b4e61df38a9754947fb3cd9f2ef574367338fb \
|
||||||
--hash=sha256:1f276ceab23a1410aec09508248c76ae0989c67fb7a0c287e0d4564994295531 \
|
--hash=sha256:553e43571f4a35604c36cfd07d8b61a5eb7a714e3c67f8c4ff2cf674fefbaef9 \
|
||||||
--hash=sha256:218d53e7919e885bd98e9196d9cb952d82178b299aa36da6f7f39333eb7400ed \
|
--hash=sha256:56b01fd2519637a4ca88344f61c96225f540c98ff18bca321d4eaa7bb0f7aa2f \
|
||||||
--hash=sha256:21f708d02b6588323ffdbfdba38830dd0ecfd626db50aa6006b296b5470e52f9 \
|
--hash=sha256:56d3b198b64dd0a19b2b66e257deaed2ecea568e722ae5352f3c6fb62027f89d \
|
||||||
--hash=sha256:334d2a212ebf42a0e55d57561926af7679fe1e878175e11dcb81ad8df892844e \
|
--hash=sha256:62f7f5b235c4f7876db305c36997aea07b7af29b1a068f373d0e2547e25f32ff \
|
||||||
--hash=sha256:3c3bc66fcae41eff133cfe326dd65d82567a2fb5d4efe2128773b10ec2766819 \
|
--hash=sha256:666f6822e3b9200abfa7e95eb0ddd576460adb8d66b550c0ad2c70abc84a2048 \
|
||||||
--hash=sha256:5dfc73299d441cc6454e36ed0a976877415024143dfca6592dc36f7701424383 \
|
--hash=sha256:7bdf2f572378de78e1f388d24691c89db51b7caf07cf90f2bfcc1d6b18b70a76 \
|
||||||
--hash=sha256:7505aeb8bf2a62f00f12cfa496f6c965074d75c8126268776565284c8a12d5dd \
|
--hash=sha256:7e9613994610431ab8625025bd2880dbcb77c5c9fabdd21134cda12d840a529d \
|
||||||
--hash=sha256:84243455f295ed850bd53f7089819321807d4e6ee3b1cbff6086137ae0259466 \
|
--hash=sha256:a0854d008347ce4a5fb351af132f660a390ab2a1163444d075251d43e6f74b9b \
|
||||||
--hash=sha256:87a200c21e02962e8a27374d9d152582331d57d709672431be58f4f898bf6cad \
|
--hash=sha256:a4c2ba5d67d64df8fcdefd8b280ac1149d24a73dbda82fa953a0dff9d21400ed \
|
||||||
--hash=sha256:9463cac96b8f1bb5ba740fe1d42cd6bd152b43c5b159b2f07f8fd629bcdded34 \
|
--hash=sha256:a709d576e5bea84b745d43058d8b9cd4f27f74a0b24acb4b0cbb7d3d41e0d050 \
|
||||||
--hash=sha256:a8c769987d00fbc33054ff7e342633f475ea10dc43bc60fb9fb056159d48cb90 \
|
--hash=sha256:bef3ab4c7b966bcc276a8ac6c11b63ba222d21355b48d471ea782c4104eee4e0 \
|
||||||
--hash=sha256:ba13d03b9e095216ceb4e4d554a308517f28ab0a6e4dcd07cfe94563e4c2c489 \
|
--hash=sha256:d23d2c34f7a77d974bb08f0860ef700addc8a683d81a0319f71c08f87506cfd0 \
|
||||||
--hash=sha256:be8f457d7841b7ead2a3f6b65ba668abc172a1150a0f1f6c0958af3725dbb61a \
|
--hash=sha256:e9de7e11c63c6afc40f3e9ba716374add171aee7fabc70b5146a510705c6d41b \
|
||||||
--hash=sha256:cc0880ec344fbdf736b05d8d0da01f0caaaa02409bd9a24b68d18d0127a79b0e \
|
--hash=sha256:ee8399f7c453a425291e6688efe430cfae7ab0ac4ffd50eba9f872bf878b54f6 \
|
||||||
--hash=sha256:e941e9a9d1e54b03eeaf9c3197c26a19cf76009fd5e41e16e5657c1c827bd6d3 \
|
--hash=sha256:f72047996598ac20553fb7e21ba5741e3c82dee4e9eadf10d954551a5fe09391
|
||||||
--hash=sha256:ecf41706b803827b0de8717f32a434dad1e67be9f4b8caf403e12013179ea06a
|
|
||||||
# via -r src/backend/requirements-dev.in
|
# via -r src/backend/requirements-dev.in
|
||||||
types-psycopg2==2.9.21.20260223 \
|
types-psycopg2==2.9.21.20260223 \
|
||||||
--hash=sha256:78ed70de2e56bc6b5c26c8c1da8e9af54e49fdc3c94d1504609f3519e2b84f02 \
|
--hash=sha256:78ed70de2e56bc6b5c26c8c1da8e9af54e49fdc3c94d1504609f3519e2b84f02 \
|
||||||
|
|||||||
Reference in New Issue
Block a user