| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -38,6 +38,7 @@ class Format(enum.IntEnum): | |||
| 38 | 38 | "__weakref__", | |
| 39 | 39 | "__arg__", | |
| 40 | 40 | "__globals__", | |
| 41 | + "__extra_names__", | ||
| 41 | 42 | "__code__", | |
| 42 | 43 | "__ast_node__", | |
| 43 | 44 | "__cell__", | |
@@ -82,6 +83,7 @@ def __init__( | |||
| 82 | 83 | # is created through __class__ assignment on a _Stringifier object. | |
| 83 | 84 | self.__globals__ = None | |
| 84 | 85 | self.__cell__ = None | |
| 86 | + self.__extra_names__ = None | ||
| 85 | 87 | # These are initially None but serve as a cache and may be set to a non-None | |
| 86 | 88 | # value later. | |
| 87 | 89 | self.__code__ = None | |
@@ -151,6 +153,8 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None): | |||
| 151 | 153 | if not self.__forward_is_class__ or param_name not in globals: | |
| 152 | 154 | globals[param_name] = param | |
| 153 | 155 | locals.pop(param_name, None) | |
| 156 | + if self.__extra_names__: | ||
| 157 | + locals = {**locals, **self.__extra_names__} | ||
| 154 | 158 | ||
| 155 | 159 | arg = self.__forward_arg__ | |
| 156 | 160 | if arg.isidentifier() and not keyword.iskeyword(arg): | |
@@ -231,6 +235,10 @@ def __eq__(self, other): | |||
| 231 | 235 | and self.__forward_is_class__ == other.__forward_is_class__ | |
| 232 | 236 | and self.__cell__ == other.__cell__ | |
| 233 | 237 | and self.__owner__ == other.__owner__ | |
| 238 | + and ( | ||
| 239 | + (tuple(sorted(self.__extra_names__.items())) if self.__extra_names__ else None) == | ||
| 240 | + (tuple(sorted(other.__extra_names__.items())) if other.__extra_names__ else None) | ||
| 241 | + ) | ||
| 234 | 242 | ) | |
| 235 | 243 | ||
| 236 | 244 | def __hash__(self): | |
@@ -241,6 +249,7 @@ def __hash__(self): | |||
| 241 | 249 | self.__forward_is_class__, | |
| 242 | 250 | self.__cell__, | |
| 243 | 251 | self.__owner__, | |
| 252 | + tuple(sorted(self.__extra_names__.items())) if self.__extra_names__ else None, | ||
| 244 | 253 | )) | |
| 245 | 254 | ||
| 246 | 255 | def __or__(self, other): | |
@@ -274,6 +283,7 @@ def __init__( | |||
| 274 | 283 | cell=None, | |
| 275 | 284 | *, | |
| 276 | 285 | stringifier_dict, | |
| 286 | + extra_names=None, | ||
| 277 | 287 | ): | |
| 278 | 288 | # Either an AST node or a simple str (for the common case where a ForwardRef | |
| 279 | 289 | # represent a single name). | |
@@ -285,49 +295,91 @@ def __init__( | |||
| 285 | 295 | self.__code__ = None | |
| 286 | 296 | self.__ast_node__ = node | |
| 287 | 297 | self.__globals__ = globals | |
| 298 | + self.__extra_names__ = extra_names | ||
| 288 | 299 | self.__cell__ = cell | |
| 289 | 300 | self.__owner__ = owner | |
| 290 | 301 | self.__stringifier_dict__ = stringifier_dict | |
| 291 | 302 | ||
| 292 | 303 | def __convert_to_ast(self, other): | |
| 293 | 304 | if isinstance(other, _Stringifier): | |
| 294 | 305 | if isinstance(other.__ast_node__, str): | |
| 295 | - return ast.Name(id=other.__ast_node__) | ||
| 296 | - return other.__ast_node__ | ||
| 297 | - elif isinstance(other, slice): | ||
| 306 | + return ast.Name(id=other.__ast_node__), other.__extra_names__ | ||
| 307 | + return other.__ast_node__, other.__extra_names__ | ||
| 308 | + elif ( | ||
| 309 | + # In STRING format we don't bother with the create_unique_name() dance; | ||
| 310 | + # it's better to emit the repr() of the object instead of an opaque name. | ||
| 311 | + self.__stringifier_dict__.format == Format.STRING | ||
| 312 | + or other is None | ||
| 313 | + or type(other) in (str, int, float, bool, complex) | ||
| 314 | + ): | ||
| 315 | + return ast.Constant(value=other), None | ||
| 316 | + elif type(other) is dict: | ||
| 317 | + extra_names = {} | ||
| 318 | + keys = [] | ||
| 319 | + values = [] | ||
| 320 | + for key, value in other.items(): | ||
| 321 | + new_key, new_extra_names = self.__convert_to_ast(key) | ||
| 322 | + if new_extra_names is not None: | ||
| 323 | + extra_names.update(new_extra_names) | ||
| 324 | + keys.append(new_key) | ||
| 325 | + new_value, new_extra_names = self.__convert_to_ast(value) | ||
| 326 | + if new_extra_names is not None: | ||
| 327 | + extra_names.update(new_extra_names) | ||
| 328 | + values.append(new_value) | ||
| 329 | + return ast.Dict(keys, values), extra_names | ||
| 330 | + elif type(other) in (list, tuple, set): | ||
| 331 | + extra_names = {} | ||
| 332 | + elts = [] | ||
| 333 | + for elt in other: | ||
| 334 | + new_elt, new_extra_names = self.__convert_to_ast(elt) | ||
| 335 | + if new_extra_names is not None: | ||
| 336 | + extra_names.update(new_extra_names) | ||
| 337 | + elts.append(new_elt) | ||
| 338 | + ast_class = {list: ast.List, tuple: ast.Tuple, set: ast.Set}[type(other)] | ||
| 339 | + return ast_class(elts), extra_names | ||
| 340 | + else: | ||
| 341 | + name = self.__stringifier_dict__.create_unique_name() | ||
| 342 | + return ast.Name(id=name), {name: other} | ||
| 343 | + | ||
| 344 | + def __convert_to_ast_getitem(self, other): | ||
| 345 | + if isinstance(other, slice): | ||
| 346 | + extra_names = {} | ||
| 347 | + | ||
| 348 | + def conv(obj): | ||
| 349 | + if obj is None: | ||
| 350 | + return None | ||
| 351 | + new_obj, new_extra_names = self.__convert_to_ast(obj) | ||
| 352 | + if new_extra_names is not None: | ||
| 353 | + extra_names.update(new_extra_names) | ||
| 354 | + return new_obj | ||
| 355 | + | ||
| 298 | 356 | return ast.Slice( | |
| 299 | - lower=( | ||
| 300 | - self.__convert_to_ast(other.start) | ||
| 301 | - if other.start is not None | ||
| 302 | - else None | ||
| 303 | - ), | ||
| 304 | - upper=( | ||
| 305 | - self.__convert_to_ast(other.stop) | ||
| 306 | - if other.stop is not None | ||
| 307 | - else None | ||
| 308 | - ), | ||
| 309 | - step=( | ||
| 310 | - self.__convert_to_ast(other.step) | ||
| 311 | - if other.step is not None | ||
| 312 | - else None | ||
| 313 | - ), | ||
| 314 | - ) | ||
| 357 | + lower=conv(other.start), | ||
| 358 | + upper=conv(other.stop), | ||
| 359 | + step=conv(other.step), | ||
| 360 | + ), extra_names | ||
| 315 | 361 | else: | |
| 316 | - return ast.Constant(value=other) | ||
| 362 | + return self.__convert_to_ast(other) | ||
| 317 | 363 | ||
| 318 | 364 | def __get_ast(self): | |
| 319 | 365 | node = self.__ast_node__ | |
| 320 | 366 | if isinstance(node, str): | |
| 321 | 367 | return ast.Name(id=node) | |
| 322 | 368 | return node | |
| 323 | 369 | ||
| 324 | - def __make_new(self, node): | ||
| 370 | + def __make_new(self, node, extra_names=None): | ||
| 371 | + new_extra_names = {} | ||
| 372 | + if self.__extra_names__ is not None: | ||
| 373 | + new_extra_names.update(self.__extra_names__) | ||
| 374 | + if extra_names is not None: | ||
| 375 | + new_extra_names.update(extra_names) | ||
| 325 | 376 | stringifier = _Stringifier( | |
| 326 | 377 | node, | |
| 327 | 378 | self.__globals__, | |
| 328 | 379 | self.__owner__, | |
| 329 | 380 | self.__forward_is_class__, | |
| 330 | 381 | stringifier_dict=self.__stringifier_dict__, | |
| 382 | + extra_names=new_extra_names or None, | ||
| 331 | 383 | ) | |
| 332 | 384 | self.__stringifier_dict__.stringifiers.append(stringifier) | |
| 333 | 385 | return stringifier | |
@@ -343,27 +395,37 @@ def __getitem__(self, other): | |||
| 343 | 395 | if self.__ast_node__ == "__classdict__": | |
| 344 | 396 | raise KeyError | |
| 345 | 397 | if isinstance(other, tuple): | |
| 346 | - elts = [self.__convert_to_ast(elt) for elt in other] | ||
| 398 | + extra_names = {} | ||
| 399 | + elts = [] | ||
| 400 | + for elt in other: | ||
| 401 | + new_elt, new_extra_names = self.__convert_to_ast_getitem(elt) | ||
| 402 | + if new_extra_names is not None: | ||
| 403 | + extra_names.update(new_extra_names) | ||
| 404 | + elts.append(new_elt) | ||
| 347 | 405 | other = ast.Tuple(elts) | |
| 348 | 406 | else: | |
| 349 | - other = self.__convert_to_ast(other) | ||
| 407 | + other, extra_names = self.__convert_to_ast_getitem(other) | ||
| 350 | 408 | assert isinstance(other, ast.AST), repr(other) | |
| 351 | - return self.__make_new(ast.Subscript(self.__get_ast(), other)) | ||
| 409 | + return self.__make_new(ast.Subscript(self.__get_ast(), other), extra_names) | ||
| 352 | 410 | ||
| 353 | 411 | def __getattr__(self, attr): | |
| 354 | 412 | return self.__make_new(ast.Attribute(self.__get_ast(), attr)) | |
| 355 | 413 | ||
| 356 | 414 | def __call__(self, *args, **kwargs): | |
| 357 | - return self.__make_new( | ||
| 358 | - ast.Call( | ||
| 359 | - self.__get_ast(), | ||
| 360 | - [self.__convert_to_ast(arg) for arg in args], | ||
| 361 | - [ | ||
| 362 | - ast.keyword(key, self.__convert_to_ast(value)) | ||
| 363 | - for key, value in kwargs.items() | ||
| 364 | - ], | ||
| 365 | - ) | ||
| 366 | - ) | ||
| 415 | + extra_names = {} | ||
| 416 | + ast_args = [] | ||
| 417 | + for arg in args: | ||
| 418 | + new_arg, new_extra_names = self.__convert_to_ast(arg) | ||
| 419 | + if new_extra_names is not None: | ||
| 420 | + extra_names.update(new_extra_names) | ||
| 421 | + ast_args.append(new_arg) | ||
| 422 | + ast_kwargs = [] | ||
| 423 | + for key, value in kwargs.items(): | ||
| 424 | + new_value, new_extra_names = self.__convert_to_ast(value) | ||
| 425 | + if new_extra_names is not None: | ||
| 426 | + extra_names.update(new_extra_names) | ||
| 427 | + ast_kwargs.append(ast.keyword(key, new_value)) | ||
| 428 | + return self.__make_new(ast.Call(self.__get_ast(), ast_args, ast_kwargs), extra_names) | ||
| 367 | 429 | ||
| 368 | 430 | def __iter__(self): | |
| 369 | 431 | yield self.__make_new(ast.Starred(self.__get_ast())) | |
@@ -378,8 +440,9 @@ def __format__(self, format_spec): | |||
| 378 | 440 | ||
| 379 | 441 | def _make_binop(op: ast.AST): | |
| 380 | 442 | def binop(self, other): | |
| 443 | + rhs, extra_names = self.__convert_to_ast(other) | ||
| 381 | 444 | return self.__make_new( | |
| 382 | - ast.BinOp(self.__get_ast(), op, self.__convert_to_ast(other)) | ||
| 445 | + ast.BinOp(self.__get_ast(), op, rhs), extra_names | ||
| 383 | 446 | ) | |
| 384 | 447 | ||
| 385 | 448 | return binop | |
@@ -402,8 +465,9 @@ def binop(self, other): | |||
| 402 | 465 | ||
| 403 | 466 | def _make_rbinop(op: ast.AST): | |
| 404 | 467 | def rbinop(self, other): | |
| 468 | + new_other, extra_names = self.__convert_to_ast(other) | ||
| 405 | 469 | return self.__make_new( | |
| 406 | - ast.BinOp(self.__convert_to_ast(other), op, self.__get_ast()) | ||
| 470 | + ast.BinOp(new_other, op, self.__get_ast()), extra_names | ||
| 407 | 471 | ) | |
| 408 | 472 | ||
| 409 | 473 | return rbinop | |
@@ -426,12 +490,14 @@ def rbinop(self, other): | |||
| 426 | 490 | ||
| 427 | 491 | def _make_compare(op): | |
| 428 | 492 | def compare(self, other): | |
| 493 | + rhs, extra_names = self.__convert_to_ast(other) | ||
| 429 | 494 | return self.__make_new( | |
| 430 | 495 | ast.Compare( | |
| 431 | 496 | left=self.__get_ast(), | |
| 432 | 497 | ops=[op], | |
| 433 | - comparators=[self.__convert_to_ast(other)], | ||
| 434 | - ) | ||
| 498 | + comparators=[rhs], | ||
| 499 | + ), | ||
| 500 | + extra_names, | ||
| 435 | 501 | ) | |
| 436 | 502 | ||
| 437 | 503 | return compare | |
@@ -459,13 +525,15 @@ def unary_op(self): | |||
| 459 | 525 | ||
| 460 | 526 | ||
| 461 | 527 | class _StringifierDict(dict): | |
| 462 | - def __init__(self, namespace, globals=None, owner=None, is_class=False): | ||
| 528 | + def __init__(self, namespace, *, globals=None, owner=None, is_class=False, format): | ||
| 463 | 529 | super().__init__(namespace) | |
| 464 | 530 | self.namespace = namespace | |
| 465 | 531 | self.globals = globals | |
| 466 | 532 | self.owner = owner | |
| 467 | 533 | self.is_class = is_class | |
| 468 | 534 | self.stringifiers = [] | |
| 535 | + self.next_id = 1 | ||
| 536 | + self.format = format | ||
| 469 | 537 | ||
| 470 | 538 | def __missing__(self, key): | |
| 471 | 539 | fwdref = _Stringifier( | |
@@ -478,6 +546,11 @@ def __missing__(self, key): | |||
| 478 | 546 | self.stringifiers.append(fwdref) | |
| 479 | 547 | return fwdref | |
| 480 | 548 | ||
| 549 | + def create_unique_name(self): | ||
| 550 | + name = f"__annotationlib_name_{self.next_id}__" | ||
| 551 | + self.next_id += 1 | ||
| 552 | + return name | ||
| 553 | + | ||
| 481 | 554 | ||
| 482 | 555 | def call_evaluate_function(evaluate, format, *, owner=None): | |
| 483 | 556 | """Call an evaluate function. Evaluate functions are normally generated for | |
@@ -521,7 +594,7 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): | |||
| 521 | 594 | # possibly constants if the annotate function uses them directly). We then | |
| 522 | 595 | # convert each of those into a string to get an approximation of the | |
| 523 | 596 | # original source. | |
| 524 | - globals = _StringifierDict({}) | ||
| 597 | + globals = _StringifierDict({}, format=format) | ||
| 525 | 598 | if annotate.__closure__: | |
| 526 | 599 | freevars = annotate.__code__.co_freevars | |
| 527 | 600 | new_closure = [] | |
@@ -544,9 +617,9 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): | |||
| 544 | 617 | ) | |
| 545 | 618 | annos = func(Format.VALUE_WITH_FAKE_GLOBALS) | |
| 546 | 619 | if _is_evaluate: | |
| 547 | - return annos if isinstance(annos, str) else repr(annos) | ||
| 620 | + return _stringify_single(annos) | ||
| 548 | 621 | return { | |
| 549 | - key: val if isinstance(val, str) else repr(val) | ||
| 622 | + key: _stringify_single(val) | ||
| 550 | 623 | for key, val in annos.items() | |
| 551 | 624 | } | |
| 552 | 625 | elif format == Format.FORWARDREF: | |
@@ -569,7 +642,13 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): | |||
| 569 | 642 | # that returns a bool and an defined set of attributes. | |
| 570 | 643 | namespace = {**annotate.__builtins__, **annotate.__globals__} | |
| 571 | 644 | is_class = isinstance(owner, type) | |
| 572 | - globals = _StringifierDict(namespace, annotate.__globals__, owner, is_class) | ||
| 645 | + globals = _StringifierDict( | ||
| 646 | + namespace, | ||
| 647 | + globals=annotate.__globals__, | ||
| 648 | + owner=owner, | ||
| 649 | + is_class=is_class, | ||
| 650 | + format=format, | ||
| 651 | + ) | ||
| 573 | 652 | if annotate.__closure__: | |
| 574 | 653 | freevars = annotate.__code__.co_freevars | |
| 575 | 654 | new_closure = [] | |
@@ -619,6 +698,16 @@ def call_annotate_function(annotate, format, *, owner=None, _is_evaluate=False): | |||
| 619 | 698 | raise ValueError(f"Invalid format: {format!r}") | |
| 620 | 699 | ||
| 621 | 700 | ||
| 701 | + def _stringify_single(anno): | ||
| 702 | + if anno is ...: | ||
| 703 | + return "..." | ||
| 704 | + # We have to handle str specially to support PEP 563 stringified annotations. | ||
| 705 | + elif isinstance(anno, str): | ||
| 706 | + return anno | ||
| 707 | + else: | ||
| 708 | + return repr(anno) | ||
| 709 | + | ||
| 710 | + | ||
| 622 | 711 | def get_annotate_from_class_namespace(obj): | |
| 623 | 712 | """Retrieve the annotate function from a class namespace dictionary. | |
| 624 | 713 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments