Просмотр исходного кода

Closes #21473: Enable UP rules and modernize string formatting (#21488)

Martin Hauser 6 дней назад
Родитель
Сommit
1b295f1d69
51 измененных файлов с 661 добавлено и 652 удалено
  1. 11 13
      netbox/circuits/graphql/schema.py
  2. 26 24
      netbox/circuits/graphql/types.py
  3. 2 2
      netbox/core/graphql/mixins.py
  4. 2 4
      netbox/core/graphql/schema.py
  5. 2 2
      netbox/core/graphql/types.py
  6. 1 2
      netbox/core/plugins.py
  7. 4 4
      netbox/core/tests/test_filtersets.py
  8. 2 2
      netbox/dcim/api/views.py
  9. 47 27
      netbox/dcim/graphql/mixins.py
  10. 43 45
      netbox/dcim/graphql/schema.py
  11. 184 181
      netbox/dcim/graphql/types.py
  12. 1 1
      netbox/dcim/management/commands/trace_paths.py
  13. 1 1
      netbox/dcim/svg/cables.py
  14. 5 5
      netbox/extras/graphql/mixins.py
  15. 17 19
      netbox/extras/graphql/schema.py
  16. 23 23
      netbox/extras/graphql/types.py
  17. 2 2
      netbox/extras/lookups.py
  18. 4 4
      netbox/extras/migrations/0111_rename_content_types.py
  19. 1 1
      netbox/extras/models/tags.py
  20. 2 2
      netbox/extras/tests/test_filtersets.py
  21. 2 2
      netbox/extras/tests/test_scripts.py
  22. 3 3
      netbox/ipam/graphql/mixins.py
  23. 18 20
      netbox/ipam/graphql/schema.py
  24. 79 75
      netbox/ipam/graphql/types.py
  25. 11 11
      netbox/ipam/lookups.py
  26. 7 7
      netbox/netbox/graphql/filter_lookups.py
  27. 3 3
      netbox/netbox/models/deletion.py
  28. 9 9
      netbox/netbox/navigation/__init__.py
  29. 2 2
      netbox/netbox/settings.py
  30. 2 3
      netbox/netbox/tables/columns.py
  31. 1 2
      netbox/netbox/tests/dummy_plugin/graphql.py
  32. 2 2
      netbox/netbox/tests/test_authentication.py
  33. 2 2
      netbox/tenancy/graphql/mixins.py
  34. 6 8
      netbox/tenancy/graphql/schema.py
  35. 30 30
      netbox/tenancy/graphql/types.py
  36. 4 6
      netbox/users/graphql/schema.py
  37. 1 2
      netbox/users/graphql/types.py
  38. 1 1
      netbox/users/models/users.py
  39. 5 6
      netbox/utilities/forms/widgets/apiselect.py
  40. 5 6
      netbox/utilities/release.py
  41. 2 2
      netbox/utilities/templatetags/helpers.py
  42. 3 3
      netbox/utilities/testing/filtersets.py
  43. 1 1
      netbox/utilities/views.py
  44. 6 8
      netbox/virtualization/graphql/schema.py
  45. 19 18
      netbox/virtualization/graphql/types.py
  46. 10 12
      netbox/vpn/graphql/schema.py
  47. 19 18
      netbox/vpn/graphql/types.py
  48. 10 10
      netbox/vpn/migrations/0005_rename_indexes.py
  49. 3 5
      netbox/wireless/graphql/schema.py
  50. 11 10
      netbox/wireless/graphql/types.py
  51. 4 1
      ruff.toml

+ 11 - 13
netbox/circuits/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,34 +7,34 @@ from .types import *
 @strawberry.type(name="Query")
 class CircuitsQuery:
     circuit: CircuitType = strawberry_django.field()
-    circuit_list: List[CircuitType] = strawberry_django.field()
+    circuit_list: list[CircuitType] = strawberry_django.field()
 
     circuit_termination: CircuitTerminationType = strawberry_django.field()
-    circuit_termination_list: List[CircuitTerminationType] = strawberry_django.field()
+    circuit_termination_list: list[CircuitTerminationType] = strawberry_django.field()
 
     circuit_type: CircuitTypeType = strawberry_django.field()
-    circuit_type_list: List[CircuitTypeType] = strawberry_django.field()
+    circuit_type_list: list[CircuitTypeType] = strawberry_django.field()
 
     circuit_group: CircuitGroupType = strawberry_django.field()
-    circuit_group_list: List[CircuitGroupType] = strawberry_django.field()
+    circuit_group_list: list[CircuitGroupType] = strawberry_django.field()
 
     circuit_group_assignment: CircuitGroupAssignmentType = strawberry_django.field()
-    circuit_group_assignment_list: List[CircuitGroupAssignmentType] = strawberry_django.field()
+    circuit_group_assignment_list: list[CircuitGroupAssignmentType] = strawberry_django.field()
 
     provider: ProviderType = strawberry_django.field()
-    provider_list: List[ProviderType] = strawberry_django.field()
+    provider_list: list[ProviderType] = strawberry_django.field()
 
     provider_account: ProviderAccountType = strawberry_django.field()
-    provider_account_list: List[ProviderAccountType] = strawberry_django.field()
+    provider_account_list: list[ProviderAccountType] = strawberry_django.field()
 
     provider_network: ProviderNetworkType = strawberry_django.field()
-    provider_network_list: List[ProviderNetworkType] = strawberry_django.field()
+    provider_network_list: list[ProviderNetworkType] = strawberry_django.field()
 
     virtual_circuit: VirtualCircuitType = strawberry_django.field()
-    virtual_circuit_list: List[VirtualCircuitType] = strawberry_django.field()
+    virtual_circuit_list: list[VirtualCircuitType] = strawberry_django.field()
 
     virtual_circuit_termination: VirtualCircuitTerminationType = strawberry_django.field()
-    virtual_circuit_termination_list: List[VirtualCircuitTerminationType] = strawberry_django.field()
+    virtual_circuit_termination_list: list[VirtualCircuitTerminationType] = strawberry_django.field()
 
     virtual_circuit_type: VirtualCircuitTypeType = strawberry_django.field()
-    virtual_circuit_type_list: List[VirtualCircuitTypeType] = strawberry_django.field()
+    virtual_circuit_type_list: list[VirtualCircuitTypeType] = strawberry_django.field()

+ 26 - 24
netbox/circuits/graphql/types.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List, Union
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -37,10 +37,10 @@ __all__ = (
     pagination=True
 )
 class ProviderType(ContactsMixin, PrimaryObjectType):
-    networks: List[Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')]]
-    circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
-    asns: List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]
-    accounts: List[Annotated["ProviderAccountType", strawberry.lazy('circuits.graphql.types')]]
+    networks: list[Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')]]
+    circuits: list[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
+    asns: list[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]
+    accounts: list[Annotated["ProviderAccountType", strawberry.lazy('circuits.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -51,7 +51,7 @@ class ProviderType(ContactsMixin, PrimaryObjectType):
 )
 class ProviderAccountType(ContactsMixin, PrimaryObjectType):
     provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
-    circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
+    circuits: list[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -62,7 +62,7 @@ class ProviderAccountType(ContactsMixin, PrimaryObjectType):
 )
 class ProviderNetworkType(PrimaryObjectType):
     provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
-    circuit_terminations: List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]
+    circuit_terminations: list[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -72,16 +72,17 @@ class ProviderNetworkType(PrimaryObjectType):
     pagination=True
 )
 class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType):
-    circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]
+    circuit: Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]
 
     @strawberry_django.field
-    def termination(self) -> Annotated[Union[
-        Annotated["LocationType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RegionType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')],
-    ], strawberry.union("CircuitTerminationTerminationType")] | None:
+    def termination(self) -> Annotated[
+        Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RegionType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteGroupType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['ProviderNetworkType', strawberry.lazy('circuits.graphql.types')],
+        strawberry.union('CircuitTerminationTerminationType'),
+    ] | None:
         return self.termination
 
 
@@ -94,7 +95,7 @@ class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, Ob
 class CircuitTypeType(OrganizationalObjectType):
     color: str
 
-    circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
+    circuits: list[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -110,7 +111,7 @@ class CircuitType(PrimaryObjectType, ContactsMixin):
     termination_z: CircuitTerminationType | None
     type: CircuitTypeType
     tenant: TenantType | None
-    terminations: List[CircuitTerminationType]
+    terminations: list[CircuitTerminationType]
 
 
 @strawberry_django.type(
@@ -130,13 +131,14 @@ class CircuitGroupType(OrganizationalObjectType):
     pagination=True
 )
 class CircuitGroupAssignmentType(TagsMixin, BaseObjectType):
-    group: Annotated["CircuitGroupType", strawberry.lazy('circuits.graphql.types')]
+    group: Annotated['CircuitGroupType', strawberry.lazy('circuits.graphql.types')]
 
     @strawberry_django.field
-    def member(self) -> Annotated[Union[
-        Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')],
-        Annotated["VirtualCircuitType", strawberry.lazy('circuits.graphql.types')],
-    ], strawberry.union("CircuitGroupAssignmentMemberType")] | None:
+    def member(self) -> Annotated[
+        Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]
+        | Annotated['VirtualCircuitType', strawberry.lazy('circuits.graphql.types')],
+        strawberry.union('CircuitGroupAssignmentMemberType'),
+    ] | None:
         return self.member
 
 
@@ -149,7 +151,7 @@ class CircuitGroupAssignmentType(TagsMixin, BaseObjectType):
 class VirtualCircuitTypeType(OrganizationalObjectType):
     color: str
 
-    virtual_circuits: List[Annotated["VirtualCircuitType", strawberry.lazy('circuits.graphql.types')]]
+    virtual_circuits: list[Annotated["VirtualCircuitType", strawberry.lazy('circuits.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -182,4 +184,4 @@ class VirtualCircuitType(PrimaryObjectType):
         select_related=["type"]
     )
     tenant: TenantType | None
-    terminations: List[VirtualCircuitTerminationType]
+    terminations: list[VirtualCircuitTerminationType]

+ 2 - 2
netbox/core/graphql/mixins.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -20,7 +20,7 @@ __all__ = (
 class ChangelogMixin:
 
     @strawberry_django.field
-    def changelog(self, info: Info) -> List[Annotated['ObjectChangeType', strawberry.lazy('.types')]]:  # noqa: F821
+    def changelog(self, info: Info) -> list[Annotated['ObjectChangeType', strawberry.lazy('.types')]]:  # noqa: F821
         content_type = ContentType.objects.get_for_model(self)
         object_changes = ObjectChange.objects.filter(
             changed_object_type=content_type,

+ 2 - 4
netbox/core/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,7 +7,7 @@ from .types import *
 @strawberry.type(name="Query")
 class CoreQuery:
     data_file: DataFileType = strawberry_django.field()
-    data_file_list: List[DataFileType] = strawberry_django.field()
+    data_file_list: list[DataFileType] = strawberry_django.field()
 
     data_source: DataSourceType = strawberry_django.field()
-    data_source_list: List[DataSourceType] = strawberry_django.field()
+    data_source_list: list[DataSourceType] = strawberry_django.field()

+ 2 - 2
netbox/core/graphql/types.py

@@ -1,4 +1,4 @@
-from typing import Annotated, List
+from typing import Annotated
 
 import strawberry
 import strawberry_django
@@ -34,7 +34,7 @@ class DataFileType(BaseObjectType):
     pagination=True
 )
 class DataSourceType(PrimaryObjectType):
-    datafiles: List[Annotated["DataFileType", strawberry.lazy('core.graphql.types')]]
+    datafiles: list[Annotated["DataFileType", strawberry.lazy('core.graphql.types')]]
 
 
 @strawberry_django.type(

+ 1 - 2
netbox/core/plugins.py

@@ -1,7 +1,6 @@
 import datetime
 import importlib
 from dataclasses import dataclass, field
-from typing import Optional
 
 import requests
 from django.conf import settings
@@ -55,7 +54,7 @@ class Plugin:
     tag_line: str = ''
     description_short: str = ''
     slug: str = ''
-    author: Optional[PluginAuthor] = None
+    author: PluginAuthor | None = None
     created_at: datetime.datetime = None
     updated_at: datetime.datetime = None
     license_type: str = ''

+ 4 - 4
netbox/core/tests/test_filtersets.py

@@ -1,5 +1,5 @@
 import uuid
-from datetime import datetime, timezone
+from datetime import UTC, datetime
 
 from django.contrib.contenttypes.models import ContentType
 from django.test import TestCase
@@ -100,21 +100,21 @@ class DataFileTestCase(TestCase, ChangeLoggedFilterSetTests):
             DataFile(
                 source=data_sources[0],
                 path='dir1/file1.txt',
-                last_updated=datetime(2023, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
+                last_updated=datetime(2023, 1, 1, 0, 0, 0, tzinfo=UTC),
                 size=1000,
                 hash='442da078f0111cbdf42f21903724f6597c692535f55bdfbbea758a1ae99ad9e1'
             ),
             DataFile(
                 source=data_sources[1],
                 path='dir1/file2.txt',
-                last_updated=datetime(2023, 1, 2, 0, 0, 0, tzinfo=timezone.utc),
+                last_updated=datetime(2023, 1, 2, 0, 0, 0, tzinfo=UTC),
                 size=2000,
                 hash='a78168c7c97115bafd96450ed03ea43acec495094c5caa28f0d02e20e3a76cc2'
             ),
             DataFile(
                 source=data_sources[2],
                 path='dir1/file3.txt',
-                last_updated=datetime(2023, 1, 3, 0, 0, 0, tzinfo=timezone.utc),
+                last_updated=datetime(2023, 1, 3, 0, 0, 0, tzinfo=UTC),
                 size=3000,
                 hash='12b8827a14c4d5a2f30b6c6e2b7983063988612391c6cbe8ee7493b59054827a'
             ),

+ 2 - 2
netbox/dcim/api/views.py

@@ -36,7 +36,7 @@ class DCIMRootView(APIRootView):
 
 # Mixins
 
-class PathEndpointMixin(object):
+class PathEndpointMixin:
 
     @action(detail=True, url_path='trace')
     def trace(self, request, pk):
@@ -76,7 +76,7 @@ class PathEndpointMixin(object):
         return Response(path)
 
 
-class PassThroughPortMixin(object):
+class PassThroughPortMixin:
 
     @action(detail=True, url_path='paths')
     def paths(self, request, pk):

+ 47 - 27
netbox/dcim/graphql/mixins.py

@@ -1,7 +1,21 @@
-from typing import Annotated, List, Union
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 
+if TYPE_CHECKING:
+    from circuits.graphql.types import CircuitTerminationType, ProviderNetworkType, VirtualCircuitTerminationType
+    from dcim.graphql.types import (
+        CableType,
+        ConsolePortType,
+        ConsoleServerPortType,
+        FrontPortType,
+        InterfaceType,
+        PowerFeedType,
+        PowerOutletType,
+        PowerPortType,
+        RearPortType,
+    )
+
 __all__ = (
     'CabledObjectMixin',
     'PathEndpointMixin',
@@ -10,34 +24,40 @@ __all__ = (
 
 @strawberry.type
 class CabledObjectMixin:
-    cable: Annotated["CableType", strawberry.lazy('dcim.graphql.types')] | None  # noqa: F821
-
-    link_peers: List[Annotated[Union[
-        Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')],  # noqa: F821
-        Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-    ], strawberry.union("LinkPeerType")]]
+    cable: Annotated["CableType", strawberry.lazy('dcim.graphql.types')] | None
+
+    link_peers: list[
+        Annotated[
+            Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
+            | Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],
+            strawberry.union("LinkPeerType"),
+        ]
+    ]
 
 
 @strawberry.type
 class PathEndpointMixin:
 
-    connected_endpoints: List[Annotated[Union[
-        Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')],  # noqa: F821
-        Annotated["VirtualCircuitTerminationType", strawberry.lazy('circuits.graphql.types')],  # noqa: F821
-        Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-        Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')],  # noqa: F821
-        Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],  # noqa: F821
-    ], strawberry.union("ConnectedEndpointType")]]
+    connected_endpoints: list[
+        Annotated[
+            Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
+            | Annotated["VirtualCircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
+            | Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')]
+            | Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')]
+            | Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],
+            strawberry.union("ConnectedEndpointType"),
+        ]
+    ]

+ 43 - 45
netbox/dcim/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,130 +7,130 @@ from .types import *
 @strawberry.type(name="Query")
 class DCIMQuery:
     cable: CableType = strawberry_django.field()
-    cable_list: List[CableType] = strawberry_django.field()
+    cable_list: list[CableType] = strawberry_django.field()
 
     console_port: ConsolePortType = strawberry_django.field()
-    console_port_list: List[ConsolePortType] = strawberry_django.field()
+    console_port_list: list[ConsolePortType] = strawberry_django.field()
 
     console_port_template: ConsolePortTemplateType = strawberry_django.field()
-    console_port_template_list: List[ConsolePortTemplateType] = strawberry_django.field()
+    console_port_template_list: list[ConsolePortTemplateType] = strawberry_django.field()
 
     console_server_port: ConsoleServerPortType = strawberry_django.field()
-    console_server_port_list: List[ConsoleServerPortType] = strawberry_django.field()
+    console_server_port_list: list[ConsoleServerPortType] = strawberry_django.field()
 
     console_server_port_template: ConsoleServerPortTemplateType = strawberry_django.field()
-    console_server_port_template_list: List[ConsoleServerPortTemplateType] = strawberry_django.field()
+    console_server_port_template_list: list[ConsoleServerPortTemplateType] = strawberry_django.field()
 
     device: DeviceType = strawberry_django.field()
-    device_list: List[DeviceType] = strawberry_django.field()
+    device_list: list[DeviceType] = strawberry_django.field()
 
     device_bay: DeviceBayType = strawberry_django.field()
-    device_bay_list: List[DeviceBayType] = strawberry_django.field()
+    device_bay_list: list[DeviceBayType] = strawberry_django.field()
 
     device_bay_template: DeviceBayTemplateType = strawberry_django.field()
-    device_bay_template_list: List[DeviceBayTemplateType] = strawberry_django.field()
+    device_bay_template_list: list[DeviceBayTemplateType] = strawberry_django.field()
 
     device_role: DeviceRoleType = strawberry_django.field()
-    device_role_list: List[DeviceRoleType] = strawberry_django.field()
+    device_role_list: list[DeviceRoleType] = strawberry_django.field()
 
     device_type: DeviceTypeType = strawberry_django.field()
-    device_type_list: List[DeviceTypeType] = strawberry_django.field()
+    device_type_list: list[DeviceTypeType] = strawberry_django.field()
 
     front_port: FrontPortType = strawberry_django.field()
-    front_port_list: List[FrontPortType] = strawberry_django.field()
+    front_port_list: list[FrontPortType] = strawberry_django.field()
 
     front_port_template: FrontPortTemplateType = strawberry_django.field()
-    front_port_template_list: List[FrontPortTemplateType] = strawberry_django.field()
+    front_port_template_list: list[FrontPortTemplateType] = strawberry_django.field()
 
     mac_address: MACAddressType = strawberry_django.field()
-    mac_address_list: List[MACAddressType] = strawberry_django.field()
+    mac_address_list: list[MACAddressType] = strawberry_django.field()
 
     interface: InterfaceType = strawberry_django.field()
-    interface_list: List[InterfaceType] = strawberry_django.field()
+    interface_list: list[InterfaceType] = strawberry_django.field()
 
     interface_template: InterfaceTemplateType = strawberry_django.field()
-    interface_template_list: List[InterfaceTemplateType] = strawberry_django.field()
+    interface_template_list: list[InterfaceTemplateType] = strawberry_django.field()
 
     inventory_item: InventoryItemType = strawberry_django.field()
-    inventory_item_list: List[InventoryItemType] = strawberry_django.field()
+    inventory_item_list: list[InventoryItemType] = strawberry_django.field()
 
     inventory_item_role: InventoryItemRoleType = strawberry_django.field()
-    inventory_item_role_list: List[InventoryItemRoleType] = strawberry_django.field()
+    inventory_item_role_list: list[InventoryItemRoleType] = strawberry_django.field()
 
     inventory_item_template: InventoryItemTemplateType = strawberry_django.field()
-    inventory_item_template_list: List[InventoryItemTemplateType] = strawberry_django.field()
+    inventory_item_template_list: list[InventoryItemTemplateType] = strawberry_django.field()
 
     location: LocationType = strawberry_django.field()
-    location_list: List[LocationType] = strawberry_django.field()
+    location_list: list[LocationType] = strawberry_django.field()
 
     manufacturer: ManufacturerType = strawberry_django.field()
-    manufacturer_list: List[ManufacturerType] = strawberry_django.field()
+    manufacturer_list: list[ManufacturerType] = strawberry_django.field()
 
     module: ModuleType = strawberry_django.field()
-    module_list: List[ModuleType] = strawberry_django.field()
+    module_list: list[ModuleType] = strawberry_django.field()
 
     module_bay: ModuleBayType = strawberry_django.field()
-    module_bay_list: List[ModuleBayType] = strawberry_django.field()
+    module_bay_list: list[ModuleBayType] = strawberry_django.field()
 
     module_bay_template: ModuleBayTemplateType = strawberry_django.field()
-    module_bay_template_list: List[ModuleBayTemplateType] = strawberry_django.field()
+    module_bay_template_list: list[ModuleBayTemplateType] = strawberry_django.field()
 
     module_type_profile: ModuleTypeProfileType = strawberry_django.field()
-    module_type_profile_list: List[ModuleTypeProfileType] = strawberry_django.field()
+    module_type_profile_list: list[ModuleTypeProfileType] = strawberry_django.field()
 
     module_type: ModuleTypeType = strawberry_django.field()
-    module_type_list: List[ModuleTypeType] = strawberry_django.field()
+    module_type_list: list[ModuleTypeType] = strawberry_django.field()
 
     platform: PlatformType = strawberry_django.field()
-    platform_list: List[PlatformType] = strawberry_django.field()
+    platform_list: list[PlatformType] = strawberry_django.field()
 
     power_feed: PowerFeedType = strawberry_django.field()
-    power_feed_list: List[PowerFeedType] = strawberry_django.field()
+    power_feed_list: list[PowerFeedType] = strawberry_django.field()
 
     power_outlet: PowerOutletType = strawberry_django.field()
-    power_outlet_list: List[PowerOutletType] = strawberry_django.field()
+    power_outlet_list: list[PowerOutletType] = strawberry_django.field()
 
     power_outlet_template: PowerOutletTemplateType = strawberry_django.field()
-    power_outlet_template_list: List[PowerOutletTemplateType] = strawberry_django.field()
+    power_outlet_template_list: list[PowerOutletTemplateType] = strawberry_django.field()
 
     power_panel: PowerPanelType = strawberry_django.field()
-    power_panel_list: List[PowerPanelType] = strawberry_django.field()
+    power_panel_list: list[PowerPanelType] = strawberry_django.field()
 
     power_port: PowerPortType = strawberry_django.field()
-    power_port_list: List[PowerPortType] = strawberry_django.field()
+    power_port_list: list[PowerPortType] = strawberry_django.field()
 
     power_port_template: PowerPortTemplateType = strawberry_django.field()
-    power_port_template_list: List[PowerPortTemplateType] = strawberry_django.field()
+    power_port_template_list: list[PowerPortTemplateType] = strawberry_django.field()
 
     rack_type: RackTypeType = strawberry_django.field()
-    rack_type_list: List[RackTypeType] = strawberry_django.field()
+    rack_type_list: list[RackTypeType] = strawberry_django.field()
 
     rack: RackType = strawberry_django.field()
-    rack_list: List[RackType] = strawberry_django.field()
+    rack_list: list[RackType] = strawberry_django.field()
 
     rack_reservation: RackReservationType = strawberry_django.field()
-    rack_reservation_list: List[RackReservationType] = strawberry_django.field()
+    rack_reservation_list: list[RackReservationType] = strawberry_django.field()
 
     rack_role: RackRoleType = strawberry_django.field()
-    rack_role_list: List[RackRoleType] = strawberry_django.field()
+    rack_role_list: list[RackRoleType] = strawberry_django.field()
 
     rear_port: RearPortType = strawberry_django.field()
-    rear_port_list: List[RearPortType] = strawberry_django.field()
+    rear_port_list: list[RearPortType] = strawberry_django.field()
 
     rear_port_template: RearPortTemplateType = strawberry_django.field()
-    rear_port_template_list: List[RearPortTemplateType] = strawberry_django.field()
+    rear_port_template_list: list[RearPortTemplateType] = strawberry_django.field()
 
     region: RegionType = strawberry_django.field()
-    region_list: List[RegionType] = strawberry_django.field()
+    region_list: list[RegionType] = strawberry_django.field()
 
     site: SiteType = strawberry_django.field()
-    site_list: List[SiteType] = strawberry_django.field()
+    site_list: list[SiteType] = strawberry_django.field()
 
     site_group: SiteGroupType = strawberry_django.field()
-    site_group_list: List[SiteGroupType] = strawberry_django.field()
+    site_group_list: list[SiteGroupType] = strawberry_django.field()
 
     virtual_chassis: VirtualChassisType = strawberry_django.field()
-    virtual_chassis_list: List[VirtualChassisType] = strawberry_django.field()
+    virtual_chassis_list: list[VirtualChassisType] = strawberry_django.field()
 
     virtual_device_context: VirtualDeviceContextType = strawberry_django.field()
-    virtual_device_context_list: List[VirtualDeviceContextType] = strawberry_django.field()
+    virtual_device_context_list: list[VirtualDeviceContextType] = strawberry_django.field()

+ 184 - 181
netbox/dcim/graphql/types.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List, Union
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -106,10 +106,7 @@ class ModularComponentType(ComponentType):
 
 
 @strawberry.type
-class ComponentTemplateType(
-    ChangelogMixin,
-    BaseObjectType
-):
+class ComponentTemplateType(ChangelogMixin, BaseObjectType):
     """
     Base type for device/VM components
     """
@@ -136,18 +133,19 @@ class ModularComponentTemplateType(ComponentTemplateType):
     pagination=True
 )
 class CableTerminationType(NetBoxObjectType):
-    cable: Annotated["CableType", strawberry.lazy('dcim.graphql.types')] | None
-    termination: Annotated[Union[
-        Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')],
-        Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],
-    ], strawberry.union("CableTerminationTerminationType")] | None
+    cable: Annotated['CableType', strawberry.lazy('dcim.graphql.types')] | None
+    termination: Annotated[
+        Annotated['CircuitTerminationType', strawberry.lazy('circuits.graphql.types')]
+        | Annotated['ConsolePortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['ConsoleServerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['FrontPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerFeedType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerOutletType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RearPortType', strawberry.lazy('dcim.graphql.types')],
+        strawberry.union('CableTerminationTerminationType'),
+    ] | None
 
 
 @strawberry_django.type(
@@ -158,33 +156,35 @@ class CableTerminationType(NetBoxObjectType):
 )
 class CableType(PrimaryObjectType):
     color: str
-    tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
-
-    terminations: List[CableTerminationType]
-
-    a_terminations: List[Annotated[Union[
-        Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')],
-        Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],
-    ], strawberry.union("CableTerminationTerminationType")]]
-
-    b_terminations: List[Annotated[Union[
-        Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')],
-        Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],
-    ], strawberry.union("CableTerminationTerminationType")]]
+    tenant: Annotated['TenantType', strawberry.lazy('tenancy.graphql.types')] | None
+
+    terminations: list[CableTerminationType]
+
+    a_terminations: list[Annotated[
+        Annotated['CircuitTerminationType', strawberry.lazy('circuits.graphql.types')]
+        | Annotated['ConsolePortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['ConsoleServerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['FrontPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerFeedType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerOutletType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RearPortType', strawberry.lazy('dcim.graphql.types')],
+        strawberry.union('CableTerminationTerminationType'),
+    ]]
+
+    b_terminations: list[Annotated[
+        Annotated['CircuitTerminationType', strawberry.lazy('circuits.graphql.types')]
+        | Annotated['ConsolePortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['ConsoleServerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['FrontPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerFeedType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerOutletType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RearPortType', strawberry.lazy('dcim.graphql.types')],
+        strawberry.union('CableTerminationTerminationType'),
+    ]]
 
 
 @strawberry_django.type(
@@ -258,21 +258,21 @@ class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, Prima
     cluster: Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')] | None
     virtual_chassis: Annotated["VirtualChassisType", strawberry.lazy('dcim.graphql.types')] | None
 
-    virtual_machines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
-    modules: List[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]]
-    interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
-    rearports: List[Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')]]
-    consoleports: List[Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')]]
-    powerports: List[Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')]]
-    cabletermination_set: List[Annotated["CableTerminationType", strawberry.lazy('dcim.graphql.types')]]
-    consoleserverports: List[Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')]]
-    poweroutlets: List[Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]]
-    frontports: List[Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]]
-    devicebays: List[Annotated["DeviceBayType", strawberry.lazy('dcim.graphql.types')]]
-    modulebays: List[Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]]
-    services: List[Annotated["ServiceType", strawberry.lazy('ipam.graphql.types')]]
-    inventoryitems: List[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]]
-    vdcs: List[Annotated["VirtualDeviceContextType", strawberry.lazy('dcim.graphql.types')]]
+    virtual_machines: list[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
+    modules: list[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]]
+    interfaces: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    rearports: list[Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')]]
+    consoleports: list[Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')]]
+    powerports: list[Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')]]
+    cabletermination_set: list[Annotated["CableTerminationType", strawberry.lazy('dcim.graphql.types')]]
+    consoleserverports: list[Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')]]
+    poweroutlets: list[Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]]
+    frontports: list[Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]]
+    devicebays: list[Annotated["DeviceBayType", strawberry.lazy('dcim.graphql.types')]]
+    modulebays: list[Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]]
+    services: list[Annotated["ServiceType", strawberry.lazy('ipam.graphql.types')]]
+    inventoryitems: list[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]]
+    vdcs: list[Annotated["VirtualDeviceContextType", strawberry.lazy('dcim.graphql.types')]]
 
     @strawberry_django.field
     def vc_master_for(self) -> Annotated["VirtualChassisType", strawberry.lazy('dcim.graphql.types')] | None:
@@ -310,24 +310,25 @@ class DeviceBayTemplateType(ComponentTemplateType):
     pagination=True
 )
 class InventoryItemTemplateType(ComponentTemplateType):
-    role: Annotated["InventoryItemRoleType", strawberry.lazy('dcim.graphql.types')] | None
-    manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
+    role: Annotated['InventoryItemRoleType', strawberry.lazy('dcim.graphql.types')] | None
+    manufacturer: Annotated['ManufacturerType', strawberry.lazy('dcim.graphql.types')]
 
     @strawberry_django.field
-    def parent(self) -> Annotated["InventoryItemTemplateType", strawberry.lazy('dcim.graphql.types')] | None:
+    def parent(self) -> Annotated['InventoryItemTemplateType', strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent
 
-    child_items: List[Annotated["InventoryItemTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    child_items: list[Annotated['InventoryItemTemplateType', strawberry.lazy('dcim.graphql.types')]]
 
-    component: Annotated[Union[
-        Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],
-    ], strawberry.union("InventoryItemTemplateComponentType")] | None
+    component: Annotated[
+        Annotated['ConsolePortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['ConsoleServerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['FrontPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerOutletType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RearPortType', strawberry.lazy('dcim.graphql.types')],
+        strawberry.union('InventoryItemTemplateComponentType'),
+    ] | None
 
 
 @strawberry_django.type(
@@ -338,12 +339,12 @@ class InventoryItemTemplateType(ComponentTemplateType):
 )
 class DeviceRoleType(NestedGroupObjectType):
     parent: Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')] | None
-    children: List[Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')]]
+    children: list[Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')]]
     color: str
     config_template: Annotated["ConfigTemplateType", strawberry.lazy('extras.graphql.types')] | None
 
-    virtual_machines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
-    devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
+    virtual_machines: list[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
+    devices: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -369,17 +370,17 @@ class DeviceTypeType(PrimaryObjectType):
     manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
     default_platform: Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')] | None
 
-    frontporttemplates: List[Annotated["FrontPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    modulebaytemplates: List[Annotated["ModuleBayTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    instances: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
-    poweroutlettemplates: List[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    powerporttemplates: List[Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    inventoryitemtemplates: List[Annotated["InventoryItemTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    rearporttemplates: List[Annotated["RearPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    consoleserverporttemplates: List[Annotated["ConsoleServerPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    interfacetemplates: List[Annotated["InterfaceTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    devicebaytemplates: List[Annotated["DeviceBayTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    consoleporttemplates: List[Annotated["ConsolePortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    frontporttemplates: list[Annotated["FrontPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    modulebaytemplates: list[Annotated["ModuleBayTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    instances: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
+    poweroutlettemplates: list[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    powerporttemplates: list[Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    inventoryitemtemplates: list[Annotated["InventoryItemTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    rearporttemplates: list[Annotated["RearPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    consoleserverporttemplates: list[Annotated["ConsoleServerPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    interfacetemplates: list[Annotated["InterfaceTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    devicebaytemplates: list[Annotated["DeviceBayTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    consoleporttemplates: list[Annotated["ConsolePortTemplateType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -391,7 +392,7 @@ class DeviceTypeType(PrimaryObjectType):
 class FrontPortType(ModularComponentType, CabledObjectMixin):
     color: str
 
-    mappings: List[Annotated["PortMappingType", strawberry.lazy('dcim.graphql.types')]]
+    mappings: list[Annotated["PortMappingType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -403,7 +404,7 @@ class FrontPortType(ModularComponentType, CabledObjectMixin):
 class FrontPortTemplateType(ModularComponentTemplateType):
     color: str
 
-    mappings: List[Annotated["PortMappingTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    mappings: list[Annotated["PortMappingTemplateType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -416,10 +417,11 @@ class MACAddressType(PrimaryObjectType):
     mac_address: str
 
     @strawberry_django.field
-    def assigned_object(self) -> Annotated[Union[
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')],
-    ], strawberry.union("MACAddressAssignmentType")] | None:
+    def assigned_object(self) -> Annotated[
+        Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['VMInterfaceType', strawberry.lazy('virtualization.graphql.types')],
+        strawberry.union('MACAddressAssignmentType'),
+    ] | None:
         return self.assigned_object
 
 
@@ -443,13 +445,13 @@ class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, P
     vlan_translation_policy: Annotated["VLANTranslationPolicyType", strawberry.lazy('ipam.graphql.types')] | None
     l2vpn_termination: Annotated["L2VPNTerminationType", strawberry.lazy('vpn.graphql.types')] | None
 
-    vdcs: List[Annotated["VirtualDeviceContextType", strawberry.lazy('dcim.graphql.types')]]
-    tagged_vlans: List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
-    bridge_interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
-    wireless_lans: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]
-    member_interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
-    child_interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
-    mac_addresses: List[Annotated["MACAddressType", strawberry.lazy('dcim.graphql.types')]]
+    vdcs: list[Annotated["VirtualDeviceContextType", strawberry.lazy('dcim.graphql.types')]]
+    tagged_vlans: list[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
+    bridge_interfaces: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    wireless_lans: list[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]
+    member_interfaces: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    child_interfaces: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    mac_addresses: list[Annotated["MACAddressType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -462,7 +464,7 @@ class InterfaceTemplateType(ModularComponentTemplateType):
     _name: str
     bridge: Annotated["InterfaceTemplateType", strawberry.lazy('dcim.graphql.types')] | None
 
-    bridge_interfaces: List[Annotated["InterfaceTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    bridge_interfaces: list[Annotated["InterfaceTemplateType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -472,24 +474,25 @@ class InterfaceTemplateType(ModularComponentTemplateType):
     pagination=True
 )
 class InventoryItemType(ComponentType):
-    role: Annotated["InventoryItemRoleType", strawberry.lazy('dcim.graphql.types')] | None
-    manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] | None
+    role: Annotated['InventoryItemRoleType', strawberry.lazy('dcim.graphql.types')] | None
+    manufacturer: Annotated['ManufacturerType', strawberry.lazy('dcim.graphql.types')] | None
 
-    child_items: List[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]]
+    child_items: list[Annotated['InventoryItemType', strawberry.lazy('dcim.graphql.types')]]
 
     @strawberry_django.field
-    def parent(self) -> Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')] | None:
+    def parent(self) -> Annotated['InventoryItemType', strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent
 
-    component: Annotated[Union[
-        Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],
-    ], strawberry.union("InventoryItemComponentType")] | None
+    component: Annotated[
+        Annotated['ConsolePortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['ConsoleServerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['FrontPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerOutletType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['PowerPortType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RearPortType', strawberry.lazy('dcim.graphql.types')],
+        strawberry.union('InventoryItemComponentType'),
+    ] | None
 
 
 @strawberry_django.type(
@@ -501,8 +504,8 @@ class InventoryItemType(ComponentType):
 class InventoryItemRoleType(OrganizationalObjectType):
     color: str
 
-    inventory_items: List[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]]
-    inventory_item_templates: List[Annotated["InventoryItemTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    inventory_items: list[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]]
+    inventory_item_templates: list[Annotated["InventoryItemTemplateType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -517,18 +520,18 @@ class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, Nested
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
     parent: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
 
-    powerpanel_set: List[Annotated["PowerPanelType", strawberry.lazy('dcim.graphql.types')]]
-    cabletermination_set: List[Annotated["CableTerminationType", strawberry.lazy('dcim.graphql.types')]]
-    racks: List[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]]
-    devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
-    children: List[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]]
+    powerpanel_set: list[Annotated["PowerPanelType", strawberry.lazy('dcim.graphql.types')]]
+    cabletermination_set: list[Annotated["CableTerminationType", strawberry.lazy('dcim.graphql.types')]]
+    racks: list[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]]
+    devices: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
+    children: list[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]]
 
     @strawberry_django.field
-    def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
+    def clusters(self) -> list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
         return self.cluster_set.all()
 
     @strawberry_django.field
-    def circuit_terminations(self) -> List[
+    def circuit_terminations(self) -> list[
         Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
     ]:
         return self.circuit_terminations.all()
@@ -542,11 +545,11 @@ class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, Nested
 )
 class ManufacturerType(OrganizationalObjectType, ContactsMixin):
 
-    platforms: List[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]
-    device_types: List[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]]
-    inventory_item_templates: List[Annotated["InventoryItemTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    inventory_items: List[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]]
-    module_types: List[Annotated["ModuleTypeType", strawberry.lazy('dcim.graphql.types')]]
+    platforms: list[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]
+    device_types: list[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]]
+    inventory_item_templates: list[Annotated["InventoryItemTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    inventory_items: list[Annotated["InventoryItemType", strawberry.lazy('dcim.graphql.types')]]
+    module_types: list[Annotated["ModuleTypeType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -560,13 +563,13 @@ class ModuleType(PrimaryObjectType):
     module_bay: Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]
     module_type: Annotated["ModuleTypeType", strawberry.lazy('dcim.graphql.types')]
 
-    interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
-    powerports: List[Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')]]
-    consoleserverports: List[Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')]]
-    consoleports: List[Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')]]
-    poweroutlets: List[Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]]
-    rearports: List[Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')]]
-    frontports: List[Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]]
+    interfaces: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    powerports: list[Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')]]
+    consoleserverports: list[Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')]]
+    consoleports: list[Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')]]
+    poweroutlets: list[Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]]
+    rearports: list[Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')]]
+    frontports: list[Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -579,7 +582,7 @@ class ModuleType(PrimaryObjectType):
 class ModuleBayType(ModularComponentType):
 
     installed_module: Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')] | None
-    children: List[Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]]
+    children: list[Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')]]
 
     @strawberry_django.field
     def parent(self) -> Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')] | None:
@@ -603,7 +606,7 @@ class ModuleBayTemplateType(ModularComponentTemplateType):
     pagination=True
 )
 class ModuleTypeProfileType(PrimaryObjectType):
-    module_types: List[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]]
+    module_types: list[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -617,14 +620,14 @@ class ModuleTypeType(PrimaryObjectType):
     profile: Annotated["ModuleTypeProfileType", strawberry.lazy('dcim.graphql.types')] | None
     manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')]
 
-    frontporttemplates: List[Annotated["FrontPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    consoleserverporttemplates: List[Annotated["ConsoleServerPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    interfacetemplates: List[Annotated["InterfaceTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    powerporttemplates: List[Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    poweroutlettemplates: List[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    rearporttemplates: List[Annotated["RearPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
-    instances: List[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]]
-    consoleporttemplates: List[Annotated["ConsolePortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    frontporttemplates: list[Annotated["FrontPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    consoleserverporttemplates: list[Annotated["ConsoleServerPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    interfacetemplates: list[Annotated["InterfaceTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    powerporttemplates: list[Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    poweroutlettemplates: list[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    rearporttemplates: list[Annotated["RearPortTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    instances: list[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]]
+    consoleporttemplates: list[Annotated["ConsolePortTemplateType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -635,12 +638,12 @@ class ModuleTypeType(PrimaryObjectType):
 )
 class PlatformType(NestedGroupObjectType):
     parent: Annotated['PlatformType', strawberry.lazy('dcim.graphql.types')] | None
-    children: List[Annotated['PlatformType', strawberry.lazy('dcim.graphql.types')]]
+    children: list[Annotated['PlatformType', strawberry.lazy('dcim.graphql.types')]]
     manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] | None
     config_template: Annotated["ConfigTemplateType", strawberry.lazy('extras.graphql.types')] | None
 
-    virtual_machines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
-    devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
+    virtual_machines: list[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
+    devices: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -709,7 +712,7 @@ class PowerPanelType(ContactsMixin, PrimaryObjectType):
     site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]
     location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None
 
-    powerfeeds: List[Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')]]
+    powerfeeds: list[Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -720,7 +723,7 @@ class PowerPanelType(ContactsMixin, PrimaryObjectType):
 )
 class PowerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin):
 
-    poweroutlets: List[Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]]
+    poweroutlets: list[Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -730,7 +733,7 @@ class PowerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin):
     pagination=True
 )
 class PowerPortTemplateType(ModularComponentTemplateType):
-    poweroutlet_templates: List[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    poweroutlet_templates: list[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -757,10 +760,10 @@ class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObj
     role: Annotated["RackRoleType", strawberry.lazy('dcim.graphql.types')] | None
 
     rack_type: Annotated["RackTypeType", strawberry.lazy('dcim.graphql.types')] | None
-    reservations: List[Annotated["RackReservationType", strawberry.lazy('dcim.graphql.types')]]
-    devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
-    powerfeeds: List[Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')]]
-    cabletermination_set: List[Annotated["CableTerminationType", strawberry.lazy('dcim.graphql.types')]]
+    reservations: list[Annotated["RackReservationType", strawberry.lazy('dcim.graphql.types')]]
+    devices: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
+    powerfeeds: list[Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')]]
+    cabletermination_set: list[Annotated["CableTerminationType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -770,7 +773,7 @@ class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObj
     pagination=True
 )
 class RackReservationType(PrimaryObjectType):
-    units: List[int]
+    units: list[int]
     rack: Annotated["RackType", strawberry.lazy('dcim.graphql.types')]
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
     user: Annotated["UserType", strawberry.lazy('users.graphql.types')]
@@ -785,7 +788,7 @@ class RackReservationType(PrimaryObjectType):
 class RackRoleType(OrganizationalObjectType):
     color: str
 
-    racks: List[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]]
+    racks: list[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -797,7 +800,7 @@ class RackRoleType(OrganizationalObjectType):
 class RearPortType(ModularComponentType, CabledObjectMixin):
     color: str
 
-    mappings: List[Annotated["PortMappingType", strawberry.lazy('dcim.graphql.types')]]
+    mappings: list[Annotated["PortMappingType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -809,7 +812,7 @@ class RearPortType(ModularComponentType, CabledObjectMixin):
 class RearPortTemplateType(ModularComponentTemplateType):
     color: str
 
-    mappings: List[Annotated["PortMappingTemplateType", strawberry.lazy('dcim.graphql.types')]]
+    mappings: list[Annotated["PortMappingTemplateType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -820,19 +823,19 @@ class RearPortTemplateType(ModularComponentTemplateType):
 )
 class RegionType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
 
-    sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
-    children: List[Annotated["RegionType", strawberry.lazy('dcim.graphql.types')]]
+    sites: list[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
+    children: list[Annotated["RegionType", strawberry.lazy('dcim.graphql.types')]]
 
     @strawberry_django.field
     def parent(self) -> Annotated["RegionType", strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent
 
     @strawberry_django.field
-    def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
+    def clusters(self) -> list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
         return self.cluster_set.all()
 
     @strawberry_django.field
-    def circuit_terminations(self) -> List[
+    def circuit_terminations(self) -> list[
         Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
     ]:
         return self.circuit_terminations.all()
@@ -850,24 +853,24 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObj
     group: Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')] | None
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
 
-    prefixes: List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]
-    virtual_machines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
-    racks: List[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]]
-    cabletermination_set: List[Annotated["CableTerminationType", strawberry.lazy('dcim.graphql.types')]]
-    powerpanel_set: List[Annotated["PowerPanelType", strawberry.lazy('dcim.graphql.types')]]
-    devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
-    locations: List[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]]
-    asns: List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]
-    circuit_terminations: List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]
-    clusters: List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]
-    vlans: List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
+    prefixes: list[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]
+    virtual_machines: list[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
+    racks: list[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]]
+    cabletermination_set: list[Annotated["CableTerminationType", strawberry.lazy('dcim.graphql.types')]]
+    powerpanel_set: list[Annotated["PowerPanelType", strawberry.lazy('dcim.graphql.types')]]
+    devices: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
+    locations: list[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]]
+    asns: list[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]
+    circuit_terminations: list[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]
+    clusters: list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]
+    vlans: list[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
 
     @strawberry_django.field
-    def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
+    def clusters(self) -> list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
         return self.cluster_set.all()
 
     @strawberry_django.field
-    def circuit_terminations(self) -> List[
+    def circuit_terminations(self) -> list[
         Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
     ]:
         return self.circuit_terminations.all()
@@ -881,19 +884,19 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObj
 )
 class SiteGroupType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType):
 
-    sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
-    children: List[Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')]]
+    sites: list[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
+    children: list[Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')]]
 
     @strawberry_django.field
     def parent(self) -> Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')] | None:
         return self.parent
 
     @strawberry_django.field
-    def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
+    def clusters(self) -> list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
         return self.cluster_set.all()
 
     @strawberry_django.field
-    def circuit_terminations(self) -> List[
+    def circuit_terminations(self) -> list[
         Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]
     ]:
         return self.circuit_terminations.all()
@@ -909,7 +912,7 @@ class VirtualChassisType(PrimaryObjectType):
     member_count: BigInt
     master: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
 
-    members: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
+    members: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -924,4 +927,4 @@ class VirtualDeviceContextType(PrimaryObjectType):
     primary_ip6: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
 
-    interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    interfaces: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]

+ 1 - 1
netbox/dcim/management/commands/trace_paths.py

@@ -57,7 +57,7 @@ class Command(BaseCommand):
             # Delete all existing CablePath instances
             self.stdout.write(f"Deleting {paths_count} existing cable paths...")
             deleted_count, _ = CablePath.objects.all().delete()
-            self.stdout.write((self.style.SUCCESS(f'  Deleted {deleted_count} paths')))
+            self.stdout.write(self.style.SUCCESS(f'  Deleted {deleted_count} paths'))
 
             # Reinitialize the model's PK sequence
             self.stdout.write('Resetting database sequence for CablePath model')

+ 1 - 1
netbox/dcim/svg/cables.py

@@ -37,7 +37,7 @@ class Node(Hyperlink):
     object = None
 
     def __init__(self, position, width, url, color, labels, radius=10, object=object, **extra):
-        super(Node, self).__init__(href=url, target='_parent', **extra)
+        super().__init__(href=url, target='_parent', **extra)
 
         # Save object for reference by cable systems
         self.object = object

+ 5 - 5
netbox/extras/graphql/mixins.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -39,7 +39,7 @@ class CustomFieldsMixin:
 class ImageAttachmentsMixin:
 
     @strawberry_django.field
-    def image_attachments(self, info: Info) -> List[Annotated['ImageAttachmentType', strawberry.lazy('.types')]]:
+    def image_attachments(self, info: Info) -> list[Annotated['ImageAttachmentType', strawberry.lazy('.types')]]:
         return self.images.restrict(info.context.request.user, 'view')
 
 
@@ -47,17 +47,17 @@ class ImageAttachmentsMixin:
 class JournalEntriesMixin:
 
     @strawberry_django.field
-    def journal_entries(self, info: Info) -> List[Annotated['JournalEntryType', strawberry.lazy('.types')]]:
+    def journal_entries(self, info: Info) -> list[Annotated['JournalEntryType', strawberry.lazy('.types')]]:
         return self.journal_entries.all()
 
 
 @strawberry.type
 class TagsMixin:
 
-    tags: List[Annotated['TagType', strawberry.lazy('.types')]]
+    tags: list[Annotated['TagType', strawberry.lazy('.types')]]
 
 
 @strawberry.type
 class ContactsMixin:
 
-    contacts: List[Annotated['ContactAssignmentType', strawberry.lazy('tenancy.graphql.types')]]
+    contacts: list[Annotated['ContactAssignmentType', strawberry.lazy('tenancy.graphql.types')]]

+ 17 - 19
netbox/extras/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,52 +7,52 @@ from .types import *
 @strawberry.type(name="Query")
 class ExtrasQuery:
     config_context: ConfigContextType = strawberry_django.field()
-    config_context_list: List[ConfigContextType] = strawberry_django.field()
+    config_context_list: list[ConfigContextType] = strawberry_django.field()
 
     config_context_profile: ConfigContextProfileType = strawberry_django.field()
-    config_context_profile_list: List[ConfigContextProfileType] = strawberry_django.field()
+    config_context_profile_list: list[ConfigContextProfileType] = strawberry_django.field()
 
     config_template: ConfigTemplateType = strawberry_django.field()
-    config_template_list: List[ConfigTemplateType] = strawberry_django.field()
+    config_template_list: list[ConfigTemplateType] = strawberry_django.field()
 
     custom_field: CustomFieldType = strawberry_django.field()
-    custom_field_list: List[CustomFieldType] = strawberry_django.field()
+    custom_field_list: list[CustomFieldType] = strawberry_django.field()
 
     custom_field_choice_set: CustomFieldChoiceSetType = strawberry_django.field()
-    custom_field_choice_set_list: List[CustomFieldChoiceSetType] = strawberry_django.field()
+    custom_field_choice_set_list: list[CustomFieldChoiceSetType] = strawberry_django.field()
 
     custom_link: CustomLinkType = strawberry_django.field()
-    custom_link_list: List[CustomLinkType] = strawberry_django.field()
+    custom_link_list: list[CustomLinkType] = strawberry_django.field()
 
     export_template: ExportTemplateType = strawberry_django.field()
-    export_template_list: List[ExportTemplateType] = strawberry_django.field()
+    export_template_list: list[ExportTemplateType] = strawberry_django.field()
 
     image_attachment: ImageAttachmentType = strawberry_django.field()
-    image_attachment_list: List[ImageAttachmentType] = strawberry_django.field()
+    image_attachment_list: list[ImageAttachmentType] = strawberry_django.field()
 
     saved_filter: SavedFilterType = strawberry_django.field()
-    saved_filter_list: List[SavedFilterType] = strawberry_django.field()
+    saved_filter_list: list[SavedFilterType] = strawberry_django.field()
 
     table_config: TableConfigType = strawberry_django.field()
-    table_config_list: List[TableConfigType] = strawberry_django.field()
+    table_config_list: list[TableConfigType] = strawberry_django.field()
 
     journal_entry: JournalEntryType = strawberry_django.field()
-    journal_entry_list: List[JournalEntryType] = strawberry_django.field()
+    journal_entry_list: list[JournalEntryType] = strawberry_django.field()
 
     notification: NotificationType = strawberry_django.field()
-    notification_list: List[NotificationType] = strawberry_django.field()
+    notification_list: list[NotificationType] = strawberry_django.field()
 
     notification_group: NotificationGroupType = strawberry_django.field()
-    notification_group_list: List[NotificationGroupType] = strawberry_django.field()
+    notification_group_list: list[NotificationGroupType] = strawberry_django.field()
 
     subscription: SubscriptionType = strawberry_django.field()
-    subscription_list: List[SubscriptionType] = strawberry_django.field()
+    subscription_list: list[SubscriptionType] = strawberry_django.field()
 
     tag: TagType = strawberry_django.field()
-    tag_list: List[TagType] = strawberry_django.field()
+    tag_list: list[TagType] = strawberry_django.field()
 
     webhook: WebhookType = strawberry_django.field()
-    webhook_list: List[WebhookType] = strawberry_django.field()
+    webhook_list: list[WebhookType] = strawberry_django.field()
 
     event_rule: EventRuleType = strawberry_django.field()
-    event_rule_list: List[EventRuleType] = strawberry_django.field()
+    event_rule_list: list[EventRuleType] = strawberry_django.field()

+ 23 - 23
netbox/extras/graphql/types.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -65,19 +65,19 @@ class ConfigContextProfileType(SyncedDataMixin, PrimaryObjectType):
 )
 class ConfigContextType(SyncedDataMixin, OwnerMixin, ObjectType):
     profile: ConfigContextProfileType | None
-    roles: List[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]]
-    device_types: List[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]]
-    tags: List[Annotated["TagType", strawberry.lazy('extras.graphql.types')]]
-    platforms: List[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]
-    regions: List[Annotated["RegionType", strawberry.lazy('dcim.graphql.types')]]
-    cluster_groups: List[Annotated["ClusterGroupType", strawberry.lazy('virtualization.graphql.types')]]
-    tenant_groups: List[Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')]]
-    cluster_types: List[Annotated["ClusterTypeType", strawberry.lazy('virtualization.graphql.types')]]
-    clusters: List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]
-    locations: List[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]]
-    sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
-    tenants: List[Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')]]
-    site_groups: List[Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')]]
+    roles: list[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]]
+    device_types: list[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]]
+    tags: list[Annotated["TagType", strawberry.lazy('extras.graphql.types')]]
+    platforms: list[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]
+    regions: list[Annotated["RegionType", strawberry.lazy('dcim.graphql.types')]]
+    cluster_groups: list[Annotated["ClusterGroupType", strawberry.lazy('virtualization.graphql.types')]]
+    tenant_groups: list[Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')]]
+    cluster_types: list[Annotated["ClusterTypeType", strawberry.lazy('virtualization.graphql.types')]]
+    clusters: list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]
+    locations: list[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]]
+    sites: list[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]
+    tenants: list[Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')]]
+    site_groups: list[Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -87,10 +87,10 @@ class ConfigContextType(SyncedDataMixin, OwnerMixin, ObjectType):
     pagination=True
 )
 class ConfigTemplateType(SyncedDataMixin, OwnerMixin, TagsMixin, ObjectType):
-    virtualmachines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
-    devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
-    platforms: List[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]
-    device_roles: List[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]]
+    virtualmachines: list[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
+    devices: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
+    platforms: list[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]
+    device_roles: list[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -112,8 +112,8 @@ class CustomFieldType(OwnerMixin, ObjectType):
 )
 class CustomFieldChoiceSetType(OwnerMixin, ObjectType):
 
-    choices_for: List[Annotated["CustomFieldType", strawberry.lazy('extras.graphql.types')]]
-    extra_choices: List[List[str]] | None
+    choices_for: list[Annotated["CustomFieldType", strawberry.lazy('extras.graphql.types')]]
+    extra_choices: list[list[str]] | None
 
 
 @strawberry_django.type(
@@ -172,8 +172,8 @@ class NotificationType(ObjectType):
     pagination=True
 )
 class NotificationGroupType(ObjectType):
-    users: List[Annotated["UserType", strawberry.lazy('users.graphql.types')]]
-    groups: List[Annotated["GroupType", strawberry.lazy('users.graphql.types')]]
+    users: list[Annotated["UserType", strawberry.lazy('users.graphql.types')]]
+    groups: list[Annotated["GroupType", strawberry.lazy('users.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -214,7 +214,7 @@ class TableConfigType(ObjectType):
 class TagType(OwnerMixin, ObjectType):
     color: str
 
-    object_types: List[ContentTypeType]
+    object_types: list[ContentTypeType]
 
 
 @strawberry_django.type(

+ 2 - 2
netbox/extras/lookups.py

@@ -82,7 +82,7 @@ class NetHost(Lookup):
         lhs, lhs_params = self.process_lhs(qn, connection)
         rhs, rhs_params = self.process_rhs(qn, connection)
         params = lhs_params + rhs_params
-        return 'HOST(CAST(%s AS INET)) = HOST(%s)' % (lhs, rhs), params
+        return f'HOST(CAST({lhs} AS INET)) = HOST({rhs})', params
 
 
 class NetContainsOrEquals(Lookup):
@@ -95,7 +95,7 @@ class NetContainsOrEquals(Lookup):
         lhs, lhs_params = self.process_lhs(qn, connection)
         rhs, rhs_params = self.process_rhs(qn, connection)
         params = lhs_params + rhs_params
-        return 'CAST(%s AS INET) >>= %s' % (lhs, rhs), params
+        return f'CAST({lhs} AS INET) >>= {rhs}', params
 
 
 ArrayField.register_lookup(RangeContains)

+ 4 - 4
netbox/extras/migrations/0111_rename_content_types.py

@@ -27,15 +27,15 @@ class Migration(migrations.Migration):
                 blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype'
             ),
         ),
-        migrations.RunSQL((
+        migrations.RunSQL(
             'ALTER TABLE IF EXISTS extras_customfield_content_types_id_seq '
             'RENAME TO extras_customfield_object_types_id_seq'
-        )),
+        ),
         # Pre-v2.10 sequence name (see #15605)
-        migrations.RunSQL((
+        migrations.RunSQL(
             'ALTER TABLE IF EXISTS extras_customfield_obj_type_id_seq '
             'RENAME TO extras_customfield_object_types_id_seq'
-        )),
+        ),
         # Custom links
         migrations.RenameField(
             model_name='customlink',

+ 1 - 1
netbox/extras/models/tags.py

@@ -66,7 +66,7 @@ class Tag(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedModel, Tag
         # Allow Unicode in Tag slugs (avoids empty slugs for Tags with all-Unicode names)
         slug = slugify(tag, allow_unicode=True)
         if i is not None:
-            slug += "_%d" % i
+            slug += f'_{i}'
         return slug
 
 

+ 2 - 2
netbox/extras/tests/test_filtersets.py

@@ -1,5 +1,5 @@
 import uuid
-from datetime import datetime, timezone
+from datetime import UTC, datetime
 
 from django.contrib.contenttypes.models import ContentType
 from django.test import TestCase
@@ -862,7 +862,7 @@ class JournalEntryTestCase(TestCase, ChangeLoggedFilterSetTests):
 
     def test_created(self):
         pk_list = self.queryset.values_list('pk', flat=True)[:2]
-        self.queryset.filter(pk__in=pk_list).update(created=datetime(2021, 1, 1, 0, 0, 0, tzinfo=timezone.utc))
+        self.queryset.filter(pk__in=pk_list).update(created=datetime(2021, 1, 1, 0, 0, 0, tzinfo=UTC))
         params = {
             'created_after': '2020-12-31T00:00:00',
             'created_before': '2021-01-02T00:00:00',

+ 2 - 2
netbox/extras/tests/test_scripts.py

@@ -1,4 +1,4 @@
-from datetime import date, datetime, timezone
+from datetime import UTC, date, datetime
 from decimal import Decimal
 
 from django.core.files.uploadedfile import SimpleUploadedFile
@@ -381,7 +381,7 @@ class ScriptVariablesTest(TestCase):
         self.assertIn('var1', form.errors)
 
         # Validate valid data
-        input_datetime = datetime(2024, 4, 1, 8, 0, 0, 0, timezone.utc)
+        input_datetime = datetime(2024, 4, 1, 8, 0, 0, 0, UTC)
         data = {'var1': input_datetime}
         form = TestScript().as_form(data, None)
         self.assertTrue(form.is_valid())

+ 3 - 3
netbox/ipam/graphql/mixins.py

@@ -1,4 +1,4 @@
-from typing import Annotated, List
+from typing import Annotated
 
 import strawberry
 
@@ -10,9 +10,9 @@ __all__ = (
 
 @strawberry.type
 class IPAddressesMixin:
-    ip_addresses: List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]  # noqa: F821
+    ip_addresses: list[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]  # noqa: F821
 
 
 @strawberry.type
 class VLANGroupsMixin:
-    vlan_groups: List[Annotated["VLANGroupType", strawberry.lazy('ipam.graphql.types')]]  # noqa: F821
+    vlan_groups: list[Annotated["VLANGroupType", strawberry.lazy('ipam.graphql.types')]]  # noqa: F821

+ 18 - 20
netbox/ipam/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,55 +7,55 @@ from .types import *
 @strawberry.type(name="Query")
 class IPAMQuery:
     asn: ASNType = strawberry_django.field()
-    asn_list: List[ASNType] = strawberry_django.field()
+    asn_list: list[ASNType] = strawberry_django.field()
 
     asn_range: ASNRangeType = strawberry_django.field()
-    asn_range_list: List[ASNRangeType] = strawberry_django.field()
+    asn_range_list: list[ASNRangeType] = strawberry_django.field()
 
     aggregate: AggregateType = strawberry_django.field()
-    aggregate_list: List[AggregateType] = strawberry_django.field()
+    aggregate_list: list[AggregateType] = strawberry_django.field()
 
     ip_address: IPAddressType = strawberry_django.field()
-    ip_address_list: List[IPAddressType] = strawberry_django.field()
+    ip_address_list: list[IPAddressType] = strawberry_django.field()
 
     ip_range: IPRangeType = strawberry_django.field()
-    ip_range_list: List[IPRangeType] = strawberry_django.field()
+    ip_range_list: list[IPRangeType] = strawberry_django.field()
 
     prefix: PrefixType = strawberry_django.field()
-    prefix_list: List[PrefixType] = strawberry_django.field()
+    prefix_list: list[PrefixType] = strawberry_django.field()
 
     rir: RIRType = strawberry_django.field()
-    rir_list: List[RIRType] = strawberry_django.field()
+    rir_list: list[RIRType] = strawberry_django.field()
 
     role: RoleType = strawberry_django.field()
-    role_list: List[RoleType] = strawberry_django.field()
+    role_list: list[RoleType] = strawberry_django.field()
 
     route_target: RouteTargetType = strawberry_django.field()
-    route_target_list: List[RouteTargetType] = strawberry_django.field()
+    route_target_list: list[RouteTargetType] = strawberry_django.field()
 
     service: ServiceType = strawberry_django.field()
-    service_list: List[ServiceType] = strawberry_django.field()
+    service_list: list[ServiceType] = strawberry_django.field()
 
     service_template: ServiceTemplateType = strawberry_django.field()
-    service_template_list: List[ServiceTemplateType] = strawberry_django.field()
+    service_template_list: list[ServiceTemplateType] = strawberry_django.field()
 
     fhrp_group: FHRPGroupType = strawberry_django.field()
-    fhrp_group_list: List[FHRPGroupType] = strawberry_django.field()
+    fhrp_group_list: list[FHRPGroupType] = strawberry_django.field()
 
     fhrp_group_assignment: FHRPGroupAssignmentType = strawberry_django.field()
-    fhrp_group_assignment_list: List[FHRPGroupAssignmentType] = strawberry_django.field()
+    fhrp_group_assignment_list: list[FHRPGroupAssignmentType] = strawberry_django.field()
 
     vlan: VLANType = strawberry_django.field()
-    vlan_list: List[VLANType] = strawberry_django.field()
+    vlan_list: list[VLANType] = strawberry_django.field()
 
     vlan_group: VLANGroupType = strawberry_django.field()
-    vlan_group_list: List[VLANGroupType] = strawberry_django.field()
+    vlan_group_list: list[VLANGroupType] = strawberry_django.field()
 
     vlan_translation_policy: VLANTranslationPolicyType = strawberry_django.field()
-    vlan_translation_policy_list: List[VLANTranslationPolicyType] = strawberry_django.field()
+    vlan_translation_policy_list: list[VLANTranslationPolicyType] = strawberry_django.field()
 
     vlan_translation_rule: VLANTranslationRuleType = strawberry_django.field()
-    vlan_translation_rule_list: List[VLANTranslationRuleType] = strawberry_django.field()
+    vlan_translation_rule_list: list[VLANTranslationRuleType] = strawberry_django.field()
 
     vrf: VRFType = strawberry_django.field()
-    vrf_list: List[VRFType] = strawberry_django.field()
+    vrf_list: list[VRFType] = strawberry_django.field()

+ 79 - 75
netbox/ipam/graphql/types.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List, Union
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -21,7 +21,6 @@ if TYPE_CHECKING:
         RackType,
         RegionType,
         SiteGroupType,
-        SiteType,
     )
     from tenancy.graphql.types import TenantType
     from virtualization.graphql.types import ClusterGroupType, ClusterType, VirtualMachineType, VMInterfaceType
@@ -80,8 +79,8 @@ class ASNType(ContactsMixin, PrimaryObjectType):
     rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
 
-    sites: List[SiteType]
-    providers: List[ProviderType]
+    sites: list[SiteType]
+    providers: list[ProviderType]
 
 
 @strawberry_django.type(
@@ -116,7 +115,7 @@ class AggregateType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType):
     pagination=True
 )
 class FHRPGroupType(IPAddressesMixin, PrimaryObjectType):
-    fhrpgroupassignment_set: List[Annotated["FHRPGroupAssignmentType", strawberry.lazy('ipam.graphql.types')]]
+    fhrpgroupassignment_set: list[Annotated["FHRPGroupAssignmentType", strawberry.lazy('ipam.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -126,13 +125,14 @@ class FHRPGroupType(IPAddressesMixin, PrimaryObjectType):
     pagination=True
 )
 class FHRPGroupAssignmentType(BaseObjectType):
-    group: Annotated["FHRPGroupType", strawberry.lazy('ipam.graphql.types')]
+    group: Annotated['FHRPGroupType', strawberry.lazy('ipam.graphql.types')]
 
     @strawberry_django.field
-    def interface(self) -> Annotated[Union[
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')],
-    ], strawberry.union("FHRPGroupInterfaceType")]:
+    def interface(self) -> Annotated[
+        Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['VMInterfaceType', strawberry.lazy('virtualization.graphql.types')],
+        strawberry.union('FHRPGroupInterfaceType'),
+    ]:
         return self.interface
 
 
@@ -144,20 +144,21 @@ class FHRPGroupAssignmentType(BaseObjectType):
 )
 class IPAddressType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType):
     address: str
-    vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
-    tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
-    nat_inside: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
+    vrf: Annotated['VRFType', strawberry.lazy('ipam.graphql.types')] | None
+    tenant: Annotated['TenantType', strawberry.lazy('tenancy.graphql.types')] | None
+    nat_inside: Annotated['IPAddressType', strawberry.lazy('ipam.graphql.types')] | None
 
-    nat_outside: List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]
-    tunnel_terminations: List[Annotated["TunnelTerminationType", strawberry.lazy('vpn.graphql.types')]]
-    services: List[Annotated["ServiceType", strawberry.lazy('ipam.graphql.types')]]
+    nat_outside: list[Annotated['IPAddressType', strawberry.lazy('ipam.graphql.types')]]
+    tunnel_terminations: list[Annotated['TunnelTerminationType', strawberry.lazy('vpn.graphql.types')]]
+    services: list[Annotated['ServiceType', strawberry.lazy('ipam.graphql.types')]]
 
     @strawberry_django.field
-    def assigned_object(self) -> Annotated[Union[
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["FHRPGroupType", strawberry.lazy('ipam.graphql.types')],
-        Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')],
-    ], strawberry.union("IPAddressAssignmentType")] | None:
+    def assigned_object(self) -> Annotated[
+        Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['FHRPGroupType', strawberry.lazy('ipam.graphql.types')]
+        | Annotated['VMInterfaceType', strawberry.lazy('virtualization.graphql.types')],
+        strawberry.union('IPAddressAssignmentType'),
+    ] | None:
         return self.assigned_object
 
 
@@ -183,18 +184,19 @@ class IPRangeType(ContactsMixin, PrimaryObjectType):
 )
 class PrefixType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType):
     prefix: str
-    vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
-    tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
-    vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None
-    role: Annotated["RoleType", strawberry.lazy('ipam.graphql.types')] | None
+    vrf: Annotated['VRFType', strawberry.lazy('ipam.graphql.types')] | None
+    tenant: Annotated['TenantType', strawberry.lazy('tenancy.graphql.types')] | None
+    vlan: Annotated['VLANType', strawberry.lazy('ipam.graphql.types')] | None
+    role: Annotated['RoleType', strawberry.lazy('ipam.graphql.types')] | None
 
     @strawberry_django.field
-    def scope(self) -> Annotated[Union[
-        Annotated["LocationType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RegionType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteType", strawberry.lazy('dcim.graphql.types')],
-    ], strawberry.union("PrefixScopeType")] | None:
+    def scope(self) -> Annotated[
+        Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RegionType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteGroupType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteType', strawberry.lazy('dcim.graphql.types')],
+        strawberry.union('PrefixScopeType'),
+    ] | None:
         return self.scope
 
 
@@ -206,9 +208,9 @@ class PrefixType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType):
 )
 class RIRType(OrganizationalObjectType):
 
-    asn_ranges: List[Annotated["ASNRangeType", strawberry.lazy('ipam.graphql.types')]]
-    asns: List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]
-    aggregates: List[Annotated["AggregateType", strawberry.lazy('ipam.graphql.types')]]
+    asn_ranges: list[Annotated["ASNRangeType", strawberry.lazy('ipam.graphql.types')]]
+    asns: list[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]
+    aggregates: list[Annotated["AggregateType", strawberry.lazy('ipam.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -219,9 +221,9 @@ class RIRType(OrganizationalObjectType):
 )
 class RoleType(OrganizationalObjectType):
 
-    prefixes: List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]
-    ip_ranges: List[Annotated["IPRangeType", strawberry.lazy('ipam.graphql.types')]]
-    vlans: List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
+    prefixes: list[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]
+    ip_ranges: list[Annotated["IPRangeType", strawberry.lazy('ipam.graphql.types')]]
+    vlans: list[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -233,10 +235,10 @@ class RoleType(OrganizationalObjectType):
 class RouteTargetType(PrimaryObjectType):
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
 
-    importing_l2vpns: List[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]]
-    exporting_l2vpns: List[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]]
-    importing_vrfs: List[Annotated["VRFType", strawberry.lazy('ipam.graphql.types')]]
-    exporting_vrfs: List[Annotated["VRFType", strawberry.lazy('ipam.graphql.types')]]
+    importing_l2vpns: list[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]]
+    exporting_l2vpns: list[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]]
+    importing_vrfs: list[Annotated["VRFType", strawberry.lazy('ipam.graphql.types')]]
+    exporting_vrfs: list[Annotated["VRFType", strawberry.lazy('ipam.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -246,15 +248,16 @@ class RouteTargetType(PrimaryObjectType):
     pagination=True
 )
 class ServiceType(ContactsMixin, PrimaryObjectType):
-    ports: List[int]
-    ipaddresses: List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]
+    ports: list[int]
+    ipaddresses: list[Annotated['IPAddressType', strawberry.lazy('ipam.graphql.types')]]
 
     @strawberry_django.field
-    def parent(self) -> Annotated[Union[
-        Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')],
-        Annotated["FHRPGroupType", strawberry.lazy('ipam.graphql.types')],
-    ], strawberry.union("ServiceParentType")] | None:
+    def parent(self) -> Annotated[
+        Annotated['DeviceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['VirtualMachineType', strawberry.lazy('virtualization.graphql.types')]
+        | Annotated['FHRPGroupType', strawberry.lazy('ipam.graphql.types')],
+        strawberry.union('ServiceParentType'),
+    ] | None:
         return self.parent
 
 
@@ -265,7 +268,7 @@ class ServiceType(ContactsMixin, PrimaryObjectType):
     pagination=True
 )
 class ServiceTemplateType(PrimaryObjectType):
-    ports: List[int]
+    ports: list[int]
 
 
 @strawberry_django.type(
@@ -280,12 +283,12 @@ class VLANType(PrimaryObjectType):
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
     role: Annotated["RoleType", strawberry.lazy('ipam.graphql.types')] | None
 
-    interfaces_as_untagged: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
-    vminterfaces_as_untagged: List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
-    wirelesslan_set: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]
-    prefixes: List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]
-    interfaces_as_tagged: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
-    vminterfaces_as_tagged: List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
+    interfaces_as_untagged: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    vminterfaces_as_untagged: list[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
+    wirelesslan_set: list[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]
+    prefixes: list[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]
+    interfaces_as_tagged: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    vminterfaces_as_tagged: list[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
 
     @strawberry_django.field
     def qinq_svlan(self) -> Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None:
@@ -300,20 +303,21 @@ class VLANType(PrimaryObjectType):
 )
 class VLANGroupType(OrganizationalObjectType):
 
-    vlans: List[VLANType]
-    vid_ranges: List[str]
-    tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
+    vlans: list[VLANType]
+    vid_ranges: list[str]
+    tenant: Annotated['TenantType', strawberry.lazy('tenancy.graphql.types')] | None
 
     @strawberry_django.field
-    def scope(self) -> Annotated[Union[
-        Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')],
-        Annotated["ClusterGroupType", strawberry.lazy('virtualization.graphql.types')],
-        Annotated["LocationType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RackType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RegionType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')],
-    ], strawberry.union("VLANGroupScopeType")] | None:
+    def scope(self) -> Annotated[
+        Annotated['ClusterType', strawberry.lazy('virtualization.graphql.types')]
+        | Annotated['ClusterGroupType', strawberry.lazy('virtualization.graphql.types')]
+        | Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RackType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RegionType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteGroupType', strawberry.lazy('dcim.graphql.types')],
+        strawberry.union('VLANGroupScopeType'),
+    ] | None:
         return self.scope
 
 
@@ -324,7 +328,7 @@ class VLANGroupType(OrganizationalObjectType):
     pagination=True
 )
 class VLANTranslationPolicyType(PrimaryObjectType):
-    rules: List[Annotated["VLANTranslationRuleType", strawberry.lazy('ipam.graphql.types')]]
+    rules: list[Annotated["VLANTranslationRuleType", strawberry.lazy('ipam.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -349,10 +353,10 @@ class VLANTranslationRuleType(NetBoxObjectType):
 class VRFType(PrimaryObjectType):
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
 
-    interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
-    ip_addresses: List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]
-    vminterfaces: List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
-    ip_ranges: List[Annotated["IPRangeType", strawberry.lazy('ipam.graphql.types')]]
-    export_targets: List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]
-    import_targets: List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]
-    prefixes: List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]
+    interfaces: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    ip_addresses: list[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]
+    vminterfaces: list[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
+    ip_ranges: list[Annotated["IPRangeType", strawberry.lazy('ipam.graphql.types')]]
+    export_targets: list[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]
+    import_targets: list[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]
+    prefixes: list[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]

+ 11 - 11
netbox/ipam/lookups.py

@@ -1,19 +1,19 @@
 from django.db.models import IntegerField, Lookup, Transform, lookups
 
 
-class NetFieldDecoratorMixin(object):
+class NetFieldDecoratorMixin:
 
     def process_lhs(self, qn, connection, lhs=None):
         lhs = lhs or self.lhs
         lhs_string, lhs_params = qn.compile(lhs)
-        lhs_string = 'TEXT(%s)' % lhs_string
+        lhs_string = f'TEXT({lhs_string})'
         return lhs_string, lhs_params
 
 
 class IExact(NetFieldDecoratorMixin, lookups.IExact):
 
     def get_rhs_op(self, connection, rhs):
-        return '= LOWER(%s)' % rhs
+        return f'= LOWER({rhs})'
 
 
 class EndsWith(NetFieldDecoratorMixin, lookups.EndsWith):
@@ -24,7 +24,7 @@ class IEndsWith(NetFieldDecoratorMixin, lookups.IEndsWith):
     pass
 
     def get_rhs_op(self, connection, rhs):
-        return 'LIKE LOWER(%s)' % rhs
+        return f'LIKE LOWER({rhs})'
 
 
 class StartsWith(NetFieldDecoratorMixin, lookups.StartsWith):
@@ -35,7 +35,7 @@ class IStartsWith(NetFieldDecoratorMixin, lookups.IStartsWith):
     pass
 
     def get_rhs_op(self, connection, rhs):
-        return 'LIKE LOWER(%s)' % rhs
+        return f'LIKE LOWER({rhs})'
 
 
 class Regex(NetFieldDecoratorMixin, lookups.Regex):
@@ -53,7 +53,7 @@ class NetContainsOrEquals(Lookup):
         lhs, lhs_params = self.process_lhs(qn, connection)
         rhs, rhs_params = self.process_rhs(qn, connection)
         params = lhs_params + rhs_params
-        return '%s >>= %s' % (lhs, rhs), params
+        return f'{lhs} >>= {rhs}', params
 
 
 class NetContains(Lookup):
@@ -63,7 +63,7 @@ class NetContains(Lookup):
         lhs, lhs_params = self.process_lhs(qn, connection)
         rhs, rhs_params = self.process_rhs(qn, connection)
         params = lhs_params + rhs_params
-        return '%s >> %s' % (lhs, rhs), params
+        return f'{lhs} >> {rhs}', params
 
 
 class NetContained(Lookup):
@@ -73,7 +73,7 @@ class NetContained(Lookup):
         lhs, lhs_params = self.process_lhs(qn, connection)
         rhs, rhs_params = self.process_rhs(qn, connection)
         params = lhs_params + rhs_params
-        return '%s << %s' % (lhs, rhs), params
+        return f'{lhs} << {rhs}', params
 
 
 class NetContainedOrEqual(Lookup):
@@ -83,7 +83,7 @@ class NetContainedOrEqual(Lookup):
         lhs, lhs_params = self.process_lhs(qn, connection)
         rhs, rhs_params = self.process_rhs(qn, connection)
         params = lhs_params + rhs_params
-        return '%s <<= %s' % (lhs, rhs), params
+        return f'{lhs} <<= {rhs}', params
 
 
 class NetHost(Lookup):
@@ -97,7 +97,7 @@ class NetHost(Lookup):
         if rhs_params:
             rhs_params[0] = rhs_params[0].split('/')[0]
         params = lhs_params + rhs_params
-        return 'HOST(%s) = %s' % (lhs, rhs), params
+        return f'HOST({lhs}) = {rhs}', params
 
 
 class NetIn(Lookup):
@@ -151,7 +151,7 @@ class NetHostContained(Lookup):
         lhs, lhs_params = self.process_lhs(qn, connection)
         rhs, rhs_params = self.process_rhs(qn, connection)
         params = lhs_params + rhs_params
-        return 'CAST(HOST(%s) AS INET) <<= %s' % (lhs, rhs), params
+        return f'CAST(HOST({lhs}) AS INET) <<= {rhs}', params
 
 
 class NetFamily(Transform):

+ 7 - 7
netbox/netbox/graphql/filter_lookups.py

@@ -1,5 +1,5 @@
 from enum import Enum
-from typing import Generic, Tuple, TypeVar
+from typing import Generic, TypeVar
 
 import strawberry
 import strawberry_django
@@ -72,7 +72,7 @@ class IntegerLookup:
         return None
 
     @strawberry_django.filter_field
-    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]:
+    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> tuple[QuerySet, Q]:
         filters = self.get_filter()
 
         if not filters:
@@ -95,7 +95,7 @@ class BigIntegerLookup:
         return None
 
     @strawberry_django.filter_field
-    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]:
+    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> tuple[QuerySet, Q]:
         filters = self.get_filter()
 
         if not filters:
@@ -118,7 +118,7 @@ class FloatLookup:
         return None
 
     @strawberry_django.filter_field
-    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]:
+    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> tuple[QuerySet, Q]:
         filters = self.get_filter()
 
         if not filters:
@@ -137,7 +137,7 @@ class JSONFilter:
     lookup: JSONLookup
 
     @strawberry_django.filter_field
-    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]:
+    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> tuple[QuerySet, Q]:
         filters = self.lookup.get_filter()
 
         if not filters:
@@ -164,7 +164,7 @@ class TreeNodeFilter:
     match_type: TreeNodeMatch
 
     @strawberry_django.filter_field
-    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> Tuple[QuerySet, Q]:
+    def filter(self, info: Info, queryset: QuerySet, prefix: DirectiveValue[str] = '') -> tuple[QuerySet, Q]:
         model_field_name = prefix.removesuffix('__').removesuffix('_id')
         model_field = None
         try:
@@ -257,7 +257,7 @@ class RangeArrayValueLookup(Generic[T]):
     )
 
     @strawberry_django.filter_field
-    def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]:
+    def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> tuple[QuerySet, Q]:
         """
         Map GraphQL: { <field>: { contains: <T> } } To Django ORM: <field>__range_contains=<T>
         """

+ 3 - 3
netbox/netbox/models/deletion.py

@@ -70,9 +70,9 @@ class DeleteMixin:
         Override delete to use our custom collector.
         """
         using = using or router.db_for_write(self.__class__, instance=self)
-        assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (
-            self._meta.object_name,
-            self._meta.pk.attname,
+        assert self._get_pk_val() is not None, (
+            f"{self._meta.object_name} object can't be deleted because its "
+            f"{self._meta.pk.attname} attribute is set to None."
         )
 
         collector = CustomCollector(using=using)

+ 9 - 9
netbox/netbox/navigation/__init__.py

@@ -1,5 +1,5 @@
+from collections.abc import Sequence
 from dataclasses import dataclass
-from typing import Optional, Sequence
 
 from django.urls import reverse_lazy
 
@@ -23,9 +23,9 @@ class MenuItemButton:
     link: str
     title: str
     icon_class: str
-    _url: Optional[str] = None
-    permissions: Optional[Sequence[str]] = ()
-    color: Optional[str] = None
+    _url: str | None = None
+    permissions: Sequence[str] | None = ()
+    color: str | None = None
 
     def __post_init__(self):
         if self.link:
@@ -45,11 +45,11 @@ class MenuItem:
 
     link: str
     link_text: str
-    _url: Optional[str] = None
-    permissions: Optional[Sequence[str]] = ()
-    auth_required: Optional[bool] = False
-    staff_only: Optional[bool] = False
-    buttons: Optional[Sequence[MenuItemButton]] = ()
+    _url: str | None = None
+    permissions: Sequence[str] | None = ()
+    auth_required: bool | None = False
+    staff_only: bool | None = False
+    buttons: Sequence[MenuItemButton] | None = ()
 
     def __post_init__(self):
         if self.link:

+ 2 - 2
netbox/netbox/settings.py

@@ -33,8 +33,8 @@ VERSION = RELEASE.full_version  # Retained for backward compatibility
 # Set the base directory two levels up
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
-# Validate Python version
-if sys.version_info < (3, 12):
+# Validate the Python version
+if sys.version_info < (3, 12):  # noqa: UP036
     raise RuntimeError(
         f"NetBox requires Python 3.12 or later. (Currently installed: Python {platform.python_version()})"
     )

+ 2 - 3
netbox/netbox/tables/columns.py

@@ -1,6 +1,5 @@
 import zoneinfo
 from dataclasses import dataclass
-from typing import Optional
 from urllib.parse import quote
 
 import django_tables2 as tables
@@ -229,8 +228,8 @@ class BooleanColumn(tables.Column):
 class ActionsItem:
     title: str
     icon: str
-    permission: Optional[str] = None
-    css_class: Optional[str] = 'secondary'
+    permission: str | None = None
+    css_class: str | None = 'secondary'
 
 
 class ActionsColumn(tables.Column):

+ 1 - 2
netbox/netbox/tests/dummy_plugin/graphql.py

@@ -1,4 +1,3 @@
-from typing import List
 
 import strawberry
 import strawberry_django
@@ -17,7 +16,7 @@ class DummyModelType:
 @strawberry.type(name="Query")
 class DummyQuery:
     dummymodel: DummyModelType = strawberry_django.field()
-    dummymodel_list: List[DummyModelType] = strawberry_django.field()
+    dummymodel_list: list[DummyModelType] = strawberry_django.field()
 
 
 schema = [

+ 2 - 2
netbox/netbox/tests/test_authentication.py

@@ -97,7 +97,7 @@ class TokenAuthenticationTestCase(APITestCase):
         url = reverse('dcim-api:site-list')
 
         # Create v1 & v2 tokens
-        future = datetime.datetime(2100, 1, 1, tzinfo=datetime.timezone.utc)
+        future = datetime.datetime(2100, 1, 1, tzinfo=datetime.UTC)
         token1 = Token.objects.create(version=1, user=self.user, expires=future)
         token2 = Token.objects.create(version=2, user=self.user, expires=future)
 
@@ -108,7 +108,7 @@ class TokenAuthenticationTestCase(APITestCase):
         self.assertEqual(response.status_code, 200)
 
         # Request with an expired token should fail
-        past = datetime.datetime(2020, 1, 1, tzinfo=datetime.timezone.utc)
+        past = datetime.datetime(2020, 1, 1, tzinfo=datetime.UTC)
         token1.expires = past
         token1.save()
         token2.expires = past

+ 2 - 2
netbox/tenancy/graphql/mixins.py

@@ -1,4 +1,4 @@
-from typing import Annotated, List
+from typing import Annotated
 
 import strawberry
 
@@ -9,4 +9,4 @@ __all__ = (
 
 @strawberry.type
 class ContactAssignmentsMixin:
-    assignments: List[Annotated["ContactAssignmentType", strawberry.lazy('tenancy.graphql.types')]]  # noqa: F821
+    assignments: list[Annotated["ContactAssignmentType", strawberry.lazy('tenancy.graphql.types')]]  # noqa: F821

+ 6 - 8
netbox/tenancy/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,19 +7,19 @@ from .types import *
 @strawberry.type(name="Query")
 class TenancyQuery:
     tenant: TenantType = strawberry_django.field()
-    tenant_list: List[TenantType] = strawberry_django.field()
+    tenant_list: list[TenantType] = strawberry_django.field()
 
     tenant_group: TenantGroupType = strawberry_django.field()
-    tenant_group_list: List[TenantGroupType] = strawberry_django.field()
+    tenant_group_list: list[TenantGroupType] = strawberry_django.field()
 
     contact: ContactType = strawberry_django.field()
-    contact_list: List[ContactType] = strawberry_django.field()
+    contact_list: list[ContactType] = strawberry_django.field()
 
     contact_role: ContactRoleType = strawberry_django.field()
-    contact_role_list: List[ContactRoleType] = strawberry_django.field()
+    contact_role_list: list[ContactRoleType] = strawberry_django.field()
 
     contact_group: ContactGroupType = strawberry_django.field()
-    contact_group_list: List[ContactGroupType] = strawberry_django.field()
+    contact_group_list: list[ContactGroupType] = strawberry_django.field()
 
     contact_assignment: ContactAssignmentType = strawberry_django.field()
-    contact_assignment_list: List[ContactAssignmentType] = strawberry_django.field()
+    contact_assignment_list: list[ContactAssignmentType] = strawberry_django.field()

+ 30 - 30
netbox/tenancy/graphql/types.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -60,30 +60,30 @@ __all__ = (
 )
 class TenantType(ContactsMixin, PrimaryObjectType):
     group: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None
-    asns: List[Annotated['ASNType', strawberry.lazy('ipam.graphql.types')]]
-    circuits: List[Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]]
-    sites: List[Annotated['SiteType', strawberry.lazy('dcim.graphql.types')]]
-    vlans: List[Annotated['VLANType', strawberry.lazy('ipam.graphql.types')]]
-    wireless_lans: List[Annotated['WirelessLANType', strawberry.lazy('wireless.graphql.types')]]
-    route_targets: List[Annotated['RouteTargetType', strawberry.lazy('ipam.graphql.types')]]
-    locations: List[Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]]
-    ip_ranges: List[Annotated['IPRangeType', strawberry.lazy('ipam.graphql.types')]]
-    rackreservations: List[Annotated['RackReservationType', strawberry.lazy('dcim.graphql.types')]]
-    racks: List[Annotated['RackType', strawberry.lazy('dcim.graphql.types')]]
-    vdcs: List[Annotated['VirtualDeviceContextType', strawberry.lazy('dcim.graphql.types')]]
-    prefixes: List[Annotated['PrefixType', strawberry.lazy('ipam.graphql.types')]]
-    cables: List[Annotated['CableType', strawberry.lazy('dcim.graphql.types')]]
-    virtual_machines: List[Annotated['VirtualMachineType', strawberry.lazy('virtualization.graphql.types')]]
-    vrfs: List[Annotated['VRFType', strawberry.lazy('ipam.graphql.types')]]
-    asn_ranges: List[Annotated['ASNRangeType', strawberry.lazy('ipam.graphql.types')]]
-    wireless_links: List[Annotated['WirelessLinkType', strawberry.lazy('wireless.graphql.types')]]
-    aggregates: List[Annotated['AggregateType', strawberry.lazy('ipam.graphql.types')]]
-    power_feeds: List[Annotated['PowerFeedType', strawberry.lazy('dcim.graphql.types')]]
-    devices: List[Annotated['DeviceType', strawberry.lazy('dcim.graphql.types')]]
-    tunnels: List[Annotated['TunnelType', strawberry.lazy('vpn.graphql.types')]]
-    ip_addresses: List[Annotated['IPAddressType', strawberry.lazy('ipam.graphql.types')]]
-    clusters: List[Annotated['ClusterType', strawberry.lazy('virtualization.graphql.types')]]
-    l2vpns: List[Annotated['L2VPNType', strawberry.lazy('vpn.graphql.types')]]
+    asns: list[Annotated['ASNType', strawberry.lazy('ipam.graphql.types')]]
+    circuits: list[Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]]
+    sites: list[Annotated['SiteType', strawberry.lazy('dcim.graphql.types')]]
+    vlans: list[Annotated['VLANType', strawberry.lazy('ipam.graphql.types')]]
+    wireless_lans: list[Annotated['WirelessLANType', strawberry.lazy('wireless.graphql.types')]]
+    route_targets: list[Annotated['RouteTargetType', strawberry.lazy('ipam.graphql.types')]]
+    locations: list[Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]]
+    ip_ranges: list[Annotated['IPRangeType', strawberry.lazy('ipam.graphql.types')]]
+    rackreservations: list[Annotated['RackReservationType', strawberry.lazy('dcim.graphql.types')]]
+    racks: list[Annotated['RackType', strawberry.lazy('dcim.graphql.types')]]
+    vdcs: list[Annotated['VirtualDeviceContextType', strawberry.lazy('dcim.graphql.types')]]
+    prefixes: list[Annotated['PrefixType', strawberry.lazy('ipam.graphql.types')]]
+    cables: list[Annotated['CableType', strawberry.lazy('dcim.graphql.types')]]
+    virtual_machines: list[Annotated['VirtualMachineType', strawberry.lazy('virtualization.graphql.types')]]
+    vrfs: list[Annotated['VRFType', strawberry.lazy('ipam.graphql.types')]]
+    asn_ranges: list[Annotated['ASNRangeType', strawberry.lazy('ipam.graphql.types')]]
+    wireless_links: list[Annotated['WirelessLinkType', strawberry.lazy('wireless.graphql.types')]]
+    aggregates: list[Annotated['AggregateType', strawberry.lazy('ipam.graphql.types')]]
+    power_feeds: list[Annotated['PowerFeedType', strawberry.lazy('dcim.graphql.types')]]
+    devices: list[Annotated['DeviceType', strawberry.lazy('dcim.graphql.types')]]
+    tunnels: list[Annotated['TunnelType', strawberry.lazy('vpn.graphql.types')]]
+    ip_addresses: list[Annotated['IPAddressType', strawberry.lazy('ipam.graphql.types')]]
+    clusters: list[Annotated['ClusterType', strawberry.lazy('virtualization.graphql.types')]]
+    l2vpns: list[Annotated['L2VPNType', strawberry.lazy('vpn.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -95,8 +95,8 @@ class TenantType(ContactsMixin, PrimaryObjectType):
 class TenantGroupType(NestedGroupObjectType):
     parent: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None
 
-    tenants: List[TenantType]
-    children: List[Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')]]
+    tenants: list[TenantType]
+    children: list[Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')]]
 
 
 #
@@ -110,7 +110,7 @@ class TenantGroupType(NestedGroupObjectType):
     pagination=True
 )
 class ContactType(ContactAssignmentsMixin, PrimaryObjectType):
-    groups: List[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]]
+    groups: list[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -132,8 +132,8 @@ class ContactRoleType(ContactAssignmentsMixin, OrganizationalObjectType):
 class ContactGroupType(NestedGroupObjectType):
     parent: Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')] | None
 
-    contacts: List[ContactType]
-    children: List[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]]
+    contacts: list[ContactType]
+    children: list[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]]
 
 
 @strawberry_django.type(

+ 4 - 6
netbox/users/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,13 +7,13 @@ from .types import *
 @strawberry.type(name="Query")
 class UsersQuery:
     group: GroupType = strawberry_django.field()
-    group_list: List[GroupType] = strawberry_django.field()
+    group_list: list[GroupType] = strawberry_django.field()
 
     user: UserType = strawberry_django.field()
-    user_list: List[UserType] = strawberry_django.field()
+    user_list: list[UserType] = strawberry_django.field()
 
     owner_group: OwnerGroupType = strawberry_django.field()
-    owner_group_list: List[OwnerGroupType] = strawberry_django.field()
+    owner_group_list: list[OwnerGroupType] = strawberry_django.field()
 
     owner: OwnerType = strawberry_django.field()
-    owner_list: List[OwnerType] = strawberry_django.field()
+    owner_list: list[OwnerType] = strawberry_django.field()

+ 1 - 2
netbox/users/graphql/types.py

@@ -1,4 +1,3 @@
-from typing import List
 
 import strawberry_django
 
@@ -34,7 +33,7 @@ class GroupType(BaseObjectType):
     pagination=True
 )
 class UserType(BaseObjectType):
-    groups: List[GroupType]
+    groups: list[GroupType]
 
 
 @strawberry_django.type(

+ 1 - 1
netbox/users/models/users.py

@@ -189,7 +189,7 @@ class User(AbstractBaseUser, PermissionsMixin):
         """
         Return the first_name plus the last_name, with a space in between.
         """
-        full_name = "%s %s" % (self.first_name, self.last_name)
+        full_name = f"{self.first_name} {self.last_name}"
         return full_name.strip()
 
     def get_short_name(self):

+ 5 - 6
netbox/utilities/forms/widgets/apiselect.py

@@ -1,5 +1,4 @@
 import json
-from typing import Dict, List, Tuple
 
 from django import forms
 from django.conf import settings
@@ -19,8 +18,8 @@ class APISelect(forms.Select):
     """
     template_name = 'widgets/apiselect.html'
     option_template_name = 'widgets/select_option.html'
-    dynamic_params: Dict[str, str]
-    static_params: Dict[str, List[str]]
+    dynamic_params: dict[str, str]
+    static_params: dict[str, list[str]]
 
     def get_context(self, name, value, attrs):
         context = super().get_context(name, value, attrs)
@@ -35,8 +34,8 @@ class APISelect(forms.Select):
         super().__init__(*args, **kwargs)
 
         self.attrs['class'] = 'api-select'
-        self.dynamic_params: Dict[str, List[str]] = {}
-        self.static_params: Dict[str, List[str]] = {}
+        self.dynamic_params: dict[str, list[str]] = {}
+        self.static_params: dict[str, list[str]] = {}
 
         if api_url:
             self.attrs['data-url'] = '/{}{}'.format(settings.BASE_PATH, api_url.lstrip('/'))  # Inject BASE_PATH
@@ -96,7 +95,7 @@ class APISelect(forms.Select):
         Process an entire query_params dictionary, and handle primitive or list values.
         """
         for key, value in query_params.items():
-            if isinstance(value, (List, Tuple)):
+            if isinstance(value, (list, tuple)):
                 # If value is a list/tuple, iterate through each item.
                 for item in value:
                     self._process_query_param(key, item)

+ 5 - 6
netbox/utilities/release.py

@@ -1,7 +1,6 @@
 import datetime
 import os
 from dataclasses import asdict, dataclass, field
-from typing import Union
 
 import yaml
 from django.core.exceptions import ImproperlyConfigured
@@ -28,9 +27,9 @@ class FeatureSet:
 class ReleaseInfo:
     version: str
     edition: str
-    published: Union[datetime.date, None] = None
-    designation: Union[str, None] = None
-    build: Union[str, None] = None
+    published: datetime.date | None = None
+    designation: str | None = None
+    build: str | None = None
     features: FeatureSet = field(default_factory=FeatureSet)
 
     @property
@@ -57,12 +56,12 @@ def load_release_data():
     base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
     # Load canonical release attributes
-    with open(os.path.join(base_path, RELEASE_PATH), 'r') as release_file:
+    with open(os.path.join(base_path, RELEASE_PATH)) as release_file:
         data = yaml.safe_load(release_file)
 
     # Overlay any local release date (if defined)
     try:
-        with open(os.path.join(base_path, LOCAL_RELEASE_PATH), 'r') as release_file:
+        with open(os.path.join(base_path, LOCAL_RELEASE_PATH)) as release_file:
             local_data = yaml.safe_load(release_file)
     except FileNotFoundError:
         local_data = {}

+ 2 - 2
netbox/utilities/templatetags/helpers.py

@@ -1,5 +1,5 @@
 import json
-from typing import Any, Dict
+from typing import Any
 from urllib.parse import quote
 
 from django import template
@@ -306,7 +306,7 @@ def startswith(text: str, starts: str) -> bool:
 
 
 @register.filter
-def get_key(value: Dict, arg: str) -> Any:
+def get_key(value: dict, arg: str) -> Any:
     """
     Template implementation of `dict.get()`, for accessing dict values
     by key when the key is not able to be used in a template. For

+ 3 - 3
netbox/utilities/testing/filtersets.py

@@ -1,4 +1,4 @@
-from datetime import datetime, timezone
+from datetime import UTC, datetime
 from itertools import chain
 
 import django_filters
@@ -156,12 +156,12 @@ class ChangeLoggedFilterSetTests(BaseFilterSetTests):
 
     def test_created(self):
         pk_list = self.queryset.values_list('pk', flat=True)[:2]
-        self.queryset.filter(pk__in=pk_list).update(created=datetime(2021, 1, 1, 0, 0, 0, tzinfo=timezone.utc))
+        self.queryset.filter(pk__in=pk_list).update(created=datetime(2021, 1, 1, 0, 0, 0, tzinfo=UTC))
         params = {'created': ['2021-01-01T00:00:00']}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_last_updated(self):
         pk_list = self.queryset.values_list('pk', flat=True)[:2]
-        self.queryset.filter(pk__in=pk_list).update(last_updated=datetime(2021, 1, 2, 0, 0, 0, tzinfo=timezone.utc))
+        self.queryset.filter(pk__in=pk_list).update(last_updated=datetime(2021, 1, 2, 0, 0, 0, tzinfo=UTC))
         params = {'last_updated': ['2021-01-02T00:00:00']}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)

+ 1 - 1
netbox/utilities/views.py

@@ -1,5 +1,5 @@
+from collections.abc import Iterable
 from dataclasses import dataclass
-from typing import Iterable
 
 from django.conf import settings
 from django.contrib.auth.mixins import AccessMixin

+ 6 - 8
netbox/virtualization/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,19 +7,19 @@ from .types import *
 @strawberry.type(name="Query")
 class VirtualizationQuery:
     cluster: ClusterType = strawberry_django.field()
-    cluster_list: List[ClusterType] = strawberry_django.field()
+    cluster_list: list[ClusterType] = strawberry_django.field()
 
     cluster_group: ClusterGroupType = strawberry_django.field()
-    cluster_group_list: List[ClusterGroupType] = strawberry_django.field()
+    cluster_group_list: list[ClusterGroupType] = strawberry_django.field()
 
     cluster_type: ClusterTypeType = strawberry_django.field()
-    cluster_type_list: List[ClusterTypeType] = strawberry_django.field()
+    cluster_type_list: list[ClusterTypeType] = strawberry_django.field()
 
     virtual_machine: VirtualMachineType = strawberry_django.field()
-    virtual_machine_list: List[VirtualMachineType] = strawberry_django.field()
+    virtual_machine_list: list[VirtualMachineType] = strawberry_django.field()
 
     vm_interface: VMInterfaceType = strawberry_django.field()
-    vm_interface_list: List[VMInterfaceType] = strawberry_django.field()
+    vm_interface_list: list[VMInterfaceType] = strawberry_django.field()
 
     virtual_disk: VirtualDiskType = strawberry_django.field()
-    virtual_disk_list: List[VirtualDiskType] = strawberry_django.field()
+    virtual_disk_list: list[VirtualDiskType] = strawberry_django.field()

+ 19 - 18
netbox/virtualization/graphql/types.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List, Union
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -55,16 +55,17 @@ class ClusterType(ContactsMixin, VLANGroupsMixin, PrimaryObjectType):
     type: Annotated["ClusterTypeType", strawberry.lazy('virtualization.graphql.types')] | None
     group: Annotated["ClusterGroupType", strawberry.lazy('virtualization.graphql.types')] | None
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
-    virtual_machines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
-    devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
+    virtual_machines: list[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]
+    devices: list[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]
 
     @strawberry_django.field
-    def scope(self) -> Annotated[Union[
-        Annotated["LocationType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RegionType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteType", strawberry.lazy('dcim.graphql.types')],
-    ], strawberry.union("ClusterScopeType")] | None:
+    def scope(self) -> Annotated[
+        Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RegionType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteGroupType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteType', strawberry.lazy('dcim.graphql.types')],
+        strawberry.union('ClusterScopeType'),
+    ] | None:
         return self.scope
 
 
@@ -76,7 +77,7 @@ class ClusterType(ContactsMixin, VLANGroupsMixin, PrimaryObjectType):
 )
 class ClusterGroupType(ContactsMixin, VLANGroupsMixin, OrganizationalObjectType):
 
-    clusters: List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]
+    clusters: list[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -87,7 +88,7 @@ class ClusterGroupType(ContactsMixin, VLANGroupsMixin, OrganizationalObjectType)
 )
 class ClusterTypeType(OrganizationalObjectType):
 
-    clusters: List[ClusterType]
+    clusters: list[ClusterType]
 
 
 @strawberry_django.type(
@@ -110,9 +111,9 @@ class VirtualMachineType(ConfigContextMixin, ContactsMixin, PrimaryObjectType):
     primary_ip4: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
     primary_ip6: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
 
-    interfaces: List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
-    services: List[Annotated["ServiceType", strawberry.lazy('ipam.graphql.types')]]
-    virtualdisks: List[Annotated["VirtualDiskType", strawberry.lazy('virtualization.graphql.types')]]
+    interfaces: list[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
+    services: list[Annotated["ServiceType", strawberry.lazy('ipam.graphql.types')]]
+    virtualdisks: list[Annotated["VirtualDiskType", strawberry.lazy('virtualization.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -132,10 +133,10 @@ class VMInterfaceType(IPAddressesMixin, ComponentType):
     qinq_svlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None
     vlan_translation_policy: Annotated["VLANTranslationPolicyType", strawberry.lazy('ipam.graphql.types')] | None
 
-    tagged_vlans: List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
-    bridge_interfaces: List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
-    child_interfaces: List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
-    mac_addresses: List[Annotated["MACAddressType", strawberry.lazy('dcim.graphql.types')]]
+    tagged_vlans: list[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]
+    bridge_interfaces: list[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
+    child_interfaces: list[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]
+    mac_addresses: list[Annotated["MACAddressType", strawberry.lazy('dcim.graphql.types')]]
 
 
 @strawberry_django.type(

+ 10 - 12
netbox/vpn/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,31 +7,31 @@ from .types import *
 @strawberry.type(name="Query")
 class VPNQuery:
     ike_policy: IKEPolicyType = strawberry_django.field()
-    ike_policy_list: List[IKEPolicyType] = strawberry_django.field()
+    ike_policy_list: list[IKEPolicyType] = strawberry_django.field()
 
     ike_proposal: IKEProposalType = strawberry_django.field()
-    ike_proposal_list: List[IKEProposalType] = strawberry_django.field()
+    ike_proposal_list: list[IKEProposalType] = strawberry_django.field()
 
     ipsec_policy: IPSecPolicyType = strawberry_django.field()
-    ipsec_policy_list: List[IPSecPolicyType] = strawberry_django.field()
+    ipsec_policy_list: list[IPSecPolicyType] = strawberry_django.field()
 
     ipsec_profile: IPSecProfileType = strawberry_django.field()
-    ipsec_profile_list: List[IPSecProfileType] = strawberry_django.field()
+    ipsec_profile_list: list[IPSecProfileType] = strawberry_django.field()
 
     ipsec_proposal: IPSecProposalType = strawberry_django.field()
-    ipsec_proposal_list: List[IPSecProposalType] = strawberry_django.field()
+    ipsec_proposal_list: list[IPSecProposalType] = strawberry_django.field()
 
     l2vpn: L2VPNType = strawberry_django.field()
-    l2vpn_list: List[L2VPNType] = strawberry_django.field()
+    l2vpn_list: list[L2VPNType] = strawberry_django.field()
 
     l2vpn_termination: L2VPNTerminationType = strawberry_django.field()
-    l2vpn_termination_list: List[L2VPNTerminationType] = strawberry_django.field()
+    l2vpn_termination_list: list[L2VPNTerminationType] = strawberry_django.field()
 
     tunnel: TunnelType = strawberry_django.field()
-    tunnel_list: List[TunnelType] = strawberry_django.field()
+    tunnel_list: list[TunnelType] = strawberry_django.field()
 
     tunnel_group: TunnelGroupType = strawberry_django.field()
-    tunnel_group_list: List[TunnelGroupType] = strawberry_django.field()
+    tunnel_group_list: list[TunnelGroupType] = strawberry_django.field()
 
     tunnel_termination: TunnelTerminationType = strawberry_django.field()
-    tunnel_termination_list: List[TunnelTerminationType] = strawberry_django.field()
+    tunnel_termination_list: list[TunnelTerminationType] = strawberry_django.field()

+ 19 - 18
netbox/vpn/graphql/types.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List, Union
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -38,7 +38,7 @@ __all__ = (
 )
 class TunnelGroupType(ContactsMixin, OrganizationalObjectType):
 
-    tunnels: List[Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]]
+    tunnels: list[Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -64,7 +64,7 @@ class TunnelType(ContactsMixin, PrimaryObjectType):
     ipsec_profile: Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')] | None
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
 
-    terminations: List[Annotated["TunnelTerminationType", strawberry.lazy('vpn.graphql.types')]]
+    terminations: list[Annotated["TunnelTerminationType", strawberry.lazy('vpn.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -74,7 +74,7 @@ class TunnelType(ContactsMixin, PrimaryObjectType):
     pagination=True
 )
 class IKEProposalType(PrimaryObjectType):
-    ike_policies: List[Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')]]
+    ike_policies: list[Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -84,8 +84,8 @@ class IKEProposalType(PrimaryObjectType):
     pagination=True
 )
 class IKEPolicyType(PrimaryObjectType):
-    proposals: List[Annotated["IKEProposalType", strawberry.lazy('vpn.graphql.types')]]
-    ipsec_profiles: List[Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')]]
+    proposals: list[Annotated["IKEProposalType", strawberry.lazy('vpn.graphql.types')]]
+    ipsec_profiles: list[Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -95,7 +95,7 @@ class IKEPolicyType(PrimaryObjectType):
     pagination=True
 )
 class IPSecProposalType(PrimaryObjectType):
-    ipsec_policies: List[Annotated["IPSecPolicyType", strawberry.lazy('vpn.graphql.types')]]
+    ipsec_policies: list[Annotated["IPSecPolicyType", strawberry.lazy('vpn.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -105,8 +105,8 @@ class IPSecProposalType(PrimaryObjectType):
     pagination=True
 )
 class IPSecPolicyType(PrimaryObjectType):
-    proposals: List[Annotated["IPSecProposalType", strawberry.lazy('vpn.graphql.types')]]
-    ipsec_profiles: List[Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')]]
+    proposals: list[Annotated["IPSecProposalType", strawberry.lazy('vpn.graphql.types')]]
+    ipsec_profiles: list[Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -119,7 +119,7 @@ class IPSecProfileType(PrimaryObjectType):
     ike_policy: Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')]
     ipsec_policy: Annotated["IPSecPolicyType", strawberry.lazy('vpn.graphql.types')]
 
-    tunnels: List[Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]]
+    tunnels: list[Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -131,9 +131,9 @@ class IPSecProfileType(PrimaryObjectType):
 class L2VPNType(ContactsMixin, PrimaryObjectType):
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
 
-    export_targets: List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]
-    terminations: List[Annotated["L2VPNTerminationType", strawberry.lazy('vpn.graphql.types')]]
-    import_targets: List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]
+    export_targets: list[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]
+    terminations: list[Annotated["L2VPNTerminationType", strawberry.lazy('vpn.graphql.types')]]
+    import_targets: list[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -146,9 +146,10 @@ class L2VPNTerminationType(NetBoxObjectType):
     l2vpn: Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]
 
     @strawberry_django.field
-    def assigned_object(self) -> Annotated[Union[
-        Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["VLANType", strawberry.lazy('ipam.graphql.types')],
-        Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')],
-    ], strawberry.union("L2VPNAssignmentType")]:
+    def assigned_object(self) -> Annotated[
+        Annotated['InterfaceType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['VLANType', strawberry.lazy('ipam.graphql.types')]
+        | Annotated['VMInterfaceType', strawberry.lazy('virtualization.graphql.types')],
+        strawberry.union('L2VPNAssignmentType'),
+    ]:
         return self.assigned_object

+ 10 - 10
netbox/vpn/migrations/0005_rename_indexes.py

@@ -8,11 +8,11 @@ class Migration(migrations.Migration):
 
     operations = [
         # Rename vpn_l2vpn constraints
-        migrations.RunSQL((
+        migrations.RunSQL(
             'ALTER TABLE vpn_l2vpn '
             'RENAME CONSTRAINT ipam_l2vpn_tenant_id_bb2564a6_fk_tenancy_tenant_id '
             'TO vpn_l2vpn_tenant_id_57ec8f92_fk_tenancy_tenant_id'
-        )),
+        ),
         # Rename ipam_l2vpn_* sequences
         migrations.RunSQL('ALTER TABLE ipam_l2vpn_export_targets_id_seq RENAME TO vpn_l2vpn_export_targets_id_seq'),
         migrations.RunSQL('ALTER TABLE ipam_l2vpn_id_seq RENAME TO vpn_l2vpn_id_seq'),
@@ -28,29 +28,29 @@ class Migration(migrations.Migration):
         migrations.RunSQL('ALTER INDEX IF EXISTS ipam_l2vpn_slug_24008406_uniq RENAME TO vpn_l2vpn_slug_76b5a174_uniq'),
         migrations.RunSQL('ALTER INDEX IF EXISTS ipam_l2vpn_slug_key RENAME TO vpn_l2vpn_slug_key'),
         # Rename vpn_l2vpntermination constraints
-        migrations.RunSQL((
+        migrations.RunSQL(
             'ALTER TABLE vpn_l2vpntermination '
             'RENAME CONSTRAINT ipam_l2vpntermination_assigned_object_id_check '
             'TO vpn_l2vpntermination_assigned_object_id_check'
-        )),
-        migrations.RunSQL((
+        ),
+        migrations.RunSQL(
             'ALTER TABLE vpn_l2vpntermination '
             'RENAME CONSTRAINT ipam_l2vpnterminatio_assigned_object_type_3923c124_fk_django_co '
             'TO vpn_l2vpntermination_assigned_object_type_id_f063b865_fk_django_co'
-        )),
-        migrations.RunSQL((
+        ),
+        migrations.RunSQL(
             'ALTER TABLE vpn_l2vpntermination '
             'RENAME CONSTRAINT ipam_l2vpntermination_l2vpn_id_9e570aa1_fk_ipam_l2vpn_id '
             'TO vpn_l2vpntermination_l2vpn_id_f5367bbe_fk_vpn_l2vpn_id'
-        )),
+        ),
         # Rename ipam_l2vpn_termination_* sequences
         migrations.RunSQL('ALTER TABLE ipam_l2vpntermination_id_seq RENAME TO vpn_l2vpntermination_id_seq'),
         # Rename ipam_l2vpn_* indexes
         migrations.RunSQL('ALTER INDEX ipam_l2vpntermination_pkey RENAME TO vpn_l2vpntermination_pkey'),
-        migrations.RunSQL((
+        migrations.RunSQL(
             'ALTER INDEX ipam_l2vpntermination_assigned_object_type_id_3923c124 '
             'RENAME TO vpn_l2vpntermination_assigned_object_type_id_f063b865'
-        )),
+        ),
         migrations.RunSQL(
             'ALTER INDEX ipam_l2vpntermination_l2vpn_id_9e570aa1 RENAME TO vpn_l2vpntermination_l2vpn_id_f5367bbe'
         ),

+ 3 - 5
netbox/wireless/graphql/schema.py

@@ -1,5 +1,3 @@
-from typing import List
-
 import strawberry
 import strawberry_django
 
@@ -9,10 +7,10 @@ from .types import *
 @strawberry.type(name="Query")
 class WirelessQuery:
     wireless_lan: WirelessLANType = strawberry_django.field()
-    wireless_lan_list: List[WirelessLANType] = strawberry_django.field()
+    wireless_lan_list: list[WirelessLANType] = strawberry_django.field()
 
     wireless_lan_group: WirelessLANGroupType = strawberry_django.field()
-    wireless_lan_group_list: List[WirelessLANGroupType] = strawberry_django.field()
+    wireless_lan_group_list: list[WirelessLANGroupType] = strawberry_django.field()
 
     wireless_link: WirelessLinkType = strawberry_django.field()
-    wireless_link_list: List[WirelessLinkType] = strawberry_django.field()
+    wireless_link_list: list[WirelessLinkType] = strawberry_django.field()

+ 11 - 10
netbox/wireless/graphql/types.py

@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Annotated, List, Union
+from typing import TYPE_CHECKING, Annotated
 
 import strawberry
 import strawberry_django
@@ -29,8 +29,8 @@ __all__ = (
 class WirelessLANGroupType(NestedGroupObjectType):
     parent: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None
 
-    wireless_lans: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]
-    children: List[Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')]]
+    wireless_lans: list[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]
+    children: list[Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')]]
 
 
 @strawberry_django.type(
@@ -44,15 +44,16 @@ class WirelessLANType(PrimaryObjectType):
     vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None
     tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
 
-    interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
+    interfaces: list[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
 
     @strawberry_django.field
-    def scope(self) -> Annotated[Union[
-        Annotated["LocationType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["RegionType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')],
-        Annotated["SiteType", strawberry.lazy('dcim.graphql.types')],
-    ], strawberry.union("WirelessLANScopeType")] | None:
+    def scope(self) -> Annotated[
+        Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['RegionType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteGroupType', strawberry.lazy('dcim.graphql.types')]
+        | Annotated['SiteType', strawberry.lazy('dcim.graphql.types')],
+        strawberry.union('WirelessLANScopeType'),
+    ] | None:
         return self.scope
 
 

+ 4 - 1
ruff.toml

@@ -37,7 +37,8 @@ extend-select = [
     "E501",  # pycodestyle: line too long (enforced with `line-length` above)
     "W",     # pycodestyle warnings (various style warnings, often whitespace/newlines)
     "I",     # import sorting (isort-equivalent)
-    "RET",   # return semantics (flake8-return family)
+    "RET",   # return semantics (flake8-return family: consistent/explicit returns; remove redundant else/assign before return)
+    "UP",    # pyupgrade: modernize syntax for your target Python (e.g., f-strings, built-in generics, newer stdlib idioms)
 ]
 ignore = [
     "F403",  # pyflakes: `from ... import *` used; unable to detect undefined names
@@ -66,6 +67,8 @@ known-first-party = [
 
 [lint.per-file-ignores]
 "template_code.py" = ["E501"]
+"netbox/netbox/graphql/filter_lookups.py" = ["UP046"]  # Strawberry typing: keep `Generic[T]` for now
+"netbox/netbox/graphql/scalars.py" = ["UP007"]         # Strawberry scalar typing: `Union[...]` required
 
 [format]
 # Use single quotes for strings.