Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cattrs/_compat.py: 54%

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

176 statements  

1import sys 

2from collections import Counter, deque 

3from collections.abc import Mapping as AbcMapping 

4from collections.abc import MutableMapping as AbcMutableMapping 

5from collections.abc import MutableSequence as AbcMutableSequence 

6from collections.abc import MutableSet as AbcMutableSet 

7from collections.abc import Sequence as AbcSequence 

8from collections.abc import Set as AbcSet 

9from dataclasses import MISSING, Field, is_dataclass 

10from dataclasses import fields as dataclass_fields 

11from functools import partial 

12from inspect import signature as _signature 

13from types import GenericAlias 

14from typing import ( 

15 Annotated, 

16 Any, 

17 Deque, 

18 Dict, 

19 Final, 

20 FrozenSet, 

21 Generic, 

22 List, 

23 Literal, 

24 NewType, 

25 Optional, 

26 Protocol, 

27 Tuple, 

28 Union, 

29 _AnnotatedAlias, 

30 _GenericAlias, 

31 _SpecialGenericAlias, 

32 get_args, 

33 get_origin, 

34 get_type_hints, 

35) 

36from typing import Counter as TypingCounter 

37from typing import Mapping as TypingMapping 

38from typing import MutableMapping as TypingMutableMapping 

39from typing import MutableSequence as TypingMutableSequence 

40from typing import MutableSet as TypingMutableSet 

41from typing import Sequence as TypingSequence 

42from typing import Set as TypingSet 

43 

44from attrs import NOTHING, Attribute, Factory, NothingType, resolve_types 

45from attrs import fields as attrs_fields 

46from attrs import fields_dict as attrs_fields_dict 

47 

48__all__ = [ 

49 "ANIES", 

50 "ExceptionGroup", 

51 "ExtensionsTypedDict", 

52 "TypeAlias", 

53 "adapted_fields", 

54 "fields_dict", 

55 "has", 

56 "is_typeddict", 

57] 

58 

59try: 

60 from typing_extensions import TypedDict as ExtensionsTypedDict 

61except ImportError: # pragma: no cover 

62 ExtensionsTypedDict = None 

63 

64if sys.version_info >= (3, 11): 

65 from builtins import ExceptionGroup 

66else: 

67 from exceptiongroup import ExceptionGroup 

68 

69try: 

70 from typing_extensions import is_typeddict as _is_typeddict 

71except ImportError: # pragma: no cover 

72 assert sys.version_info >= (3, 10) 

73 from typing import is_typeddict as _is_typeddict 

74 

75try: 

76 from typing_extensions import TypeAlias 

77except ImportError: # pragma: no cover 

78 assert sys.version_info >= (3, 11) 

79 from typing import TypeAlias 

80 

81LITERALS = {Literal} 

82try: 

83 from typing_extensions import Literal as teLiteral 

84 

85 LITERALS.add(teLiteral) 

86except ImportError: # pragma: no cover 

87 pass 

88 

89# On some Python versions, `typing_extensions.Any` is different than 

90# `typing.Any`. 

91try: 

92 from typing_extensions import Any as teAny 

93 

94 ANIES = frozenset([Any, teAny]) 

95except ImportError: # pragma: no cover 

96 ANIES = frozenset([Any]) 

97 

98NoneType = type(None) 

99 

100 

101def is_optional(typ: Any) -> bool: 

102 return is_union_type(typ) and NoneType in typ.__args__ and len(typ.__args__) == 2 

103 

104 

105def is_typeddict(cls: Any): 

106 """Thin wrapper around typing(_extensions).is_typeddict""" 

107 return _is_typeddict(getattr(cls, "__origin__", cls)) 

108 

109 

110def has(cls): 

111 return hasattr(cls, "__attrs_attrs__") or hasattr(cls, "__dataclass_fields__") 

112 

113 

114def has_with_generic(cls): 

115 """Test whether the class if a normal or generic attrs or dataclass.""" 

116 return has(cls) or has(get_origin(cls)) 

117 

118 

119def fields(type): 

120 try: 

121 return type.__attrs_attrs__ 

122 except AttributeError: 

123 return dataclass_fields(type) 

124 

125 

126def fields_dict(type) -> dict[str, Union[Attribute, Field]]: 

127 """Return the fields_dict for attrs and dataclasses.""" 

128 if is_dataclass(type): 

129 return {f.name: f for f in dataclass_fields(type)} 

130 return attrs_fields_dict(type) 

131 

132 

133def adapted_fields(cl: type) -> list[Attribute]: 

134 """Return the attrs format of `fields()` for attrs and dataclasses. 

135 

136 Resolves `attrs` stringified annotations, if present. 

137 """ 

138 if is_dataclass(cl): 

139 attrs = dataclass_fields(cl) 

140 if any(isinstance(a.type, str) for a in attrs): 

141 # Do this conditionally in case `get_type_hints` fails, so 

142 # users can resolve on their own first. 

143 type_hints = get_type_hints(cl) 

144 else: 

145 type_hints = {} 

146 return [ 

147 Attribute( 

148 attr.name, 

149 ( 

150 attr.default 

151 if attr.default is not MISSING 

152 else ( 

153 Factory(attr.default_factory) 

154 if attr.default_factory is not MISSING 

155 else NOTHING 

156 ) 

157 ), 

158 None, 

159 True, 

160 None, 

161 True, 

162 attr.init, 

163 True, 

164 type=type_hints.get(attr.name, attr.type), 

165 alias=attr.name, 

166 kw_only=getattr(attr, "kw_only", False), 

167 ) 

168 for attr in attrs 

169 ] 

170 attribs = attrs_fields(cl) 

171 if any(isinstance(a.type, str) for a in attribs): 

172 # PEP 563 annotations - need to be resolved. 

173 resolve_types(cl) 

174 attribs = attrs_fields(cl) 

175 return attribs 

176 

177 

178def is_subclass(obj: type, bases) -> bool: 

179 """A safe version of issubclass (won't raise).""" 

180 try: 

181 return issubclass(obj, bases) 

182 except TypeError: 

183 return False 

184 

185 

186def is_hetero_tuple(type: Any) -> bool: 

187 origin = getattr(type, "__origin__", None) 

188 return origin is tuple and ... not in type.__args__ 

189 

190 

191def is_protocol(type: Any) -> bool: 

192 return is_subclass(type, Protocol) and getattr(type, "_is_protocol", False) 

193 

194 

195def is_bare_final(type) -> bool: 

196 return type is Final 

197 

198 

199def get_final_base(type) -> Optional[type]: 

200 """Return the base of the Final annotation, if it is Final.""" 

201 if type is Final: 

202 return Any 

203 if type.__class__ is _GenericAlias and type.__origin__ is Final: 

204 return type.__args__[0] 

205 return None 

206 

207 

208OriginAbstractSet = AbcSet 

209OriginMutableSet = AbcMutableSet 

210 

211signature = _signature 

212 

213if sys.version_info >= (3, 10): 

214 signature = partial(_signature, eval_str=True) 

215 

216 

217try: 

218 # Not present on 3.9.0, so we try carefully. 

219 from typing import _LiteralGenericAlias 

220 

221 def is_literal(type: Any) -> bool: 

222 """Is this a literal?""" 

223 return type in LITERALS or ( 

224 isinstance( 

225 type, (_GenericAlias, _LiteralGenericAlias, _SpecialGenericAlias) 

226 ) 

227 and type.__origin__ in LITERALS 

228 ) 

229 

230except ImportError: # pragma: no cover 

231 

232 def is_literal(_) -> bool: 

233 return False 

234 

235 

236Set = AbcSet 

237MutableSet = AbcMutableSet 

238Sequence = AbcSequence 

239MutableSequence = AbcMutableSequence 

240MutableMapping = AbcMutableMapping 

241Mapping = AbcMapping 

242FrozenSetSubscriptable = frozenset 

243TupleSubscriptable = tuple 

244 

245 

246def is_annotated(type) -> bool: 

247 return getattr(type, "__class__", None) is _AnnotatedAlias 

248 

249 

250def is_tuple(type): 

251 return ( 

252 type in (Tuple, tuple) 

253 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, Tuple)) 

254 or (getattr(type, "__origin__", None) is tuple) 

255 ) 

256 

257 

258if sys.version_info >= (3, 14): 

259 

260 def is_union_type(obj): 

261 from types import UnionType # noqa: PLC0415 

262 

263 return obj is Union or isinstance(obj, UnionType) 

264 

265 def get_newtype_base(typ: Any) -> Optional[type]: 

266 if typ is NewType or isinstance(typ, NewType): 

267 return typ.__supertype__ 

268 return None 

269 

270 from typing import NotRequired, Required 

271 

272elif sys.version_info >= (3, 10): 

273 from typing import _UnionGenericAlias 

274 

275 def is_union_type(obj): 

276 from types import UnionType # noqa: PLC0415 

277 

278 return ( 

279 obj is Union 

280 or (isinstance(obj, _UnionGenericAlias) and obj.__origin__ is Union) 

281 or isinstance(obj, UnionType) 

282 ) 

283 

284 def get_newtype_base(typ: Any) -> Optional[type]: 

285 if typ is NewType or isinstance(typ, NewType): 

286 return typ.__supertype__ 

287 return None 

288 

289 if sys.version_info >= (3, 11): 

290 from typing import NotRequired, Required 

291 else: 

292 from typing_extensions import NotRequired, Required 

293 

294else: 

295 # 3.9 

296 from typing import _UnionGenericAlias 

297 

298 from typing_extensions import NotRequired, Required 

299 

300 def is_union_type(obj): 

301 return obj is Union or ( 

302 isinstance(obj, _UnionGenericAlias) and obj.__origin__ is Union 

303 ) 

304 

305 def get_newtype_base(typ: Any) -> Optional[type]: 

306 supertype = getattr(typ, "__supertype__", None) 

307 if ( 

308 supertype is not None 

309 and getattr(typ, "__qualname__", "") == "NewType.<locals>.new_type" 

310 and typ.__module__ in ("typing", "typing_extensions") 

311 ): 

312 return supertype 

313 return None 

314 

315 

316def get_notrequired_base(type) -> Union[Any, NothingType]: 

317 if is_annotated(type): 

318 # Handle `Annotated[NotRequired[int]]` 

319 type = get_args(type)[0] 

320 if get_origin(type) in (NotRequired, Required): 

321 return get_args(type)[0] 

322 return NOTHING 

323 

324 

325def is_mutable_sequence(type: Any) -> bool: 

326 """A predicate function for mutable sequences. 

327 

328 Matches lists, mutable sequences, and deques. 

329 """ 

330 origin = getattr(type, "__origin__", None) 

331 return ( 

332 type in (List, list, TypingMutableSequence, AbcMutableSequence, deque, Deque) 

333 or ( 

334 type.__class__ is _GenericAlias 

335 and ( 

336 ((origin is not tuple) and is_subclass(origin, TypingMutableSequence)) 

337 or (origin is tuple and type.__args__[1] is ...) 

338 ) 

339 ) 

340 or (origin in (list, deque, AbcMutableSequence)) 

341 ) 

342 

343 

344def is_sequence(type: Any) -> bool: 

345 """A predicate function for sequences. 

346 

347 Matches lists, sequences, mutable sequences, deques and homogenous 

348 tuples. 

349 """ 

350 origin = getattr(type, "__origin__", None) 

351 return is_mutable_sequence(type) or ( 

352 type in (TypingSequence, tuple, Tuple) 

353 or ( 

354 type.__class__ is _GenericAlias 

355 and ( 

356 ((origin is not tuple) and is_subclass(origin, TypingSequence)) 

357 or (origin is tuple and type.__args__[1] is ...) 

358 ) 

359 ) 

360 or (origin is AbcSequence) 

361 or (origin is tuple and type.__args__[1] is ...) 

362 ) 

363 

364 

365def is_deque(type): 

366 return ( 

367 type in (deque, Deque) 

368 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, deque)) 

369 or (getattr(type, "__origin__", None) is deque) 

370 ) 

371 

372 

373def is_mutable_set(type: Any) -> bool: 

374 """A predicate function for (mutable) sets. 

375 

376 Matches built-in sets and sets from the typing module. 

377 """ 

378 return ( 

379 type in (TypingSet, TypingMutableSet, set) 

380 or ( 

381 type.__class__ is _GenericAlias 

382 and is_subclass(type.__origin__, TypingMutableSet) 

383 ) 

384 or (getattr(type, "__origin__", None) in (set, AbcMutableSet, AbcSet)) 

385 ) 

386 

387 

388def is_frozenset(type: Any) -> bool: 

389 """A predicate function for frozensets. 

390 

391 Matches built-in frozensets and frozensets from the typing module. 

392 """ 

393 return ( 

394 type in (FrozenSet, frozenset) 

395 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, FrozenSet)) 

396 or (getattr(type, "__origin__", None) is frozenset) 

397 ) 

398 

399 

400def is_bare(type): 

401 return isinstance(type, _SpecialGenericAlias) or ( 

402 not hasattr(type, "__origin__") and not hasattr(type, "__args__") 

403 ) 

404 

405 

406def is_mapping(type: Any) -> bool: 

407 """A predicate function for mappings.""" 

408 return ( 

409 type in (dict, Dict, TypingMapping, TypingMutableMapping, AbcMutableMapping) 

410 or ( 

411 type.__class__ is _GenericAlias 

412 and is_subclass(type.__origin__, TypingMapping) 

413 ) 

414 or is_subclass( 

415 getattr(type, "__origin__", type), (dict, AbcMutableMapping, AbcMapping) 

416 ) 

417 ) 

418 

419 

420def is_counter(type): 

421 return ( 

422 type in (Counter, TypingCounter) or getattr(type, "__origin__", None) is Counter 

423 ) 

424 

425 

426def is_generic(type) -> bool: 

427 """Whether `type` is a generic type.""" 

428 # Inheriting from protocol will inject `Generic` into the MRO 

429 # without `__orig_bases__`. 

430 return ( 

431 isinstance(type, (_GenericAlias, GenericAlias)) 

432 or (is_subclass(type, Generic) and hasattr(type, "__orig_bases__")) 

433 or type.__class__ is Union # On 3.14, unions are no longer typing._GenericAlias 

434 ) 

435 

436 

437def copy_with(type, args): 

438 """Replace a generic type's arguments.""" 

439 if is_annotated(type): 

440 # typing.Annotated requires a special case. 

441 return Annotated[args] 

442 if isinstance(args, tuple) and len(args) == 1: 

443 # Some annotations can't handle 1-tuples. 

444 args = args[0] 

445 return type.__origin__[args] 

446 

447 

448def get_full_type_hints(obj, globalns=None, localns=None): 

449 return get_type_hints(obj, globalns, localns, include_extras=True) 

450 

451 

452def is_generic_attrs(type) -> bool: 

453 """Return True for both specialized (A[int]) and unspecialized (A) generics.""" 

454 return is_generic(type) and has(type.__origin__)