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