diff --git a/mreg_cli/__main__.py b/mreg_cli/__main__.py index 69b77fe4..705f4391 100644 --- a/mreg_cli/__main__.py +++ b/mreg_cli/__main__.py @@ -1,5 +1,7 @@ """Entry point for the mreg_cli application.""" +from __future__ import annotations + from . import main if __name__ == "__main__": diff --git a/mreg_cli/api/__init__.py b/mreg_cli/api/__init__.py index ac8686ba..da9a168f 100644 --- a/mreg_cli/api/__init__.py +++ b/mreg_cli/api/__init__.py @@ -6,6 +6,8 @@ guarantees about the data it is working with. """ +from __future__ import annotations + import re from ipaddress import ip_address diff --git a/mreg_cli/api/abstracts.py b/mreg_cli/api/abstracts.py index 61322d4b..ea349547 100644 --- a/mreg_cli/api/abstracts.py +++ b/mreg_cli/api/abstracts.py @@ -1,5 +1,7 @@ """Abstract models for the API.""" +from __future__ import annotations + from abc import ABC, abstractmethod from datetime import datetime from typing import Any, Generic, TypeVar, cast diff --git a/mreg_cli/api/endpoints.py b/mreg_cli/api/endpoints.py index e5b9d945..82acd78c 100644 --- a/mreg_cli/api/endpoints.py +++ b/mreg_cli/api/endpoints.py @@ -1,5 +1,7 @@ """API endpoints for mreg.""" +from __future__ import annotations + from enum import Enum from urllib.parse import quote diff --git a/mreg_cli/api/fields.py b/mreg_cli/api/fields.py index ef75be0b..10e1aebc 100644 --- a/mreg_cli/api/fields.py +++ b/mreg_cli/api/fields.py @@ -1,5 +1,7 @@ """Fields for models of the API.""" +from __future__ import annotations + import ipaddress import re from typing import Annotated, Any diff --git a/mreg_cli/api/models.py b/mreg_cli/api/models.py index 9a332d74..00d9125e 100644 --- a/mreg_cli/api/models.py +++ b/mreg_cli/api/models.py @@ -1,9 +1,11 @@ """Pydantic models for the mreg_cli package.""" +from __future__ import annotations + import ipaddress import re from datetime import datetime -from typing import Any, Optional, Union +from typing import Any from pydantic import AliasChoices, BaseModel, Field, field_validator, model_validator @@ -62,7 +64,7 @@ class WithHost(BaseModel): host: int - def resolve_host(self) -> Union["Host", None]: + def resolve_host(self) -> Host | None: """Resolve the host ID to a Host object. Notes @@ -84,7 +86,7 @@ class WithZone(BaseModel): zone: int - def resolve_zone(self) -> Union["Zone", None]: + def resolve_zone(self) -> Zone | None: """Resolve the zone ID to a (Forward)Zone object. Notes @@ -131,7 +133,7 @@ def is_delegated(self) -> bool: return False @classmethod - def get_from_hostname(cls, hostname: HostT) -> Union["Delegation", "Zone", None]: + def get_from_hostname(cls, hostname: HostT) -> Delegation | Zone | None: """Get the zone from a hostname. Note: This method may return either a Delegation or a Zone object. @@ -205,7 +207,7 @@ def output(self, padding: int = 14) -> None: OutputManager().add_line(f"{'Role:':<{padding}}{self.name} ({self.description})") @classmethod - def output_multiple(cls, roles: list["Role"], padding: int = 14) -> None: + def output_multiple(cls, roles: list[Role], padding: int = 14) -> None: """Output multiple roles to the console. :param roles: List of roles to output. @@ -239,7 +241,7 @@ def endpoint(cls) -> Endpoint: return Endpoint.Networks @classmethod - def get_by_ip(cls, ip: IP_AddressT) -> "Network": + def get_by_ip(cls, ip: IP_AddressT) -> Network: """Get a network by IP address. :param ip: The IP address to search for. @@ -249,7 +251,7 @@ def get_by_ip(cls, ip: IP_AddressT) -> "Network": return Network(**data.json()) @classmethod - def get_by_netmask(cls, netmask: str) -> "Network": + def get_by_netmask(cls, netmask: str) -> Network: """Get a network by netmask. :param netmask: The netmask to search for. @@ -294,7 +296,7 @@ def convert_ip_address(cls, values: Any): return values @classmethod - def get_by_ip(cls, ip: IP_AddressT) -> Union["IPAddress", None]: + def get_by_ip(cls, ip: IP_AddressT) -> IPAddress | None: """Get an IP address object by IP address. :param ip: The IP address to search for. @@ -335,7 +337,7 @@ def ip(self) -> IP_AddressT: """Return the IP address.""" return self.ipaddress.address - def associate_mac(self, mac: MACAddressField | str, force: bool = False) -> "IPAddress": + def associate_mac(self, mac: MACAddressField | str, force: bool = False) -> IPAddress: """Associate a MAC address with the IP address. :param mac: The MAC address to associate. @@ -365,7 +367,7 @@ def output(self, len_ip: int, len_names: int, names: bool = False): OutputManager().add_line(f"{name:<{len_names}}{ip:<{len_ip}}{mac}") @classmethod - def output_multiple(cls, ips: list["IPAddress"], padding: int = 14, names: bool = False): + def output_multiple(cls, ips: list[IPAddress], padding: int = 14, names: bool = False): """Output IP addresses to the console.""" output_manager = OutputManager() len_ip = max(padding, max([len(str(ip.ipaddress)) for ip in ips], default=0) + 2) @@ -435,7 +437,7 @@ def output(self, padding: int = 14) -> None: OutputManager().add_line(f"{'Cname:':<{padding}}{self.name} -> {host}") @classmethod - def output_multiple(cls, cnames: list["CNAME"], padding: int = 14) -> None: + def output_multiple(cls, cnames: list[CNAME], padding: int = 14) -> None: """Output multiple CNAME records to the console. :param cnames: List of CNAME records to output. @@ -461,7 +463,7 @@ def output(self, padding: int = 14) -> None: OutputManager().add_line(f"{'TXT:':<{padding}}{self.txt}") @classmethod - def output_multiple(cls, txts: list["TXT"], padding: int = 14) -> None: + def output_multiple(cls, txts: list[TXT], padding: int = 14) -> None: """Output multiple TXT records to the console. :param txts: List of TXT records to output. @@ -491,7 +493,7 @@ def output(self, padding: int = 14) -> None: ) @classmethod - def output_multiple(cls, mxs: list["MX"], padding: int = 14) -> None: + def output_multiple(cls, mxs: list[MX], padding: int = 14) -> None: """Output MX records to the console.""" if not mxs: return @@ -549,7 +551,7 @@ def headers(cls) -> list[str]: ] @classmethod - def output_multiple(cls, naptrs: list["NAPTR"], padding: int = 14) -> None: + def output_multiple(cls, naptrs: list[NAPTR], padding: int = 14) -> None: """Output multiple NAPTR records to the console.""" headers = cls.headers() row_format = f"{{:<{padding}}}" * len(headers) @@ -609,7 +611,7 @@ def output(self, padding: int = 14, host_id_name_map: dict[int, str] | None = No ) @classmethod - def output_multiple(cls, srvs: list["Srv"], padding: int = 14) -> None: + def output_multiple(cls, srvs: list[Srv], padding: int = 14) -> None: """Output multiple SRV records. This method adjusts the padding dynamically based on the longest record name. @@ -645,7 +647,7 @@ class PTR_override(FrozenModelWithTimestamps, WithHost): ipaddress: str # For now, should be an IP address @classmethod - def output_multiple(cls, ptrs: list["PTR_override"], padding: int = 14): + def output_multiple(cls, ptrs: list[PTR_override], padding: int = 14): """Output multiple PTR override records to the console. :param ptrs: List of PTR override records to output. @@ -683,7 +685,7 @@ def endpoint(cls) -> Endpoint: return Endpoint.Sshfps @classmethod - def output_multiple(cls, sshfps: list["SSHFP"], padding: int = 14): + def output_multiple(cls, sshfps: list[SSHFP], padding: int = 14): """Output multiple SSHFP records to the console. :param sshfps: List of SSHFP records to output. @@ -753,7 +755,7 @@ def endpoint(cls) -> Endpoint: @classmethod def get_by_any_means( cls, identifier: str | HostT, inform_as_cname: bool = True - ) -> Optional["Host"]: + ) -> Host | None: """Get a host by the given identifier. - If the identifier is numeric, it will be treated as an ID. @@ -859,7 +861,7 @@ def ipv6_addresses(self): def associate_mac_to_ip( self, mac: MACAddressField | str, ip: IPAddressField | str, force: bool = False - ) -> "Host": + ) -> Host: """Associate a MAC address to an IP address. :param mac: The MAC address to associate. @@ -1092,7 +1094,7 @@ def endpoint(cls) -> Endpoint: return Endpoint.Hosts @classmethod - def get(cls, params: dict[str, Any] | None = None) -> "HostList": + def get(cls, params: dict[str, Any] | None = None) -> HostList: """Get a list of hosts. :param params: Optional parameters to pass to the API. diff --git a/mreg_cli/cli.py b/mreg_cli/cli.py index 2395dba3..585c1d0b 100644 --- a/mreg_cli/cli.py +++ b/mreg_cli/cli.py @@ -3,13 +3,15 @@ This file contains the main CLI class and the top level parser. """ +from __future__ import annotations + import argparse import html import os import shlex import sys from collections.abc import Generator -from typing import TYPE_CHECKING, Any, NoReturn, Optional +from typing import TYPE_CHECKING, Any, NoReturn from prompt_toolkit import HTML, document, print_formatted_text from prompt_toolkit.completion import CompleteEvent, Completer, Completion @@ -35,7 +37,7 @@ if TYPE_CHECKING: # Can't use _SubParsersAction as generic in Python <3.9 - SubparserType = argparse._SubParsersAction[argparse.ArgumentParser] + SubparserType = argparse._SubParsersAction[argparse.ArgumentParser] # type: ignore # private attribute class CliExit(Exception): @@ -44,7 +46,7 @@ class CliExit(Exception): pass -def _create_command_group(parent: argparse.ArgumentParser) -> "SubparserType": +def _create_command_group(parent: argparse.ArgumentParser) -> SubparserType: """Create a sub parser for a command.""" parent_name = parent.prog.strip() @@ -79,7 +81,7 @@ def __init__(self, parser: argparse.ArgumentParser, flags: list[Flag], short_des self.parser = parser # sub is an object used for creating sub parser for this command. A # command/ArgParser can only have one of this object. - self.sub: Optional["SubparserType"] = None + self.sub: SubparserType | None = None self.short_desc = short_desc self.children: dict[str, Command] = {} diff --git a/mreg_cli/commands/base.py b/mreg_cli/commands/base.py index 2d60f45b..7d13365e 100644 --- a/mreg_cli/commands/base.py +++ b/mreg_cli/commands/base.py @@ -4,6 +4,8 @@ base class for all CLI command classes. """ +from __future__ import annotations + from typing import Any from mreg_cli.commands.registry import CommandRegistry diff --git a/mreg_cli/commands/dhcp.py b/mreg_cli/commands/dhcp.py index aff5764f..bf1afbe9 100644 --- a/mreg_cli/commands/dhcp.py +++ b/mreg_cli/commands/dhcp.py @@ -1,5 +1,7 @@ """DHCP commands for mreg_cli.""" +from __future__ import annotations + import argparse from typing import Any diff --git a/mreg_cli/commands/group.py b/mreg_cli/commands/group.py index d922ea9b..369a17bd 100644 --- a/mreg_cli/commands/group.py +++ b/mreg_cli/commands/group.py @@ -1,5 +1,7 @@ """Hostgroups commands for mreg_cli.""" +from __future__ import annotations + import argparse from itertools import chain from typing import Any diff --git a/mreg_cli/commands/help.py b/mreg_cli/commands/help.py index cbc43e03..a404255d 100644 --- a/mreg_cli/commands/help.py +++ b/mreg_cli/commands/help.py @@ -1,5 +1,7 @@ """Help command for the CLI.""" +from __future__ import annotations + import argparse from typing import Any diff --git a/mreg_cli/commands/host.py b/mreg_cli/commands/host.py index 972938c6..fb9650d9 100644 --- a/mreg_cli/commands/host.py +++ b/mreg_cli/commands/host.py @@ -5,6 +5,8 @@ add their commands to. """ +from __future__ import annotations + import importlib import os import pkgutil diff --git a/mreg_cli/commands/host_submodules/a_aaaa.py b/mreg_cli/commands/host_submodules/a_aaaa.py index 4c1f8e32..13c4db3a 100644 --- a/mreg_cli/commands/host_submodules/a_aaaa.py +++ b/mreg_cli/commands/host_submodules/a_aaaa.py @@ -13,6 +13,8 @@ - aaaa_show """ +from __future__ import annotations + import argparse from mreg_cli.commands.host import registry as command_registry diff --git a/mreg_cli/commands/host_submodules/bacnet.py b/mreg_cli/commands/host_submodules/bacnet.py index a014ab63..27762eca 100644 --- a/mreg_cli/commands/host_submodules/bacnet.py +++ b/mreg_cli/commands/host_submodules/bacnet.py @@ -7,6 +7,8 @@ - bacnetid_list """ +from __future__ import annotations + import argparse from mreg_cli.commands.host import registry as command_registry diff --git a/mreg_cli/commands/host_submodules/cname.py b/mreg_cli/commands/host_submodules/cname.py index 5c46cc8f..8f24413a 100644 --- a/mreg_cli/commands/host_submodules/cname.py +++ b/mreg_cli/commands/host_submodules/cname.py @@ -1,5 +1,7 @@ """Sub module for the 'host' command handling CNAME records.""" +from __future__ import annotations + import argparse from mreg_cli.commands.host import registry as command_registry diff --git a/mreg_cli/commands/host_submodules/core.py b/mreg_cli/commands/host_submodules/core.py index 2482495e..87c454ae 100644 --- a/mreg_cli/commands/host_submodules/core.py +++ b/mreg_cli/commands/host_submodules/core.py @@ -11,6 +11,8 @@ - set_contact """ +from __future__ import annotations + import argparse import ipaddress diff --git a/mreg_cli/commands/host_submodules/rr.py b/mreg_cli/commands/host_submodules/rr.py index b498f2b3..6baf9703 100644 --- a/mreg_cli/commands/host_submodules/rr.py +++ b/mreg_cli/commands/host_submodules/rr.py @@ -37,6 +37,8 @@ - cname_show """ +from __future__ import annotations + import argparse from typing import Any diff --git a/mreg_cli/commands/label.py b/mreg_cli/commands/label.py index 6b4be0a3..87e7545d 100644 --- a/mreg_cli/commands/label.py +++ b/mreg_cli/commands/label.py @@ -1,5 +1,7 @@ """Label-related commands for mreg_cli.""" +from __future__ import annotations + import argparse from typing import Any diff --git a/mreg_cli/commands/network.py b/mreg_cli/commands/network.py index 23498570..873a9943 100644 --- a/mreg_cli/commands/network.py +++ b/mreg_cli/commands/network.py @@ -1,5 +1,7 @@ """Network commands for mreg_cli.""" +from __future__ import annotations + import argparse import ipaddress import urllib.parse diff --git a/mreg_cli/commands/permission.py b/mreg_cli/commands/permission.py index 1c3a9e55..fc1cf009 100644 --- a/mreg_cli/commands/permission.py +++ b/mreg_cli/commands/permission.py @@ -1,5 +1,7 @@ """Permission commands for mreg_cli.""" +from __future__ import annotations + import argparse import ipaddress from typing import Any diff --git a/mreg_cli/commands/policy.py b/mreg_cli/commands/policy.py index 4d687232..23b926a5 100644 --- a/mreg_cli/commands/policy.py +++ b/mreg_cli/commands/policy.py @@ -1,5 +1,7 @@ """Policy commands for mreg_cli.""" +from __future__ import annotations + import argparse from typing import Any diff --git a/mreg_cli/commands/registry.py b/mreg_cli/commands/registry.py index abdeb12c..79e64461 100644 --- a/mreg_cli/commands/registry.py +++ b/mreg_cli/commands/registry.py @@ -4,6 +4,8 @@ a command and its attributes, so it may be used as a subcommand of mreg-cli. """ +from __future__ import annotations + from collections.abc import Callable from mreg_cli.types import Command, CommandFunc, Flag diff --git a/mreg_cli/commands/zone.py b/mreg_cli/commands/zone.py index 8e1582e0..76c9b61c 100644 --- a/mreg_cli/commands/zone.py +++ b/mreg_cli/commands/zone.py @@ -1,5 +1,7 @@ """Zone commands for mreg_cli.""" +from __future__ import annotations + import argparse from typing import Any diff --git a/mreg_cli/config.py b/mreg_cli/config.py index adb4e36c..7e0997e3 100644 --- a/mreg_cli/config.py +++ b/mreg_cli/config.py @@ -14,6 +14,8 @@ 3. :py:const:`logging.DEBUG` """ +from __future__ import annotations + import configparser import logging import os @@ -64,7 +66,7 @@ class MregCliConfig: _config_file: dict[str, str] _config_env: dict[str, str] - def __new__(cls) -> "MregCliConfig": + def __new__(cls) -> MregCliConfig: """Create a new instance of the configuration class. This ensures that only one instance of the configuration class is created. @@ -89,12 +91,10 @@ def _load_env_config() -> dict[str, str]: } @overload - def get(self, key: str) -> str | None: - ... + def get(self, key: str) -> str | None: ... @overload - def get(self, key: str, default: DefaultType = ...) -> str | DefaultType: - ... + def get(self, key: str, default: DefaultType = ...) -> str | DefaultType: ... def get(self, key: str, default: DefaultType | None = None) -> str | DefaultType | None: """Get a configuration value with priority: cmdline, env, file. diff --git a/mreg_cli/exceptions.py b/mreg_cli/exceptions.py index ba5b3534..a7651b67 100644 --- a/mreg_cli/exceptions.py +++ b/mreg_cli/exceptions.py @@ -5,6 +5,8 @@ context of a CLI command. """ +from __future__ import annotations + import sys from prompt_toolkit import print_formatted_text diff --git a/mreg_cli/help_formatter.py b/mreg_cli/help_formatter.py index 866e9553..c5c24ade 100644 --- a/mreg_cli/help_formatter.py +++ b/mreg_cli/help_formatter.py @@ -1,5 +1,7 @@ """A formatter for the CLI that adds extra information and improves command formatting.""" +from __future__ import annotations + import argparse from typing import Any diff --git a/mreg_cli/log.py b/mreg_cli/log.py index e11e8794..efe27014 100644 --- a/mreg_cli/log.py +++ b/mreg_cli/log.py @@ -1,5 +1,7 @@ """Logging functions for the CLI.""" +from __future__ import annotations + import getpass import inspect import re diff --git a/mreg_cli/log.pyi b/mreg_cli/log.pyi index ed57b2cc..3b950f5a 100644 --- a/mreg_cli/log.pyi +++ b/mreg_cli/log.pyi @@ -1,38 +1,37 @@ -from typing import TYPE_CHECKING, NoReturn, Optional, Type, overload +from __future__ import annotations -if TYPE_CHECKING: - from typing_extensions import Literal +from typing import Literal, NoReturn, overload @overload def cli_error(msg: str) -> NoReturn: ... @overload def cli_error( - msg: str, raise_exception: "Literal[True]" = True, exception: Type[Exception] = ... + msg: str, raise_exception: Literal[True] = True, exception: type[Exception] = ... ) -> NoReturn: ... @overload def cli_error( msg: str, - raise_exception: "Literal[False]" = False, - exception: Type[Exception] = ..., + raise_exception: Literal[False] = False, + exception: type[Exception] = ..., ) -> None: ... @overload def cli_error( - msg: str, raise_exception: bool = ..., exception: Type[Exception] = ... -) -> Optional[NoReturn]: ... + msg: str, raise_exception: bool = ..., exception: type[Exception] = ... +) -> NoReturn | None: ... @overload def cli_warning(msg: str) -> NoReturn: ... @overload def cli_warning( - msg: str, raise_exception: "Literal[True]" = True, exception: Type[Exception] = ... + msg: str, raise_exception: Literal[True] = True, exception: type[Exception] = ... ) -> NoReturn: ... @overload def cli_warning( msg: str, - raise_exception: "Literal[False]" = False, - exception: Type[Exception] = ..., + raise_exception: Literal[False] = False, + exception: type[Exception] = ..., ) -> None: ... @overload def cli_warning( - msg: str, raise_exception: bool = ..., exception: Type[Exception] = ... -) -> Optional[NoReturn]: ... + msg: str, raise_exception: bool = ..., exception: type[Exception] = ... +) -> NoReturn | None: ... def cli_info(msg: str, print_msg: bool = ...) -> None: ... diff --git a/mreg_cli/main.py b/mreg_cli/main.py index fc4c60e2..67ed0523 100644 --- a/mreg_cli/main.py +++ b/mreg_cli/main.py @@ -1,5 +1,7 @@ """Main entry point for mreg_cli.""" +from __future__ import annotations + import argparse import getpass import logging diff --git a/mreg_cli/outputmanager.py b/mreg_cli/outputmanager.py index 1f916b94..2cd4c8b5 100644 --- a/mreg_cli/outputmanager.py +++ b/mreg_cli/outputmanager.py @@ -5,13 +5,15 @@ command. """ +from __future__ import annotations + import atexit import datetime import json import os import re from collections.abc import Sequence -from typing import TYPE_CHECKING, Any, Optional, overload +from typing import Any, Literal, overload from urllib.parse import urlencode, urlparse import requests @@ -19,20 +21,17 @@ from mreg_cli.exceptions import CliError from mreg_cli.types import RecordingEntry, TimeInfo -if TYPE_CHECKING: - from typing import Literal - @overload -def find_char_outside_quotes(line: str, target_char: str, return_position: "Literal[True]") -> int: - ... +def find_char_outside_quotes( + line: str, target_char: str, return_position: Literal[True] +) -> int: ... @overload def find_char_outside_quotes( - line: str, target_char: str, return_position: "Literal[False]" -) -> str: - ... + line: str, target_char: str, return_position: Literal[False] +) -> str: ... def find_char_outside_quotes( @@ -138,7 +137,7 @@ def __new__(cls): def clear(self) -> None: """Clear the object.""" self._output: list[str] = [] - self._filter_re: Optional["re.Pattern[str]"] = None + self._filter_re: re.Pattern[str] | None = None self._filter_negate: bool = False self._command_executed: str = "" self._command_issued: str = "" diff --git a/mreg_cli/types.py b/mreg_cli/types.py index 5a94bf8d..07b707ee 100644 --- a/mreg_cli/types.py +++ b/mreg_cli/types.py @@ -1,18 +1,15 @@ """Typing definitions for mreg_cli.""" +from __future__ import annotations + import argparse import ipaddress from collections.abc import Callable -from typing import TYPE_CHECKING, Any, NamedTuple, Optional, TypedDict, TypeVar +from typing import Any, Literal, NamedTuple, Protocol, TypeAlias, TypedDict, TypeVar -CommandFunc = Callable[[argparse.Namespace], None] +from requests.structures import CaseInsensitiveDict -# This is a seperate import due to using string annotation, so the -# import can't be consolidated into a single line with other imports -# from typing_extensions. This hack is required for RHEL7 support that -# has a typing_extensions library that has some issues. -if TYPE_CHECKING: - from typing import Literal, TypeAlias # noqa: F401 +CommandFunc = Callable[[argparse.Namespace], None] class TimeInfo(TypedDict): @@ -38,15 +35,12 @@ class RecordingEntry(TypedDict): time: TimeInfo | None -IP_Version: "TypeAlias" = "Literal[4, 6]" +IP_Version: TypeAlias = Literal[4, 6] IP_networkT = TypeVar("IP_networkT", ipaddress.IPv4Network, ipaddress.IPv6Network) IP_AddressT = ipaddress.IPv4Address | ipaddress.IPv6Address - - -if TYPE_CHECKING: - # https://github.com/python/typeshed/blob/16933b838eef7be92ee02f66b87aa1a7532cee63/stdlib/argparse.pyi#L40-L43 - NargsStr = Literal["?", "*", "+", "...", "A...", "==SUPPRESS=="] - NargsType = int | NargsStr +# https://github.com/python/typeshed/blob/16933b838eef7be92ee02f66b87aa1a7532cee63/stdlib/argparse.pyi#L40-L43 +NargsStr = Literal["?", "*", "+", "...", "A...", "==SUPPRESS=="] +NargsType = int | NargsStr class Flag: @@ -57,7 +51,7 @@ def __init__( name: str, description: str = "", short_desc: str = "", - nargs: Optional["NargsType"] = None, + nargs: NargsType | None = None, default: Any = None, flag_type: Any = None, choices: list[str] | None = None, @@ -91,35 +85,30 @@ class Command(NamedTuple): # Config DefaultType = TypeVar("DefaultType") -if TYPE_CHECKING: - from typing import Any - - from requests.structures import CaseInsensitiveDict - from typing_extensions import Protocol - class ResponseLike(Protocol): - """Interface for objects that resemble a requests.Response object.""" +class ResponseLike(Protocol): + """Interface for objects that resemble a requests.Response object.""" - @property - def ok(self) -> bool: - """Return True if the response was successful.""" - ... + @property + def ok(self) -> bool: + """Return True if the response was successful.""" + ... - @property - def status_code(self) -> int: - """Return the HTTP status code.""" - ... + @property + def status_code(self) -> int: + """Return the HTTP status code.""" + ... - @property - def reason(self) -> str: - """Return the HTTP status reason.""" - ... + @property + def reason(self) -> str: + """Return the HTTP status reason.""" + ... - @property - def headers(self) -> CaseInsensitiveDict[str]: - """Return the dictionary of response headers.""" - ... + @property + def headers(self) -> CaseInsensitiveDict[str]: + """Return the dictionary of response headers.""" + ... - def json(self, **kwargs: Any) -> Any: - """Return the response body as JSON.""" - ... + def json(self, **kwargs: Any) -> Any: + """Return the response body as JSON.""" + ... diff --git a/mreg_cli/utilities/api.py b/mreg_cli/utilities/api.py index 1f0219d4..0de95294 100644 --- a/mreg_cli/utilities/api.py +++ b/mreg_cli/utilities/api.py @@ -4,29 +4,25 @@ And this rule is promptly broken by importing from mreg_cli.outputmanager... """ +from __future__ import annotations + import json import logging import os import re import sys -from typing import TYPE_CHECKING, Any, NoReturn, Optional, cast, overload +from typing import Any, Literal, NoReturn, cast, overload from urllib.parse import urljoin from uuid import uuid4 import requests - -from mreg_cli.outputmanager import OutputManager - -if TYPE_CHECKING: - from typing import Literal - - from mreg_cli.types import ResponseLike - from prompt_toolkit import prompt from mreg_cli.config import MregCliConfig from mreg_cli.exceptions import CliError, LoginFailedError from mreg_cli.log import cli_error, cli_warning +from mreg_cli.outputmanager import OutputManager +from mreg_cli.types import ResponseLike session = requests.Session() session.headers.update({"User-Agent": "mreg-cli"}) @@ -185,7 +181,7 @@ def auth_and_update_token(username: str | None, password: str) -> None: set_file_permissions(mreg_auth_token_file, 0o600) -def result_check(result: "ResponseLike", operation_type: str, url: str) -> None: +def result_check(result: ResponseLike, operation_type: str, url: str) -> None: """Check the result of a request.""" if not result.ok: message = f'{operation_type} "{url}": {result.status_code}: {result.reason}' @@ -206,7 +202,7 @@ def _request_wrapper( first: bool = True, use_json: bool = False, **data: Any, -) -> Optional["ResponseLike"]: +) -> ResponseLike | None: """Wrap request calls to MREG for logging and token management.""" if params is None: params = {} @@ -233,28 +229,24 @@ def _request_wrapper( @overload -def get(path: str, params: dict[str, Any], ok404: "Literal[True]") -> Optional["ResponseLike"]: - ... +def get(path: str, params: dict[str, Any], ok404: Literal[True]) -> ResponseLike | None: ... @overload -def get(path: str, params: dict[str, Any], ok404: "Literal[False]") -> "ResponseLike": - ... +def get(path: str, params: dict[str, Any], ok404: Literal[False]) -> ResponseLike: ... @overload -def get(path: str, params: dict[str, Any] = ..., *, ok404: bool) -> Optional["ResponseLike"]: - ... +def get(path: str, params: dict[str, Any] = ..., *, ok404: bool) -> ResponseLike | None: ... @overload -def get(path: str, params: dict[str, Any] = ...) -> "ResponseLike": - ... +def get(path: str, params: dict[str, Any] = ...) -> ResponseLike: ... def get( path: str, params: dict[str, Any] | None = None, ok404: bool = False -) -> Optional["ResponseLike"]: +) -> ResponseLike | None: """Make a standard get request.""" if params is None: params = {} @@ -428,9 +420,7 @@ def _check_expect_one_result( return _check_expect_one_result(ret) -def post( - path: str, params: dict[str, Any] | None = None, **kwargs: Any -) -> Optional["ResponseLike"]: +def post(path: str, params: dict[str, Any] | None = None, **kwargs: Any) -> ResponseLike | None: """Use requests to make a post request. Assumes that all kwargs are data fields.""" if params is None: params = {} @@ -439,14 +429,14 @@ def post( def patch( path: str, params: dict[str, Any] | None = None, use_json: bool = False, **kwargs: Any -) -> Optional["ResponseLike"]: +) -> ResponseLike | None: """Use requests to make a patch request. Assumes that all kwargs are data fields.""" if params is None: params = {} return _request_wrapper("patch", path, params=params, use_json=use_json, **kwargs) -def delete(path: str, params: dict[str, Any] | None = None) -> Optional["ResponseLike"]: +def delete(path: str, params: dict[str, Any] | None = None) -> ResponseLike | None: """Use requests to make a delete request.""" if params is None: params = {} diff --git a/mreg_cli/utilities/history.py b/mreg_cli/utilities/history.py index c46247f8..b4d3780f 100644 --- a/mreg_cli/utilities/history.py +++ b/mreg_cli/utilities/history.py @@ -1,5 +1,7 @@ """History log related functions.""" +from __future__ import annotations + import json from typing import Any diff --git a/mreg_cli/utilities/host.py b/mreg_cli/utilities/host.py index 2d5e2b6f..b534122b 100644 --- a/mreg_cli/utilities/host.py +++ b/mreg_cli/utilities/host.py @@ -1,9 +1,11 @@ """Host-related utilities.""" +from __future__ import annotations + import argparse import ipaddress import urllib.parse -from typing import Any, Union +from typing import Any from mreg_cli.exceptions import CliWarning, HostNotFoundWarning from mreg_cli.log import cli_error, cli_info, cli_warning @@ -204,7 +206,7 @@ def add_ip_to_host( cli_info(f"added ip {ip} to {info['name']}", print_msg=True) -def get_requested_ip(ip: str, force: bool, ipversion: Union[IP_Version, None] = None) -> str: +def get_requested_ip(ip: str, force: bool, ipversion: IP_Version | None = None) -> str: """Return an IP address from the given args. - If the given ip is an ip, then that ip is returned. diff --git a/mreg_cli/utilities/network.py b/mreg_cli/utilities/network.py index 3c451910..ae8ff420 100644 --- a/mreg_cli/utilities/network.py +++ b/mreg_cli/utilities/network.py @@ -4,6 +4,8 @@ And this rule is promptly broken by importing from mreg_cli.outputmanager... """ +from __future__ import annotations + import ipaddress import urllib.parse from collections.abc import Iterable diff --git a/mreg_cli/utilities/output.py b/mreg_cli/utilities/output.py index e243253f..1d9cd2cc 100644 --- a/mreg_cli/utilities/output.py +++ b/mreg_cli/utilities/output.py @@ -7,6 +7,8 @@ related to the field or dataset being formatted. """ +from __future__ import annotations + from collections.abc import Iterable from typing import Any diff --git a/mreg_cli/utilities/shared.py b/mreg_cli/utilities/shared.py index d92b518c..d96c6b33 100644 --- a/mreg_cli/utilities/shared.py +++ b/mreg_cli/utilities/shared.py @@ -1,5 +1,7 @@ """Shared utilities for the mreg_cli package.""" +from __future__ import annotations + import re from typing import Any diff --git a/mreg_cli/utilities/validators.py b/mreg_cli/utilities/validators.py index d793cdd4..d7d1d539 100644 --- a/mreg_cli/utilities/validators.py +++ b/mreg_cli/utilities/validators.py @@ -4,17 +4,15 @@ """ +from __future__ import annotations + import ipaddress import re -from typing import TYPE_CHECKING from mreg_cli.config import MregCliConfig from mreg_cli.log import cli_warning from mreg_cli.types import IP_Version -if TYPE_CHECKING: - pass - def is_valid_ip(ip: str) -> bool: """Check if ip is valid ipv4 og ipv6.""" diff --git a/mreg_cli/utilities/zone.py b/mreg_cli/utilities/zone.py index 36d39857..6cb82960 100644 --- a/mreg_cli/utilities/zone.py +++ b/mreg_cli/utilities/zone.py @@ -1,5 +1,7 @@ """Zone-related utilities for mreg_cli.""" +from __future__ import annotations + from typing import Any from mreg_cli.log import cli_warning diff --git a/pyproject.toml b/pyproject.toml index 491b0f76..07dece76 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,8 @@ line-length = 99 # Assume Python 3.11. target-version = "py311" +[tool.ruff.lint.isort] +required-imports = ["from __future__ import annotations"] [tool.ruff.lint] select = [