Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/util/compat.py: 75%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

139 statements  

1# util/compat.py 

2# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: https://www.opensource.org/licenses/mit-license.php 

7# mypy: allow-untyped-defs, allow-untyped-calls 

8 

9"""Handle Python version/platform incompatibilities.""" 

10 

11from __future__ import annotations 

12 

13import base64 

14import dataclasses 

15import hashlib 

16from importlib import metadata as importlib_metadata 

17import inspect 

18import operator 

19import platform 

20import sys 

21import typing 

22from typing import Any 

23from typing import Callable 

24from typing import Dict 

25from typing import Iterable 

26from typing import List 

27from typing import Mapping 

28from typing import Optional 

29from typing import Sequence 

30from typing import Set 

31from typing import Tuple 

32from typing import Type 

33 

34py314b1 = sys.version_info >= (3, 14, 0, "beta", 1) 

35py314 = sys.version_info >= (3, 14) 

36py313 = sys.version_info >= (3, 13) 

37py312 = sys.version_info >= (3, 12) 

38py311 = sys.version_info >= (3, 11) 

39pypy = platform.python_implementation() == "PyPy" 

40cpython = platform.python_implementation() == "CPython" 

41 

42win32 = sys.platform.startswith("win") 

43osx = sys.platform.startswith("darwin") 

44arm = "aarch" in platform.machine().lower() 

45is64bit = sys.maxsize > 2**32 

46 

47has_refcount_gc = bool(cpython) 

48 

49dottedgetter = operator.attrgetter 

50 

51 

52class FullArgSpec(typing.NamedTuple): 

53 args: List[str] 

54 varargs: Optional[str] 

55 varkw: Optional[str] 

56 defaults: Optional[Tuple[Any, ...]] 

57 kwonlyargs: List[str] 

58 kwonlydefaults: Optional[Dict[str, Any]] 

59 annotations: Dict[str, Any] 

60 

61 

62def inspect_getfullargspec(func: Callable[..., Any]) -> FullArgSpec: 

63 """Fully vendored version of getfullargspec from Python 3.3.""" 

64 

65 if inspect.ismethod(func): 

66 func = func.__func__ 

67 if not inspect.isfunction(func): 

68 raise TypeError(f"{func!r} is not a Python function") 

69 

70 co = func.__code__ 

71 if not inspect.iscode(co): 

72 raise TypeError(f"{co!r} is not a code object") 

73 

74 nargs = co.co_argcount 

75 names = co.co_varnames 

76 nkwargs = co.co_kwonlyargcount 

77 args = list(names[:nargs]) 

78 kwonlyargs = list(names[nargs : nargs + nkwargs]) 

79 

80 nargs += nkwargs 

81 varargs = None 

82 if co.co_flags & inspect.CO_VARARGS: 

83 varargs = co.co_varnames[nargs] 

84 nargs = nargs + 1 

85 varkw = None 

86 if co.co_flags & inspect.CO_VARKEYWORDS: 

87 varkw = co.co_varnames[nargs] 

88 

89 return FullArgSpec( 

90 args, 

91 varargs, 

92 varkw, 

93 func.__defaults__, 

94 kwonlyargs, 

95 func.__kwdefaults__, 

96 func.__annotations__, 

97 ) 

98 

99 

100# python stubs don't have a public type for this. not worth 

101# making a protocol 

102def md5_not_for_security() -> Any: 

103 return hashlib.md5(usedforsecurity=False) 

104 

105 

106def importlib_metadata_get(group): 

107 ep = importlib_metadata.entry_points() 

108 if typing.TYPE_CHECKING or hasattr(ep, "select"): 

109 return ep.select(group=group) 

110 else: 

111 return ep.get(group, ()) 

112 

113 

114def b(s): 

115 return s.encode("latin-1") 

116 

117 

118def b64decode(x: str) -> bytes: 

119 return base64.b64decode(x.encode("ascii")) 

120 

121 

122def b64encode(x: bytes) -> str: 

123 return base64.b64encode(x).decode("ascii") 

124 

125 

126def decode_backslashreplace(text: bytes, encoding: str) -> str: 

127 return text.decode(encoding, errors="backslashreplace") 

128 

129 

130def cmp(a, b): 

131 return (a > b) - (a < b) 

132 

133 

134def _formatannotation(annotation, base_module=None): 

135 """vendored from python 3.7""" 

136 

137 if isinstance(annotation, str): 

138 return annotation 

139 

140 if getattr(annotation, "__module__", None) == "typing": 

141 return repr(annotation).replace("typing.", "").replace("~", "") 

142 if isinstance(annotation, type): 

143 if annotation.__module__ in ("builtins", base_module): 

144 return repr(annotation.__qualname__) 

145 return annotation.__module__ + "." + annotation.__qualname__ 

146 elif isinstance(annotation, typing.TypeVar): 

147 return repr(annotation).replace("~", "") 

148 return repr(annotation).replace("~", "") 

149 

150 

151def inspect_formatargspec( 

152 args: List[str], 

153 varargs: Optional[str] = None, 

154 varkw: Optional[str] = None, 

155 defaults: Optional[Sequence[Any]] = None, 

156 kwonlyargs: Optional[Sequence[str]] = (), 

157 kwonlydefaults: Optional[Mapping[str, Any]] = {}, 

158 annotations: Mapping[str, Any] = {}, 

159 formatarg: Callable[[str], str] = str, 

160 formatvarargs: Callable[[str], str] = lambda name: "*" + name, 

161 formatvarkw: Callable[[str], str] = lambda name: "**" + name, 

162 formatvalue: Callable[[Any], str] = lambda value: "=" + repr(value), 

163 formatreturns: Callable[[Any], str] = lambda text: " -> " + str(text), 

164 formatannotation: Callable[[Any], str] = _formatannotation, 

165) -> str: 

166 """Copy formatargspec from python 3.7 standard library. 

167 

168 Python 3 has deprecated formatargspec and requested that Signature 

169 be used instead, however this requires a full reimplementation 

170 of formatargspec() in terms of creating Parameter objects and such. 

171 Instead of introducing all the object-creation overhead and having 

172 to reinvent from scratch, just copy their compatibility routine. 

173 

174 Ultimately we would need to rewrite our "decorator" routine completely 

175 which is not really worth it right now, until all Python 2.x support 

176 is dropped. 

177 

178 """ 

179 

180 kwonlydefaults = kwonlydefaults or {} 

181 annotations = annotations or {} 

182 

183 def formatargandannotation(arg): 

184 result = formatarg(arg) 

185 if arg in annotations: 

186 result += ": " + formatannotation(annotations[arg]) 

187 return result 

188 

189 specs = [] 

190 if defaults: 

191 firstdefault = len(args) - len(defaults) 

192 else: 

193 firstdefault = -1 

194 

195 for i, arg in enumerate(args): 

196 spec = formatargandannotation(arg) 

197 if defaults and i >= firstdefault: 

198 spec = spec + formatvalue(defaults[i - firstdefault]) 

199 specs.append(spec) 

200 

201 if varargs is not None: 

202 specs.append(formatvarargs(formatargandannotation(varargs))) 

203 else: 

204 if kwonlyargs: 

205 specs.append("*") 

206 

207 if kwonlyargs: 

208 for kwonlyarg in kwonlyargs: 

209 spec = formatargandannotation(kwonlyarg) 

210 if kwonlydefaults and kwonlyarg in kwonlydefaults: 

211 spec += formatvalue(kwonlydefaults[kwonlyarg]) 

212 specs.append(spec) 

213 

214 if varkw is not None: 

215 specs.append(formatvarkw(formatargandannotation(varkw))) 

216 

217 result = "(" + ", ".join(specs) + ")" 

218 if "return" in annotations: 

219 result += formatreturns(formatannotation(annotations["return"])) 

220 return result 

221 

222 

223def dataclass_fields(cls: Type[Any]) -> Iterable[dataclasses.Field[Any]]: 

224 """Return a sequence of all dataclasses.Field objects associated 

225 with a class as an already processed dataclass. 

226 

227 The class must **already be a dataclass** for Field objects to be returned. 

228 

229 """ 

230 

231 if dataclasses.is_dataclass(cls): 

232 return dataclasses.fields(cls) 

233 else: 

234 return [] 

235 

236 

237def local_dataclass_fields(cls: Type[Any]) -> Iterable[dataclasses.Field[Any]]: 

238 """Return a sequence of all dataclasses.Field objects associated with 

239 an already processed dataclass, excluding those that originate from a 

240 superclass. 

241 

242 The class must **already be a dataclass** for Field objects to be returned. 

243 

244 """ 

245 

246 if dataclasses.is_dataclass(cls): 

247 super_fields: Set[dataclasses.Field[Any]] = set() 

248 for sup in cls.__bases__: 

249 super_fields.update(dataclass_fields(sup)) 

250 return [f for f in dataclasses.fields(cls) if f not in super_fields] 

251 else: 

252 return []