| 2245 | |
| 2246 | |
| 2247 | def _cleanup_mapped_str_annotation( |
| 2248 | annotation: str, originating_module: str |
| 2249 | ) -> str: |
| 2250 | # fix up an annotation that comes in as the form: |
| 2251 | # 'Mapped[List[Address]]' so that it instead looks like: |
| 2252 | # 'Mapped[List["Address"]]' , which will allow us to get |
| 2253 | # "Address" as a string |
| 2254 | |
| 2255 | # additionally, resolve symbols for these names since this is where |
| 2256 | # we'd have to do it |
| 2257 | |
| 2258 | inner: Optional[Match[str]] |
| 2259 | |
| 2260 | mm = re.match(r"^([^ \|]+?)\[(.+)\]$", annotation) |
| 2261 | |
| 2262 | if not mm: |
| 2263 | return annotation |
| 2264 | |
| 2265 | # ticket #8759. Resolve the Mapped name to a real symbol. |
| 2266 | # originally this just checked the name. |
| 2267 | try: |
| 2268 | obj = eval_name_only(mm.group(1), originating_module) |
| 2269 | except NameError as ne: |
| 2270 | raise _CleanupError( |
| 2271 | f'For annotation "{annotation}", could not resolve ' |
| 2272 | f'container type "{mm.group(1)}". ' |
| 2273 | "Please ensure this type is imported at the module level " |
| 2274 | "outside of TYPE_CHECKING blocks" |
| 2275 | ) from ne |
| 2276 | |
| 2277 | if obj is typing.ClassVar: |
| 2278 | real_symbol = "ClassVar" |
| 2279 | else: |
| 2280 | try: |
| 2281 | if issubclass(obj, _MappedAnnotationBase): |
| 2282 | real_symbol = obj.__name__ |
| 2283 | else: |
| 2284 | return annotation |
| 2285 | except TypeError: |
| 2286 | # avoid isinstance(obj, type) check, just catch TypeError |
| 2287 | return annotation |
| 2288 | |
| 2289 | # note: if one of the codepaths above didn't define real_symbol and |
| 2290 | # then didn't return, real_symbol raises UnboundLocalError |
| 2291 | # which is actually a NameError, and the calling routines don't |
| 2292 | # notice this since they are catching NameError anyway. Just in case |
| 2293 | # this is being modified in the future, something to be aware of. |
| 2294 | |
| 2295 | stack = [] |
| 2296 | inner = mm |
| 2297 | while True: |
| 2298 | stack.append(real_symbol if mm is inner else inner.group(1)) |
| 2299 | g2 = inner.group(2) |
| 2300 | inner = re.match(r"^([^ \|]+?)\[(.+)\]$", g2) |
| 2301 | if inner is None: |
| 2302 | stack.append(g2) |
| 2303 | break |
| 2304 | |