alembic 1.12.1__py3-none-any.whl → 1.13.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. alembic/__init__.py +1 -3
  2. alembic/autogenerate/__init__.py +10 -10
  3. alembic/autogenerate/api.py +8 -5
  4. alembic/autogenerate/compare.py +134 -199
  5. alembic/autogenerate/render.py +39 -24
  6. alembic/autogenerate/rewriter.py +26 -13
  7. alembic/command.py +7 -2
  8. alembic/config.py +20 -9
  9. alembic/context.pyi +12 -6
  10. alembic/ddl/__init__.py +1 -1
  11. alembic/ddl/_autogen.py +325 -0
  12. alembic/ddl/base.py +12 -9
  13. alembic/ddl/impl.py +110 -13
  14. alembic/ddl/mssql.py +4 -1
  15. alembic/ddl/mysql.py +9 -6
  16. alembic/ddl/oracle.py +4 -1
  17. alembic/ddl/postgresql.py +147 -73
  18. alembic/ddl/sqlite.py +8 -6
  19. alembic/op.pyi +46 -8
  20. alembic/operations/base.py +70 -14
  21. alembic/operations/batch.py +7 -8
  22. alembic/operations/ops.py +53 -31
  23. alembic/operations/schemaobj.py +5 -4
  24. alembic/operations/toimpl.py +8 -5
  25. alembic/runtime/environment.py +17 -7
  26. alembic/runtime/migration.py +27 -11
  27. alembic/script/base.py +34 -27
  28. alembic/script/revision.py +30 -17
  29. alembic/script/write_hooks.py +3 -0
  30. alembic/templates/async/alembic.ini.mako +3 -3
  31. alembic/templates/generic/alembic.ini.mako +3 -3
  32. alembic/templates/multidb/alembic.ini.mako +3 -3
  33. alembic/testing/requirements.py +12 -0
  34. alembic/testing/schemacompare.py +9 -0
  35. alembic/util/__init__.py +31 -31
  36. alembic/util/compat.py +25 -9
  37. alembic/util/langhelpers.py +78 -33
  38. alembic/util/messaging.py +6 -3
  39. alembic/util/pyfiles.py +7 -3
  40. alembic/util/sqla_compat.py +52 -26
  41. {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/METADATA +5 -4
  42. alembic-1.13.1.dist-info/RECORD +83 -0
  43. {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/WHEEL +1 -1
  44. alembic-1.12.1.dist-info/RECORD +0 -82
  45. {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/LICENSE +0 -0
  46. {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/entry_points.txt +0 -0
  47. {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/top_level.txt +0 -0
alembic/script/base.py CHANGED
@@ -23,6 +23,7 @@ from . import revision
23
23
  from . import write_hooks
24
24
  from .. import util
25
25
  from ..runtime import migration
26
+ from ..util import compat
26
27
  from ..util import not_none
27
28
 
28
29
  if TYPE_CHECKING:
@@ -35,9 +36,14 @@ if TYPE_CHECKING:
35
36
  from ..runtime.migration import StampStep
36
37
 
37
38
  try:
38
- from dateutil import tz
39
+ if compat.py39:
40
+ from zoneinfo import ZoneInfo
41
+ from zoneinfo import ZoneInfoNotFoundError
42
+ else:
43
+ from backports.zoneinfo import ZoneInfo # type: ignore[import-not-found,no-redef] # noqa: E501
44
+ from backports.zoneinfo import ZoneInfoNotFoundError # type: ignore[no-redef] # noqa: E501
39
45
  except ImportError:
40
- tz = None # type: ignore[assignment]
46
+ ZoneInfo = None # type: ignore[assignment, misc]
41
47
 
42
48
  _sourceless_rev_file = re.compile(r"(?!\.\#|__init__)(.*\.py)(c|o)?$")
43
49
  _only_source_rev_file = re.compile(r"(?!\.\#|__init__)(.*\.py)$")
@@ -113,7 +119,7 @@ class ScriptDirectory:
113
119
  return loc[0]
114
120
 
115
121
  @util.memoized_property
116
- def _version_locations(self):
122
+ def _version_locations(self) -> Sequence[str]:
117
123
  if self.version_locations:
118
124
  return [
119
125
  os.path.abspath(util.coerce_resource_to_filename(location))
@@ -297,24 +303,22 @@ class ScriptDirectory:
297
303
  ):
298
304
  yield cast(Script, rev)
299
305
 
300
- def get_revisions(self, id_: _GetRevArg) -> Tuple[Optional[Script], ...]:
306
+ def get_revisions(self, id_: _GetRevArg) -> Tuple[Script, ...]:
301
307
  """Return the :class:`.Script` instance with the given rev identifier,
302
308
  symbolic name, or sequence of identifiers.
303
309
 
304
310
  """
305
311
  with self._catch_revision_errors():
306
312
  return cast(
307
- Tuple[Optional[Script], ...],
313
+ Tuple[Script, ...],
308
314
  self.revision_map.get_revisions(id_),
309
315
  )
310
316
 
311
- def get_all_current(self, id_: Tuple[str, ...]) -> Set[Optional[Script]]:
317
+ def get_all_current(self, id_: Tuple[str, ...]) -> Set[Script]:
312
318
  with self._catch_revision_errors():
313
- return cast(
314
- Set[Optional[Script]], self.revision_map._get_all_current(id_)
315
- )
319
+ return cast(Set[Script], self.revision_map._get_all_current(id_))
316
320
 
317
- def get_revision(self, id_: str) -> Optional[Script]:
321
+ def get_revision(self, id_: str) -> Script:
318
322
  """Return the :class:`.Script` instance with the given rev id.
319
323
 
320
324
  .. seealso::
@@ -324,7 +328,7 @@ class ScriptDirectory:
324
328
  """
325
329
 
326
330
  with self._catch_revision_errors():
327
- return cast(Optional[Script], self.revision_map.get_revision(id_))
331
+ return cast(Script, self.revision_map.get_revision(id_))
328
332
 
329
333
  def as_revision_number(
330
334
  self, id_: Optional[str]
@@ -579,7 +583,7 @@ class ScriptDirectory:
579
583
  util.load_python_file(self.dir, "env.py")
580
584
 
581
585
  @property
582
- def env_py_location(self):
586
+ def env_py_location(self) -> str:
583
587
  return os.path.abspath(os.path.join(self.dir, "env.py"))
584
588
 
585
589
  def _generate_template(self, src: str, dest: str, **kw: Any) -> None:
@@ -604,23 +608,26 @@ class ScriptDirectory:
604
608
 
605
609
  def _generate_create_date(self) -> datetime.datetime:
606
610
  if self.timezone is not None:
607
- if tz is None:
611
+ if ZoneInfo is None:
608
612
  raise util.CommandError(
609
- "The library 'python-dateutil' is required "
610
- "for timezone support"
613
+ "Python >= 3.9 is required for timezone support or"
614
+ "the 'backports.zoneinfo' package must be installed."
611
615
  )
612
616
  # First, assume correct capitalization
613
- tzinfo = tz.gettz(self.timezone)
614
- if tzinfo is None:
615
- # Fall back to uppercase
616
- tzinfo = tz.gettz(self.timezone.upper())
617
+ try:
618
+ tzinfo = ZoneInfo(self.timezone)
619
+ except ZoneInfoNotFoundError:
620
+ tzinfo = None
617
621
  if tzinfo is None:
618
- raise util.CommandError(
619
- "Can't locate timezone: %s" % self.timezone
620
- )
622
+ try:
623
+ tzinfo = ZoneInfo(self.timezone.upper())
624
+ except ZoneInfoNotFoundError:
625
+ raise util.CommandError(
626
+ "Can't locate timezone: %s" % self.timezone
627
+ ) from None
621
628
  create_date = (
622
629
  datetime.datetime.utcnow()
623
- .replace(tzinfo=tz.tzutc())
630
+ .replace(tzinfo=datetime.timezone.utc)
624
631
  .astimezone(tzinfo)
625
632
  )
626
633
  else:
@@ -675,7 +682,7 @@ class ScriptDirectory:
675
682
  self.revision_map.get_revisions(head),
676
683
  )
677
684
  for h in heads:
678
- assert h != "base"
685
+ assert h != "base" # type: ignore[comparison-overlap]
679
686
 
680
687
  if len(set(heads)) != len(heads):
681
688
  raise util.CommandError("Duplicate head revisions specified")
@@ -814,7 +821,7 @@ class Script(revision.Revision):
814
821
  self.path = path
815
822
  super().__init__(
816
823
  rev_id,
817
- module.down_revision, # type: ignore[attr-defined]
824
+ module.down_revision,
818
825
  branch_labels=util.to_tuple(
819
826
  getattr(module, "branch_labels", None), default=()
820
827
  ),
@@ -847,7 +854,7 @@ class Script(revision.Revision):
847
854
  if doc:
848
855
  if hasattr(self.module, "_alembic_source_encoding"):
849
856
  doc = doc.decode( # type: ignore[attr-defined]
850
- self.module._alembic_source_encoding # type: ignore[attr-defined] # noqa
857
+ self.module._alembic_source_encoding
851
858
  )
852
859
  return doc.strip() # type: ignore[union-attr]
853
860
  else:
@@ -889,7 +896,7 @@ class Script(revision.Revision):
889
896
  )
890
897
  return entry
891
898
 
892
- def __str__(self):
899
+ def __str__(self) -> str:
893
900
  return "%s -> %s%s%s%s, %s" % (
894
901
  self._format_down_revision(),
895
902
  self.revision,
@@ -14,6 +14,7 @@ from typing import Iterator
14
14
  from typing import List
15
15
  from typing import Optional
16
16
  from typing import overload
17
+ from typing import Protocol
17
18
  from typing import Sequence
18
19
  from typing import Set
19
20
  from typing import Tuple
@@ -47,6 +48,18 @@ _relative_destination = re.compile(r"(?:(.+?)@)?(\w+)?((?:\+|-)\d+)")
47
48
  _revision_illegal_chars = ["@", "-", "+"]
48
49
 
49
50
 
51
+ class _CollectRevisionsProtocol(Protocol):
52
+ def __call__(
53
+ self,
54
+ upper: _RevisionIdentifierType,
55
+ lower: _RevisionIdentifierType,
56
+ inclusive: bool,
57
+ implicit_base: bool,
58
+ assert_relative_length: bool,
59
+ ) -> Tuple[Set[Revision], Tuple[Optional[_RevisionOrBase], ...]]:
60
+ ...
61
+
62
+
50
63
  class RevisionError(Exception):
51
64
  pass
52
65
 
@@ -396,7 +409,7 @@ class RevisionMap:
396
409
  for rev in self._get_ancestor_nodes(
397
410
  [revision],
398
411
  include_dependencies=False,
399
- map_=cast(_RevisionMapType, map_),
412
+ map_=map_,
400
413
  ):
401
414
  if rev is revision:
402
415
  continue
@@ -791,7 +804,7 @@ class RevisionMap:
791
804
  The iterator yields :class:`.Revision` objects.
792
805
 
793
806
  """
794
- fn: Callable
807
+ fn: _CollectRevisionsProtocol
795
808
  if select_for_downgrade:
796
809
  fn = self._collect_downgrade_revisions
797
810
  else:
@@ -818,7 +831,7 @@ class RevisionMap:
818
831
  ) -> Iterator[Any]:
819
832
  if omit_immediate_dependencies:
820
833
 
821
- def fn(rev):
834
+ def fn(rev: Revision) -> Iterable[str]:
822
835
  if rev not in targets:
823
836
  return rev._all_nextrev
824
837
  else:
@@ -826,12 +839,12 @@ class RevisionMap:
826
839
 
827
840
  elif include_dependencies:
828
841
 
829
- def fn(rev):
842
+ def fn(rev: Revision) -> Iterable[str]:
830
843
  return rev._all_nextrev
831
844
 
832
845
  else:
833
846
 
834
- def fn(rev):
847
+ def fn(rev: Revision) -> Iterable[str]:
835
848
  return rev.nextrev
836
849
 
837
850
  return self._iterate_related_revisions(
@@ -847,12 +860,12 @@ class RevisionMap:
847
860
  ) -> Iterator[Revision]:
848
861
  if include_dependencies:
849
862
 
850
- def fn(rev):
863
+ def fn(rev: Revision) -> Iterable[str]:
851
864
  return rev._normalized_down_revisions
852
865
 
853
866
  else:
854
867
 
855
- def fn(rev):
868
+ def fn(rev: Revision) -> Iterable[str]:
856
869
  return rev._versioned_down_revisions
857
870
 
858
871
  return self._iterate_related_revisions(
@@ -861,7 +874,7 @@ class RevisionMap:
861
874
 
862
875
  def _iterate_related_revisions(
863
876
  self,
864
- fn: Callable,
877
+ fn: Callable[[Revision], Iterable[str]],
865
878
  targets: Collection[Optional[_RevisionOrBase]],
866
879
  map_: Optional[_RevisionMapType],
867
880
  check: bool = False,
@@ -923,7 +936,7 @@ class RevisionMap:
923
936
 
924
937
  id_to_rev = self._revision_map
925
938
 
926
- def get_ancestors(rev_id):
939
+ def get_ancestors(rev_id: str) -> Set[str]:
927
940
  return {
928
941
  r.revision
929
942
  for r in self._get_ancestor_nodes([id_to_rev[rev_id]])
@@ -1041,7 +1054,7 @@ class RevisionMap:
1041
1054
  children: Sequence[Optional[_RevisionOrBase]]
1042
1055
  for _ in range(abs(steps)):
1043
1056
  if steps > 0:
1044
- assert initial != "base"
1057
+ assert initial != "base" # type: ignore[comparison-overlap]
1045
1058
  # Walk up
1046
1059
  walk_up = [
1047
1060
  is_revision(rev)
@@ -1055,7 +1068,7 @@ class RevisionMap:
1055
1068
  children = walk_up
1056
1069
  else:
1057
1070
  # Walk down
1058
- if initial == "base":
1071
+ if initial == "base": # type: ignore[comparison-overlap]
1059
1072
  children = ()
1060
1073
  else:
1061
1074
  children = self.get_revisions(
@@ -1189,7 +1202,7 @@ class RevisionMap:
1189
1202
  # No relative destination given, revision specified is absolute.
1190
1203
  branch_label, _, symbol = target.rpartition("@")
1191
1204
  if not branch_label:
1192
- branch_label = None # type:ignore[assignment]
1205
+ branch_label = None
1193
1206
  return branch_label, self.get_revision(symbol)
1194
1207
 
1195
1208
  def _parse_upgrade_target(
@@ -1301,11 +1314,11 @@ class RevisionMap:
1301
1314
  def _collect_downgrade_revisions(
1302
1315
  self,
1303
1316
  upper: _RevisionIdentifierType,
1304
- target: _RevisionIdentifierType,
1317
+ lower: _RevisionIdentifierType,
1305
1318
  inclusive: bool,
1306
1319
  implicit_base: bool,
1307
1320
  assert_relative_length: bool,
1308
- ) -> Any:
1321
+ ) -> Tuple[Set[Revision], Tuple[Optional[_RevisionOrBase], ...]]:
1309
1322
  """
1310
1323
  Compute the set of current revisions specified by :upper, and the
1311
1324
  downgrade target specified by :target. Return all dependents of target
@@ -1316,7 +1329,7 @@ class RevisionMap:
1316
1329
 
1317
1330
  branch_label, target_revision = self._parse_downgrade_target(
1318
1331
  current_revisions=upper,
1319
- target=target,
1332
+ target=lower,
1320
1333
  assert_relative_length=assert_relative_length,
1321
1334
  )
1322
1335
  if target_revision == "base":
@@ -1408,7 +1421,7 @@ class RevisionMap:
1408
1421
  inclusive: bool,
1409
1422
  implicit_base: bool,
1410
1423
  assert_relative_length: bool,
1411
- ) -> Tuple[Set[Revision], Tuple[Optional[_RevisionOrBase]]]:
1424
+ ) -> Tuple[Set[Revision], Tuple[Revision, ...]]:
1412
1425
  """
1413
1426
  Compute the set of required revisions specified by :upper, and the
1414
1427
  current set of active revisions specified by :lower. Find the
@@ -1500,7 +1513,7 @@ class RevisionMap:
1500
1513
  )
1501
1514
  needs.intersection_update(lower_descendents)
1502
1515
 
1503
- return needs, tuple(targets) # type:ignore[return-value]
1516
+ return needs, tuple(targets)
1504
1517
 
1505
1518
  def _get_all_current(
1506
1519
  self, id_: Tuple[str, ...]
@@ -1,3 +1,6 @@
1
+ # mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls
2
+ # mypy: no-warn-return-any, allow-any-generics
3
+
1
4
  from __future__ import annotations
2
5
 
3
6
  import shlex
@@ -14,9 +14,9 @@ prepend_sys_path = .
14
14
 
15
15
  # timezone to use when rendering the date within the migration file
16
16
  # as well as the filename.
17
- # If specified, requires the python-dateutil library that can be
18
- # installed by adding `alembic[tz]` to the pip requirements
19
- # string value is passed to dateutil.tz.gettz()
17
+ # If specified, requires the python>=3.9 or backports.zoneinfo library.
18
+ # Any required deps can installed by adding `alembic[tz]` to the pip requirements
19
+ # string value is passed to ZoneInfo()
20
20
  # leave blank for localtime
21
21
  # timezone =
22
22
 
@@ -16,9 +16,9 @@ prepend_sys_path = .
16
16
 
17
17
  # timezone to use when rendering the date within the migration file
18
18
  # as well as the filename.
19
- # If specified, requires the python-dateutil library that can be
20
- # installed by adding `alembic[tz]` to the pip requirements
21
- # string value is passed to dateutil.tz.gettz()
19
+ # If specified, requires the python>=3.9 or backports.zoneinfo library.
20
+ # Any required deps can installed by adding `alembic[tz]` to the pip requirements
21
+ # string value is passed to ZoneInfo()
22
22
  # leave blank for localtime
23
23
  # timezone =
24
24
 
@@ -16,9 +16,9 @@ prepend_sys_path = .
16
16
 
17
17
  # timezone to use when rendering the date within the migration file
18
18
  # as well as the filename.
19
- # If specified, requires the python-dateutil library that can be
20
- # installed by adding `alembic[tz]` to the pip requirements
21
- # string value is passed to dateutil.tz.gettz()
19
+ # If specified, requires the python>=3.9 or backports.zoneinfo library.
20
+ # Any required deps can installed by adding `alembic[tz]` to the pip requirements
21
+ # string value is passed to ZoneInfo()
22
22
  # leave blank for localtime
23
23
  # timezone =
24
24
 
@@ -95,6 +95,18 @@ class SuiteRequirements(Requirements):
95
95
  "SQLAlchemy 2.x test",
96
96
  )
97
97
 
98
+ @property
99
+ def asyncio(self):
100
+ def go(config):
101
+ try:
102
+ import greenlet # noqa: F401
103
+ except ImportError:
104
+ return False
105
+ else:
106
+ return True
107
+
108
+ return self.sqlalchemy_14 + exclusions.only_if(go)
109
+
98
110
  @property
99
111
  def comments(self):
100
112
  return exclusions.only_if(
@@ -1,6 +1,7 @@
1
1
  from itertools import zip_longest
2
2
 
3
3
  from sqlalchemy import schema
4
+ from sqlalchemy.sql.elements import ClauseList
4
5
 
5
6
 
6
7
  class CompareTable:
@@ -60,6 +61,14 @@ class CompareIndex:
60
61
  def __ne__(self, other):
61
62
  return not self.__eq__(other)
62
63
 
64
+ def __repr__(self):
65
+ expr = ClauseList(*self.index.expressions)
66
+ try:
67
+ expr_str = expr.compile().string
68
+ except Exception:
69
+ expr_str = str(expr)
70
+ return f"<CompareIndex {self.index.name}({expr_str})>"
71
+
63
72
 
64
73
  class CompareCheckConstraint:
65
74
  def __init__(self, constraint):
alembic/util/__init__.py CHANGED
@@ -1,34 +1,34 @@
1
- from .editor import open_in_editor
2
- from .exc import AutogenerateDiffsDetected
3
- from .exc import CommandError
4
- from .langhelpers import _with_legacy_names
5
- from .langhelpers import asbool
6
- from .langhelpers import dedupe_tuple
7
- from .langhelpers import Dispatcher
8
- from .langhelpers import EMPTY_DICT
9
- from .langhelpers import immutabledict
10
- from .langhelpers import memoized_property
11
- from .langhelpers import ModuleClsProxy
12
- from .langhelpers import not_none
13
- from .langhelpers import rev_id
14
- from .langhelpers import to_list
15
- from .langhelpers import to_tuple
16
- from .langhelpers import unique_list
17
- from .messaging import err
18
- from .messaging import format_as_comma
19
- from .messaging import msg
20
- from .messaging import obfuscate_url_pw
21
- from .messaging import status
22
- from .messaging import warn
23
- from .messaging import write_outstream
24
- from .pyfiles import coerce_resource_to_filename
25
- from .pyfiles import load_python_file
26
- from .pyfiles import pyc_file_from_path
27
- from .pyfiles import template_to_file
28
- from .sqla_compat import has_computed
29
- from .sqla_compat import sqla_13
30
- from .sqla_compat import sqla_14
31
- from .sqla_compat import sqla_2
1
+ from .editor import open_in_editor as open_in_editor
2
+ from .exc import AutogenerateDiffsDetected as AutogenerateDiffsDetected
3
+ from .exc import CommandError as CommandError
4
+ from .langhelpers import _with_legacy_names as _with_legacy_names
5
+ from .langhelpers import asbool as asbool
6
+ from .langhelpers import dedupe_tuple as dedupe_tuple
7
+ from .langhelpers import Dispatcher as Dispatcher
8
+ from .langhelpers import EMPTY_DICT as EMPTY_DICT
9
+ from .langhelpers import immutabledict as immutabledict
10
+ from .langhelpers import memoized_property as memoized_property
11
+ from .langhelpers import ModuleClsProxy as ModuleClsProxy
12
+ from .langhelpers import not_none as not_none
13
+ from .langhelpers import rev_id as rev_id
14
+ from .langhelpers import to_list as to_list
15
+ from .langhelpers import to_tuple as to_tuple
16
+ from .langhelpers import unique_list as unique_list
17
+ from .messaging import err as err
18
+ from .messaging import format_as_comma as format_as_comma
19
+ from .messaging import msg as msg
20
+ from .messaging import obfuscate_url_pw as obfuscate_url_pw
21
+ from .messaging import status as status
22
+ from .messaging import warn as warn
23
+ from .messaging import write_outstream as write_outstream
24
+ from .pyfiles import coerce_resource_to_filename as coerce_resource_to_filename
25
+ from .pyfiles import load_python_file as load_python_file
26
+ from .pyfiles import pyc_file_from_path as pyc_file_from_path
27
+ from .pyfiles import template_to_file as template_to_file
28
+ from .sqla_compat import has_computed as has_computed
29
+ from .sqla_compat import sqla_13 as sqla_13
30
+ from .sqla_compat import sqla_14 as sqla_14
31
+ from .sqla_compat import sqla_2 as sqla_2
32
32
 
33
33
 
34
34
  if not sqla_13:
alembic/util/compat.py CHANGED
@@ -1,3 +1,5 @@
1
+ # mypy: no-warn-unused-ignores
2
+
1
3
  from __future__ import annotations
2
4
 
3
5
  from configparser import ConfigParser
@@ -5,18 +7,26 @@ import io
5
7
  import os
6
8
  import sys
7
9
  import typing
10
+ from typing import Any
11
+ from typing import List
12
+ from typing import Optional
8
13
  from typing import Sequence
9
14
  from typing import Union
10
15
 
11
- from sqlalchemy.util import inspect_getfullargspec # noqa
12
- from sqlalchemy.util.compat import inspect_formatargspec # noqa
16
+ if True:
17
+ # zimports hack for too-long names
18
+ from sqlalchemy.util import ( # noqa: F401
19
+ inspect_getfullargspec as inspect_getfullargspec,
20
+ )
21
+ from sqlalchemy.util.compat import ( # noqa: F401
22
+ inspect_formatargspec as inspect_formatargspec,
23
+ )
13
24
 
14
25
  is_posix = os.name == "posix"
15
26
 
16
27
  py311 = sys.version_info >= (3, 11)
17
28
  py310 = sys.version_info >= (3, 10)
18
29
  py39 = sys.version_info >= (3, 9)
19
- py38 = sys.version_info >= (3, 8)
20
30
 
21
31
 
22
32
  # produce a wrapper that allows encoded text to stream
@@ -28,9 +38,13 @@ class EncodedIO(io.TextIOWrapper):
28
38
 
29
39
 
30
40
  if py39:
31
- from importlib import resources as importlib_resources
32
- from importlib import metadata as importlib_metadata
33
- from importlib.metadata import EntryPoint
41
+ from importlib import resources as _resources
42
+
43
+ importlib_resources = _resources
44
+ from importlib import metadata as _metadata
45
+
46
+ importlib_metadata = _metadata
47
+ from importlib.metadata import EntryPoint as EntryPoint
34
48
  else:
35
49
  import importlib_resources # type:ignore # noqa
36
50
  import importlib_metadata # type:ignore # noqa
@@ -40,12 +54,14 @@ else:
40
54
  def importlib_metadata_get(group: str) -> Sequence[EntryPoint]:
41
55
  ep = importlib_metadata.entry_points()
42
56
  if hasattr(ep, "select"):
43
- return ep.select(group=group) # type: ignore
57
+ return ep.select(group=group)
44
58
  else:
45
59
  return ep.get(group, ()) # type: ignore
46
60
 
47
61
 
48
- def formatannotation_fwdref(annotation, base_module=None):
62
+ def formatannotation_fwdref(
63
+ annotation: Any, base_module: Optional[Any] = None
64
+ ) -> str:
49
65
  """vendored from python 3.7"""
50
66
  # copied over _formatannotation from sqlalchemy 2.0
51
67
 
@@ -66,7 +82,7 @@ def formatannotation_fwdref(annotation, base_module=None):
66
82
  def read_config_parser(
67
83
  file_config: ConfigParser,
68
84
  file_argument: Sequence[Union[str, os.PathLike[str]]],
69
- ) -> list[str]:
85
+ ) -> List[str]:
70
86
  if py310:
71
87
  return file_config.read(file_argument, encoding="locale")
72
88
  else: