Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/raw_building.py: 70%
315 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
5"""this module contains a set of functions to create astroid trees from scratch
6(build_* functions) or from living object (object_build_* functions)
7"""
9from __future__ import annotations
11import builtins
12import inspect
13import io
14import logging
15import os
16import sys
17import types
18import warnings
19from collections.abc import Iterable
20from contextlib import redirect_stderr, redirect_stdout
21from typing import Any, Union
23from astroid import bases, nodes
24from astroid.const import _EMPTY_OBJECT_MARKER, IS_PYPY
25from astroid.manager import AstroidManager
26from astroid.nodes import node_classes
28logger = logging.getLogger(__name__)
31_FunctionTypes = Union[
32 types.FunctionType,
33 types.MethodType,
34 types.BuiltinFunctionType,
35 types.WrapperDescriptorType,
36 types.MethodDescriptorType,
37 types.ClassMethodDescriptorType,
38]
40# the keys of CONST_CLS eg python builtin types
41_CONSTANTS = tuple(node_classes.CONST_CLS)
42_BUILTINS = vars(builtins)
43TYPE_NONE = type(None)
44TYPE_NOTIMPLEMENTED = type(NotImplemented)
45TYPE_ELLIPSIS = type(...)
48def _attach_local_node(parent, node, name: str) -> None:
49 node.name = name # needed by add_local_node
50 parent.add_local_node(node)
53def _add_dunder_class(func, member) -> None:
54 """Add a __class__ member to the given func node, if we can determine it."""
55 python_cls = member.__class__
56 cls_name = getattr(python_cls, "__name__", None)
57 if not cls_name:
58 return
59 cls_bases = [ancestor.__name__ for ancestor in python_cls.__bases__]
60 ast_klass = build_class(cls_name, cls_bases, python_cls.__doc__)
61 func.instance_attrs["__class__"] = [ast_klass]
64def attach_dummy_node(node, name: str, runtime_object=_EMPTY_OBJECT_MARKER) -> None:
65 """create a dummy node and register it in the locals of the given
66 node with the specified name
67 """
68 enode = nodes.EmptyNode()
69 enode.object = runtime_object
70 _attach_local_node(node, enode, name)
73def attach_const_node(node, name: str, value) -> None:
74 """create a Const node and register it in the locals of the given
75 node with the specified name
76 """
77 if name not in node.special_attributes:
78 _attach_local_node(node, nodes.const_factory(value), name)
81def attach_import_node(node, modname: str, membername: str) -> None:
82 """create a ImportFrom node and register it in the locals of the given
83 node with the specified name
84 """
85 from_node = nodes.ImportFrom(modname, [(membername, None)])
86 _attach_local_node(node, from_node, membername)
89def build_module(name: str, doc: str | None = None) -> nodes.Module:
90 """create and initialize an astroid Module node"""
91 node = nodes.Module(name, pure_python=False, package=False)
92 node.postinit(
93 body=[],
94 doc_node=nodes.Const(value=doc) if doc else None,
95 )
96 return node
99def build_class(
100 name: str, basenames: Iterable[str] = (), doc: str | None = None
101) -> nodes.ClassDef:
102 """Create and initialize an astroid ClassDef node."""
103 node = nodes.ClassDef(
104 name,
105 lineno=0,
106 col_offset=0,
107 end_lineno=0,
108 end_col_offset=0,
109 parent=nodes.Unknown(),
110 )
111 node.postinit(
112 bases=[
113 nodes.Name(
114 name=base,
115 lineno=0,
116 col_offset=0,
117 parent=node,
118 end_lineno=None,
119 end_col_offset=None,
120 )
121 for base in basenames
122 ],
123 body=[],
124 decorators=None,
125 doc_node=nodes.Const(value=doc) if doc else None,
126 )
127 return node
130def build_function(
131 name: str,
132 args: list[str] | None = None,
133 posonlyargs: list[str] | None = None,
134 defaults: list[Any] | None = None,
135 doc: str | None = None,
136 kwonlyargs: list[str] | None = None,
137 kwonlydefaults: list[Any] | None = None,
138) -> nodes.FunctionDef:
139 """create and initialize an astroid FunctionDef node"""
140 # first argument is now a list of decorators
141 func = nodes.FunctionDef(
142 name,
143 lineno=0,
144 col_offset=0,
145 parent=node_classes.Unknown(),
146 end_col_offset=0,
147 end_lineno=0,
148 )
149 argsnode = nodes.Arguments(parent=func, vararg=None, kwarg=None)
151 # If args is None we don't have any information about the signature
152 # (in contrast to when there are no arguments and args == []). We pass
153 # this to the builder to indicate this.
154 if args is not None:
155 # We set the lineno and col_offset to 0 because we don't have any
156 # information about the location of the function definition.
157 arguments = [
158 nodes.AssignName(
159 name=arg,
160 parent=argsnode,
161 lineno=0,
162 col_offset=0,
163 end_lineno=None,
164 end_col_offset=None,
165 )
166 for arg in args
167 ]
168 else:
169 arguments = None
171 default_nodes: list[nodes.NodeNG] | None
172 if defaults is None:
173 default_nodes = None
174 else:
175 default_nodes = []
176 for default in defaults:
177 default_node = nodes.const_factory(default)
178 default_node.parent = argsnode
179 default_nodes.append(default_node)
181 kwonlydefault_nodes: list[nodes.NodeNG | None] | None
182 if kwonlydefaults is None:
183 kwonlydefault_nodes = None
184 else:
185 kwonlydefault_nodes = []
186 for kwonlydefault in kwonlydefaults:
187 kwonlydefault_node = nodes.const_factory(kwonlydefault)
188 kwonlydefault_node.parent = argsnode
189 kwonlydefault_nodes.append(kwonlydefault_node)
191 # We set the lineno and col_offset to 0 because we don't have any
192 # information about the location of the kwonly and posonlyargs.
193 argsnode.postinit(
194 args=arguments,
195 defaults=default_nodes,
196 kwonlyargs=[
197 nodes.AssignName(
198 name=arg,
199 parent=argsnode,
200 lineno=0,
201 col_offset=0,
202 end_lineno=None,
203 end_col_offset=None,
204 )
205 for arg in kwonlyargs or ()
206 ],
207 kw_defaults=kwonlydefault_nodes,
208 annotations=[],
209 posonlyargs=[
210 nodes.AssignName(
211 name=arg,
212 parent=argsnode,
213 lineno=0,
214 col_offset=0,
215 end_lineno=None,
216 end_col_offset=None,
217 )
218 for arg in posonlyargs or ()
219 ],
220 kwonlyargs_annotations=[],
221 posonlyargs_annotations=[],
222 )
223 func.postinit(
224 args=argsnode,
225 body=[],
226 doc_node=nodes.Const(value=doc) if doc else None,
227 )
228 if args:
229 register_arguments(func)
230 return func
233def build_from_import(fromname: str, names: list[str]) -> nodes.ImportFrom:
234 """create and initialize an astroid ImportFrom import statement"""
235 return nodes.ImportFrom(fromname, [(name, None) for name in names])
238def register_arguments(func: nodes.FunctionDef, args: list | None = None) -> None:
239 """add given arguments to local
241 args is a list that may contains nested lists
242 (i.e. def func(a, (b, c, d)): ...)
243 """
244 # If no args are passed in, get the args from the function.
245 if args is None:
246 if func.args.vararg:
247 func.set_local(func.args.vararg, func.args)
248 if func.args.kwarg:
249 func.set_local(func.args.kwarg, func.args)
250 args = func.args.args
251 # If the function has no args, there is nothing left to do.
252 if args is None:
253 return
254 for arg in args:
255 if isinstance(arg, nodes.AssignName):
256 func.set_local(arg.name, arg)
257 else:
258 register_arguments(func, arg.elts)
261def object_build_class(
262 node: nodes.Module | nodes.ClassDef, member: type, localname: str
263) -> nodes.ClassDef:
264 """create astroid for a living class object"""
265 basenames = [base.__name__ for base in member.__bases__]
266 return _base_class_object_build(node, member, basenames, localname=localname)
269def _get_args_info_from_callable(
270 member: _FunctionTypes,
271) -> tuple[list[str], list[str], list[Any], list[str], list[Any]]:
272 """Returns args, posonlyargs, defaults, kwonlyargs.
274 :note: currently ignores the return annotation.
275 """
276 signature = inspect.signature(member)
277 args: list[str] = []
278 defaults: list[Any] = []
279 posonlyargs: list[str] = []
280 kwonlyargs: list[str] = []
281 kwonlydefaults: list[Any] = []
283 for param_name, param in signature.parameters.items():
284 if param.kind == inspect.Parameter.POSITIONAL_ONLY:
285 posonlyargs.append(param_name)
286 elif param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
287 args.append(param_name)
288 elif param.kind == inspect.Parameter.VAR_POSITIONAL:
289 args.append(param_name)
290 elif param.kind == inspect.Parameter.VAR_KEYWORD:
291 args.append(param_name)
292 elif param.kind == inspect.Parameter.KEYWORD_ONLY:
293 kwonlyargs.append(param_name)
294 if param.default is not inspect.Parameter.empty:
295 kwonlydefaults.append(param.default)
296 continue
297 if param.default is not inspect.Parameter.empty:
298 defaults.append(param.default)
300 return args, posonlyargs, defaults, kwonlyargs, kwonlydefaults
303def object_build_function(
304 node: nodes.Module | nodes.ClassDef, member: _FunctionTypes, localname: str
305) -> None:
306 """create astroid for a living function object"""
307 (
308 args,
309 posonlyargs,
310 defaults,
311 kwonlyargs,
312 kwonly_defaults,
313 ) = _get_args_info_from_callable(member)
315 func = build_function(
316 getattr(member, "__name__", None) or localname,
317 args,
318 posonlyargs,
319 defaults,
320 member.__doc__,
321 kwonlyargs=kwonlyargs,
322 kwonlydefaults=kwonly_defaults,
323 )
325 node.add_local_node(func, localname)
328def object_build_datadescriptor(
329 node: nodes.Module | nodes.ClassDef, member: type, name: str
330) -> nodes.ClassDef:
331 """create astroid for a living data descriptor object"""
332 return _base_class_object_build(node, member, [], name)
335def object_build_methoddescriptor(
336 node: nodes.Module | nodes.ClassDef,
337 member: _FunctionTypes,
338 localname: str,
339) -> None:
340 """create astroid for a living method descriptor object"""
341 # FIXME get arguments ?
342 func = build_function(
343 getattr(member, "__name__", None) or localname, doc=member.__doc__
344 )
345 node.add_local_node(func, localname)
346 _add_dunder_class(func, member)
349def _base_class_object_build(
350 node: nodes.Module | nodes.ClassDef,
351 member: type,
352 basenames: list[str],
353 name: str | None = None,
354 localname: str | None = None,
355) -> nodes.ClassDef:
356 """create astroid for a living class object, with a given set of base names
357 (e.g. ancestors)
358 """
359 class_name = name or getattr(member, "__name__", None) or localname
360 assert isinstance(class_name, str)
361 klass = build_class(
362 class_name,
363 basenames,
364 member.__doc__,
365 )
366 klass._newstyle = isinstance(member, type)
367 node.add_local_node(klass, localname)
368 try:
369 # limit the instantiation trick since it's too dangerous
370 # (such as infinite test execution...)
371 # this at least resolves common case such as Exception.args,
372 # OSError.errno
373 if issubclass(member, Exception):
374 instdict = member().__dict__
375 else:
376 raise TypeError
377 except TypeError:
378 pass
379 else:
380 for item_name, obj in instdict.items():
381 valnode = nodes.EmptyNode()
382 valnode.object = obj
383 valnode.parent = klass
384 valnode.lineno = 1
385 klass.instance_attrs[item_name] = [valnode]
386 return klass
389def _build_from_function(
390 node: nodes.Module | nodes.ClassDef,
391 name: str,
392 member: _FunctionTypes,
393 module: types.ModuleType,
394) -> None:
395 # verify this is not an imported function
396 try:
397 code = member.__code__ # type: ignore[union-attr]
398 except AttributeError:
399 # Some implementations don't provide the code object,
400 # such as Jython.
401 code = None
402 filename = getattr(code, "co_filename", None)
403 if filename is None:
404 assert isinstance(member, object)
405 object_build_methoddescriptor(node, member, name)
406 elif filename != getattr(module, "__file__", None):
407 attach_dummy_node(node, name, member)
408 else:
409 object_build_function(node, member, name)
412def _safe_has_attribute(obj, member: str) -> bool:
413 """Required because unexpected RunTimeError can be raised.
415 See https://github.com/pylint-dev/astroid/issues/1958
416 """
417 try:
418 return hasattr(obj, member)
419 except Exception: # pylint: disable=broad-except
420 return False
423class InspectBuilder:
424 """class for building nodes from living object
426 this is actually a really minimal representation, including only Module,
427 FunctionDef and ClassDef nodes and some others as guessed.
428 """
430 def __init__(self, manager_instance: AstroidManager | None = None) -> None:
431 self._manager = manager_instance or AstroidManager()
432 self._done: dict[types.ModuleType | type, nodes.Module | nodes.ClassDef] = {}
433 self._module: types.ModuleType
435 def inspect_build(
436 self,
437 module: types.ModuleType,
438 modname: str | None = None,
439 path: str | None = None,
440 ) -> nodes.Module:
441 """build astroid from a living module (i.e. using inspect)
442 this is used when there is no python source code available (either
443 because it's a built-in module or because the .py is not available)
444 """
445 self._module = module
446 if modname is None:
447 modname = module.__name__
448 try:
449 node = build_module(modname, module.__doc__)
450 except AttributeError:
451 # in jython, java modules have no __doc__ (see #109562)
452 node = build_module(modname)
453 if path is None:
454 node.path = node.file = path
455 else:
456 node.path = [os.path.abspath(path)]
457 node.file = node.path[0]
458 node.name = modname
459 self._manager.cache_module(node)
460 node.package = hasattr(module, "__path__")
461 self._done = {}
462 self.object_build(node, module)
463 return node
465 def object_build(
466 self, node: nodes.Module | nodes.ClassDef, obj: types.ModuleType | type
467 ) -> None:
468 """recursive method which create a partial ast from real objects
469 (only function, class, and method are handled)
470 """
471 if obj in self._done:
472 return None
473 self._done[obj] = node
474 for name in dir(obj):
475 # inspect.ismethod() and inspect.isbuiltin() in PyPy return
476 # the opposite of what they do in CPython for __class_getitem__.
477 pypy__class_getitem__ = IS_PYPY and name == "__class_getitem__"
478 try:
479 with warnings.catch_warnings():
480 warnings.simplefilter("ignore")
481 member = getattr(obj, name)
482 except AttributeError:
483 # damned ExtensionClass.Base, I know you're there !
484 attach_dummy_node(node, name)
485 continue
486 if inspect.ismethod(member) and not pypy__class_getitem__:
487 member = member.__func__
488 if inspect.isfunction(member):
489 _build_from_function(node, name, member, self._module)
490 elif inspect.isbuiltin(member) or pypy__class_getitem__:
491 if self.imported_member(node, member, name):
492 continue
493 object_build_methoddescriptor(node, member, name)
494 elif inspect.isclass(member):
495 if self.imported_member(node, member, name):
496 continue
497 if member in self._done:
498 class_node = self._done[member]
499 assert isinstance(class_node, nodes.ClassDef)
500 if class_node not in node.locals.get(name, ()):
501 node.add_local_node(class_node, name)
502 else:
503 class_node = object_build_class(node, member, name)
504 # recursion
505 self.object_build(class_node, member)
506 if name == "__class__" and class_node.parent is None:
507 class_node.parent = self._done[self._module]
508 elif inspect.ismethoddescriptor(member):
509 object_build_methoddescriptor(node, member, name)
510 elif inspect.isdatadescriptor(member):
511 object_build_datadescriptor(node, member, name)
512 elif isinstance(member, _CONSTANTS):
513 attach_const_node(node, name, member)
514 elif inspect.isroutine(member):
515 # This should be called for Jython, where some builtin
516 # methods aren't caught by isbuiltin branch.
517 _build_from_function(node, name, member, self._module)
518 elif _safe_has_attribute(member, "__all__"):
519 module = build_module(name)
520 _attach_local_node(node, module, name)
521 # recursion
522 self.object_build(module, member)
523 else:
524 # create an empty node so that the name is actually defined
525 attach_dummy_node(node, name, member)
526 return None
528 def imported_member(self, node, member, name: str) -> bool:
529 """verify this is not an imported class or handle it"""
530 # /!\ some classes like ExtensionClass doesn't have a __module__
531 # attribute ! Also, this may trigger an exception on badly built module
532 # (see http://www.logilab.org/ticket/57299 for instance)
533 try:
534 modname = getattr(member, "__module__", None)
535 except TypeError:
536 modname = None
537 if modname is None:
538 if name in {"__new__", "__subclasshook__"}:
539 # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14)
540 # >>> print object.__new__.__module__
541 # None
542 modname = builtins.__name__
543 else:
544 attach_dummy_node(node, name, member)
545 return True
547 # On PyPy during bootstrapping we infer _io while _module is
548 # builtins. In CPython _io names itself io, see http://bugs.python.org/issue18602
549 # Therefore, this basically checks whether we are not in PyPy.
550 if modname == "_io" and not self._module.__name__ == "builtins":
551 return False
553 real_name = {"gtk": "gtk_gtk"}.get(modname, modname)
555 if real_name != self._module.__name__:
556 # check if it sounds valid and then add an import node, else use a
557 # dummy node
558 try:
559 with redirect_stderr(io.StringIO()) as stderr, redirect_stdout(
560 io.StringIO()
561 ) as stdout:
562 getattr(sys.modules[modname], name)
563 stderr_value = stderr.getvalue()
564 if stderr_value:
565 logger.error(
566 "Captured stderr while getting %s from %s:\n%s",
567 name,
568 sys.modules[modname],
569 stderr_value,
570 )
571 stdout_value = stdout.getvalue()
572 if stdout_value:
573 logger.info(
574 "Captured stdout while getting %s from %s:\n%s",
575 name,
576 sys.modules[modname],
577 stdout_value,
578 )
579 except (KeyError, AttributeError):
580 attach_dummy_node(node, name, member)
581 else:
582 attach_import_node(node, modname, name)
583 return True
584 return False
587# astroid bootstrapping ######################################################
589_CONST_PROXY: dict[type, nodes.ClassDef] = {}
592def _set_proxied(const) -> nodes.ClassDef:
593 # TODO : find a nicer way to handle this situation;
594 return _CONST_PROXY[const.value.__class__]
597def _astroid_bootstrapping() -> None:
598 """astroid bootstrapping the builtins module"""
599 # this boot strapping is necessary since we need the Const nodes to
600 # inspect_build builtins, and then we can proxy Const
601 builder = InspectBuilder()
602 astroid_builtin = builder.inspect_build(builtins)
604 for cls, node_cls in node_classes.CONST_CLS.items():
605 if cls is TYPE_NONE:
606 proxy = build_class("NoneType")
607 proxy.parent = astroid_builtin
608 elif cls is TYPE_NOTIMPLEMENTED:
609 proxy = build_class("NotImplementedType")
610 proxy.parent = astroid_builtin
611 elif cls is TYPE_ELLIPSIS:
612 proxy = build_class("Ellipsis")
613 proxy.parent = astroid_builtin
614 else:
615 proxy = astroid_builtin.getattr(cls.__name__)[0]
616 assert isinstance(proxy, nodes.ClassDef)
617 if cls in (dict, list, set, tuple):
618 node_cls._proxied = proxy
619 else:
620 _CONST_PROXY[cls] = proxy
622 # Set the builtin module as parent for some builtins.
623 nodes.Const._proxied = property(_set_proxied)
625 _GeneratorType = nodes.ClassDef(
626 types.GeneratorType.__name__,
627 lineno=0,
628 col_offset=0,
629 end_lineno=0,
630 end_col_offset=0,
631 parent=nodes.Unknown(),
632 )
633 _GeneratorType.parent = astroid_builtin
634 generator_doc_node = (
635 nodes.Const(value=types.GeneratorType.__doc__)
636 if types.GeneratorType.__doc__
637 else None
638 )
639 _GeneratorType.postinit(
640 bases=[],
641 body=[],
642 decorators=None,
643 doc_node=generator_doc_node,
644 )
645 bases.Generator._proxied = _GeneratorType
646 builder.object_build(bases.Generator._proxied, types.GeneratorType)
648 if hasattr(types, "AsyncGeneratorType"):
649 _AsyncGeneratorType = nodes.ClassDef(
650 types.AsyncGeneratorType.__name__,
651 lineno=0,
652 col_offset=0,
653 end_lineno=0,
654 end_col_offset=0,
655 parent=nodes.Unknown(),
656 )
657 _AsyncGeneratorType.parent = astroid_builtin
658 async_generator_doc_node = (
659 nodes.Const(value=types.AsyncGeneratorType.__doc__)
660 if types.AsyncGeneratorType.__doc__
661 else None
662 )
663 _AsyncGeneratorType.postinit(
664 bases=[],
665 body=[],
666 decorators=None,
667 doc_node=async_generator_doc_node,
668 )
669 bases.AsyncGenerator._proxied = _AsyncGeneratorType
670 builder.object_build(bases.AsyncGenerator._proxied, types.AsyncGeneratorType)
672 if hasattr(types, "UnionType"):
673 _UnionTypeType = nodes.ClassDef(
674 types.UnionType.__name__,
675 lineno=0,
676 col_offset=0,
677 end_lineno=0,
678 end_col_offset=0,
679 parent=nodes.Unknown(),
680 )
681 _UnionTypeType.parent = astroid_builtin
682 union_type_doc_node = (
683 nodes.Const(value=types.UnionType.__doc__)
684 if types.UnionType.__doc__
685 else None
686 )
687 _UnionTypeType.postinit(
688 bases=[],
689 body=[],
690 decorators=None,
691 doc_node=union_type_doc_node,
692 )
693 bases.UnionType._proxied = _UnionTypeType
694 builder.object_build(bases.UnionType._proxied, types.UnionType)
696 builtin_types = (
697 types.GetSetDescriptorType,
698 types.GeneratorType,
699 types.MemberDescriptorType,
700 TYPE_NONE,
701 TYPE_NOTIMPLEMENTED,
702 types.FunctionType,
703 types.MethodType,
704 types.BuiltinFunctionType,
705 types.ModuleType,
706 types.TracebackType,
707 )
708 for _type in builtin_types:
709 if _type.__name__ not in astroid_builtin:
710 klass = nodes.ClassDef(
711 _type.__name__,
712 lineno=0,
713 col_offset=0,
714 end_lineno=0,
715 end_col_offset=0,
716 parent=nodes.Unknown(),
717 )
718 klass.parent = astroid_builtin
719 klass.postinit(
720 bases=[],
721 body=[],
722 decorators=None,
723 doc_node=nodes.Const(value=_type.__doc__) if _type.__doc__ else None,
724 )
725 builder.object_build(klass, _type)
726 astroid_builtin[_type.__name__] = klass
729_astroid_bootstrapping()