diff --git a/docs/managers/account-move.md b/docs/managers/account-move.md index e30cbe4..893fca5 100644 --- a/docs/managers/account-move.md +++ b/docs/managers/account-move.md @@ -60,7 +60,7 @@ All specified records will be processed in a single request. ```python send_openstack_invoice_email( account_move: int | AccountMove, - email_ctx: Optional[Mapping[str, Any]] = None, + email_ctx: Mapping[str, Any] | None = None, ) -> None ``` @@ -291,7 +291,7 @@ Change this draft account move (invoice) into "posted" state. ```python send_openstack_invoice_email( - email_ctx: Optional[Mapping[str, Any]] = None, + email_ctx: Mapping[str, Any] | None = None, ) -> None ``` diff --git a/docs/managers/custom.md b/docs/managers/custom.md index fbe314b..546e7d1 100644 --- a/docs/managers/custom.md +++ b/docs/managers/custom.md @@ -180,12 +180,12 @@ the type hint should be defined as shown below. ```python from __future__ import annotations -from typing import Literal, Union +from typing import Literal from openstack_odooclient import RecordBase class CustomRecord(RecordBase["CustomRecordManager"]): - custom_field: Union[str, Literal[False]] + custom_field: str | Literal[False] """Description of the field.""" ``` @@ -197,12 +197,10 @@ the type hint should be defined as shown below instead. ```python from __future__ import annotations -from typing import Optional - from openstack_odooclient import RecordBase class CustomRecord(RecordBase["CustomRecordManager"]): - custom_field: Optional[str] + custom_field: str | None """Description of the field.""" ``` @@ -223,8 +221,9 @@ The type of the alias **must** match the type of the target field. ```python from __future__ import annotations +from typing import Annotated + from openstack_odooclient import FieldAlias, RecordBase -from typing_extensions import Annotated class CustomRecord(RecordBase["CustomRecordManager"]): custom_field: str @@ -265,8 +264,9 @@ The first is to expose the record's ID directly as an integer. ```python from __future__ import annotations +from typing import Annotated + from openstack_odooclient import ModelRef, RecordBase, User -from typing_extensions import Annotated class CustomRecord(RecordBase["CustomRecordManager"]): user_id: Annotated[int, ModelRef("user_id", User)] @@ -278,8 +278,9 @@ The second is to expose the target record's display name as a string. ```python from __future__ import annotations +from typing import Annotated + from openstack_odooclient import ModelRef, RecordBase, User -from typing_extensions import Annotated class CustomRecord(RecordBase["CustomRecordManager"]): user_name: Annotated[str, ModelRef("user_id", User)] @@ -291,8 +292,9 @@ The third and final one is to define the target record itself as a record object ```python from __future__ import annotations +from typing import Annotated + from openstack_odooclient import ModelRef, RecordBase, User -from typing_extensions import Annotated class CustomRecord(RecordBase["CustomRecordManager"]): user: Annotated[User, ModelRef("user_id", User)] @@ -315,8 +317,9 @@ The final result should be the following fields defined on your record class. ```python from __future__ import annotations +from typing import Annotated + from openstack_odooclient import ModelRef, RecordBase, User -from typing_extensions import Annotated class CustomRecord(RecordBase["CustomRecordManager"]): user_id: Annotated[int, ModelRef("user_id", User)] @@ -339,25 +342,24 @@ and [creating new records](index.md#create). The record ID or the record object can be passed directly to those methods (the record display name is not guaranteed to be unique, and thus, not accepted). -Record references can be made optional by encasing the type hint with `Optional`. +Record references can be made optional by unioning with `None`. If the record reference is not set, `None` will be returned. ```python from __future__ import annotations -from typing import Optional +from typing import Annotated from openstack_odooclient import ModelRef, RecordBase, User -from typing_extensions import Annotated class CustomRecord(RecordBase["CustomRecordManager"]): - user_id: Annotated[Optional[int], ModelRef("user_id", User)] + user_id: Annotated[int | None, ModelRef("user_id", User)] """ID for the user that owns this record.""" - user_name: Annotated[Optional[str], ModelRef("user_id", User)] + user_name: Annotated[str | None, ModelRef("user_id", User)] """Name of the user that owns this record.""" - user: Annotated[Optional[User], ModelRef("user_id", User)] + user: Annotated[User | None, ModelRef("user_id", User)] """The user that owns this record. This fetches the full record from Odoo once, @@ -371,19 +373,19 @@ In the below example, `Self` resolves to `CustomRecord`. ```python from __future__ import annotations -from typing import Optional +from typing import Annotated from openstack_odooclient import ModelRef, RecordBase -from typing_extensions import Annotated, Self +from typing_extensions import Self class CustomRecord(RecordBase["CustomRecordManager"]): - record_id: Annotated[Optional[int], ModelRef("user_id", Self)] + record_id: Annotated[int | None, ModelRef("user_id", Self)] """ID for the record related to this one, if set..""" - record_name: Annotated[Optional[str], ModelRef("user_id", Self)] + record_name: Annotated[str | None, ModelRef("user_id", Self)] """Name of the record related to this one, if set.""" - record: Annotated[Optional[Self], ModelRef("user_id", Self)] + record: Annotated[Self | None, ModelRef("user_id", Self)] """The record related to this one, if set. This fetches the full record from Odoo once, @@ -408,13 +410,12 @@ The first is to expose the record IDs directly as a list of integers. ```python from __future__ import annotations -from typing import List +from typing import Annotated from openstack_odooclient import ModelRef, RecordBase, Product -from typing_extensions import Annotated class CustomRecord(RecordBase["CustomRecordManager"]): - product_ids: Annotated[List[int], ModelRef("product_id", Product)] + product_ids: Annotated[list[int], ModelRef("product_id", Product)] """The list of IDs for the products to use.""" ``` @@ -423,13 +424,12 @@ The second and final one is to expose the records as a list of record objects. ```python from __future__ import annotations -from typing import List +from typing import Annotated from openstack_odooclient import ModelRef, RecordBase, Product -from typing_extensions import Annotated class CustomRecord(RecordBase["CustomRecordManager"]): - products: Annotated[List[Product], ModelRef("product_id", Product)] + products: Annotated[list[Product], ModelRef("product_id", Product)] """The list of products to use. This fetches the full records from Odoo once, @@ -449,16 +449,15 @@ The final result should be the following fields defined on your record class. ```python from __future__ import annotations -from typing import List +from typing import Annotated from openstack_odooclient import ModelRef, RecordBase, Product -from typing_extensions import Annotated class CustomRecord(RecordBase["CustomRecordManager"]): - product_ids: Annotated[List[int], ModelRef("product_id", Product)] + product_ids: Annotated[list[int], ModelRef("product_id", Product)] """The list of IDs for the products to use.""" - products: Annotated[List[Product], ModelRef("product_id", Product)] + products: Annotated[list[Product], ModelRef("product_id", Product)] """The list of products to use. This fetches the full records from Odoo once, @@ -478,16 +477,16 @@ In the below example, `Self` resolves to `CustomRecord`. ```python from __future__ import annotations -from typing import List +from typing import Annotated from openstack_odooclient import ModelRef, RecordBase -from typing_extensions import Annotated, Self +from typing_extensions import Self class CustomRecord(RecordBase["CustomRecordManager"]): - child_ids: Annotated[List[int], ModelRef("child_id", Self)] + child_ids: Annotated[list[int], ModelRef("child_id", Self)] """The list of IDs for the child records.""" - children: Annotated[List[Self], ModelRef("child_id", Self)] + children: Annotated[list[Self], ModelRef("child_id", Self)] """The list of child records. This fetches the full records from Odoo once, @@ -511,16 +510,15 @@ Below is an example of two record classes that correctly reference each other. ```python title="parent.py" from __future__ import annotations -from typing import List +from typing import Annotated from openstack_odooclient import ModelRef, RecordBase, RecordManagerBase -from typing_extensions import Annotated class Parent(RecordBase["ParentManager"]): - child_ids: Annotated[List[int], ModelRef("child_id", Child)] + child_ids: Annotated[list[int], ModelRef("child_id", Child)] """The list of IDs for the children records.""" - children: Annotated[List[Parent], ModelRef("child_id", Child)] + children: Annotated[list[Parent], ModelRef("child_id", Child)] """The list of children records. This fetches the full records from Odoo once, @@ -537,19 +535,18 @@ from .child import Child # noqa: E402 ```python title="child.py" from __future__ import annotations -from typing import Optional +from typing import Annotated from openstack_odooclient import ModelRef, RecordBase, RecordManagerBase -from typing_extensions import Annotated class Child(RecordBase["ChildManager"]): - parent_id: Annotated[Optional[int], ModelRef("parent_id", Parent)] + parent_id: Annotated[int | None, ModelRef("parent_id", Parent)] """ID for the parent record, if it has one.""" - parent_name: Annotated[Optional[str], ModelRef("parent_id", Parent)] + parent_name: Annotated[str | None, ModelRef("parent_id", Parent)] """Name of the parent record, if it has one.""" - parent: Annotated[Optional[Parent], ModelRef("parent_id", Parent)] + parent: Annotated[Parent | None, ModelRef("parent_id", Parent)] """The parent record, if it has one. This fetches the full record from Odoo once, @@ -686,8 +683,6 @@ Below is a simple example of a custom record type and its manager class. ```python from __future__ import annotations -from typing import List, Union - from openstack_odooclient import RecordBase, RecordManagerBase class CustomRecord(RecordBase["CustomRecordManager"]): @@ -710,8 +705,6 @@ This will allow manager methods to be used by calling them on the manager object ```python from __future__ import annotations -from typing import List, Union - from openstack_odooclient import Client, RecordBase, RecordManagerBase class CustomRecord(RecordBase["CustomRecordManager"]): @@ -736,8 +729,6 @@ custom manager class. ```python from __future__ import annotations -from typing import List, Union - from openstack_odooclient import RecordBase, RecordManagerBase class CustomRecord(RecordBase["CustomRecordManager"]): @@ -768,8 +759,6 @@ Methods can be defined on manager classes to provide additional functionality. ```python from __future__ import annotations -from typing import List, Union - from openstack_odooclient import RecordBase, RecordManagerBase class CustomRecord(RecordBase["CustomRecordManager"]): @@ -780,10 +769,10 @@ class CustomRecordManager(RecordManagerBase[CustomRecord]): env_name = "custom.record" record_class = CustomRecord - def search_by_custom_field(self, custom_field: str) -> List[Record]: + def search_by_custom_field(self, custom_field: str) -> list[Record]: return self.search([("custom_field", "ilike", custom_field)]) - def perform_action(self, custom_record: Union[int, CustomRecord]) -> None: + def perform_action(self, custom_record: int | CustomRecord) -> None: self._env.perform_action( ( custom_record.id diff --git a/docs/managers/index.md b/docs/managers/index.md index 3a570a2..8a58686 100644 --- a/docs/managers/index.md +++ b/docs/managers/index.md @@ -324,7 +324,7 @@ a ``dict`` object, instead of a record object. ```python search( - filters: Sequence[Tuple[str, str, Any] | Sequence[Any] | str] | None = None, + filters: Sequence[tuple[str, str, Any] | Sequence[Any] | str] | None = None, fields: Iterable[str] | None = None, order: str | None = None, as_id: bool = False, @@ -334,7 +334,7 @@ search( ```python search( - filters: Sequence[Tuple[str, str, Any] | Sequence[Any] | str] | None = None, + filters: Sequence[tuple[str, str, Any] | Sequence[Any] | str] | None = None, fields: Iterable[str] | None = None, order: str | None = None, as_id: bool = True, @@ -344,7 +344,7 @@ search( ```python search( - filters: Sequence[Tuple[str, str, Any] | Sequence[Any] | str] | None = None, + filters: Sequence[tuple[str, str, Any] | Sequence[Any] | str] | None = None, fields: Iterable[str] | None = None, order: str | None = None, as_id: bool = False, @@ -564,7 +564,7 @@ a list of `dict` objects, instead of record objects. | Name | Type | Description | Default | |-----------|---------------------------------------------------------------|---------------------------------------------------|---------| -| `filters` | `Sequence[Tuple[str, str, Any] | Sequence[Any] | str] | None` | Filters to query by (or `None` for no filters) | `None` | +| `filters` | `Sequence[tuple[str, str, Any] | Sequence[Any] | str] | None` | Filters to query by (or `None` for no filters) | `None` | | `fields` | `Iterable[str] | None` | Fields to select (or `None` to select all fields) | `None` | | `order` | `str | None` | Field to order results by, if ordering results | `None` | | `as_id` | `bool` | Return the record IDs only | `False` | diff --git a/openstack_odooclient/base/client.py b/openstack_odooclient/base/client.py index e41ef41..04bd4c2 100644 --- a/openstack_odooclient/base/client.py +++ b/openstack_odooclient/base/client.py @@ -19,19 +19,17 @@ import urllib.request from pathlib import Path -from typing import TYPE_CHECKING, overload +from typing import TYPE_CHECKING, Literal, Type, overload from odoorpc import ODOO # type: ignore[import] from packaging.version import Version -from typing_extensions import get_type_hints +from typing_extensions import get_type_hints # 3.11 and later from ..util import is_subclass from .record import RecordBase from .record_manager import RecordManagerBase if TYPE_CHECKING: - from typing import Dict, Literal, Optional, Type, Union - from odoorpc.db import DB # type: ignore[import] from odoorpc.env import Environment # type: ignore[import] from odoorpc.report import Report # type: ignore[import] @@ -66,35 +64,35 @@ class ClientBase: All parameters must be specified as keyword arguments. :param hostname: Server hostname, required if ``odoo`` is not set - :type hostname: Optional[str], optional + :type hostname: str | None, optional :param database: Database name, required if ``odoo`` is not set - :type database: Optional[str], optional + :type database: str | None, optional :param username: Username, required if ``odoo`` is not set - :type username: Optional[str], optional + :type username: str | None, optional :param password: Password (or API key), required if ``odoo`` is not set - :type password: Optional[str], optional + :type password: str | None, optional :param protocol: Communication protocol, defaults to ``jsonrpc`` :type protocol: str, optional :param port: Access port, defaults to ``8069`` :type port: int, optional :param verify: Configure SSL cert verification, defaults to ``True`` - :type verify: Union[bool, str, Path] + :type verify: bool | str | Path :param version: Server version, defaults to ``None`` (auto-detect) - :type version: Optional[str], optional + :type version: str | None, optional """ @overload def __init__( self, *, - hostname: Optional[str] = ..., - database: Optional[str] = ..., - username: Optional[str] = ..., - password: Optional[str] = ..., + hostname: str | None = ..., + database: str | None = ..., + username: str | None = ..., + password: str | None = ..., protocol: str = "jsonrpc", port: int = 8069, - verify: Union[bool, str, Path] = ..., - version: Optional[str] = ..., + verify: bool | str | Path = ..., + version: str | None = ..., odoo: ODOO, ) -> None: ... @@ -108,8 +106,8 @@ def __init__( password: str, protocol: str = "jsonrpc", port: int = 8069, - verify: Union[bool, str, Path] = ..., - version: Optional[str] = ..., + verify: bool | str | Path = ..., + version: str | None = ..., odoo: Literal[None] = ..., ) -> None: ... @@ -117,29 +115,29 @@ def __init__( def __init__( self, *, - hostname: Optional[str] = ..., - database: Optional[str] = ..., - username: Optional[str] = ..., - password: Optional[str] = ..., + hostname: str | None = ..., + database: str | None = ..., + username: str | None = ..., + password: str | None = ..., protocol: str = "jsonrpc", port: int = 8069, - verify: Union[bool, str, Path] = ..., - version: Optional[str] = ..., - odoo: Optional[ODOO] = ..., + verify: bool | str | Path = ..., + version: str | None = ..., + odoo: ODOO | None = ..., ) -> None: ... def __init__( self, *, - hostname: Optional[str] = None, - database: Optional[str] = None, - username: Optional[str] = None, - password: Optional[str] = None, + hostname: str | None = None, + database: str | None = None, + username: str | None = None, + password: str | None = None, protocol: str = "jsonrpc", port: int = 8069, - verify: Union[bool, str, Path] = True, - version: Optional[str] = None, - odoo: Optional[ODOO] = None, + verify: bool | str | Path = True, + version: str | None = None, + odoo: ODOO | None = None, ) -> None: # If an OdooRPC object is provided, use that directly. # Otherwise, make a new one with the provided settings. @@ -169,7 +167,7 @@ def __init__( opener=opener, ) self._odoo.login(database, username, password) - self._record_manager_mapping: Dict[ + self._record_manager_mapping: dict[ Type[RecordBase], RecordManagerBase, ] = {} diff --git a/openstack_odooclient/base/record.py b/openstack_odooclient/base/record.py index 1da33da..e650409 100644 --- a/openstack_odooclient/base/record.py +++ b/openstack_odooclient/base/record.py @@ -22,20 +22,16 @@ from types import MappingProxyType from typing import ( TYPE_CHECKING, + Annotated, Any, - Dict, Generic, Literal, - Mapping, - Optional, - Sequence, Type, TypeVar, Union, ) from typing_extensions import ( - Annotated, Self, get_args as get_type_args, get_origin as get_type_origin, @@ -44,6 +40,8 @@ from ..util import is_subclass if TYPE_CHECKING: + from collections.abc import Mapping, Sequence + from odoorpc import ODOO # type: ignore[import] from odoorpc.env import Environment # type: ignore[import] @@ -54,7 +52,7 @@ class AnnotationBase: @classmethod - def get(cls, type_hint: Any) -> Optional[Self]: + def get(cls, type_hint: Any) -> Self | None: """Return the annotation applied to the given type hint, if the type hint is annotated with this type of annotation. @@ -64,11 +62,11 @@ def get(cls, type_hint: Any) -> Optional[Self]: :param type_hint: The type hint to parse :type type_hint: Any :return: Applied annotation, or ``None`` if no annotation was found - :rtype: Optional[Self] + :rtype: Self | None """ if get_type_origin(type_hint) is not Annotated: return None - matching_annotation: Optional[Self] = None + matching_annotation: Self | None = None for annotation in get_type_args(type_hint)[1:]: if isinstance(annotation, cls): matching_annotation = annotation @@ -96,7 +94,7 @@ class FieldAlias(AnnotationBase): when searching or creating records, or referencing field values on record objects. - >>> from typing_extensions import Annotated + >>> from typing import Annotated >>> from openstack_odooclient import FieldAlias, RecordBase >>> class CustomRecord(RecordBase["CustomRecordManager"]): ... name: str @@ -116,7 +114,7 @@ class ModelRef(AnnotationBase): the second argument is the record class that type is represented by in the OpenStack Odoo Client library. - >>> from typing_extensions import Annotated + >>> from typing import Annotated >>> from openstack_odooclient import ModelRef, RecordBase, User >>> class CustomRecord(RecordBase["CustomRecordManager"]): ... user_id: Annotated[int, ModelRef("user_id", User)] @@ -145,13 +143,13 @@ class RecordBase(Generic[RecordManager]): create_date: datetime """The time the record was created.""" - create_uid: Annotated[Optional[int], ModelRef("create_uid", User)] + create_uid: Annotated[int | None, ModelRef("create_uid", User)] """The ID of the user that created this record.""" - create_name: Annotated[Optional[str], ModelRef("create_uid", User)] + create_name: Annotated[str | None, ModelRef("create_uid", User)] """The name of the user that created this record.""" - create_user: Annotated[Optional[User], ModelRef("create_uid", User)] + create_user: Annotated[User | None, ModelRef("create_uid", User)] """The user that created this record. This fetches the full record from Odoo once, @@ -161,20 +159,20 @@ class RecordBase(Generic[RecordManager]): write_date: datetime """The time the record was last modified.""" - write_uid: Annotated[Optional[int], ModelRef("write_uid", User)] + write_uid: Annotated[int | None, ModelRef("write_uid", User)] """The ID for the user that last modified this record.""" - write_name: Annotated[Optional[str], ModelRef("write_uid", User)] + write_name: Annotated[str | None, ModelRef("write_uid", User)] """The name of the user that last modified this record.""" - write_user: Annotated[Optional[User], ModelRef("write_uid", User)] + write_user: Annotated[User | None, ModelRef("write_uid", User)] """The user that last modified this record. This fetches the full record from Odoo once, and caches it for subsequence accesses. """ - _field_mapping: Dict[Optional[str], Dict[str, str]] = {} + _field_mapping: dict[str | None, dict[str, str]] = {} """A dictionary structure mapping field names in the local class with the equivalents on specific versions of Odoo. @@ -191,7 +189,7 @@ def __init__( self, client: ClientBase, record: Mapping[str, Any], - fields: Optional[Sequence[str]], + fields: Sequence[str] | None, ) -> None: self._client = client """The Odoo client that created this record object.""" @@ -199,7 +197,7 @@ def __init__( """The raw record fields from OdooRPC.""" self._fields = tuple(fields) if fields else None """The fields selected in the query that created this record object.""" - self._values: Dict[str, Any] = {} + self._values: dict[str, Any] = {} """The cache for the processed record field values.""" @property @@ -242,7 +240,7 @@ def from_record_obj(cls, record_obj: RecordBase) -> Self: fields=record_obj._fields, ) - def as_dict(self, raw: bool = False) -> Dict[str, Any]: + def as_dict(self, raw: bool = False) -> dict[str, Any]: """Convert this record object to a dictionary. The fields and values in the dictionary are the same @@ -257,7 +255,7 @@ def as_dict(self, raw: bool = False) -> Dict[str, Any]: :param raw: Return raw dictionary from OdooRPC, defaults to False :type raw: bool, optional :return: Record dictionary - :rtype: Dict[str, Any] + :rtype: dict[str, Any] """ return ( copy.deepcopy(dict(self._record)) @@ -452,7 +450,7 @@ def _decode_value(cls, type_hint: Any, value: Any) -> Any: if value_type is Union: attr_union_types = get_type_args(type_hint) if len(attr_union_types) == 2: # noqa: PLR2004 - # Optional[T] + # T | None if type(None) in attr_union_types and value is not None: return cls._decode_value( next( @@ -464,7 +462,7 @@ def _decode_value(cls, type_hint: Any, value: Any) -> Any: ), value, ) - # Union[T, Literal[False]] + # T | Literal[False] if Literal[False] in attr_union_types and value is not False: return cls._decode_value( next( diff --git a/openstack_odooclient/base/record_manager.py b/openstack_odooclient/base/record_manager.py index 647b9de..123ec70 100644 --- a/openstack_odooclient/base/record_manager.py +++ b/openstack_odooclient/base/record_manager.py @@ -15,21 +15,16 @@ from __future__ import annotations +import builtins + from datetime import date, datetime from types import MappingProxyType from typing import ( TYPE_CHECKING, + Annotated, Any, - Dict, Generic, - Iterable, - List, Literal, - Mapping, - Optional, - Sequence, - Set, - Tuple, Type, TypeVar, Union, @@ -37,7 +32,6 @@ ) from typing_extensions import ( - Annotated, Self, get_args as get_type_args, get_origin as get_type_origin, @@ -53,13 +47,16 @@ from .record import FieldAlias, ModelRef, RecordBase if TYPE_CHECKING: + from collections.abc import Iterable, Mapping, Sequence + from odoorpc import ODOO # type: ignore[import] from odoorpc.env import Environment # type: ignore[import] from .client import ClientBase + FilterCriterion = tuple[str, str, Any] | Sequence[Any] | str + Record = TypeVar("Record", bound=RecordBase) -FilterCriterion = Union[Tuple[str, str, Any], Sequence[Any], str] class RecordManagerBase(Generic[Record]): @@ -100,7 +97,7 @@ class and add a type hint for your custom record manager. record_class: Type[Record] """The record object type to instantiate using this manager.""" - default_fields: Optional[Tuple[str, ...]] = None + default_fields: tuple[str, ...] | None = None """List of fields to fetch by default if a field list is not supplied in queries. @@ -133,7 +130,7 @@ def __init__(self, client: ClientBase) -> None: record class, mapping Odoo version-specific remote field names to their representations on the record class. """ - self._model_ref_mapping: Dict[str, str] = {} + self._model_ref_mapping: dict[str, str] = {} """Mapping of the remote field name for a model ref to the local field name representing the model ref's IDs. @@ -169,40 +166,40 @@ def _env(self) -> Environment: @overload def list( self, - ids: Union[int, Iterable[int]], + ids: int | Iterable[int], *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_dict: Literal[False] = ..., optional: bool = ..., - ) -> List[Record]: ... + ) -> builtins.list[Record]: ... @overload def list( self, - ids: Union[int, Iterable[int]], + ids: int | Iterable[int], *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_dict: Literal[True], optional: bool = ..., - ) -> List[Dict[str, Any]]: ... + ) -> builtins.list[dict[str, Any]]: ... @overload def list( self, - ids: Union[int, Iterable[int]], + ids: int | Iterable[int], *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_dict: bool = ..., optional: bool = ..., - ) -> Union[List[Record], List[Dict[str, Any]]]: ... + ) -> builtins.list[Record] | builtins.list[dict[str, Any]]: ... def list( self, - ids: Union[int, Iterable[int]], - fields: Optional[Iterable[str]] = None, + ids: int | Iterable[int], + fields: Iterable[str] | None = None, as_dict: bool = False, optional: bool = False, - ) -> Union[List[Record], List[Dict[str, Any]]]: + ) -> builtins.list[Record] | builtins.list[dict[str, Any]]: """Get one or more specific records by ID. By default all fields available on the record model @@ -222,19 +219,19 @@ def list( returns an empty list. :param ids: Record ID, or list of record IDs - :type ids: Union[int, Iterable[int]] + :type ids: int | Iterable[int] :param fields: Fields to select, defaults to ``None`` (select all) - :type fields: Optional[Iterable[str]], optional + :type fields: Iterable[str] | None, optional :param as_dict: Return records as dictionaries, defaults to ``False`` :type as_dict: bool, optional :param optional: Disable missing record errors, defaults to ``False`` :type optional: bool, optional :raises RecordNotFoundError: If IDs are required but some are missing :return: List of records - :rtype: list[Record] or list[dict[str, Any]] + :rtype: list[Record] | list[dict[str, Any]] """ if isinstance(ids, int): - _ids: Union[int, List[int]] = ids + _ids: int | list[int] = ids else: _ids = list(ids) if not _ids: @@ -249,7 +246,7 @@ def list( if fields is not None else None ) - records: Iterable[Dict[str, Any]] = self._env.read( + records: Iterable[dict[str, Any]] = self._env.read( _ids, fields=_fields, ) @@ -272,7 +269,7 @@ def list( ] if not optional: required_ids = {_ids} if isinstance(_ids, int) else set(_ids) - found_ids: Set[int] = ( + found_ids: set[int] = ( set(record["id"] for record in res_dicts) if as_dict else set(record.id for record in res_objs) @@ -293,7 +290,7 @@ def get( self, id: int, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_dict: Literal[False] = ..., optional: Literal[False] = ..., ) -> Record: ... @@ -303,48 +300,48 @@ def get( self, id: int, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_dict: Literal[True], optional: Literal[False] = ..., - ) -> Dict[str, Any]: ... + ) -> dict[str, Any]: ... @overload def get( self, id: int, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[Record]: ... + ) -> Record | None: ... @overload def get( self, id: int, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_dict: Literal[True], optional: Literal[True], - ) -> Optional[Dict[str, Any]]: ... + ) -> dict[str, Any] | None: ... @overload def get( self, id: int, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_dict: bool = ..., optional: bool = ..., - ) -> Optional[Union[Record, Dict[str, Any]]]: ... + ) -> Record | dict[str, Any] | None: ... def get( self, id: int, # noqa: A002 - fields: Optional[Iterable[str]] = None, + fields: Iterable[str] | None = None, as_dict: bool = False, optional: bool = False, - ) -> Optional[Union[Record, Dict[str, Any]]]: + ) -> Record | dict[str, Any] | None: """Get a single record by ID. By default all fields available on the record model @@ -363,7 +360,7 @@ def get( :param optional: Return ``None`` if not found, defaults to ``False`` :raises RecordNotFoundError: Record with the given ID not found :return: List of records - :rtype: Union[Record, List[str, Any]] + :rtype: Record | list[str, Any] """ try: return self.list( @@ -386,64 +383,72 @@ def get( @overload def search( self, - filters: Optional[Sequence[FilterCriterion]] = ..., - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + filters: Sequence[FilterCriterion] | None = ..., + fields: Iterable[str] | None = ..., + order: str | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., - ) -> List[Record]: ... + ) -> builtins.list[Record]: ... @overload def search( self, - filters: Optional[Sequence[FilterCriterion]] = ..., - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + filters: Sequence[FilterCriterion] | None = ..., + fields: Iterable[str] | None = ..., + order: str | None = ..., *, as_id: Literal[True], as_dict: Literal[False] = ..., - ) -> List[int]: ... + ) -> builtins.list[int]: ... @overload def search( self, - filters: Optional[Sequence[FilterCriterion]] = ..., - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + filters: Sequence[FilterCriterion] | None = ..., + fields: Iterable[str] | None = ..., + order: str | None = ..., as_id: Literal[False] = ..., *, as_dict: Literal[True], - ) -> List[Dict[str, Any]]: ... + ) -> builtins.list[dict[str, Any]]: ... @overload def search( self, - filters: Optional[Sequence[FilterCriterion]] = ..., - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + filters: Sequence[FilterCriterion] | None = ..., + fields: Iterable[str] | None = ..., + order: str | None = ..., *, as_id: Literal[True], as_dict: Literal[True], - ) -> List[int]: ... + ) -> builtins.list[int]: ... @overload def search( self, - filters: Optional[Sequence[FilterCriterion]] = ..., - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + filters: Sequence[FilterCriterion] | None = ..., + fields: Iterable[str] | None = ..., + order: str | None = ..., as_id: bool = ..., as_dict: bool = ..., - ) -> Union[List[Record], List[int], List[Dict[str, Any]]]: ... + ) -> ( + builtins.list[Record] + | builtins.list[int] + | builtins.list[dict[str, Any]] + ): ... def search( self, - filters: Optional[Sequence[FilterCriterion]] = None, - fields: Optional[Iterable[str]] = None, - order: Optional[str] = None, + filters: Sequence[FilterCriterion] | None = None, + fields: Iterable[str] | None = None, + order: str | None = None, as_id: bool = False, as_dict: bool = False, - ) -> Union[List[Record], List[int], List[Dict[str, Any]]]: + ) -> ( + builtins.list[Record] + | builtins.list[int] + | builtins.list[dict[str, Any]] + ): """Query the ERP for records, optionally defining filters to constrain the search and other parameters, and return the results. @@ -510,7 +515,7 @@ def search( a list of ``dict`` objects, instead of record objects. :param filters: Filters to query by, defaults to ``None`` (no filters) - :type filters: Union[Tuple[str, str, Any], Sequence[Any], str] | None + :type filters: tuple[str, str, Any] | Sequence[Any] | str | None :param fields: Fields to select, defaults to ``None`` (select all) :type fields: Iterable[str] or None, optional :param order: Order results by field name, defaults to ``None`` @@ -520,9 +525,9 @@ def search( :param as_dict: Return records as dictionaries, defaults to ``False`` :type as_dict: bool, optional :return: List of records - :rtype: list[Record] or list[int] or list[dict[str, Any]] + :rtype: list[Record] | list[int] | list[dict[str, Any]] """ - ids: List[int] = self._env.search( + ids: list[int] = self._env.search( (self._encode_filters(filters) if filters else []), order=order, ) @@ -543,8 +548,8 @@ def search( def _encode_filters( self, filters: Sequence[FilterCriterion], - ) -> List[Union[str, Tuple[str, str, Any]]]: - _filters: List[Union[str, Tuple[str, str, Any]]] = [] + ) -> builtins.list[str | tuple[str, str, Any]]: + _filters: list[str | tuple[str, str, Any]] = [] for f in filters: if isinstance(f, str): _filters.append(f) @@ -576,7 +581,7 @@ def _encode_filters( _filters.append((field_name, operator, value)) return _filters - def _encode_filter_field(self, field: str) -> Tuple[Any, str]: + def _encode_filter_field(self, field: str) -> tuple[Any, str]: # The field reference in a filter may be nested. # Split the reference by the delimiter (.), # so we can perform a recursive lookup of the correct field @@ -657,7 +662,7 @@ def create(self, **fields) -> int: """ return self._env.create(self._encode_create_fields(fields)) - def create_multi(self, *records: Mapping[str, Any]) -> List[int]: + def create_multi(self, *records: Mapping[str, Any]) -> builtins.list[int]: """Create one or more new records in a single request, passing in the mappings containing the record's input fields as positional arguments. @@ -669,9 +674,9 @@ def create_multi(self, *records: Mapping[str, Any]) -> List[int]: pass the returned IDs to the ``list`` method. :return: The IDs of the newly created records - :rtype: List[int] + :rtype: list[int] """ - res: Union[int, List[int]] = self._env.create( + res: int | list[int] = self._env.create( [self._encode_create_fields(record) for record in records], ) if isinstance(res, int): @@ -681,9 +686,9 @@ def create_multi(self, *records: Mapping[str, Any]) -> List[int]: def _encode_create_fields( self, fields: Mapping[str, Any], - ) -> Dict[str, Any]: - create_fields: Dict[str, Any] = {} - field_remote_mapping: Dict[str, str] = {} + ) -> dict[str, Any]: + create_fields: dict[str, Any] = {} + field_remote_mapping: dict[str, str] = {} for field, value in fields.items(): remote_field, remote_value = self._encode_create_field( field=field, @@ -706,7 +711,7 @@ def _encode_create_field( self, field: str, value: Any, - ) -> Tuple[str, Any]: + ) -> tuple[str, Any]: # Fetch the local and remote representations of the given field. # Field aliases and model ref target fields are resolved # at this point. @@ -749,11 +754,8 @@ def _encode_create_field( if get_type_origin(attr_type) is list: if not value: return (remote_field, []) - remote_values: List[ - Union[ - Tuple[int, int], - Tuple[int, int, Dict[str, Any]], - ], + remote_values: list[ + tuple[int, int] | tuple[int, int, dict[str, Any]] ] = [] for v in value: if isinstance(v, int): @@ -808,7 +810,7 @@ def _encode_create_field( def unlink( self, - *records: Union[int, Record, Iterable[Union[int, Record]]], + *records: int | Record | Iterable[int | Record], ) -> None: """Delete one or more records from Odoo. @@ -818,9 +820,9 @@ def unlink( All specified records will be deleted in a single request. :param records: The records to delete (object, ID, or record/ID list) - :type records: Union[Record, int, Iterable[Union[Record, int]]] + :type records: Record | int | Iterable[Record | int] """ - _ids: List[int] = [] + _ids: list[int] = [] for ids in records: if isinstance(ids, int): _ids.append(ids) @@ -834,7 +836,7 @@ def unlink( def delete( self, - *records: Union[Record, int, Iterable[Union[Record, int]]], + *records: Record | int | Iterable[Record | int], ) -> None: """Delete one or more records from Odoo. @@ -844,7 +846,7 @@ def delete( All specified records will be deleted in a single request. :param records: The records to delete (object, ID, or record/ID list) - :type records: Union[Record, int, Iterable[Union[Record, int]]] + :type records: Record | int | Iterable[Record | int] """ self.unlink(*records) @@ -882,8 +884,8 @@ def _resolve_alias(self, field: str) -> str: return field # NOTE(callumdickinson): Continually resolve field aliases # until we get to a field that is not an alias. - resolved_aliases: Set[str] = set() - alias_chain: List[str] = [] + resolved_aliases: set[str] = set() + alias_chain: list[str] = [] annotation = FieldAlias.get(self._record_type_hints[field]) while annotation: # Check if field aliases loop back on each other. diff --git a/openstack_odooclient/base/record_manager_coded.py b/openstack_odooclient/base/record_manager_coded.py index 607b75f..91e3420 100644 --- a/openstack_odooclient/base/record_manager_coded.py +++ b/openstack_odooclient/base/record_manager_coded.py @@ -15,7 +15,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, overload +from typing import TYPE_CHECKING, Any, Literal, overload from .record_manager_with_unique_field import ( Record, @@ -23,14 +23,7 @@ ) if TYPE_CHECKING: - from typing import ( - Any, - Dict, - Iterable, - Literal, - Optional, - Union, - ) + from collections.abc import Iterable class CodedRecordManagerBase(RecordManagerWithUniqueFieldBase[Record, str]): @@ -55,29 +48,29 @@ def get_by_code( self, code: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[True], - ) -> Optional[int]: ... + ) -> int | None: ... @overload def get_by_code( self, code: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[int]: ... + ) -> int | None: ... @overload def get_by_code( self, code: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[False] = ..., @@ -88,7 +81,7 @@ def get_by_code( self, code: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -99,40 +92,40 @@ def get_by_code( self, code: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[True], - ) -> Optional[Dict[str, Any]]: ... + ) -> dict[str, Any] | None: ... @overload def get_by_code( self, code: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[False] = ..., - ) -> Dict[str, Any]: ... + ) -> dict[str, Any]: ... @overload def get_by_code( self, code: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[Record]: ... + ) -> Record | None: ... @overload def get_by_code( self, code: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -143,20 +136,20 @@ def get_by_code( self, code: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: bool = ..., as_dict: bool = ..., optional: bool = ..., - ) -> Optional[Union[Record, int, Dict[str, Any]]]: ... + ) -> Record | int | dict[str, Any] | None: ... def get_by_code( self, code: str, - fields: Optional[Iterable[str]] = None, + fields: Iterable[str] | None = None, as_id: bool = False, as_dict: bool = False, optional: bool = False, - ) -> Optional[Union[Record, int, Dict[str, Any]]]: + ) -> Record | int | dict[str, Any] | None: """Query a unique record by code. A number of parameters are available to configure the return type, @@ -180,7 +173,7 @@ def get_by_code( :param as_id: Return a record ID, defaults to False :type as_id: bool, optional :param fields: Fields to select, defaults to ``None`` (select all) - :type fields: Iterable[str] or None, optional + :type fields: Iterable[str] | None, optional :param as_dict: Return the record as a dictionary, defaults to False :type as_dict: bool, optional :param optional: Return ``None`` if not found, defaults to False @@ -188,7 +181,7 @@ def get_by_code( :raises MultipleRecordsFoundError: Multiple records with the same code :raises RecordNotFoundError: Record with the given code not found :return: Query result (or ``None`` if record not found and optional) - :rtype: Optional[Union[Record, int, Dict[str, Any]]] + :rtype: Record | int | dict[str, Any] | None """ return self._get_by_unique_field( field=self.code_field, diff --git a/openstack_odooclient/base/record_manager_named.py b/openstack_odooclient/base/record_manager_named.py index ed79532..d8d1379 100644 --- a/openstack_odooclient/base/record_manager_named.py +++ b/openstack_odooclient/base/record_manager_named.py @@ -15,7 +15,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, overload +from typing import TYPE_CHECKING, Any, Literal, overload from .record_manager_with_unique_field import ( Record, @@ -23,14 +23,7 @@ ) if TYPE_CHECKING: - from typing import ( - Any, - Dict, - Iterable, - Literal, - Optional, - Union, - ) + from collections.abc import Iterable class NamedRecordManagerBase(RecordManagerWithUniqueFieldBase[Record, str]): @@ -55,29 +48,29 @@ def get_by_name( self, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[True], - ) -> Optional[int]: ... + ) -> int | None: ... @overload def get_by_name( self, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[int]: ... + ) -> int | None: ... @overload def get_by_name( self, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[False] = ..., @@ -88,7 +81,7 @@ def get_by_name( self, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -99,40 +92,40 @@ def get_by_name( self, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[True], - ) -> Optional[Dict[str, Any]]: ... + ) -> dict[str, Any] | None: ... @overload def get_by_name( self, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[False] = ..., - ) -> Dict[str, Any]: ... + ) -> dict[str, Any]: ... @overload def get_by_name( self, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[Record]: ... + ) -> Record | None: ... @overload def get_by_name( self, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -143,20 +136,20 @@ def get_by_name( self, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: bool = ..., as_dict: bool = ..., optional: bool = ..., - ) -> Optional[Union[Record, int, Dict[str, Any]]]: ... + ) -> Record | int | dict[str, Any] | None: ... def get_by_name( self, name: str, - fields: Optional[Iterable[str]] = None, + fields: Iterable[str] | None = None, as_id: bool = False, as_dict: bool = False, optional: bool = False, - ) -> Optional[Union[Record, int, Dict[str, Any]]]: + ) -> Record | int | dict[str, Any] | None: """Query a unique record by name. A number of parameters are available to configure the return type, @@ -180,7 +173,7 @@ def get_by_name( :param as_id: Return a record ID, defaults to False :type as_id: bool, optional :param fields: Fields to select, defaults to ``None`` (select all) - :type fields: Iterable[str] or None, optional + :type fields: Iterable[str] | None, optional :param as_dict: Return the record as a dictionary, defaults to False :type as_dict: bool, optional :param optional: Return ``None`` if not found, defaults to False @@ -188,7 +181,7 @@ def get_by_name( :raises MultipleRecordsFoundError: Multiple records with the same name :raises RecordNotFoundError: Record with the given name not found :return: Query result (or ``None`` if record not found and optional) - :rtype: Optional[Union[Record, int, Dict[str, Any]]] + :rtype: Record | int | dict[str, Any] | None """ return self._get_by_unique_field( field=self.name_field, diff --git a/openstack_odooclient/base/record_manager_with_unique_field.py b/openstack_odooclient/base/record_manager_with_unique_field.py index c6360f2..f263b3d 100644 --- a/openstack_odooclient/base/record_manager_with_unique_field.py +++ b/openstack_odooclient/base/record_manager_with_unique_field.py @@ -17,20 +17,13 @@ import itertools -from typing import TYPE_CHECKING, Generic, TypeVar, overload +from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, overload from ..exceptions import MultipleRecordsFoundError, RecordNotFoundError from .record_manager import Record, RecordManagerBase if TYPE_CHECKING: - from typing import ( - Any, - Dict, - Iterable, - Literal, - Optional, - Union, - ) + from collections.abc import Iterable T = TypeVar("T") @@ -69,12 +62,12 @@ def _get_by_unique_field( field: str, value: T, *, - filters: Optional[Iterable[Any]] = ..., - fields: Optional[Iterable[str]] = ..., + filters: Iterable[Any] | None = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[True], - ) -> Optional[int]: ... + ) -> int: ... @overload def _get_by_unique_field( @@ -82,12 +75,12 @@ def _get_by_unique_field( field: str, value: T, *, - filters: Optional[Iterable[Any]] = ..., - fields: Optional[Iterable[str]] = ..., + filters: Iterable[Any] | None = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[int]: ... + ) -> int | None: ... @overload def _get_by_unique_field( @@ -95,8 +88,8 @@ def _get_by_unique_field( field: str, value: T, *, - filters: Optional[Iterable[Any]] = ..., - fields: Optional[Iterable[str]] = ..., + filters: Iterable[Any] | None = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[False] = ..., @@ -108,8 +101,8 @@ def _get_by_unique_field( field: str, value: T, *, - filters: Optional[Iterable[Any]] = ..., - fields: Optional[Iterable[str]] = ..., + filters: Iterable[Any] | None = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -121,12 +114,12 @@ def _get_by_unique_field( field: str, value: T, *, - filters: Optional[Iterable[Any]] = ..., - fields: Optional[Iterable[str]] = ..., + filters: Iterable[Any] | None = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[True], - ) -> Optional[Dict[str, Any]]: ... + ) -> dict[str, Any] | None: ... @overload def _get_by_unique_field( @@ -134,12 +127,12 @@ def _get_by_unique_field( field: str, value: T, *, - filters: Optional[Iterable[Any]] = ..., - fields: Optional[Iterable[str]] = ..., + filters: Iterable[Any] | None = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[False] = ..., - ) -> Dict[str, Any]: ... + ) -> dict[str, Any]: ... @overload def _get_by_unique_field( @@ -147,12 +140,12 @@ def _get_by_unique_field( field: str, value: T, *, - filters: Optional[Iterable[Any]] = ..., - fields: Optional[Iterable[str]] = ..., + filters: Iterable[Any] | None = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[Record]: ... + ) -> Record | None: ... @overload def _get_by_unique_field( @@ -160,8 +153,8 @@ def _get_by_unique_field( field: str, value: T, *, - filters: Optional[Iterable[Any]] = ..., - fields: Optional[Iterable[str]] = ..., + filters: Iterable[Any] | None = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -173,23 +166,23 @@ def _get_by_unique_field( field: str, value: T, *, - filters: Optional[Iterable[Any]] = ..., - fields: Optional[Iterable[str]] = ..., + filters: Iterable[Any] | None = ..., + fields: Iterable[str] | None = ..., as_id: bool = ..., as_dict: bool = ..., optional: bool = ..., - ) -> Optional[Union[Record, int, Dict[str, Any]]]: ... + ) -> Record | int | dict[str, Any] | None: ... def _get_by_unique_field( self, field: str, value: T, - filters: Optional[Iterable[Any]] = None, - fields: Optional[Iterable[str]] = None, + filters: Iterable[Any] | None = None, + fields: Iterable[str] | None = None, as_id: bool = False, as_dict: bool = False, optional: bool = False, - ) -> Optional[Union[Record, int, Dict[str, Any]]]: + ) -> Record | int | dict[str, Any] | None: """Query a unique record by a specific field. A number of parameters are available to configure the return type, @@ -218,9 +211,9 @@ def _get_by_unique_field( :param value: The unique field value :type value: T :param filters: Optional additional filters to apply, defaults to None - :type filters: Optional[Iterable[Any]], optional + :type filters: Iterable[Any] | None, optional :param fields: Fields to select, defaults to ``None`` (select all) - :type fields: Iterable[str] or None, optional + :type fields: Iterable[str] | None, optional :param as_id: Return a record ID, defaults to False :type as_id: bool, optional :param as_dict: Return the record as a dictionary, defaults to False @@ -230,7 +223,7 @@ def _get_by_unique_field( :raises MultipleRecordsFoundError: Multiple records with the same name :raises RecordNotFoundError: Record with the given name not found :return: Query result (or ``None`` if record not found and optional) - :rtype: Optional[Union[Record, int, Dict[str, Any]]] + :rtype: Record | int | dict[str, Any] | None """ field_filter = [(field, "=", value)] try: diff --git a/openstack_odooclient/client.py b/openstack_odooclient/client.py index fd41852..00ea202 100644 --- a/openstack_odooclient/client.py +++ b/openstack_odooclient/client.py @@ -67,21 +67,21 @@ class Client(ClientBase): All parameters must be specified as keyword arguments. :param hostname: Server hostname, required if ``odoo`` is not set - :type hostname: Optional[str], optional + :type hostname: str | None, optional :param database: Database name, required if ``odoo`` is not set - :type database: Optional[str], optional + :type database: str | None, optional :param username: Username, required if ``odoo`` is not set - :type username: Optional[str], optional + :type username: str | None, optional :param password: Password (or API key), required if ``odoo`` is not set - :type password: Optional[str], optional + :type password: str | None, optional :param protocol: Communication protocol, defaults to ``jsonrpc`` :type protocol: str, optional :param port: Access port, defaults to ``8069`` :type port: int, optional :param verify: Configure SSL cert verification, defaults to ``True`` - :type verify: Union[bool, str, Path] + :type verify: bool | str | Path :param version: Server version, defaults to ``None`` (auto-detect) - :type version: Optional[str], optional + :type version: str | None, optional """ account_moves: AccountMoveManager diff --git a/openstack_odooclient/managers/account_move.py b/openstack_odooclient/managers/account_move.py index f2d3283..c401485 100644 --- a/openstack_odooclient/managers/account_move.py +++ b/openstack_odooclient/managers/account_move.py @@ -16,13 +16,14 @@ from __future__ import annotations from datetime import date -from typing import Any, Iterable, List, Literal, Mapping, Optional, Union - -from typing_extensions import Annotated +from typing import TYPE_CHECKING, Annotated, Any, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping + class AccountMove(RecordBase["AccountMoveManager"]): amount_total: float @@ -51,7 +52,7 @@ class AccountMove(RecordBase["AccountMoveManager"]): """The due date that the account move (invoice) must be paid by.""" invoice_line_ids: Annotated[ - List[int], + list[int], ModelRef("invoice_line_ids", AccountMoveLine), ] """The list of the IDs for the account move (invoice) lines @@ -59,7 +60,7 @@ class AccountMove(RecordBase["AccountMoveManager"]): """ invoice_lines: Annotated[ - List[AccountMoveLine], + list[AccountMoveLine], ModelRef("invoice_line_ids", AccountMoveLine), ] """A list of account move (invoice) lines @@ -94,20 +95,20 @@ class AccountMove(RecordBase["AccountMoveManager"]): * ``in_receipt`` - Purchase Receipt """ - name: Union[str, Literal[False]] + name: str | Literal[False] """Name assigned to the account move (invoice), if posted.""" - os_project_id: Annotated[Optional[int], ModelRef("os_project", Project)] + os_project_id: Annotated[int | None, ModelRef("os_project", Project)] """The ID of the OpenStack project this account move (invoice) was generated for, if this is an invoice for OpenStack project usage. """ - os_project_name: Annotated[Optional[str], ModelRef("os_project", Project)] + os_project_name: Annotated[str | None, ModelRef("os_project", Project)] """The name of the OpenStack project this account move (invoice) was generated for, if this is an invoice for OpenStack project usage. """ - os_project: Annotated[Optional[Project], ModelRef("os_project", Project)] + os_project: Annotated[Project | None, ModelRef("os_project", Project)] """The OpenStack project this account move (invoice) was generated for, if this is an invoice for OpenStack project usage. @@ -162,12 +163,12 @@ def action_post(self) -> None: def send_openstack_invoice_email( self, - email_ctx: Optional[Mapping[str, Any]] = None, + email_ctx: Mapping[str, Any] | None = None, ) -> None: """Send an OpenStack invoice email for this account move (invoice). :param email_ctx: Optional email context, defaults to None - :type email_ctx: Optional[Mapping[str, Any]], optional + :type email_ctx: Mapping[str, Any] | None, optional """ self._env.send_openstack_invoice_email( self.id, @@ -181,11 +182,7 @@ class AccountMoveManager(NamedRecordManagerBase[AccountMove]): def action_post( self, - *account_moves: Union[ - int, - AccountMove, - Iterable[Union[int, AccountMove]], - ], + *account_moves: int | AccountMove | Iterable[int | AccountMove], ) -> None: """Change one or more draft account moves (invoices) into "posted" state. @@ -198,7 +195,7 @@ def action_post( :param account_moves: Record objects, IDs, or record/ID iterables :type account_moves: int | AccountMove | Iterable[int | AccountMove] """ - _ids: List[int] = [] + _ids: list[int] = [] for ids in account_moves: if isinstance(ids, int): _ids.append(ids) @@ -217,8 +214,8 @@ def action_post( def send_openstack_invoice_email( self, - account_move: Union[int, AccountMove], - email_ctx: Optional[Mapping[str, Any]] = None, + account_move: int | AccountMove, + email_ctx: Mapping[str, Any] | None = None, ) -> None: """Send an OpenStack invoice email for the given account move (invoice). @@ -226,7 +223,7 @@ def send_openstack_invoice_email( :param account_move: The account move (invoice) to send an email for :type account_move: int | AccountMove :param email_ctx: Optional email context, defaults to None - :type email_ctx: Optional[Mapping[str, Any]], optional + :type email_ctx: Mapping[str, Any] | None, optional """ self._env.send_openstack_invoice_email( ( diff --git a/openstack_odooclient/managers/account_move_line.py b/openstack_odooclient/managers/account_move_line.py index ead00eb..c738389 100644 --- a/openstack_odooclient/managers/account_move_line.py +++ b/openstack_odooclient/managers/account_move_line.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import Literal, Optional, Union - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase @@ -61,17 +59,17 @@ class AccountMoveLine(RecordBase["AccountMoveLineManager"]): name: str """Name of the product charged on the account move (invoice) line.""" - os_project_id: Annotated[Optional[int], ModelRef("os_project", Project)] + os_project_id: Annotated[int | None, ModelRef("os_project", Project)] """The ID for the OpenStack project this account move (invoice) line was generated for. """ - os_project_name: Annotated[Optional[str], ModelRef("os_project", Project)] + os_project_name: Annotated[str | None, ModelRef("os_project", Project)] """The name of the OpenStack project this account move (invoice) line was generated for. """ - os_project: Annotated[Optional[Project], ModelRef("os_project", Project)] + os_project: Annotated[Project | None, ModelRef("os_project", Project)] """The OpenStack project this account move (invoice) line was generated for. @@ -79,17 +77,17 @@ class AccountMoveLine(RecordBase["AccountMoveLineManager"]): and caches it for subsequent accesses. """ - os_region: Union[str, Literal[False]] + os_region: str | Literal[False] """The OpenStack region the account move (invoice) line was created from. """ - os_resource_id: Union[str, Literal[False]] + os_resource_id: str | Literal[False] """The OpenStack resource ID for the resource that generated this account move (invoice) line. """ - os_resource_name: Union[str, Literal[False]] + os_resource_name: str | Literal[False] """The name of the OpenStack resource tier or flavour, as used by services such as Distil for rating purposes. @@ -97,7 +95,7 @@ class AccountMoveLine(RecordBase["AccountMoveLineManager"]): for a compute instance, this would be set to the instance's flavour name. """ - os_resource_type: Union[str, Literal[False]] + os_resource_type: str | Literal[False] """A human-readable description of the type of resource captured by this account move (invoice) line. """ diff --git a/openstack_odooclient/managers/company.py b/openstack_odooclient/managers/company.py index 22d57c7..f36a352 100644 --- a/openstack_odooclient/managers/company.py +++ b/openstack_odooclient/managers/company.py @@ -15,9 +15,9 @@ from __future__ import annotations -from typing import List, Literal, Optional, Union +from typing import Annotated, Literal -from typing_extensions import Annotated, Self +from typing_extensions import Self from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase @@ -27,10 +27,10 @@ class Company(RecordBase["CompanyManager"]): active: bool """Whether or not this company is active (enabled).""" - child_ids: Annotated[List[int], ModelRef("child_ids", Self)] + child_ids: Annotated[list[int], ModelRef("child_ids", Self)] """A list of IDs for the child companies.""" - children: Annotated[List[Self], ModelRef("child_ids", Self)] + children: Annotated[list[Self], ModelRef("child_ids", Self)] """The list of child companies. This fetches the full records from Odoo once, @@ -40,17 +40,17 @@ class Company(RecordBase["CompanyManager"]): name: str """Company name, set from the partner name.""" - parent_id: Annotated[Optional[int], ModelRef("parent_id", Self)] + parent_id: Annotated[int | None, ModelRef("parent_id", Self)] """The ID for the parent company, if this company is the child of another company. """ - parent_name: Annotated[Optional[str], ModelRef("parent_id", Self)] + parent_name: Annotated[str | None, ModelRef("parent_id", Self)] """The name of the parent company, if this company is the child of another company. """ - parent: Annotated[Optional[Self], ModelRef("parent_id", Self)] + parent: Annotated[Self | None, ModelRef("parent_id", Self)] """The parent company, if this company is the child of another company. @@ -58,7 +58,7 @@ class Company(RecordBase["CompanyManager"]): and caches it for subsequent accesses. """ - parent_path: Union[str, Literal[False]] + parent_path: str | Literal[False] """The path of the parent company, if there is a parent.""" partner_id: Annotated[int, ModelRef("partner_id", Partner)] diff --git a/openstack_odooclient/managers/credit.py b/openstack_odooclient/managers/credit.py index c09409c..ea55c7e 100644 --- a/openstack_odooclient/managers/credit.py +++ b/openstack_odooclient/managers/credit.py @@ -16,9 +16,7 @@ from __future__ import annotations from datetime import date -from typing import List, Optional - -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase @@ -54,7 +52,7 @@ class Credit(RecordBase["CreditManager"]): """The start date of the credit.""" transaction_ids: Annotated[ - List[int], + list[int], ModelRef("transactions", CreditTransaction), ] """A list of IDs for the transactions that have been made @@ -62,7 +60,7 @@ class Credit(RecordBase["CreditManager"]): """ transactions: Annotated[ - List[CreditTransaction], + list[CreditTransaction], ModelRef("transactions", CreditTransaction), ] """The transactions that have been made using this credit. @@ -72,7 +70,7 @@ class Credit(RecordBase["CreditManager"]): """ voucher_code_id: Annotated[ - Optional[int], + int | None, ModelRef("voucher_code", VoucherCode), ] """The ID of the voucher code used when applying for the credit, @@ -80,7 +78,7 @@ class Credit(RecordBase["CreditManager"]): """ voucher_code_name: Annotated[ - Optional[str], + str | None, ModelRef("voucher_code", VoucherCode), ] """The name of the voucher code used when applying for the credit, @@ -88,7 +86,7 @@ class Credit(RecordBase["CreditManager"]): """ voucher_code: Annotated[ - Optional[VoucherCode], + VoucherCode | None, ModelRef("voucher_code", VoucherCode), ] """The voucher code used when applying for the credit, diff --git a/openstack_odooclient/managers/credit_transaction.py b/openstack_odooclient/managers/credit_transaction.py index a8c3eca..7f60b51 100644 --- a/openstack_odooclient/managers/credit_transaction.py +++ b/openstack_odooclient/managers/credit_transaction.py @@ -15,7 +15,7 @@ from __future__ import annotations -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase diff --git a/openstack_odooclient/managers/credit_type.py b/openstack_odooclient/managers/credit_type.py index decde0a..e9af61a 100644 --- a/openstack_odooclient/managers/credit_type.py +++ b/openstack_odooclient/managers/credit_type.py @@ -15,19 +15,17 @@ from __future__ import annotations -from typing import List - -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase class CreditType(RecordBase["CreditTypeManager"]): - credit_ids: Annotated[List[int], ModelRef("credits", Credit)] + credit_ids: Annotated[list[int], ModelRef("credits", Credit)] """A list of IDs for the credits which are of this credit type.""" - credits: Annotated[List[Credit], ModelRef("credits", Credit)] + credits: Annotated[list[Credit], ModelRef("credits", Credit)] """A list of credits which are of this credit type. This fetches the full records from Odoo once, @@ -38,7 +36,7 @@ class CreditType(RecordBase["CreditTypeManager"]): """Name of the Credit Type.""" only_for_product_ids: Annotated[ - List[int], + list[int], ModelRef("only_for_products", Product), ] """A list of IDs for the products this credit applies to. @@ -49,7 +47,7 @@ class CreditType(RecordBase["CreditTypeManager"]): """ only_for_products: Annotated[ - List[Product], + list[Product], ModelRef("only_for_products", Product), ] """A list of products which this credit applies to. @@ -63,7 +61,7 @@ class CreditType(RecordBase["CreditTypeManager"]): """ only_for_product_category_ids: Annotated[ - List[int], + list[int], ModelRef("only_for_product_categories", ProductCategory), ] """A list of IDs for the product categories this credit applies to. @@ -73,7 +71,7 @@ class CreditType(RecordBase["CreditTypeManager"]): """ only_for_product_categories: Annotated[ - List[ProductCategory], + list[ProductCategory], ModelRef("only_for_product_categories", ProductCategory), ] """A list of product categories which this credit applies to. diff --git a/openstack_odooclient/managers/currency.py b/openstack_odooclient/managers/currency.py index 8393ac8..b8d4d2e 100644 --- a/openstack_odooclient/managers/currency.py +++ b/openstack_odooclient/managers/currency.py @@ -16,7 +16,7 @@ from __future__ import annotations from datetime import date as datetime_date -from typing import Literal, Union +from typing import Literal from ..base.record import RecordBase from ..base.record_manager_named import NamedRecordManagerBase @@ -26,10 +26,10 @@ class Currency(RecordBase["CurrencyManager"]): active: bool """Whether or not this currency is active (enabled).""" - currency_unit_label: Union[str, Literal[False]] + currency_unit_label: str | Literal[False] """The unit label for this currency, if set.""" - currency_subunit_label: Union[str, Literal[False]] + currency_subunit_label: str | Literal[False] """The sub-unit label for this currency, if set.""" date: datetime_date diff --git a/openstack_odooclient/managers/customer_group.py b/openstack_odooclient/managers/customer_group.py index 2208e5b..76e4aae 100644 --- a/openstack_odooclient/managers/customer_group.py +++ b/openstack_odooclient/managers/customer_group.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import List, Optional - -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase @@ -27,32 +25,29 @@ class CustomerGroup(RecordBase["CustomerGroupManager"]): name: str """The name of the customer group.""" - partner_ids: Annotated[List[int], ModelRef("partners", Partner)] + partner_ids: Annotated[list[int], ModelRef("partners", Partner)] """A list of IDs for the partners that are part of this customer group. """ - partners: Annotated[List[Partner], ModelRef("partners", Partner)] + partners: Annotated[list[Partner], ModelRef("partners", Partner)] """The partners that are part of this customer group. This fetches the full records from Odoo once, and caches them for subsequent accesses. """ - pricelist_id: Annotated[Optional[int], ModelRef("pricelist", Pricelist)] + pricelist_id: Annotated[int | None, ModelRef("pricelist", Pricelist)] """The ID for the pricelist this customer group uses, if not the default one. """ - pricelist_name: Annotated[Optional[str], ModelRef("pricelist", Pricelist)] + pricelist_name: Annotated[str | None, ModelRef("pricelist", Pricelist)] """The name of the pricelist this customer group uses, if not the default one. """ - pricelist: Annotated[ - Optional[Pricelist], - ModelRef("pricelist", Pricelist), - ] + pricelist: Annotated[Pricelist | None, ModelRef("pricelist", Pricelist)] """The pricelist this customer group uses, if not the default one. This fetches the full record from Odoo once, diff --git a/openstack_odooclient/managers/grant.py b/openstack_odooclient/managers/grant.py index 1669cd9..5b4d33c 100644 --- a/openstack_odooclient/managers/grant.py +++ b/openstack_odooclient/managers/grant.py @@ -16,9 +16,7 @@ from __future__ import annotations from datetime import date -from typing import Optional - -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase @@ -51,7 +49,7 @@ class Grant(RecordBase["GrantManager"]): """The value of the grant.""" voucher_code_id: Annotated[ - Optional[int], + int | None, ModelRef("voucher_code", VoucherCode), ] """The ID of the voucher code used when applying for the grant, @@ -59,7 +57,7 @@ class Grant(RecordBase["GrantManager"]): """ voucher_code_name: Annotated[ - Optional[str], + str | None, ModelRef("voucher_code", VoucherCode), ] """The name of the voucher code used when applying for the grant, @@ -67,7 +65,7 @@ class Grant(RecordBase["GrantManager"]): """ voucher_code: Annotated[ - Optional[VoucherCode], + VoucherCode | None, ModelRef("voucher_code", VoucherCode), ] """The voucher code used when applying for the grant, diff --git a/openstack_odooclient/managers/grant_type.py b/openstack_odooclient/managers/grant_type.py index 032b384..641d922 100644 --- a/openstack_odooclient/managers/grant_type.py +++ b/openstack_odooclient/managers/grant_type.py @@ -15,19 +15,17 @@ from __future__ import annotations -from typing import List - -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase class GrantType(RecordBase["GrantTypeManager"]): - grant_ids: Annotated[List[int], ModelRef("grants", Grant)] + grant_ids: Annotated[list[int], ModelRef("grants", Grant)] """A list of IDs for the grants which are of this grant type.""" - grants: Annotated[List[Grant], ModelRef("grants", Grant)] + grants: Annotated[list[Grant], ModelRef("grants", Grant)] """A list of grants which are of this grant type. This fetches the full records from Odoo once, @@ -38,7 +36,7 @@ class GrantType(RecordBase["GrantTypeManager"]): """Name of the Grant Type.""" only_for_product_ids: Annotated[ - List[int], + list[int], ModelRef("only_for_products", Product), ] """A list of IDs for the products this grant applies to. @@ -48,7 +46,7 @@ class GrantType(RecordBase["GrantTypeManager"]): """ only_for_products: Annotated[ - List[Product], + list[Product], ModelRef("only_for_products", Product), ] """A list of products which this grant applies to. @@ -61,7 +59,7 @@ class GrantType(RecordBase["GrantTypeManager"]): """ only_for_product_category_ids: Annotated[ - List[int], + list[int], ModelRef( "only_for_product_categories", ProductCategory, @@ -75,7 +73,7 @@ class GrantType(RecordBase["GrantTypeManager"]): """ only_for_product_categories: Annotated[ - List[ProductCategory], + list[ProductCategory], ModelRef( "only_for_product_categories", ProductCategory, diff --git a/openstack_odooclient/managers/partner.py b/openstack_odooclient/managers/partner.py index ed92cc8..1fdd814 100644 --- a/openstack_odooclient/managers/partner.py +++ b/openstack_odooclient/managers/partner.py @@ -15,9 +15,9 @@ from __future__ import annotations -from typing import List, Literal, Optional, Union +from typing import Annotated, Literal -from typing_extensions import Annotated, Self +from typing_extensions import Self from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase @@ -47,7 +47,7 @@ class Partner(RecordBase["PartnerManager"]): """Full name of the partner.""" os_customer_group_id: Annotated[ - Optional[int], + int | None, ModelRef("os_customer_group", CustomerGroup), ] """The ID for the customer group this partner is part of, @@ -55,7 +55,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_customer_group_name: Annotated[ - Optional[str], + str | None, ModelRef("os_customer_group", CustomerGroup), ] """The name of the customer group this partner is part of, @@ -63,7 +63,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_customer_group: Annotated[ - Optional[CustomerGroup], + CustomerGroup | None, ModelRef("os_customer_group", CustomerGroup), ] """The customer group this partner is part of, @@ -73,12 +73,12 @@ class Partner(RecordBase["PartnerManager"]): and caches it for subsequent accesses. """ - os_project_ids: Annotated[List[int], ModelRef("os_projects", Project)] + os_project_ids: Annotated[list[int], ModelRef("os_projects", Project)] """A list of IDs for the OpenStack projects that belong to this partner. """ - os_projects: Annotated[List[Project], ModelRef("os_projects", Project)] + os_projects: Annotated[list[Project], ModelRef("os_projects", Project)] """The OpenStack projects that belong to this partner. This fetches the full records from Odoo once, @@ -86,7 +86,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_project_contact_ids: Annotated[ - List[int], + list[int], ModelRef("os_project_contacts", ProjectContact), ] """A list of IDs for the project contacts that are associated @@ -94,7 +94,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_project_contacts: Annotated[ - List[ProjectContact], + list[ProjectContact], ModelRef("os_project_contacts", ProjectContact), ] """The project contacts that are associated with this partner. @@ -104,7 +104,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_referral_id: Annotated[ - Optional[int], + int | None, ModelRef("os_referral", ReferralCode), ] """The ID for the referral code the partner used on sign-up, @@ -112,7 +112,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_referral_name: Annotated[ - Optional[str], + str | None, ModelRef("os_referral", ReferralCode), ] """The name of the referral code the partner used on sign-up, @@ -120,7 +120,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_referral: Annotated[ - Optional[ReferralCode], + ReferralCode | None, ModelRef("os_referral", ReferralCode), ] """The referral code the partner used on sign-up, if one was used. @@ -130,13 +130,13 @@ class Partner(RecordBase["PartnerManager"]): """ os_referral_code_ids: Annotated[ - List[int], + list[int], ModelRef("os_referral_codes", ReferralCode), ] """A list of IDs for the referral codes the partner has used.""" os_referral_codes: Annotated[ - List[ReferralCode], + list[ReferralCode], ModelRef("os_referral_codes", ReferralCode), ] """The referral codes the partner has used. @@ -146,7 +146,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_reseller_id: Annotated[ - Optional[int], + int | None, ModelRef("os_reseller", Reseller), ] """The ID for the reseller for this partner, if this partner @@ -154,7 +154,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_reseller_name: Annotated[ - Optional[str], + str | None, ModelRef("os_reseller", Reseller), ] """The name of the reseller for this partner, if this partner @@ -162,7 +162,7 @@ class Partner(RecordBase["PartnerManager"]): """ os_reseller: Annotated[ - Optional[Reseller], + Reseller | None, ModelRef("os_reseller", Reseller), ] """The reseller for this partner, if this partner @@ -172,17 +172,17 @@ class Partner(RecordBase["PartnerManager"]): and caches it for subsequent accesses. """ - os_trial_id: Annotated[Optional[int], ModelRef("os_trial", Trial)] + os_trial_id: Annotated[int | None, ModelRef("os_trial", Trial)] """The ID for the sign-up trial for this partner, if signed up under a trial. """ - os_trial_name: Annotated[Optional[str], ModelRef("os_trial", Trial)] + os_trial_name: Annotated[str | None, ModelRef("os_trial", Trial)] """The name of the sign-up trial for this partner, if signed up under a trial. """ - os_trial: Annotated[Optional[Trial], ModelRef("os_trial", Trial)] + os_trial: Annotated[Trial | None, ModelRef("os_trial", Trial)] """The sign-up trial for this partner, if signed up under a trial. @@ -191,7 +191,7 @@ class Partner(RecordBase["PartnerManager"]): """ parent_id: Annotated[ - Optional[int], + int | None, ModelRef("parent_id", Self), ] """The ID for the parent partner of this partner, @@ -199,14 +199,14 @@ class Partner(RecordBase["PartnerManager"]): """ parent_name: Annotated[ - Optional[str], + str | None, ModelRef("parent_id", Self), ] """The name of the parent partner of this partner, if it has a parent. """ - parent: Annotated[Optional[Self], ModelRef("parent_id", Self)] + parent: Annotated[Self | None, ModelRef("parent_id", Self)] """The parent partner of this partner, if it has a parent. @@ -215,7 +215,7 @@ class Partner(RecordBase["PartnerManager"]): """ property_product_pricelist_id: Annotated[ - Optional[int], + int | None, ModelRef("property_product_pricelist", Pricelist), ] """The ID for the pricelist this partner uses, if explicitly set. @@ -226,7 +226,7 @@ class Partner(RecordBase["PartnerManager"]): """ property_product_pricelist_name: Annotated[ - Optional[str], + str | None, ModelRef("property_product_pricelist", Pricelist), ] """The name of the pricelist this partner uses, if explicitly set. @@ -237,7 +237,7 @@ class Partner(RecordBase["PartnerManager"]): """ property_product_pricelist: Annotated[ - Optional[Pricelist], + Pricelist | None, ModelRef("property_product_pricelist", Pricelist), ] """The pricelist this partner uses, if explicitly set. @@ -250,20 +250,20 @@ class Partner(RecordBase["PartnerManager"]): and caches it for subsequent accesses. """ - stripe_customer_id: Union[str, Literal[False]] + stripe_customer_id: str | Literal[False] """The Stripe customer ID for this partner, if one has been assigned.""" - user_id: Annotated[Optional[int], ModelRef("user_id", User)] + user_id: Annotated[int | None, ModelRef("user_id", User)] """The ID of the internal user associated with this partner, if one is assigned. """ - user_name: Annotated[Optional[str], ModelRef("user_id", User)] + user_name: Annotated[str | None, ModelRef("user_id", User)] """The name of the internal user associated with this partner, if one is assigned. """ - user: Annotated[Optional[User], ModelRef("user_id", User)] + user: Annotated[User | None, ModelRef("user_id", User)] """The internal user associated with this partner, if one is assigned. diff --git a/openstack_odooclient/managers/partner_category.py b/openstack_odooclient/managers/partner_category.py index 762b5cf..849a7bf 100644 --- a/openstack_odooclient/managers/partner_category.py +++ b/openstack_odooclient/managers/partner_category.py @@ -15,9 +15,9 @@ from __future__ import annotations -from typing import List, Literal, Optional, Union +from typing import Annotated, Literal -from typing_extensions import Annotated, Self +from typing_extensions import Self from ..base.record import FieldAlias, ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase @@ -27,10 +27,10 @@ class PartnerCategory(RecordBase["PartnerCategoryManager"]): active: bool """Whether or not the partner category is active (enabled).""" - child_ids: Annotated[List[int], ModelRef("child_id", Self)] + child_ids: Annotated[list[int], ModelRef("child_id", Self)] """A list of IDs for the child categories.""" - children: Annotated[List[Self], ModelRef("child_id", Self)] + children: Annotated[list[Self], ModelRef("child_id", Self)] """The list of child categories. This fetches the full records from Odoo once, @@ -46,17 +46,17 @@ class PartnerCategory(RecordBase["PartnerCategoryManager"]): name: str """The name of the partner category.""" - parent_id: Annotated[Optional[int], ModelRef("parent_id", Self)] + parent_id: Annotated[int | None, ModelRef("parent_id", Self)] """The ID for the parent partner category, if this category is the child of another category. """ - parent_name: Annotated[Optional[str], ModelRef("parent_id", Self)] + parent_name: Annotated[str | None, ModelRef("parent_id", Self)] """The name of the parent partner category, if this category is the child of another category. """ - parent: Annotated[Optional[Self], ModelRef("parent_id", Self)] + parent: Annotated[Self | None, ModelRef("parent_id", Self)] """The parent partner category, if this category is the child of another category. @@ -64,13 +64,13 @@ class PartnerCategory(RecordBase["PartnerCategoryManager"]): and caches it for subsequent accesses. """ - parent_path: Union[str, Literal[False]] + parent_path: str | Literal[False] """The path of the parent partner category, if there is a parent.""" - partner_ids: Annotated[List[int], ModelRef("partner_id", Partner)] + partner_ids: Annotated[list[int], ModelRef("partner_id", Partner)] """A list of IDs for the partners in this category.""" - partners: Annotated[List[Partner], ModelRef("partner_id", Partner)] + partners: Annotated[list[Partner], ModelRef("partner_id", Partner)] """The list of partners in this category. This fetches the full records from Odoo once, diff --git a/openstack_odooclient/managers/pricelist.py b/openstack_odooclient/managers/pricelist.py index ef44149..bb8d2b1 100644 --- a/openstack_odooclient/managers/pricelist.py +++ b/openstack_odooclient/managers/pricelist.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import Literal, Optional, Union - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase @@ -27,13 +25,13 @@ class Pricelist(RecordBase["PricelistManager"]): active: bool """Whether or not the pricelist is active.""" - company_id: Annotated[Optional[int], ModelRef("company_id", Company)] + company_id: Annotated[int | None, ModelRef("company_id", Company)] """The ID for the company for this pricelist, if set.""" - company_name: Annotated[Optional[str], ModelRef("company_id", Company)] + company_name: Annotated[str | None, ModelRef("company_id", Company)] """The name of the company for this pricelist, if set.""" - company: Annotated[Optional[Company], ModelRef("company_id", Company)] + company: Annotated[Company | None, ModelRef("company_id", Company)] """The company for this pricelist, if set. This fetches the full record from Odoo once, @@ -65,11 +63,11 @@ class Pricelist(RecordBase["PricelistManager"]): name: str """The name of this pricelist.""" - def get_price(self, product: Union[int, Product], qty: float) -> float: + def get_price(self, product: int | Product, qty: float) -> float: """Get the price to charge for a given product and quantity. :param product: Product to get the price for (ID or object) - :type product: int or Product + :type product: int | Product :param qty: Quantity to charge for :type qty: float :return: Price to charge @@ -89,17 +87,17 @@ class PricelistManager(NamedRecordManagerBase[Pricelist]): def get_price( self, - pricelist: Union[int, Pricelist], - product: Union[int, Product], + pricelist: int | Pricelist, + product: int | Product, qty: float, ) -> float: """Get the price to charge for a given pricelist, product and quantity. :param pricelist: Pricelist to reference (ID or object) - :type pricelist: int or Pricelist + :type pricelist: int | Pricelist :param product: Product to get the price for (ID or object) - :type product: int or Product + :type product: int | Product :param qty: Quantity to charge for :type qty: float :return: Price to charge @@ -115,8 +113,8 @@ def get_price( def get_price( manager: PricelistManager, - pricelist: Union[int, Pricelist], - product: Union[int, Product], + pricelist: int | Pricelist, + product: int | Product, qty: float, ) -> float: pricelist_id = ( diff --git a/openstack_odooclient/managers/product.py b/openstack_odooclient/managers/product.py index 17c6eee..d57eaae 100644 --- a/openstack_odooclient/managers/product.py +++ b/openstack_odooclient/managers/product.py @@ -15,24 +15,16 @@ from __future__ import annotations -from typing import ( - Any, - Dict, - Iterable, - List, - Literal, - Optional, - Union, - overload, -) - -from typing_extensions import Annotated +from typing import TYPE_CHECKING, Annotated, Any, Literal, overload from ..base.record import ModelRef, RecordBase from ..base.record_manager_with_unique_field import ( RecordManagerWithUniqueFieldBase, ) +if TYPE_CHECKING: + from collections.abc import Iterable + class Product(RecordBase["ProductManager"]): categ_id: Annotated[int, ModelRef("categ_id", ProductCategory)] @@ -48,13 +40,13 @@ class Product(RecordBase["ProductManager"]): and caches it for subsequent accesses. """ - company_id: Annotated[Optional[int], ModelRef("company_id", Company)] + company_id: Annotated[int | None, ModelRef("company_id", Company)] """The ID for the company that owns this product, if set.""" - company_name: Annotated[Optional[str], ModelRef("company_id", Company)] + company_name: Annotated[str | None, ModelRef("company_id", Company)] """The name of the company that owns this product, if set.""" - company: Annotated[Optional[Company], ModelRef("company_id", Company)] + company: Annotated[Company | None, ModelRef("company_id", Company)] """The company that owns this product, if set. This fetches the full record from Odoo once, @@ -104,80 +96,80 @@ class ProductManager(RecordManagerWithUniqueFieldBase[Product, str]): @overload def get_sellable_company_products( self, - company: Union[int, Company], + company: int | Company, *, - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + fields: Iterable[str] | None = ..., + order: str | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., - ) -> List[Product]: ... + ) -> list[Product]: ... @overload def get_sellable_company_products( self, - company: Union[int, Company], + company: int | Company, *, - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + fields: Iterable[str] | None = ..., + order: str | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., - ) -> List[int]: ... + ) -> list[int]: ... @overload def get_sellable_company_products( self, - company: Union[int, Company], - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + company: int | Company, + fields: Iterable[str] | None = ..., + order: str | None = ..., *, as_id: Literal[True], as_dict: Literal[True], - ) -> List[int]: ... + ) -> list[int]: ... @overload def get_sellable_company_products( self, - company: Union[int, Company], + company: int | Company, *, - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + fields: Iterable[str] | None = ..., + order: str | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], - ) -> List[Dict[str, Any]]: ... + ) -> list[dict[str, Any]]: ... @overload def get_sellable_company_products( self, - company: Union[int, Company], + company: int | Company, *, - fields: Optional[Iterable[str]] = ..., - order: Optional[str] = ..., + fields: Iterable[str] | None = ..., + order: str | None = ..., as_id: bool = ..., as_dict: bool = ..., - ) -> Union[List[Product], List[int], Union[List[Dict[str, Any]]]]: ... + ) -> list[Product] | list[int] | list[dict[str, Any]]: ... def get_sellable_company_products( self, - company: Union[int, Company], - fields: Optional[Iterable[str]] = None, - order: Optional[str] = None, + company: int | Company, + fields: Iterable[str] | None = None, + order: str | None = None, as_id: bool = False, as_dict: bool = False, - ) -> Union[List[Product], List[int], Union[List[Dict[str, Any]]]]: + ) -> list[Product] | list[int] | list[dict[str, Any]]: """Fetch a list of active and saleable products for the given company. :param company: The company to search for products (ID or object) :type company: int | Company :param fields: Fields to select, defaults to ``None`` (select all) - :type fields: Iterable[str] or None, optional + :type fields: Iterable[str] | None, optional :param order: Order results by a specific field, defaults to None - :type order: Optional[str], optional + :type order: str | None, optional :param as_id: Return the record IDs only, defaults to False :type as_id: bool, optional :param as_dict: Return records as dictionaries, defaults to False :type as_dict: bool, optional :return: List of products - :rtype: Union[List[Product], List[int], Union[Dict[str, Any]]] + :rtype: list[Product] | list[int] | list[dict[str, Any]] """ return self.search( [ @@ -194,34 +186,34 @@ def get_sellable_company_products( @overload def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[True], - ) -> Optional[int]: ... + ) -> int | None: ... @overload def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[int]: ... + ) -> int | None: ... @overload def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[False] = ..., @@ -230,10 +222,10 @@ def get_sellable_company_product_by_name( @overload def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -242,46 +234,46 @@ def get_sellable_company_product_by_name( @overload def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[True], - ) -> Optional[Dict[str, Any]]: ... + ) -> dict[str, Any] | None: ... @overload def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[False] = ..., - ) -> Dict[str, Any]: ... + ) -> dict[str, Any]: ... @overload def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[Product]: ... + ) -> Product | None: ... @overload def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -290,24 +282,24 @@ def get_sellable_company_product_by_name( @overload def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: bool = ..., as_dict: bool = ..., optional: bool = ..., - ) -> Optional[Union[Product, int, Dict[str, Any]]]: ... + ) -> Product | int | dict[str, Any] | None: ... def get_sellable_company_product_by_name( self, - company: Union[int, Company], + company: int | Company, name: str, - fields: Optional[Iterable[str]] = None, + fields: Iterable[str] | None = None, as_id: bool = False, as_dict: bool = False, optional: bool = False, - ) -> Optional[Union[Product, int, Dict[str, Any]]]: + ) -> Product | int | dict[str, Any] | None: """Query a unique product for the given company by name. A number of parameters are available to configure the return type, @@ -331,7 +323,7 @@ def get_sellable_company_product_by_name( :param name: The product name :type name: str :param fields: Fields to select, defaults to ``None`` (select all) - :type fields: Iterable[str] or None, optional + :type fields: Iterable[str] | None, optional :param as_id: Return a record ID, defaults to False :type as_id: bool, optional :param as_dict: Return the record as a dictionary, defaults to False @@ -341,7 +333,7 @@ def get_sellable_company_product_by_name( :raises MultipleRecordsFoundError: Multiple records with the same name :raises RecordNotFoundError: Record with the given name not found :return: Product (or ``None`` if record not found and optional) - :rtype: Optional[Union[Record, int, Dict[str, Any]]] + :rtype: Record | int | dict[str, Any] | None """ return self._get_by_unique_field( field="name", diff --git a/openstack_odooclient/managers/product_category.py b/openstack_odooclient/managers/product_category.py index 3ae0753..029e990 100644 --- a/openstack_odooclient/managers/product_category.py +++ b/openstack_odooclient/managers/product_category.py @@ -15,22 +15,22 @@ from __future__ import annotations -from typing import List, Literal, Optional, Union +from typing import Annotated, Literal -from typing_extensions import Annotated, Self +from typing_extensions import Self from ..base.record import FieldAlias, ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase class ProductCategory(RecordBase["ProductCategoryManager"]): - child_id: Annotated[List[int], ModelRef("child_id", Self)] + child_id: Annotated[list[int], ModelRef("child_id", Self)] """A list of IDs for the child categories.""" - child_ids: Annotated[List[int], FieldAlias("child_id")] + child_ids: Annotated[list[int], FieldAlias("child_id")] """An alias for ``child_id``.""" - children: Annotated[List[Self], ModelRef("child_id", Self)] + children: Annotated[list[Self], ModelRef("child_id", Self)] """The list of child categories. This fetches the full records from Odoo once, @@ -43,17 +43,17 @@ class ProductCategory(RecordBase["ProductCategoryManager"]): name: str """Name of the product category.""" - parent_id: Annotated[Optional[int], ModelRef("parent_id", Self)] + parent_id: Annotated[int | None, ModelRef("parent_id", Self)] """The ID for the parent product category, if this category is the child of another category. """ - parent_name: Annotated[Optional[str], ModelRef("parent_id", Self)] + parent_name: Annotated[str | None, ModelRef("parent_id", Self)] """The name of the parent product category, if this category is the child of another category. """ - parent: Annotated[Optional[Self], ModelRef("parent_id", Self)] + parent: Annotated[Self | None, ModelRef("parent_id", Self)] """The parent product category, if this category is the child of another category. @@ -61,7 +61,7 @@ class ProductCategory(RecordBase["ProductCategoryManager"]): and caches it for subsequent accesses. """ - parent_path: Union[str, Literal[False]] + parent_path: str | Literal[False] """The path of the parent product category, if there is a parent.""" product_count: int diff --git a/openstack_odooclient/managers/project.py b/openstack_odooclient/managers/project.py index def5762..8a322fb 100644 --- a/openstack_odooclient/managers/project.py +++ b/openstack_odooclient/managers/project.py @@ -15,24 +15,18 @@ from __future__ import annotations -from typing import ( - Any, - Dict, - Iterable, - List, - Literal, - Optional, - Union, - overload, -) +from typing import TYPE_CHECKING, Annotated, Any, Literal, overload -from typing_extensions import Annotated, Self +from typing_extensions import Self from ..base.record import ModelRef, RecordBase from ..base.record_manager_with_unique_field import ( RecordManagerWithUniqueFieldBase, ) +if TYPE_CHECKING: + from collections.abc import Iterable + class Project(RecordBase["ProjectManager"]): billing_type: Literal["customer", "internal"] @@ -77,17 +71,17 @@ class Project(RecordBase["ProjectManager"]): and caches it for subsequent accesses. """ - parent_id: Annotated[Optional[int], ModelRef("parent", Self)] + parent_id: Annotated[int | None, ModelRef("parent", Self)] """The ID for the parent project, if this project is the child of another project. """ - parent_name: Annotated[Optional[str], ModelRef("parent", Self)] + parent_name: Annotated[str | None, ModelRef("parent", Self)] """The name of the parent project, if this project is the child of another project. """ - parent: Annotated[Optional[Self], ModelRef("parent", Self)] + parent: Annotated[Self | None, ModelRef("parent", Self)] """The parent project, if this project is the child of another project. @@ -104,17 +98,17 @@ class Project(RecordBase["ProjectManager"]): * ``credit_card`` - Project is paid by credit card """ - po_number: Union[str, Literal[False]] - """The PO number set for this specific Project (if set).""" + po_number: str | Literal[False] + """The PO number set for this specific project (if set).""" project_contact_ids: Annotated[ - List[int], + list[int], ModelRef("project_contacts", ProjectContact), ] """A list of IDs for the contacts for this project.""" project_contacts: Annotated[ - List[ProjectContact], + list[ProjectContact], ModelRef("project_contacts", ProjectContact), ] """The contacts for this project. @@ -124,13 +118,13 @@ class Project(RecordBase["ProjectManager"]): """ project_credit_ids: Annotated[ - List[int], + list[int], ModelRef("project_credits", Credit), ] """A list of IDs for the credits that apply to this project.""" project_credits: Annotated[ - List[Credit], + list[Credit], ModelRef("project_credits", Credit), ] """The credits that apply to this project. @@ -139,17 +133,17 @@ class Project(RecordBase["ProjectManager"]): and caches them for subsequent accesses. """ - project_grant_ids: Annotated[List[int], ModelRef("project_grants", Grant)] + project_grant_ids: Annotated[list[int], ModelRef("project_grants", Grant)] """A list of IDs for the grants that apply to this project.""" - project_grants: Annotated[List[Grant], ModelRef("project_grants", Grant)] + project_grants: Annotated[list[Grant], ModelRef("project_grants", Grant)] """The grants that apply to this project. This fetches the full records from Odoo once, and caches them for subsequent accesses. """ - stripe_card_id: Union[str, Literal[False]] + stripe_card_id: str | Literal[False] """The card ID used for credit card payments on this project using Stripe, if the payment method is set to ``credit_card``. @@ -158,7 +152,7 @@ class Project(RecordBase["ProjectManager"]): """ support_subscription_id: Annotated[ - Optional[int], + int | None, ModelRef("support_subscription", SupportSubscription), ] """The ID for the support subscription for this project, @@ -166,7 +160,7 @@ class Project(RecordBase["ProjectManager"]): """ support_subscription_name: Annotated[ - Optional[str], + str | None, ModelRef("support_subscription", SupportSubscription), ] """The name of the support subscription for this project, @@ -174,7 +168,7 @@ class Project(RecordBase["ProjectManager"]): """ support_subscription: Annotated[ - Optional[SupportSubscription], + SupportSubscription | None, ModelRef("support_subscription", SupportSubscription), ] """The support subscription for this project, @@ -185,13 +179,13 @@ class Project(RecordBase["ProjectManager"]): """ term_discount_ids: Annotated[ - List[int], + list[int], ModelRef("term_discounts", TermDiscount), ] """A list of IDs for the term discounts that apply to this project.""" term_discounts: Annotated[ - List[TermDiscount], + list[TermDiscount], ModelRef("term_discounts", TermDiscount), ] """The term discounts that apply to this project. @@ -210,29 +204,29 @@ def get_by_os_id( self, os_id: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[True], - ) -> Optional[int]: ... + ) -> int | None: ... @overload def get_by_os_id( self, os_id: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[int]: ... + ) -> int | None: ... @overload def get_by_os_id( self, os_id: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[True], optional: Literal[False] = ..., @@ -243,7 +237,7 @@ def get_by_os_id( self, os_id: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[True], as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -254,40 +248,40 @@ def get_by_os_id( self, os_id: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[True], - ) -> Optional[Dict[str, Any]]: ... + ) -> dict[str, Any] | None: ... @overload def get_by_os_id( self, os_id: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[True], optional: Literal[False] = ..., - ) -> Dict[str, Any]: ... + ) -> dict[str, Any]: ... @overload def get_by_os_id( self, os_id: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[True], - ) -> Optional[Project]: ... + ) -> Project | None: ... @overload def get_by_os_id( self, os_id: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: Literal[False] = ..., as_dict: Literal[False] = ..., optional: Literal[False] = ..., @@ -298,20 +292,20 @@ def get_by_os_id( self, os_id: str, *, - fields: Optional[Iterable[str]] = ..., + fields: Iterable[str] | None = ..., as_id: bool = ..., as_dict: bool = ..., optional: bool = ..., - ) -> Optional[Union[Project, int, Dict[str, Any]]]: ... + ) -> Project | int | dict[str, Any] | None: ... def get_by_os_id( self, os_id: str, - fields: Optional[Iterable[str]] = None, + fields: Iterable[str] | None = None, as_id: bool = False, as_dict: bool = False, optional: bool = False, - ) -> Optional[Union[Project, int, Dict[str, Any]]]: + ) -> Project | int | dict[str, Any] | None: """Query a unique record by OpenStack project ID. A number of parameters are available to configure the return type, @@ -335,7 +329,7 @@ def get_by_os_id( :param as_id: Return a record ID, defaults to False :type as_id: bool, optional :param fields: Fields to select, defaults to ``None`` (select all) - :type fields: Iterable[str] or None, optional + :type fields: Iterable[str] | None, optional :param as_dict: Return the record as a dictionary, defaults to False :type as_dict: bool, optional :param optional: Return ``None`` if not found, defaults to False @@ -343,7 +337,7 @@ def get_by_os_id( :raises MultipleRecordsFoundError: Multiple with matching project IDs :raises RecordNotFoundError: No record with the given project ID found :return: Query result (or ``None`` if record not found and optional) - :rtype: Optional[Union[Project, int, Dict[str, Any]]] + :rtype: Project | int | dict[str, Any] | None """ return self._get_by_unique_field( field="os_id", diff --git a/openstack_odooclient/managers/project_contact.py b/openstack_odooclient/managers/project_contact.py index 887c6e0..056ece2 100644 --- a/openstack_odooclient/managers/project_contact.py +++ b/openstack_odooclient/managers/project_contact.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import Literal, Optional - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase @@ -49,13 +47,13 @@ class ProjectContact(RecordBase["ProjectContactManager"]): and caches it for subsequent accesses. """ - project_id: Annotated[Optional[int], ModelRef("project", Project)] + project_id: Annotated[int | None, ModelRef("project", Project)] """The ID for the project this contact is linked to, if set.""" - project_name: Annotated[Optional[str], ModelRef("project", Project)] + project_name: Annotated[str | None, ModelRef("project", Project)] """The name of the project this contact is linked to, if set.""" - project: Annotated[Optional[Project], ModelRef("project", Project)] + project: Annotated[Project | None, ModelRef("project", Project)] """The project this contact is linked to, if set. This fetches the full record from Odoo once, diff --git a/openstack_odooclient/managers/referral_code.py b/openstack_odooclient/managers/referral_code.py index e18b908..ea17dad 100644 --- a/openstack_odooclient/managers/referral_code.py +++ b/openstack_odooclient/managers/referral_code.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import List - -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager_coded import CodedRecordManagerBase @@ -41,12 +39,12 @@ class ReferralCode(RecordBase["ReferralCodeManager"]): name: str """Automatically generated name for the referral code.""" - referral_ids: Annotated[List[int], ModelRef("referrals", Partner)] + referral_ids: Annotated[list[int], ModelRef("referrals", Partner)] """A list of IDs for the partners that signed up using this referral code. """ - referrals: Annotated[List[Partner], ModelRef("referrals", Partner)] + referrals: Annotated[list[Partner], ModelRef("referrals", Partner)] """The partners that signed up using this referral code. This fetches the full records from Odoo once, diff --git a/openstack_odooclient/managers/reseller.py b/openstack_odooclient/managers/reseller.py index 490620f..5eef66f 100644 --- a/openstack_odooclient/managers/reseller.py +++ b/openstack_odooclient/managers/reseller.py @@ -15,35 +15,33 @@ from __future__ import annotations -from typing import Optional - -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase class Reseller(RecordBase["ResellerManager"]): - alternative_billing_url: Optional[str] + alternative_billing_url: str | None """The URL to the cloud billing page for the reseller, if available.""" - alternative_support_url: Optional[str] + alternative_support_url: str | None """The URL to the cloud support centre for the reseller, if available.""" demo_project_id: Annotated[ - Optional[int], + int | None, ModelRef("demo_project", Project), ] """The ID for the optional demo project belonging to the reseller.""" demo_project_name: Annotated[ - Optional[str], + str | None, ModelRef("demo_project", Project), ] """The name of the optional demo project belonging to the reseller.""" demo_project: Annotated[ - Optional[Project], + Project | None, ModelRef("demo_project", Project), ] """An optional demo project belonging to the reseller. diff --git a/openstack_odooclient/managers/reseller_tier.py b/openstack_odooclient/managers/reseller_tier.py index ebb345d..41f54f0 100644 --- a/openstack_odooclient/managers/reseller_tier.py +++ b/openstack_odooclient/managers/reseller_tier.py @@ -15,7 +15,7 @@ from __future__ import annotations -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase diff --git a/openstack_odooclient/managers/sale_order.py b/openstack_odooclient/managers/sale_order.py index a4f71a2..a2ee550 100644 --- a/openstack_odooclient/managers/sale_order.py +++ b/openstack_odooclient/managers/sale_order.py @@ -16,9 +16,7 @@ from __future__ import annotations from datetime import date, datetime -from typing import List, Literal, Optional, Union - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import FieldAlias, ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase @@ -34,7 +32,7 @@ class SaleOrder(RecordBase["SaleOrderManager"]): amount_total: float """The taxed total cost of the sale order.""" - client_order_ref: Union[str, Literal[False]] + client_order_ref: str | Literal[False] """The customer reference for this sale order, if defined.""" currency_id: Annotated[int, ModelRef("currency_id", Currency)] @@ -77,13 +75,13 @@ class SaleOrder(RecordBase["SaleOrderManager"]): """ order_line_ids: Annotated[ - List[int], + list[int], ModelRef("order_line", SaleOrderLine), ] """A list of IDs for the lines added to the sale order.""" order_line: Annotated[ - List[SaleOrderLine], + list[SaleOrderLine], ModelRef("order_line", SaleOrderLine), ] """The lines added to the sale order. @@ -92,7 +90,7 @@ class SaleOrder(RecordBase["SaleOrderManager"]): and caches them for subsequent accesses. """ - order_lines: Annotated[List[SaleOrderLine], FieldAlias("order_line")] + order_lines: Annotated[list[SaleOrderLine], FieldAlias("order_line")] """An alias for ``order_line``.""" os_invoice_date: date @@ -105,17 +103,17 @@ class SaleOrder(RecordBase["SaleOrderManager"]): from the sale order. """ - os_project_id: Annotated[Optional[int], ModelRef("os_project", Project)] + os_project_id: Annotated[int | None, ModelRef("os_project", Project)] """The ID for the the OpenStack project this sale order was was generated for. """ - os_project_name: Annotated[Optional[str], ModelRef("os_project", Project)] + os_project_name: Annotated[str | None, ModelRef("os_project", Project)] """The name of the the OpenStack project this sale order was was generated for. """ - os_project: Annotated[Optional[Project], ModelRef("os_project", Project)] + os_project: Annotated[Project | None, ModelRef("os_project", Project)] """The OpenStack project this sale order was was generated for. @@ -160,11 +158,11 @@ class SaleOrderManager(NamedRecordManagerBase[SaleOrder]): env_name = "sale.order" record_class = SaleOrder - def action_confirm(self, sale_order: Union[int, SaleOrder]) -> None: + def action_confirm(self, sale_order: int | SaleOrder) -> None: """Confirm the given sale order. :param sale_order: The sale order to confirm - :type sale_order: Union[int, SaleOrder] + :type sale_order: int | SaleOrder """ self._env.action_confirm( ( @@ -174,11 +172,11 @@ def action_confirm(self, sale_order: Union[int, SaleOrder]) -> None: ), ) - def create_invoices(self, sale_order: Union[int, SaleOrder]) -> None: + def create_invoices(self, sale_order: int | SaleOrder) -> None: """Create invoices from the given sale order. :param sale_order: The sale order to create invoices from - :type sale_order: Union[int, SaleOrder] + :type sale_order: int | SaleOrder """ self._env.create_invoices( ( diff --git a/openstack_odooclient/managers/sale_order_line.py b/openstack_odooclient/managers/sale_order_line.py index adf7019..ade8a5a 100644 --- a/openstack_odooclient/managers/sale_order_line.py +++ b/openstack_odooclient/managers/sale_order_line.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import List, Literal, Optional, Union - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase @@ -62,7 +60,7 @@ class SaleOrderLine(RecordBase["SaleOrderLineManager"]): """Display name for the sale order line in the sale order.""" invoice_line_ids: Annotated[ - List[int], + list[int], ModelRef("invoice_lines", AccountMoveLine), ] """A list of IDs for the account move (invoice) lines created @@ -70,7 +68,7 @@ class SaleOrderLine(RecordBase["SaleOrderLineManager"]): """ invoice_lines: Annotated[ - List[AccountMoveLine], + list[AccountMoveLine], ModelRef("invoice_lines", AccountMoveLine), ] """The account move (invoice) lines created @@ -131,17 +129,17 @@ class SaleOrderLine(RecordBase["SaleOrderLineManager"]): and caches it for subsequent accesses. """ - os_project_id: Annotated[Optional[int], ModelRef("os_project", Project)] + os_project_id: Annotated[int | None, ModelRef("os_project", Project)] """The ID for the the OpenStack project this sale order line was was generated for. """ - os_project_name: Annotated[Optional[str], ModelRef("os_project", Project)] + os_project_name: Annotated[str | None, ModelRef("os_project", Project)] """The name of the the OpenStack project this sale order line was was generated for. """ - os_project: Annotated[Optional[Project], ModelRef("os_project", Project)] + os_project: Annotated[Project | None, ModelRef("os_project", Project)] """The OpenStack project this sale order line was was generated for. @@ -149,15 +147,15 @@ class SaleOrderLine(RecordBase["SaleOrderLineManager"]): and caches it for subsequent accesses. """ - os_region: Union[str, Literal[False]] + os_region: str | Literal[False] """The OpenStack region the sale order line was created from.""" - os_resource_id: Union[str, Literal[False]] + os_resource_id: str | Literal[False] """The OpenStack resource ID for the resource that generated this sale order line. """ - os_resource_name: Union[str, Literal[False]] + os_resource_name: str | Literal[False] """The name of the OpenStack resource tier or flavour, as used by services such as Distil for rating purposes. @@ -165,7 +163,7 @@ class SaleOrderLine(RecordBase["SaleOrderLineManager"]): this would be set to the instance's flavour name. """ - os_resource_type: Union[str, Literal[False]] + os_resource_type: str | Literal[False] """A human-readable description of the type of resource captured by this sale order line. """ diff --git a/openstack_odooclient/managers/support_subscription.py b/openstack_odooclient/managers/support_subscription.py index 80b4cf3..dd58050 100644 --- a/openstack_odooclient/managers/support_subscription.py +++ b/openstack_odooclient/managers/support_subscription.py @@ -16,9 +16,7 @@ from __future__ import annotations from datetime import date -from typing import Literal, Optional - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase @@ -37,7 +35,7 @@ class SupportSubscription(RecordBase["SupportSubscriptionManager"]): end_date: date """The end date of the credit.""" - partner_id: Annotated[Optional[int], ModelRef("partner", Partner)] + partner_id: Annotated[int | None, ModelRef("partner", Partner)] """The ID for the partner linked to this support subscription, if it is linked to a partner. @@ -45,7 +43,7 @@ class SupportSubscription(RecordBase["SupportSubscriptionManager"]): cover all projects the partner owns. """ - partner_name: Annotated[Optional[str], ModelRef("partner", Partner)] + partner_name: Annotated[str | None, ModelRef("partner", Partner)] """The name of the partner linked to this support subscription, if it is linked to a partner. @@ -53,7 +51,7 @@ class SupportSubscription(RecordBase["SupportSubscriptionManager"]): cover all projects the partner owns. """ - partner: Annotated[Optional[Partner], ModelRef("partner", Partner)] + partner: Annotated[Partner | None, ModelRef("partner", Partner)] """The partner linked to this support subscription, if it is linked to a partner. @@ -64,17 +62,17 @@ class SupportSubscription(RecordBase["SupportSubscriptionManager"]): and caches it for subsequent accesses. """ - project_id: Annotated[Optional[int], ModelRef("project", Project)] + project_id: Annotated[int | None, ModelRef("project", Project)] """The ID of the project this support subscription is for, if it is linked to a specific project. """ - project_name: Annotated[Optional[str], ModelRef("project", Project)] + project_name: Annotated[str | None, ModelRef("project", Project)] """The name of the project this support subscription is for, if it is linked to a specific project. """ - project: Annotated[Optional[Project], ModelRef("project", Project)] + project: Annotated[Project | None, ModelRef("project", Project)] """The project this support subscription is for, if it is linked to a specific project. diff --git a/openstack_odooclient/managers/support_subscription_type.py b/openstack_odooclient/managers/support_subscription_type.py index 82640fc..d05b1a0 100644 --- a/openstack_odooclient/managers/support_subscription_type.py +++ b/openstack_odooclient/managers/support_subscription_type.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import List, Literal - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase @@ -52,13 +50,13 @@ class SupportSubscriptionType(RecordBase["SupportSubscriptionTypeManager"]): """Percentage of usage compared to price (0-100).""" support_subscription_ids: Annotated[ - List[int], + list[int], ModelRef("support_subscription", SupportSubscription), ] """A list of IDs for the support subscriptions of this type.""" support_subscription: Annotated[ - List[SupportSubscription], + list[SupportSubscription], ModelRef("support_subscription", SupportSubscription), ] """The list of support subscriptions of this type. @@ -68,7 +66,7 @@ class SupportSubscriptionType(RecordBase["SupportSubscriptionTypeManager"]): """ support_subscriptions: Annotated[ - List[SupportSubscription], + list[SupportSubscription], ModelRef("support_subscription", SupportSubscription), ] """An alias for ``support_subscription``.""" diff --git a/openstack_odooclient/managers/tax.py b/openstack_odooclient/managers/tax.py index 1fda300..03c5fdc 100644 --- a/openstack_odooclient/managers/tax.py +++ b/openstack_odooclient/managers/tax.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase diff --git a/openstack_odooclient/managers/term_discount.py b/openstack_odooclient/managers/term_discount.py index 377c9b4..6117925 100644 --- a/openstack_odooclient/managers/term_discount.py +++ b/openstack_odooclient/managers/term_discount.py @@ -16,9 +16,9 @@ from __future__ import annotations from datetime import date -from typing import Optional +from typing import Annotated -from typing_extensions import Annotated, Self +from typing_extensions import Self from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase @@ -28,7 +28,7 @@ class TermDiscount(RecordBase["TermDiscountManager"]): discount_percent: float """The maximum discount percentage for this term discount (0-100).""" - early_termination_date: Optional[date] + early_termination_date: date | None """An optional early termination date for the term discount.""" end_date: date @@ -50,7 +50,7 @@ class TermDiscount(RecordBase["TermDiscountManager"]): and caches it for subsequent accesses. """ - project_id: Annotated[Optional[int], ModelRef("project", Project)] + project_id: Annotated[int | None, ModelRef("project", Project)] """The ID for the project this term discount applies to, if it is a project-specific term discount. @@ -58,7 +58,7 @@ class TermDiscount(RecordBase["TermDiscountManager"]): the partner owns. """ - project_name: Annotated[Optional[str], ModelRef("project", Project)] + project_name: Annotated[str | None, ModelRef("project", Project)] """The name of the project this term discount applies to, if it is a project-specific term discount. @@ -66,7 +66,7 @@ class TermDiscount(RecordBase["TermDiscountManager"]): the partner owns. """ - project: Annotated[Optional[Project], ModelRef("project", Project)] + project: Annotated[Project | None, ModelRef("project", Project)] """The project this term discount applies to, if it is a project-specific term discount. @@ -81,7 +81,7 @@ class TermDiscount(RecordBase["TermDiscountManager"]): """The date from which this term discount starts.""" superseded_by_id: Annotated[ - Optional[int], + int | None, ModelRef("superseded_by", Self), ] """The ID for the term discount that supersedes this one, @@ -89,7 +89,7 @@ class TermDiscount(RecordBase["TermDiscountManager"]): """ superseded_by_name: Annotated[ - Optional[str], + str | None, ModelRef("superseded_by", Self), ] """The name of the term discount that supersedes this one, @@ -97,7 +97,7 @@ class TermDiscount(RecordBase["TermDiscountManager"]): """ superseded_by: Annotated[ - Optional[Self], + Self | None, ModelRef("superseded_by", Self), ] """The term discount that supersedes this one, diff --git a/openstack_odooclient/managers/trial.py b/openstack_odooclient/managers/trial.py index a81c030..3cfb1b6 100644 --- a/openstack_odooclient/managers/trial.py +++ b/openstack_odooclient/managers/trial.py @@ -16,22 +16,20 @@ from __future__ import annotations from datetime import date -from typing import Literal, Union - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase class Trial(RecordBase["TrialManager"]): - account_suspended_date: Union[date, Literal[False]] + account_suspended_date: date | Literal[False] """The date the account was suspended, following the end of the trial.""" - account_terminated_date: Union[date, Literal[False]] + account_terminated_date: date | Literal[False] """The date the account was terminated, following the end of the trial.""" - account_upgraded_date: Union[date, Literal[False]] + account_upgraded_date: date | Literal[False] """The date the account was upgraded to a full account, following the end of the trial. """ diff --git a/openstack_odooclient/managers/uom.py b/openstack_odooclient/managers/uom.py index dbb50c9..8e42a7e 100644 --- a/openstack_odooclient/managers/uom.py +++ b/openstack_odooclient/managers/uom.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase diff --git a/openstack_odooclient/managers/user.py b/openstack_odooclient/managers/user.py index 3013822..934562c 100644 --- a/openstack_odooclient/managers/user.py +++ b/openstack_odooclient/managers/user.py @@ -15,7 +15,7 @@ from __future__ import annotations -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase diff --git a/openstack_odooclient/managers/volume_discount_range.py b/openstack_odooclient/managers/volume_discount_range.py index 38b94f0..f6d68b6 100644 --- a/openstack_odooclient/managers/volume_discount_range.py +++ b/openstack_odooclient/managers/volume_discount_range.py @@ -15,9 +15,7 @@ from __future__ import annotations -from typing import List, Optional, Union - -from typing_extensions import Annotated +from typing import Annotated from ..base.record import ModelRef, RecordBase from ..base.record_manager import RecordManagerBase @@ -25,7 +23,7 @@ class VolumeDiscountRange(RecordBase["VolumeDiscountRangeManager"]): customer_group_id: Annotated[ - Optional[int], + int | None, ModelRef("customer_group", CustomerGroup), ] """The ID for the customer group this volume discount range @@ -36,7 +34,7 @@ class VolumeDiscountRange(RecordBase["VolumeDiscountRangeManager"]): """ customer_group_name: Annotated[ - Optional[str], + str | None, ModelRef("customer_group", CustomerGroup), ] """The name of the customer group this volume discount range @@ -47,7 +45,7 @@ class VolumeDiscountRange(RecordBase["VolumeDiscountRangeManager"]): """ customer_group: Annotated[ - Optional[CustomerGroup], + CustomerGroup | None, ModelRef("customer_group", CustomerGroup), ] """The customer group this volume discount range @@ -68,7 +66,7 @@ class VolumeDiscountRange(RecordBase["VolumeDiscountRangeManager"]): this volume discount range. """ - max: Optional[float] + max: float | None """Optional maximum charge for this volume discount range. Intended to be used when creating tiered volume discounts for customers. @@ -88,8 +86,8 @@ class VolumeDiscountRangeManager(RecordManagerBase[VolumeDiscountRange]): def get_for_charge( self, charge: float, - customer_group: Optional[Union[int, CustomerGroup]] = None, - ) -> Optional[VolumeDiscountRange]: + customer_group: int | CustomerGroup | None = None, + ) -> VolumeDiscountRange | None: """Return the volume discount range to apply to a given charge. If ``customer_group`` is supplied, volume discount ranges for @@ -104,14 +102,14 @@ def get_for_charge( :param charge: The charge to find the applicable discount range for :type charge: float :param customer_group: Get discount for a specific customer group - :type customer_group: Optional[Union[int, CustomerGroup]], optional + :type customer_group: int | CustomerGroup | None, optional :return: Highest percentage applicable discount range (if found) - :rtype: Optional[VolumeDiscountRange] + :rtype: VolumeDiscountRange | None """ ranges = self.search( [("customer_group", "=", customer_group or False)], ) - found_ranges: List[VolumeDiscountRange] = [] + found_ranges: list[VolumeDiscountRange] = [] for vol_range in ranges: if charge < vol_range.min: continue diff --git a/openstack_odooclient/managers/voucher_code.py b/openstack_odooclient/managers/voucher_code.py index a2dfcef..8db78f4 100644 --- a/openstack_odooclient/managers/voucher_code.py +++ b/openstack_odooclient/managers/voucher_code.py @@ -16,9 +16,7 @@ from __future__ import annotations from datetime import date -from typing import List, Literal, Optional, Union - -from typing_extensions import Annotated +from typing import Annotated, Literal from ..base.record import ModelRef, RecordBase from ..base.record_manager_named import NamedRecordManagerBase @@ -31,13 +29,13 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): code: str """The code string for this voucher code.""" - credit_amount: Union[float, Literal[False]] + credit_amount: float | Literal[False] """The initial credit balance for the voucher code, if a credit is to be created by the voucher code. """ credit_type_id: Annotated[ - Optional[int], + int | None, ModelRef("credit_type", CreditType), ] """The ID of the credit type to use, if a credit is to be @@ -45,7 +43,7 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): """ credit_type_name: Annotated[ - Optional[str], + str | None, ModelRef("credit_type", CreditType), ] """The name of the credit type to use, if a credit is to be @@ -53,7 +51,7 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): """ credit_type: Annotated[ - Optional[CreditType], + CreditType | None, ModelRef("credit_type", CreditType), ] """The credit type to use, if a credit is to be @@ -63,25 +61,25 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): and caches it for subsequent accesses. """ - credit_duration: Union[int, Literal[False]] + credit_duration: int | Literal[False] """The duration of the credit, in days, if a credit is to be created by the voucher code. """ customer_group_id: Annotated[ - Optional[int], + int | None, ModelRef("customer_group", CustomerGroup), ] """The ID of the customer group to add the customer to, if set.""" customer_group_name: Annotated[ - Optional[str], + str | None, ModelRef("customer_group", CustomerGroup), ] """The name of the customer group to add the customer to, if set.""" customer_group: Annotated[ - Optional[CustomerGroup], + CustomerGroup | None, ModelRef("customer_group", CustomerGroup), ] """The customer group to add the customer to, if set. @@ -90,31 +88,28 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): and caches it for subsequent accesses. """ - expiry_date: Union[date, Literal[False]] + expiry_date: date | Literal[False] """The date the voucher code expires.""" - grant_duration: Union[int, Literal[False]] + grant_duration: int | Literal[False] """The duration of the grant, in days, if a grant is to be created by the voucher code. """ - grant_type_id: Annotated[Optional[int], ModelRef("grant_type", GrantType)] + grant_type_id: Annotated[int | None, ModelRef("grant_type", GrantType)] """The ID of the grant type to use, if a grant is to be created by this voucher code. """ grant_type_name: Annotated[ - Optional[str], + str | None, ModelRef("grant_type", GrantType), ] """The name of the grant type to use, if a grant is to be created by this voucher code. """ - grant_type: Annotated[ - Optional[GrantType], - ModelRef("grant_type", GrantType), - ] + grant_type: Annotated[GrantType | None, ModelRef("grant_type", GrantType)] """The grant type to use, if a grant is to be created by this voucher code. @@ -122,7 +117,7 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): and caches it for subsequent accesses. """ - grant_value: Union[float, Literal[False]] + grant_value: float | Literal[False] """The value of the grant, if a grant is to be created by the voucher code. """ @@ -140,7 +135,7 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): This uses the code specified in the record as-is. """ - quota_size: Union[str, Literal[False]] + quota_size: str | Literal[False] """The default quota size for new projects signed up using this voucher code. @@ -148,7 +143,7 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): """ sales_person_id: Annotated[ - Optional[int], + int | None, ModelRef("sales_person", Partner), ] """The ID for the salesperson partner responsible for this @@ -156,7 +151,7 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): """ sales_person_name: Annotated[ - Optional[str], + str | None, ModelRef("sales_person", Partner), ] """The name of the salesperson partner responsible for this @@ -164,7 +159,7 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): """ sales_person: Annotated[ - Optional[Partner], + Partner | None, ModelRef("sales_person", Partner), ] """The salesperson partner responsible for this @@ -174,12 +169,12 @@ class VoucherCode(RecordBase["VoucherCodeManager"]): and caches it for subsequent accesses. """ - tag_ids: Annotated[List[int], ModelRef("tags", PartnerCategory)] + tag_ids: Annotated[list[int], ModelRef("tags", PartnerCategory)] """A list of IDs for the tags (partner categories) to assign to partners for new accounts that signed up using this voucher code. """ - tags: Annotated[List[PartnerCategory], ModelRef("tags", PartnerCategory)] + tags: Annotated[list[PartnerCategory], ModelRef("tags", PartnerCategory)] """The list of tags (partner categories) to assign to partners for new accounts that signed up using this voucher code. diff --git a/openstack_odooclient/util.py b/openstack_odooclient/util.py index ae0be04..9b870f3 100644 --- a/openstack_odooclient/util.py +++ b/openstack_odooclient/util.py @@ -15,12 +15,10 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar - -from typing_extensions import TypeGuard +from typing import TYPE_CHECKING, Any, Type, TypeGuard, TypeVar if TYPE_CHECKING: - from typing import Any, Mapping, Optional, Tuple, Type, Union + from collections.abc import Mapping # Same values as defined in odoo.tools.misc. DEFAULT_SERVER_DATE_FORMAT = "%Y-%m-%d" @@ -33,7 +31,7 @@ def get_mapped_field( - field_mapping: Mapping[Optional[str], Mapping[str, str]], + field_mapping: Mapping[str | None, Mapping[str, str]], odoo_version: str, field: str, ) -> str: @@ -45,7 +43,7 @@ def get_mapped_field( If none is found there either, return the field name as is. :param field_mapping: Field mapping structure - :type field_mapping: Mapping[Optional[str], Mapping[str, str]] + :type field_mapping: Mapping[str | None, Mapping[str, str]] :param odoo_version: Odoo server version :type odoo_version: str :param field: Field name to map @@ -65,7 +63,7 @@ def get_mapped_field( def is_subclass( type_obj: Type[Any], - classes: Union[Type[T], Tuple[Type[T], ...]], + classes: Type[T] | tuple[Type[T], ...], ) -> TypeGuard[Type[T]]: """Check whether or not the given type is a subclass of any of the given classes (single class, or tuple of one or more classes). @@ -77,7 +75,7 @@ def is_subclass( :param type_obj: Type object to check :type type_obj: Type[Any] :param classes: Classes to check the type object is a subclass of - :type classes: Union[Type[Any], Tuple[Type[Any]]] + :type classes: Type[Any] | tuple[Type[Any]] :return: ``True`` if the type is a subclass of any of the given classes :rtype: bool """