comparison mercurial/policy.py @ 51724:a3dc962cac62

typing: add type hints to `mercurial.policy` Mostly trivial, but this seems like the logical module to use to inject the hints from `cext`, `pure`, etc, given that this file has the fallback policy. This is a first step. There doesn't appear to be a predefined type for a module in py3.7, so those are omitted for now.
author Matt Harbison <matt_harbison@yahoo.com>
date Sat, 20 Jul 2024 17:03:30 -0400
parents b0a4de6c14f8
children 92845af308b4
comparison
equal deleted inserted replaced
51723:9367571fea21 51724:a3dc962cac62
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 8
9 import os 9 import os
10 import sys 10 import sys
11 import typing
12
13 if typing.TYPE_CHECKING:
14 from typing import (
15 Dict,
16 Optional,
17 Tuple,
18 )
11 19
12 # Rules for how modules can be loaded. Values are: 20 # Rules for how modules can be loaded. Values are:
13 # 21 #
14 # c - require C extensions 22 # c - require C extensions
15 # rust+c - require Rust and C extensions 23 # rust+c - require Rust and C extensions
21 # py - only load pure Python modules 29 # py - only load pure Python modules
22 # 30 #
23 # By default, fall back to the pure modules so the in-place build can 31 # By default, fall back to the pure modules so the in-place build can
24 # run without recompiling the C extensions. This will be overridden by 32 # run without recompiling the C extensions. This will be overridden by
25 # __modulepolicy__ generated by setup.py. 33 # __modulepolicy__ generated by setup.py.
26 policy = b'allow' 34 policy: bytes = b'allow'
27 _packageprefs = { 35 _packageprefs: "Dict[bytes, Tuple[Optional[str], Optional[str]]]" = {
28 # policy: (versioned package, pure package) 36 # policy: (versioned package, pure package)
29 b'c': ('cext', None), 37 b'c': ('cext', None),
30 b'allow': ('cext', 'pure'), 38 b'allow': ('cext', 'pure'),
31 b'cffi': ('cffi', None), 39 b'cffi': ('cffi', None),
32 b'cffi-allow': ('cffi', 'pure'), 40 b'cffi-allow': ('cffi', 'pure'),
37 } 45 }
38 46
39 try: 47 try:
40 from . import __modulepolicy__ # type: ignore 48 from . import __modulepolicy__ # type: ignore
41 49
42 policy = __modulepolicy__.modulepolicy 50 policy: bytes = __modulepolicy__.modulepolicy
43 except ImportError: 51 except ImportError:
44 pass 52 pass
45 53
46 # PyPy doesn't load C extensions. 54 # PyPy doesn't load C extensions.
47 # 55 #
48 # The canonical way to do this is to test platform.python_implementation(). 56 # The canonical way to do this is to test platform.python_implementation().
49 # But we don't import platform and don't bloat for it here. 57 # But we don't import platform and don't bloat for it here.
50 if '__pypy__' in sys.builtin_module_names: 58 if '__pypy__' in sys.builtin_module_names:
51 policy = b'cffi' 59 policy: bytes = b'cffi'
52 60
53 # Environment variable can always force settings. 61 # Environment variable can always force settings.
54 if 'HGMODULEPOLICY' in os.environ: 62 if 'HGMODULEPOLICY' in os.environ:
55 policy = os.environ['HGMODULEPOLICY'].encode('utf-8') 63 policy: bytes = os.environ['HGMODULEPOLICY'].encode('utf-8')
56 64
57 65
58 def _importfrom(pkgname, modname): 66 def _importfrom(pkgname: str, modname: str):
59 # from .<pkgname> import <modname> (where . is looked through this module) 67 # from .<pkgname> import <modname> (where . is looked through this module)
60 fakelocals = {} 68 fakelocals = {}
61 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1) 69 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
62 try: 70 try:
63 fakelocals[modname] = mod = getattr(pkg, modname) 71 fakelocals[modname] = mod = getattr(pkg, modname)
67 getattr(mod, '__doc__', None) 75 getattr(mod, '__doc__', None)
68 return fakelocals[modname] 76 return fakelocals[modname]
69 77
70 78
71 # keep in sync with "version" in C modules 79 # keep in sync with "version" in C modules
72 _cextversions = { 80 _cextversions: "Dict[Tuple[str, str], int]" = {
73 ('cext', 'base85'): 1, 81 ('cext', 'base85'): 1,
74 ('cext', 'bdiff'): 3, 82 ('cext', 'bdiff'): 3,
75 ('cext', 'mpatch'): 1, 83 ('cext', 'mpatch'): 1,
76 ('cext', 'osutil'): 4, 84 ('cext', 'osutil'): 4,
77 ('cext', 'parsers'): 21, 85 ('cext', 'parsers'): 21,
78 } 86 }
79 87
80 # map import request to other package or module 88 # map import request to other package or module
81 _modredirects = { 89 _modredirects: "Dict[Tuple[str, str], Tuple[str, str]]" = {
82 ('cext', 'charencode'): ('cext', 'parsers'), 90 ('cext', 'charencode'): ('cext', 'parsers'),
83 ('cffi', 'base85'): ('pure', 'base85'), 91 ('cffi', 'base85'): ('pure', 'base85'),
84 ('cffi', 'charencode'): ('pure', 'charencode'), 92 ('cffi', 'charencode'): ('pure', 'charencode'),
85 ('cffi', 'parsers'): ('pure', 'parsers'), 93 ('cffi', 'parsers'): ('pure', 'parsers'),
86 } 94 }
87 95
88 96
89 def _checkmod(pkgname, modname, mod): 97 def _checkmod(pkgname: str, modname: str, mod) -> None:
90 expected = _cextversions.get((pkgname, modname)) 98 expected = _cextversions.get((pkgname, modname))
91 actual = getattr(mod, 'version', None) 99 actual = getattr(mod, 'version', None)
92 if actual != expected: 100 if actual != expected:
93 raise ImportError( 101 raise ImportError(
94 'cannot import module %s.%s ' 102 'cannot import module %s.%s '
95 '(expected version: %d, actual: %r)' 103 '(expected version: %d, actual: %r)'
96 % (pkgname, modname, expected, actual) 104 % (pkgname, modname, expected, actual)
97 ) 105 )
98 106
99 107
100 def importmod(modname): 108 def importmod(modname: str):
101 """Import module according to policy and check API version""" 109 """Import module according to policy and check API version"""
102 try: 110 try:
103 verpkg, purepkg = _packageprefs[policy] 111 verpkg, purepkg = _packageprefs[policy]
104 except KeyError: 112 except KeyError:
105 raise ImportError('invalid HGMODULEPOLICY %r' % policy) 113 raise ImportError('invalid HGMODULEPOLICY %r' % policy)
116 raise 124 raise
117 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname)) 125 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
118 return _importfrom(pn, mn) 126 return _importfrom(pn, mn)
119 127
120 128
121 def _isrustpermissive(): 129 def _isrustpermissive() -> bool:
122 """Assuming the policy is a Rust one, tell if it's permissive.""" 130 """Assuming the policy is a Rust one, tell if it's permissive."""
123 return policy.endswith(b'-allow') 131 return policy.endswith(b'-allow')
124 132
125 133
126 def importrust(modname, member=None, default=None): 134 def importrust(modname: str, member: "Optional[str]" = None, default=None):
127 """Import Rust module according to policy and availability. 135 """Import Rust module according to policy and availability.
128 136
129 If policy isn't a Rust one, this returns `default`. 137 If policy isn't a Rust one, this returns `default`.
130 138
131 If either the module or its member is not available, this returns `default` 139 If either the module or its member is not available, this returns `default`