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