Ensure type annotations are backwards compatible

Related: https://github.com/pimutils/todoman/issues/544
This commit is contained in:
Hugo Osvaldo Barrera 2023-10-29 16:04:23 +01:00
parent 91c16b3215
commit b50f9def00
65 changed files with 140 additions and 23 deletions

View file

@ -16,6 +16,8 @@ SPDX-License-Identifier: BSD-3-Clause
SPDX-FileCopyrightText: 2021 Intevation GmbH <https://intevation.de> SPDX-FileCopyrightText: 2021 Intevation GmbH <https://intevation.de>
Author: <bernhard.reiter@intevation.de> Author: <bernhard.reiter@intevation.de>
""" """
from __future__ import annotations
import re import re
import subprocess import subprocess
import sys import sys

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import datetime import datetime
import os import os

View file

@ -14,6 +14,7 @@ target-version = "py37"
[tool.ruff.isort] [tool.ruff.isort]
force-single-line = true force-single-line = true
required-imports = ["from __future__ import annotations"]
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = """ addopts = """

View file

@ -4,6 +4,8 @@ Vdirsyncer synchronizes calendars and contacts.
Please refer to https://vdirsyncer.pimutils.org/en/stable/packaging.html for Please refer to https://vdirsyncer.pimutils.org/en/stable/packaging.html for
how to package vdirsyncer. how to package vdirsyncer.
""" """
from __future__ import annotations
from setuptools import Command from setuptools import Command
from setuptools import find_packages from setuptools import find_packages
from setuptools import setup from setuptools import setup

View file

@ -1,6 +1,8 @@
""" """
Test suite for vdirsyncer. Test suite for vdirsyncer.
""" """
from __future__ import annotations
import hypothesis.strategies as st import hypothesis.strategies as st
import urllib3.exceptions import urllib3.exceptions

View file

@ -1,6 +1,8 @@
""" """
General-purpose fixtures for vdirsyncer's testsuite. General-purpose fixtures for vdirsyncer's testsuite.
""" """
from __future__ import annotations
import logging import logging
import os import os

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import random import random
import textwrap import textwrap
import uuid import uuid

View file

@ -1,9 +1,10 @@
from __future__ import annotations
import asyncio import asyncio
import contextlib import contextlib
import subprocess import subprocess
import time import time
import uuid import uuid
from typing import Type
import aiostream import aiostream
import pytest import pytest
@ -90,7 +91,7 @@ async def slow_create_collection(request, aio_connector):
# storage limits. # storage limits.
to_delete = [] to_delete = []
async def inner(cls: Type, args: dict, collection_name: str) -> dict: async def inner(cls: type, args: dict, collection_name: str) -> dict:
"""Create a collection """Create a collection
Returns args necessary to create a Storage instance pointing to it. Returns args necessary to create a Storage instance pointing to it.

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import uuid import uuid

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import datetime import datetime
from textwrap import dedent from textwrap import dedent

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from vdirsyncer.storage.dav import CardDAVStorage from vdirsyncer.storage.dav import CardDAVStorage

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from vdirsyncer.storage.dav import _BAD_XML_CHARS from vdirsyncer.storage.dav import _BAD_XML_CHARS

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import uuid import uuid

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import subprocess import subprocess
import aiostream import aiostream

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from aiohttp import BasicAuth from aiohttp import BasicAuth
from aioresponses import CallbackResult from aioresponses import CallbackResult

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import aiostream import aiostream
import pytest import pytest
from aioresponses import CallbackResult from aioresponses import CallbackResult

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from vdirsyncer.storage.memory import MemoryStorage from vdirsyncer.storage.memory import MemoryStorage

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from vdirsyncer.storage.singlefile import SingleFileStorage from vdirsyncer.storage.singlefile import SingleFileStorage

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from textwrap import dedent from textwrap import dedent
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import io import io
from textwrap import dedent from textwrap import dedent

View file

@ -1,6 +1,7 @@
from __future__ import annotations
import json import json
from textwrap import dedent from textwrap import dedent
from typing import List
import pytest import pytest
@ -210,7 +211,7 @@ def test_collection_required(a_requires, b_requires, tmpdir, runner, monkeypatch
async def get(self, href: str): async def get(self, href: str):
raise NotImplementedError raise NotImplementedError
async def list(self) -> List[tuple]: async def list(self) -> list[tuple]:
raise NotImplementedError raise NotImplementedError
from vdirsyncer.cli.utils import storage_names from vdirsyncer.cli.utils import storage_names

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from textwrap import dedent from textwrap import dedent

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from textwrap import dedent from textwrap import dedent
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import json import json
import sys import sys
from textwrap import dedent from textwrap import dedent

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import pytest import pytest
from vdirsyncer import exceptions from vdirsyncer import exceptions

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import ssl import ssl
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging import logging
import aiohttp import aiohttp

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os import os
from vdirsyncer.cli.config import _resolve_conflict_via_command from vdirsyncer.cli.config import _resolve_conflict_via_command

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import aiostream import aiostream
import pytest import pytest

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from contextlib import contextmanager from contextlib import contextmanager
from unittest.mock import patch from unittest.mock import patch

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import hypothesis.strategies as st import hypothesis.strategies as st
from hypothesis import assume from hypothesis import assume
from hypothesis import given from hypothesis import given

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import asyncio import asyncio
from copy import deepcopy from copy import deepcopy

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from vdirsyncer import exceptions from vdirsyncer import exceptions

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import hypothesis.strategies as st import hypothesis.strategies as st
import pytest import pytest
import pytest_asyncio import pytest_asyncio

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import aiostream import aiostream
import pytest import pytest
from hypothesis import HealthCheck from hypothesis import HealthCheck

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from textwrap import dedent from textwrap import dedent
import hypothesis.strategies as st import hypothesis.strategies as st

View file

@ -1,7 +1,7 @@
""" """
Vdirsyncer synchronizes calendars and contacts. Vdirsyncer synchronizes calendars and contacts.
""" """
from __future__ import annotations
PROJECT_HOME = "https://github.com/pimutils/vdirsyncer" PROJECT_HOME = "https://github.com/pimutils/vdirsyncer"
BUGTRACKER_HOME = PROJECT_HOME + "/issues" BUGTRACKER_HOME = PROJECT_HOME + "/issues"

View file

@ -1,3 +1,5 @@
from __future__ import annotations
if __name__ == "__main__": if __name__ == "__main__":
from vdirsyncer.cli import app from vdirsyncer.cli import app

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import asyncio import asyncio
import functools import functools
import json import json

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import asyncio import asyncio
import hashlib import hashlib
import json import json

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging import logging
import click import click

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import json import json
import aiohttp import aiohttp

View file

@ -2,6 +2,7 @@
Contains exception classes used by vdirsyncer. Not all exceptions are here, Contains exception classes used by vdirsyncer. Not all exceptions are here,
only the most commonly used ones. only the most commonly used ones.
""" """
from __future__ import annotations
class Error(Exception): class Error(Exception):

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging import logging
from ssl import create_default_context from ssl import create_default_context

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging import logging
from . import exceptions from . import exceptions

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import logging import logging
from os.path import basename from os.path import basename

View file

@ -1,10 +1,10 @@
from __future__ import annotations
import contextlib import contextlib
import functools import functools
from abc import ABCMeta from abc import ABCMeta
from abc import abstractmethod from abc import abstractmethod
from typing import Iterable from typing import Iterable
from typing import List
from typing import Optional
from vdirsyncer.vobject import Item from vdirsyncer.vobject import Item
@ -73,7 +73,7 @@ class Storage(metaclass=StorageMeta):
read_only = False read_only = False
# The attribute values to show in the representation of the storage. # The attribute values to show in the representation of the storage.
_repr_attributes: List[str] = [] _repr_attributes: list[str] = []
def __init__(self, instance_name=None, read_only=None, collection=None): def __init__(self, instance_name=None, read_only=None, collection=None):
if read_only is None: if read_only is None:
@ -132,7 +132,7 @@ class Storage(metaclass=StorageMeta):
) )
@abstractmethod @abstractmethod
async def list(self) -> List[tuple]: async def list(self) -> list[tuple]:
""" """
:returns: list of (href, etag) :returns: list of (href, etag)
""" """
@ -227,7 +227,7 @@ class Storage(metaclass=StorageMeta):
""" """
yield yield
async def get_meta(self, key: str) -> Optional[str]: async def get_meta(self, key: str) -> str | None:
"""Get metadata value for collection/storage. """Get metadata value for collection/storage.
See the vdir specification for the keys that *have* to be accepted. See the vdir specification for the keys that *have* to be accepted.
@ -237,7 +237,7 @@ class Storage(metaclass=StorageMeta):
""" """
raise NotImplementedError("This storage does not support metadata.") raise NotImplementedError("This storage does not support metadata.")
async def set_meta(self, key: str, value: Optional[str]): async def set_meta(self, key: str, value: str | None):
"""Set metadata value for collection/storage. """Set metadata value for collection/storage.
:param key: The metadata key. :param key: The metadata key.
@ -246,7 +246,7 @@ class Storage(metaclass=StorageMeta):
raise NotImplementedError("This storage does not support metadata.") raise NotImplementedError("This storage does not support metadata.")
def normalize_meta_value(value) -> Optional[str]: def normalize_meta_value(value) -> str | None:
# `None` is returned by iCloud for empty properties. # `None` is returned by iCloud for empty properties.
if value is None or value == "None": if value is None or value == "None":
return None return None

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import datetime import datetime
import logging import logging
import urllib.parse as urlparse import urllib.parse as urlparse
@ -5,8 +7,6 @@ import xml.etree.ElementTree as etree
from abc import abstractmethod from abc import abstractmethod
from inspect import getfullargspec from inspect import getfullargspec
from inspect import signature from inspect import signature
from typing import Optional
from typing import Type
import aiohttp import aiohttp
import aiostream import aiostream
@ -127,7 +127,7 @@ class Discover:
@property @property
@abstractmethod @abstractmethod
def _resourcetype(self) -> Optional[str]: def _resourcetype(self) -> str | None:
pass pass
@property @property
@ -339,7 +339,7 @@ class CalDiscover(Discover):
class CardDiscover(Discover): class CardDiscover(Discover):
_namespace = "urn:ietf:params:xml:ns:carddav" _namespace = "urn:ietf:params:xml:ns:carddav"
_resourcetype: Optional[str] = "{%s}addressbook" % _namespace _resourcetype: str | None = "{%s}addressbook" % _namespace
_homeset_xml = b""" _homeset_xml = b"""
<propfind xmlns="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav"> <propfind xmlns="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav">
<prop> <prop>
@ -448,7 +448,7 @@ class DAVStorage(Storage):
@property @property
@abstractmethod @abstractmethod
def discovery_class(self) -> Type[Discover]: def discovery_class(self) -> type[Discover]:
"""Discover subclass to use.""" """Discover subclass to use."""
# The DAVSession class to use # The DAVSession class to use
@ -681,7 +681,7 @@ class DAVStorage(Storage):
for href, etag, _prop in rv: for href, etag, _prop in rv:
yield href, etag yield href, etag
async def get_meta(self, key) -> Optional[str]: async def get_meta(self, key) -> str | None:
try: try:
tagname, namespace = self._property_table[key] tagname, namespace = self._property_table[key]
except KeyError: except KeyError:

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import errno import errno
import logging import logging
import os import os

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import json import json
import logging import logging
import os import os

View file

@ -2,15 +2,14 @@
# #
# Based on: # Based on:
# https://github.com/googleapis/google-auth-library-python-oauthlib/blob/1fb16be1bad9050ee29293541be44e41e82defd7/google_auth_oauthlib/flow.py#L513 # https://github.com/googleapis/google-auth-library-python-oauthlib/blob/1fb16be1bad9050ee29293541be44e41e82defd7/google_auth_oauthlib/flow.py#L513
from __future__ import annotations
import logging import logging
import wsgiref.simple_server import wsgiref.simple_server
import wsgiref.util import wsgiref.util
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import Dict
from typing import Iterable from typing import Iterable
from typing import Optional
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -29,7 +28,7 @@ class _RedirectWSGIApp:
Stores the request URI and displays the given success message. Stores the request URI and displays the given success message.
""" """
last_request_uri: Optional[str] last_request_uri: str | None
def __init__(self, success_message: str): def __init__(self, success_message: str):
""" """
@ -41,7 +40,7 @@ class _RedirectWSGIApp:
def __call__( def __call__(
self, self,
environ: Dict[str, Any], environ: dict[str, Any],
start_response: Callable[[str, list], None], start_response: Callable[[str, list], None],
) -> Iterable[bytes]: ) -> Iterable[bytes]:
"""WSGI Callable. """WSGI Callable.

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import urllib.parse as urlparse import urllib.parse as urlparse
import aiohttp import aiohttp
@ -34,7 +36,7 @@ class HttpStorage(Storage):
auth_cert=None, auth_cert=None,
*, *,
connector, connector,
**kwargs **kwargs,
) -> None: ) -> None:
super().__init__(**kwargs) super().__init__(**kwargs)

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import random import random
from .. import exceptions from .. import exceptions

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import collections import collections
import contextlib import contextlib
import functools import functools

View file

@ -9,6 +9,8 @@ Yang: http://blog.ezyang.com/2012/08/how-offlineimap-works/
Some modifications to it are explained in Some modifications to it are explained in
https://unterwaditzer.net/2016/sync-algorithm.html https://unterwaditzer.net/2016/sync-algorithm.html
""" """
from __future__ import annotations
import contextlib import contextlib
import itertools import itertools
import logging import logging

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from .. import exceptions from .. import exceptions

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import abc import abc
import contextlib import contextlib
import sqlite3 import sqlite3

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import functools import functools
import os import os
import sys import sys

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import hashlib import hashlib
from itertools import chain from itertools import chain
from itertools import tee from itertools import tee