Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/interpreter/objectmodel.py: 51%
504 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"""
6Data object model, as per https://docs.python.org/3/reference/datamodel.html.
8This module describes, at least partially, a data object model for some
9of astroid's nodes. The model contains special attributes that nodes such
10as functions, classes, modules etc have, such as __doc__, __class__,
11__module__ etc, being used when doing attribute lookups over nodes.
13For instance, inferring `obj.__class__` will first trigger an inference
14of the `obj` variable. If it was successfully inferred, then an attribute
15`__class__ will be looked for in the inferred object. This is the part
16where the data model occurs. The model is attached to those nodes
17and the lookup mechanism will try to see if attributes such as
18`__class__` are defined by the model or not. If they are defined,
19the model will be requested to return the corresponding value of that
20attribute. Thus the model can be viewed as a special part of the lookup
21mechanism.
22"""
24from __future__ import annotations
26import itertools
27import os
28import pprint
29import types
30from collections.abc import Iterator
31from functools import lru_cache
32from typing import TYPE_CHECKING, Any, Literal
34import astroid
35from astroid import bases, nodes, util
36from astroid.context import InferenceContext, copy_context
37from astroid.exceptions import AttributeInferenceError, InferenceError, NoDefault
38from astroid.manager import AstroidManager
39from astroid.nodes import node_classes
40from astroid.typing import InferenceResult, SuccessfulInferenceResult
42if TYPE_CHECKING:
43 from astroid.objects import Property
45IMPL_PREFIX = "attr_"
46LEN_OF_IMPL_PREFIX = len(IMPL_PREFIX)
49def _dunder_dict(instance, attributes):
50 obj = node_classes.Dict(
51 parent=instance,
52 lineno=instance.lineno,
53 col_offset=instance.col_offset,
54 end_lineno=instance.end_lineno,
55 end_col_offset=instance.end_col_offset,
56 )
58 # Convert the keys to node strings
59 keys = [
60 node_classes.Const(value=value, parent=obj) for value in list(attributes.keys())
61 ]
63 # The original attribute has a list of elements for each key,
64 # but that is not useful for retrieving the special attribute's value.
65 # In this case, we're picking the last value from each list.
66 values = [elem[-1] for elem in attributes.values()]
68 obj.postinit(list(zip(keys, values)))
69 return obj
72def _get_bound_node(model: ObjectModel) -> Any:
73 # TODO: Use isinstance instead of try ... except after _instance has typing
74 try:
75 return model._instance._proxied
76 except AttributeError:
77 return model._instance
80class ObjectModel:
81 def __init__(self):
82 self._instance = None
84 def __repr__(self):
85 result = []
86 cname = type(self).__name__
87 string = "%(cname)s(%(fields)s)"
88 alignment = len(cname) + 1
89 for field in sorted(self.attributes()):
90 width = 80 - len(field) - alignment
91 lines = pprint.pformat(field, indent=2, width=width).splitlines(True)
93 inner = [lines[0]]
94 for line in lines[1:]:
95 inner.append(" " * alignment + line)
96 result.append(field)
98 return string % {
99 "cname": cname,
100 "fields": (",\n" + " " * alignment).join(result),
101 }
103 def __call__(self, instance):
104 self._instance = instance
105 return self
107 def __get__(self, instance, cls=None):
108 # ObjectModel needs to be a descriptor so that just doing
109 # `special_attributes = SomeObjectModel` should be enough in the body of a node.
110 # But at the same time, node.special_attributes should return an object
111 # which can be used for manipulating the special attributes. That's the reason
112 # we pass the instance through which it got accessed to ObjectModel.__call__,
113 # returning itself afterwards, so we can still have access to the
114 # underlying data model and to the instance for which it got accessed.
115 return self(instance)
117 def __contains__(self, name) -> bool:
118 return name in self.attributes()
120 @lru_cache # noqa
121 def attributes(self) -> list[str]:
122 """Get the attributes which are exported by this object model."""
123 return [o[LEN_OF_IMPL_PREFIX:] for o in dir(self) if o.startswith(IMPL_PREFIX)]
125 def lookup(self, name):
126 """Look up the given *name* in the current model.
128 It should return an AST or an interpreter object,
129 but if the name is not found, then an AttributeInferenceError will be raised.
130 """
131 if name in self.attributes():
132 return getattr(self, IMPL_PREFIX + name)
133 raise AttributeInferenceError(target=self._instance, attribute=name)
135 @property
136 def attr___new__(self) -> bases.BoundMethod:
137 """Calling cls.__new__(type) on an object returns an instance of 'type'."""
138 from astroid import builder # pylint: disable=import-outside-toplevel
140 node: nodes.FunctionDef = builder.extract_node(
141 """def __new__(self, cls): return cls()"""
142 )
143 # We set the parent as being the ClassDef of 'object' as that
144 # triggers correct inference as a call to __new__ in bases.py
145 node.parent = AstroidManager().builtins_module["object"]
147 return bases.BoundMethod(proxy=node, bound=_get_bound_node(self))
149 @property
150 def attr___init__(self) -> bases.BoundMethod:
151 """Calling cls.__init__() normally returns None."""
152 from astroid import builder # pylint: disable=import-outside-toplevel
154 # The *args and **kwargs are necessary not to trigger warnings about missing
155 # or extra parameters for '__init__' methods we don't infer correctly.
156 # This BoundMethod is the fallback value for those.
157 node: nodes.FunctionDef = builder.extract_node(
158 """def __init__(self, *args, **kwargs): return None"""
159 )
160 # We set the parent as being the ClassDef of 'object' as that
161 # is where this method originally comes from
162 node.parent = AstroidManager().builtins_module["object"]
164 return bases.BoundMethod(proxy=node, bound=_get_bound_node(self))
167class ModuleModel(ObjectModel):
168 def _builtins(self):
169 builtins_ast_module = AstroidManager().builtins_module
170 return builtins_ast_module.special_attributes.lookup("__dict__")
172 @property
173 def attr_builtins(self):
174 return self._builtins()
176 @property
177 def attr___path__(self):
178 if not self._instance.package:
179 raise AttributeInferenceError(target=self._instance, attribute="__path__")
181 path_objs = [
182 node_classes.Const(
183 value=path
184 if not path.endswith("__init__.py")
185 else os.path.dirname(path),
186 parent=self._instance,
187 )
188 for path in self._instance.path
189 ]
191 container = node_classes.List(parent=self._instance)
192 container.postinit(path_objs)
194 return container
196 @property
197 def attr___name__(self):
198 return node_classes.Const(value=self._instance.name, parent=self._instance)
200 @property
201 def attr___doc__(self):
202 return node_classes.Const(
203 value=getattr(self._instance.doc_node, "value", None),
204 parent=self._instance,
205 )
207 @property
208 def attr___file__(self):
209 return node_classes.Const(value=self._instance.file, parent=self._instance)
211 @property
212 def attr___dict__(self):
213 return _dunder_dict(self._instance, self._instance.globals)
215 @property
216 def attr___package__(self):
217 if not self._instance.package:
218 value = ""
219 else:
220 value = self._instance.name
222 return node_classes.Const(value=value, parent=self._instance)
224 # These are related to the Python 3 implementation of the
225 # import system,
226 # https://docs.python.org/3/reference/import.html#import-related-module-attributes
228 @property
229 def attr___spec__(self):
230 # No handling for now.
231 return node_classes.Unknown()
233 @property
234 def attr___loader__(self):
235 # No handling for now.
236 return node_classes.Unknown()
238 @property
239 def attr___cached__(self):
240 # No handling for now.
241 return node_classes.Unknown()
244class FunctionModel(ObjectModel):
245 @property
246 def attr___name__(self):
247 return node_classes.Const(value=self._instance.name, parent=self._instance)
249 @property
250 def attr___doc__(self):
251 return node_classes.Const(
252 value=getattr(self._instance.doc_node, "value", None),
253 parent=self._instance,
254 )
256 @property
257 def attr___qualname__(self):
258 return node_classes.Const(value=self._instance.qname(), parent=self._instance)
260 @property
261 def attr___defaults__(self):
262 func = self._instance
263 if not func.args.defaults:
264 return node_classes.Const(value=None, parent=func)
266 defaults_obj = node_classes.Tuple(parent=func)
267 defaults_obj.postinit(func.args.defaults)
268 return defaults_obj
270 @property
271 def attr___annotations__(self):
272 obj = node_classes.Dict(
273 parent=self._instance,
274 lineno=self._instance.lineno,
275 col_offset=self._instance.col_offset,
276 end_lineno=self._instance.end_lineno,
277 end_col_offset=self._instance.end_col_offset,
278 )
280 if not self._instance.returns:
281 returns = None
282 else:
283 returns = self._instance.returns
285 args = self._instance.args
286 pair_annotations = itertools.chain(
287 zip(args.args or [], args.annotations),
288 zip(args.kwonlyargs, args.kwonlyargs_annotations),
289 zip(args.posonlyargs or [], args.posonlyargs_annotations),
290 )
292 annotations = {
293 arg.name: annotation for (arg, annotation) in pair_annotations if annotation
294 }
295 if args.varargannotation:
296 annotations[args.vararg] = args.varargannotation
297 if args.kwargannotation:
298 annotations[args.kwarg] = args.kwargannotation
299 if returns:
300 annotations["return"] = returns
302 items = [
303 (node_classes.Const(key, parent=obj), value)
304 for (key, value) in annotations.items()
305 ]
307 obj.postinit(items)
308 return obj
310 @property
311 def attr___dict__(self):
312 return node_classes.Dict(
313 parent=self._instance,
314 lineno=self._instance.lineno,
315 col_offset=self._instance.col_offset,
316 end_lineno=self._instance.end_lineno,
317 end_col_offset=self._instance.end_col_offset,
318 )
320 attr___globals__ = attr___dict__
322 @property
323 def attr___kwdefaults__(self):
324 def _default_args(args, parent):
325 for arg in args.kwonlyargs:
326 try:
327 default = args.default_value(arg.name)
328 except NoDefault:
329 continue
331 name = node_classes.Const(arg.name, parent=parent)
332 yield name, default
334 args = self._instance.args
335 obj = node_classes.Dict(
336 parent=self._instance,
337 lineno=self._instance.lineno,
338 col_offset=self._instance.col_offset,
339 end_lineno=self._instance.end_lineno,
340 end_col_offset=self._instance.end_col_offset,
341 )
342 defaults = dict(_default_args(args, obj))
344 obj.postinit(list(defaults.items()))
345 return obj
347 @property
348 def attr___module__(self):
349 return node_classes.Const(self._instance.root().qname())
351 @property
352 def attr___get__(self):
353 func = self._instance
355 class DescriptorBoundMethod(bases.BoundMethod):
356 """Bound method which knows how to understand calling descriptor
357 binding.
358 """
360 def implicit_parameters(self) -> Literal[0]:
361 # Different than BoundMethod since the signature
362 # is different.
363 return 0
365 def infer_call_result(
366 self,
367 caller: SuccessfulInferenceResult | None,
368 context: InferenceContext | None = None,
369 ) -> Iterator[bases.BoundMethod]:
370 if len(caller.args) > 2 or len(caller.args) < 1:
371 raise InferenceError(
372 "Invalid arguments for descriptor binding",
373 target=self,
374 context=context,
375 )
377 context = copy_context(context)
378 try:
379 cls = next(caller.args[0].infer(context=context))
380 except StopIteration as e:
381 raise InferenceError(context=context, node=caller.args[0]) from e
383 if isinstance(cls, util.UninferableBase):
384 raise InferenceError(
385 "Invalid class inferred", target=self, context=context
386 )
388 # For some reason func is a Node that the below
389 # code is not expecting
390 if isinstance(func, bases.BoundMethod):
391 yield func
392 return
394 # Rebuild the original value, but with the parent set as the
395 # class where it will be bound.
396 new_func = func.__class__(
397 name=func.name,
398 lineno=func.lineno,
399 col_offset=func.col_offset,
400 parent=func.parent,
401 end_lineno=func.end_lineno,
402 end_col_offset=func.end_col_offset,
403 )
404 # pylint: disable=no-member
405 new_func.postinit(
406 func.args,
407 func.body,
408 func.decorators,
409 func.returns,
410 doc_node=func.doc_node,
411 )
413 # Build a proper bound method that points to our newly built function.
414 proxy = bases.UnboundMethod(new_func)
415 yield bases.BoundMethod(proxy=proxy, bound=cls)
417 @property
418 def args(self):
419 """Overwrite the underlying args to match those of the underlying func.
421 Usually the underlying *func* is a function/method, as in:
423 def test(self):
424 pass
426 This has only the *self* parameter but when we access test.__get__
427 we get a new object which has two parameters, *self* and *type*.
428 """
429 nonlocal func
430 arguments = astroid.Arguments(
431 parent=func.args.parent, vararg=None, kwarg=None
432 )
434 positional_or_keyword_params = func.args.args.copy()
435 positional_or_keyword_params.append(
436 astroid.AssignName(
437 name="type",
438 lineno=0,
439 col_offset=0,
440 parent=arguments,
441 end_lineno=None,
442 end_col_offset=None,
443 )
444 )
446 positional_only_params = func.args.posonlyargs.copy()
448 arguments.postinit(
449 args=positional_or_keyword_params,
450 posonlyargs=positional_only_params,
451 defaults=[],
452 kwonlyargs=[],
453 kw_defaults=[],
454 annotations=[],
455 kwonlyargs_annotations=[],
456 posonlyargs_annotations=[],
457 )
458 return arguments
460 return DescriptorBoundMethod(proxy=self._instance, bound=self._instance)
462 # These are here just for completion.
463 @property
464 def attr___ne__(self):
465 return node_classes.Unknown()
467 attr___subclasshook__ = attr___ne__
468 attr___str__ = attr___ne__
469 attr___sizeof__ = attr___ne__
470 attr___setattr___ = attr___ne__
471 attr___repr__ = attr___ne__
472 attr___reduce__ = attr___ne__
473 attr___reduce_ex__ = attr___ne__
474 attr___lt__ = attr___ne__
475 attr___eq__ = attr___ne__
476 attr___gt__ = attr___ne__
477 attr___format__ = attr___ne__
478 attr___delattr___ = attr___ne__
479 attr___getattribute__ = attr___ne__
480 attr___hash__ = attr___ne__
481 attr___dir__ = attr___ne__
482 attr___call__ = attr___ne__
483 attr___class__ = attr___ne__
484 attr___closure__ = attr___ne__
485 attr___code__ = attr___ne__
488class ClassModel(ObjectModel):
489 def __init__(self):
490 # Add a context so that inferences called from an instance don't recurse endlessly
491 self.context = InferenceContext()
493 super().__init__()
495 @property
496 def attr___module__(self):
497 return node_classes.Const(self._instance.root().qname())
499 @property
500 def attr___name__(self):
501 return node_classes.Const(self._instance.name)
503 @property
504 def attr___qualname__(self):
505 return node_classes.Const(self._instance.qname())
507 @property
508 def attr___doc__(self):
509 return node_classes.Const(getattr(self._instance.doc_node, "value", None))
511 @property
512 def attr___mro__(self):
513 if not self._instance.newstyle:
514 raise AttributeInferenceError(target=self._instance, attribute="__mro__")
516 mro = self._instance.mro()
517 obj = node_classes.Tuple(parent=self._instance)
518 obj.postinit(mro)
519 return obj
521 @property
522 def attr_mro(self):
523 if not self._instance.newstyle:
524 raise AttributeInferenceError(target=self._instance, attribute="mro")
526 other_self = self
528 # Cls.mro is a method and we need to return one in order to have a proper inference.
529 # The method we're returning is capable of inferring the underlying MRO though.
530 class MroBoundMethod(bases.BoundMethod):
531 def infer_call_result(
532 self,
533 caller: SuccessfulInferenceResult | None,
534 context: InferenceContext | None = None,
535 ) -> Iterator[node_classes.Tuple]:
536 yield other_self.attr___mro__
538 implicit_metaclass = self._instance.implicit_metaclass()
539 mro_method = implicit_metaclass.locals["mro"][0]
540 return MroBoundMethod(proxy=mro_method, bound=implicit_metaclass)
542 @property
543 def attr___bases__(self):
544 obj = node_classes.Tuple()
545 context = InferenceContext()
546 elts = list(self._instance._inferred_bases(context))
547 obj.postinit(elts=elts)
548 return obj
550 @property
551 def attr___class__(self):
552 # pylint: disable=import-outside-toplevel; circular import
553 from astroid import helpers
555 return helpers.object_type(self._instance)
557 @property
558 def attr___subclasses__(self):
559 """Get the subclasses of the underlying class.
561 This looks only in the current module for retrieving the subclasses,
562 thus it might miss a couple of them.
563 """
564 if not self._instance.newstyle:
565 raise AttributeInferenceError(
566 target=self._instance, attribute="__subclasses__"
567 )
569 qname = self._instance.qname()
570 root = self._instance.root()
571 classes = [
572 cls
573 for cls in root.nodes_of_class(nodes.ClassDef)
574 if cls != self._instance and cls.is_subtype_of(qname, context=self.context)
575 ]
577 obj = node_classes.List(parent=self._instance)
578 obj.postinit(classes)
580 class SubclassesBoundMethod(bases.BoundMethod):
581 def infer_call_result(
582 self,
583 caller: SuccessfulInferenceResult | None,
584 context: InferenceContext | None = None,
585 ) -> Iterator[node_classes.List]:
586 yield obj
588 implicit_metaclass = self._instance.implicit_metaclass()
589 subclasses_method = implicit_metaclass.locals["__subclasses__"][0]
590 return SubclassesBoundMethod(proxy=subclasses_method, bound=implicit_metaclass)
592 @property
593 def attr___dict__(self):
594 return node_classes.Dict(
595 parent=self._instance,
596 lineno=self._instance.lineno,
597 col_offset=self._instance.col_offset,
598 end_lineno=self._instance.end_lineno,
599 end_col_offset=self._instance.end_col_offset,
600 )
602 @property
603 def attr___call__(self):
604 """Calling a class A() returns an instance of A."""
605 return self._instance.instantiate_class()
608class SuperModel(ObjectModel):
609 @property
610 def attr___thisclass__(self):
611 return self._instance.mro_pointer
613 @property
614 def attr___self_class__(self):
615 return self._instance._self_class
617 @property
618 def attr___self__(self):
619 return self._instance.type
621 @property
622 def attr___class__(self):
623 return self._instance._proxied
626class UnboundMethodModel(ObjectModel):
627 @property
628 def attr___class__(self):
629 # pylint: disable=import-outside-toplevel; circular import
630 from astroid import helpers
632 return helpers.object_type(self._instance)
634 @property
635 def attr___func__(self):
636 return self._instance._proxied
638 @property
639 def attr___self__(self):
640 return node_classes.Const(value=None, parent=self._instance)
642 attr_im_func = attr___func__
643 attr_im_class = attr___class__
644 attr_im_self = attr___self__
647class ContextManagerModel(ObjectModel):
648 """Model for context managers.
650 Based on 3.3.9 of the Data Model documentation:
651 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
652 """
654 @property
655 def attr___enter__(self) -> bases.BoundMethod:
656 """Representation of the base implementation of __enter__.
658 As per Python documentation:
659 Enter the runtime context related to this object. The with statement
660 will bind this method's return value to the target(s) specified in the
661 as clause of the statement, if any.
662 """
663 from astroid import builder # pylint: disable=import-outside-toplevel
665 node: nodes.FunctionDef = builder.extract_node("""def __enter__(self): ...""")
666 # We set the parent as being the ClassDef of 'object' as that
667 # is where this method originally comes from
668 node.parent = AstroidManager().builtins_module["object"]
670 return bases.BoundMethod(proxy=node, bound=_get_bound_node(self))
672 @property
673 def attr___exit__(self) -> bases.BoundMethod:
674 """Representation of the base implementation of __exit__.
676 As per Python documentation:
677 Exit the runtime context related to this object. The parameters describe the
678 exception that caused the context to be exited. If the context was exited
679 without an exception, all three arguments will be None.
680 """
681 from astroid import builder # pylint: disable=import-outside-toplevel
683 node: nodes.FunctionDef = builder.extract_node(
684 """def __exit__(self, exc_type, exc_value, traceback): ..."""
685 )
686 # We set the parent as being the ClassDef of 'object' as that
687 # is where this method originally comes from
688 node.parent = AstroidManager().builtins_module["object"]
690 return bases.BoundMethod(proxy=node, bound=_get_bound_node(self))
693class BoundMethodModel(FunctionModel):
694 @property
695 def attr___func__(self):
696 return self._instance._proxied._proxied
698 @property
699 def attr___self__(self):
700 return self._instance.bound
703class GeneratorModel(FunctionModel, ContextManagerModel):
704 def __new__(cls, *args, **kwargs):
705 # Append the values from the GeneratorType unto this object.
706 ret = super().__new__(cls, *args, **kwargs)
707 generator = AstroidManager().builtins_module["generator"]
708 for name, values in generator.locals.items():
709 method = values[0]
711 def patched(cls, meth=method):
712 return meth
714 setattr(type(ret), IMPL_PREFIX + name, property(patched))
716 return ret
718 @property
719 def attr___name__(self):
720 return node_classes.Const(
721 value=self._instance.parent.name, parent=self._instance
722 )
724 @property
725 def attr___doc__(self):
726 return node_classes.Const(
727 value=getattr(self._instance.parent.doc_node, "value", None),
728 parent=self._instance,
729 )
732class AsyncGeneratorModel(GeneratorModel):
733 def __new__(cls, *args, **kwargs):
734 # Append the values from the AGeneratorType unto this object.
735 ret = super().__new__(cls, *args, **kwargs)
736 astroid_builtins = AstroidManager().builtins_module
737 generator = astroid_builtins.get("async_generator")
738 if generator is None:
739 # Make it backward compatible.
740 generator = astroid_builtins.get("generator")
742 for name, values in generator.locals.items():
743 method = values[0]
745 def patched(cls, meth=method):
746 return meth
748 setattr(type(ret), IMPL_PREFIX + name, property(patched))
750 return ret
753class InstanceModel(ObjectModel):
754 @property
755 def attr___class__(self):
756 return self._instance._proxied
758 @property
759 def attr___module__(self):
760 return node_classes.Const(self._instance.root().qname())
762 @property
763 def attr___doc__(self):
764 return node_classes.Const(getattr(self._instance.doc_node, "value", None))
766 @property
767 def attr___dict__(self):
768 return _dunder_dict(self._instance, self._instance.instance_attrs)
771# Exception instances
774class ExceptionInstanceModel(InstanceModel):
775 @property
776 def attr_args(self) -> nodes.Tuple:
777 return nodes.Tuple(parent=self._instance)
779 @property
780 def attr___traceback__(self):
781 builtins_ast_module = AstroidManager().builtins_module
782 traceback_type = builtins_ast_module[types.TracebackType.__name__]
783 return traceback_type.instantiate_class()
786class SyntaxErrorInstanceModel(ExceptionInstanceModel):
787 @property
788 def attr_text(self):
789 return node_classes.Const("")
792class OSErrorInstanceModel(ExceptionInstanceModel):
793 @property
794 def attr_filename(self):
795 return node_classes.Const("")
797 @property
798 def attr_errno(self):
799 return node_classes.Const(0)
801 @property
802 def attr_strerror(self):
803 return node_classes.Const("")
805 attr_filename2 = attr_filename
808class ImportErrorInstanceModel(ExceptionInstanceModel):
809 @property
810 def attr_name(self):
811 return node_classes.Const("")
813 @property
814 def attr_path(self):
815 return node_classes.Const("")
818class UnicodeDecodeErrorInstanceModel(ExceptionInstanceModel):
819 @property
820 def attr_object(self):
821 return node_classes.Const("")
824BUILTIN_EXCEPTIONS = {
825 "builtins.SyntaxError": SyntaxErrorInstanceModel,
826 "builtins.ImportError": ImportErrorInstanceModel,
827 "builtins.UnicodeDecodeError": UnicodeDecodeErrorInstanceModel,
828 # These are all similar to OSError in terms of attributes
829 "builtins.OSError": OSErrorInstanceModel,
830 "builtins.BlockingIOError": OSErrorInstanceModel,
831 "builtins.BrokenPipeError": OSErrorInstanceModel,
832 "builtins.ChildProcessError": OSErrorInstanceModel,
833 "builtins.ConnectionAbortedError": OSErrorInstanceModel,
834 "builtins.ConnectionError": OSErrorInstanceModel,
835 "builtins.ConnectionRefusedError": OSErrorInstanceModel,
836 "builtins.ConnectionResetError": OSErrorInstanceModel,
837 "builtins.FileExistsError": OSErrorInstanceModel,
838 "builtins.FileNotFoundError": OSErrorInstanceModel,
839 "builtins.InterruptedError": OSErrorInstanceModel,
840 "builtins.IsADirectoryError": OSErrorInstanceModel,
841 "builtins.NotADirectoryError": OSErrorInstanceModel,
842 "builtins.PermissionError": OSErrorInstanceModel,
843 "builtins.ProcessLookupError": OSErrorInstanceModel,
844 "builtins.TimeoutError": OSErrorInstanceModel,
845}
848class DictModel(ObjectModel):
849 @property
850 def attr___class__(self):
851 return self._instance._proxied
853 def _generic_dict_attribute(self, obj, name):
854 """Generate a bound method that can infer the given *obj*."""
856 class DictMethodBoundMethod(astroid.BoundMethod):
857 def infer_call_result(
858 self,
859 caller: SuccessfulInferenceResult | None,
860 context: InferenceContext | None = None,
861 ) -> Iterator[InferenceResult]:
862 yield obj
864 meth = next(self._instance._proxied.igetattr(name), None)
865 return DictMethodBoundMethod(proxy=meth, bound=self._instance)
867 @property
868 def attr_items(self):
869 from astroid import objects # pylint: disable=import-outside-toplevel
871 elems = []
872 obj = node_classes.List(parent=self._instance)
873 for key, value in self._instance.items:
874 elem = node_classes.Tuple(parent=obj)
875 elem.postinit((key, value))
876 elems.append(elem)
877 obj.postinit(elts=elems)
879 items_obj = objects.DictItems(obj)
880 return self._generic_dict_attribute(items_obj, "items")
882 @property
883 def attr_keys(self):
884 from astroid import objects # pylint: disable=import-outside-toplevel
886 keys = [key for (key, _) in self._instance.items]
887 obj = node_classes.List(parent=self._instance)
888 obj.postinit(elts=keys)
890 keys_obj = objects.DictKeys(obj)
891 return self._generic_dict_attribute(keys_obj, "keys")
893 @property
894 def attr_values(self):
895 from astroid import objects # pylint: disable=import-outside-toplevel
897 values = [value for (_, value) in self._instance.items]
898 obj = node_classes.List(parent=self._instance)
899 obj.postinit(values)
901 values_obj = objects.DictValues(obj)
902 return self._generic_dict_attribute(values_obj, "values")
905class PropertyModel(ObjectModel):
906 """Model for a builtin property."""
908 def _init_function(self, name):
909 function = nodes.FunctionDef(
910 name=name,
911 parent=self._instance,
912 lineno=self._instance.lineno,
913 col_offset=self._instance.col_offset,
914 end_lineno=self._instance.end_lineno,
915 end_col_offset=self._instance.end_col_offset,
916 )
918 args = nodes.Arguments(parent=function, vararg=None, kwarg=None)
919 args.postinit(
920 args=[],
921 defaults=[],
922 kwonlyargs=[],
923 kw_defaults=[],
924 annotations=[],
925 posonlyargs=[],
926 posonlyargs_annotations=[],
927 kwonlyargs_annotations=[],
928 )
930 function.postinit(args=args, body=[])
931 return function
933 @property
934 def attr_fget(self):
935 func = self._instance
937 class PropertyFuncAccessor(nodes.FunctionDef):
938 def infer_call_result(
939 self,
940 caller: SuccessfulInferenceResult | None,
941 context: InferenceContext | None = None,
942 ) -> Iterator[InferenceResult]:
943 nonlocal func
944 if caller and len(caller.args) != 1:
945 raise InferenceError(
946 "fget() needs a single argument", target=self, context=context
947 )
949 yield from func.function.infer_call_result(
950 caller=caller, context=context
951 )
953 property_accessor = PropertyFuncAccessor(
954 name="fget",
955 parent=self._instance,
956 lineno=self._instance.lineno,
957 col_offset=self._instance.col_offset,
958 end_lineno=self._instance.end_lineno,
959 end_col_offset=self._instance.end_col_offset,
960 )
961 property_accessor.postinit(args=func.args, body=func.body)
962 return property_accessor
964 @property
965 def attr_fset(self):
966 func = self._instance
968 def find_setter(func: Property) -> astroid.FunctionDef | None:
969 """
970 Given a property, find the corresponding setter function and returns it.
972 :param func: property for which the setter has to be found
973 :return: the setter function or None
974 """
975 for target in [
976 t for t in func.parent.get_children() if t.name == func.function.name
977 ]:
978 for dec_name in target.decoratornames():
979 if dec_name.endswith(func.function.name + ".setter"):
980 return target
981 return None
983 func_setter = find_setter(func)
984 if not func_setter:
985 raise InferenceError(
986 f"Unable to find the setter of property {func.function.name}"
987 )
989 class PropertyFuncAccessor(nodes.FunctionDef):
990 def infer_call_result(
991 self,
992 caller: SuccessfulInferenceResult | None,
993 context: InferenceContext | None = None,
994 ) -> Iterator[InferenceResult]:
995 nonlocal func_setter
996 if caller and len(caller.args) != 2:
997 raise InferenceError(
998 "fset() needs two arguments", target=self, context=context
999 )
1000 yield from func_setter.infer_call_result(caller=caller, context=context)
1002 property_accessor = PropertyFuncAccessor(
1003 name="fset",
1004 parent=self._instance,
1005 lineno=self._instance.lineno,
1006 col_offset=self._instance.col_offset,
1007 end_lineno=self._instance.end_lineno,
1008 end_col_offset=self._instance.end_col_offset,
1009 )
1010 property_accessor.postinit(args=func_setter.args, body=func_setter.body)
1011 return property_accessor
1013 @property
1014 def attr_setter(self):
1015 return self._init_function("setter")
1017 @property
1018 def attr_deleter(self):
1019 return self._init_function("deleter")
1021 @property
1022 def attr_getter(self):
1023 return self._init_function("getter")
1025 # pylint: enable=import-outside-toplevel