瀏覽代碼

d04 Добавление виртуального окружения, отладка АПИ

SVI 2 年之前
父節點
當前提交
73db3c09ce
共有 100 個文件被更改,包括 14341 次插入160 次删除
  1. 1 2
      Makefile
  2. 11 1
      README.md
  3. 1 1
      main.py
  4. 2 2
      pakApp/modApp.py
  5. 0 0
      pakApp/pakGui/modGui.py
  6. 0 0
      pakApp/pakGui/pakWinAddBot/modWinAddBot.py
  7. 0 0
      pakApp/pakGui/pakWinAddBot/pakFrmCmd/modFrmCmd.py
  8. 0 0
      pakApp/pakGui/pakWinAddBot/pakFrmNewBot/mofFrmNewBot.py
  9. 6 1
      pakApp/pakGui/pakWinMain/modWinMain.py
  10. 0 0
      pakApp/pakGui/pakWinMain/pakFrmCmd/modFrmCmd.py
  11. 8 1
      pakApp/pakLogic/modLogic.py
  12. 24 0
      requiments.txt
  13. 0 127
      servap/index.py
  14. 0 21
      servap/list_bot.py
  15. 8 4
      server/serv_web/serv_web.go
  16. 222 0
      venv/lib/python3.11/site-packages/_distutils_hack/__init__.py
  17. 1 0
      venv/lib/python3.11/site-packages/_distutils_hack/override.py
  18. 199 0
      venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/CONTRIBUTORS.txt
  19. 1 0
      venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/INSTALLER
  20. 508 0
      venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/LICENSE
  21. 127 0
      venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/METADATA
  22. 209 0
      venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/RECORD
  23. 5 0
      venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/WHEEL
  24. 1 0
      venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/top_level.txt
  25. 198 0
      venv/lib/python3.11/site-packages/astroid/__init__.py
  26. 6 0
      venv/lib/python3.11/site-packages/astroid/__pkginfo__.py
  27. 141 0
      venv/lib/python3.11/site-packages/astroid/_ast.py
  28. 356 0
      venv/lib/python3.11/site-packages/astroid/_backport_stdlib_names.py
  29. 26 0
      venv/lib/python3.11/site-packages/astroid/_cache.py
  30. 309 0
      venv/lib/python3.11/site-packages/astroid/arguments.py
  31. 17 0
      venv/lib/python3.11/site-packages/astroid/astroid_manager.py
  32. 711 0
      venv/lib/python3.11/site-packages/astroid/bases.py
  33. 0 0
      venv/lib/python3.11/site-packages/astroid/brain/__init__.py
  34. 44 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_argparse.py
  35. 88 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_attrs.py
  36. 31 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_boto3.py
  37. 1007 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_builtin_inference.py
  38. 126 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_collections.py
  39. 25 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_crypt.py
  40. 84 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_ctypes.py
  41. 183 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_curses.py
  42. 636 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_dataclasses.py
  43. 26 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_dateutil.py
  44. 61 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_fstrings.py
  45. 166 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_functools.py
  46. 249 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_gi.py
  47. 96 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_hashlib.py
  48. 212 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_http.py
  49. 54 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_hypothesis.py
  50. 43 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_io.py
  51. 123 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_mechanize.py
  52. 107 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_multiprocessing.py
  53. 623 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_namedtuple_enum.py
  54. 79 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_nose.py
  55. 27 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py
  56. 22 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py
  57. 29 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_function_base.py
  58. 100 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_multiarray.py
  59. 46 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_numeric.py
  60. 263 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_numerictypes.py
  61. 154 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_umath.py
  62. 31 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_ma.py
  63. 162 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_ndarray.py
  64. 71 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_random_mtrand.py
  65. 86 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_utils.py
  66. 51 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_pathlib.py
  67. 70 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_pkg_resources.py
  68. 83 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_pytest.py
  69. 88 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_qt.py
  70. 90 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_random.py
  71. 96 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_re.py
  72. 95 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_regex.py
  73. 78 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_responses.py
  74. 88 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_scipy_signal.py
  75. 119 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_signal.py
  76. 239 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_six.py
  77. 39 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_sqlalchemy.py
  78. 160 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_ssl.py
  79. 105 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_subprocess.py
  80. 31 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_threading.py
  81. 69 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_type.py
  82. 447 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_typing.py
  83. 33 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_unittest.py
  84. 18 0
      venv/lib/python3.11/site-packages/astroid/brain/brain_uuid.py
  85. 24 0
      venv/lib/python3.11/site-packages/astroid/brain/helpers.py
  86. 494 0
      venv/lib/python3.11/site-packages/astroid/builder.py
  87. 41 0
      venv/lib/python3.11/site-packages/astroid/const.py
  88. 137 0
      venv/lib/python3.11/site-packages/astroid/constraint.py
  89. 205 0
      venv/lib/python3.11/site-packages/astroid/context.py
  90. 290 0
      venv/lib/python3.11/site-packages/astroid/decorators.py
  91. 426 0
      venv/lib/python3.11/site-packages/astroid/exceptions.py
  92. 241 0
      venv/lib/python3.11/site-packages/astroid/filter_statements.py
  93. 320 0
      venv/lib/python3.11/site-packages/astroid/helpers.py
  94. 1278 0
      venv/lib/python3.11/site-packages/astroid/inference.py
  95. 85 0
      venv/lib/python3.11/site-packages/astroid/inference_tip.py
  96. 0 0
      venv/lib/python3.11/site-packages/astroid/interpreter/__init__.py
  97. 0 0
      venv/lib/python3.11/site-packages/astroid/interpreter/_import/__init__.py
  98. 475 0
      venv/lib/python3.11/site-packages/astroid/interpreter/_import/spec.py
  99. 108 0
      venv/lib/python3.11/site-packages/astroid/interpreter/_import/util.py
  100. 66 0
      venv/lib/python3.11/site-packages/astroid/interpreter/dunder_lookup.py

+ 1 - 2
Makefile

@@ -17,8 +17,7 @@ prod.run:
 	./run_prod.sh
 view.run:
 	clear
-	cd ./servap && \
-	python3 ./main.py
+	python3 -m main
 test.run:
 	clear
 	go fmt ./...

+ 11 - 1
README.md

@@ -35,7 +35,17 @@ sudo apt-get install libasound2-dev
 Для работы клиента необходимо установить `python3.11` и к нему:
 
 ```bash
-pip install requests
+source ./venv/bin/activate
+python3 -m venv venv
+python3 -m pip install requests
+```
+
+## Обновление списка зависимостей
+
+Необходимо выполнять с активированным виртуальным окружением.
+
+```bash
+pip list > requiments.txt
 ```
 
 ## Цели сборки

+ 1 - 1
servap/main.py → main.py

@@ -3,7 +3,7 @@
 from pakApp.modApp import App
 
 def main()->None:
-    app:App=App()
+    app:App=App(isProd=True)
     app.run()
 
 

+ 2 - 2
servap/pakApp/modApp.py → pakApp/modApp.py

@@ -5,8 +5,8 @@ from pakApp.pakLogic.modLogic import Logic
 
 
 class App():
-    def __init__(self)->None:
-        self.isProd=True
+    def __init__(self,isProd:bool)->None:
+        self.isProd=isProd
         self.logic=Logic(self)
         self.gui:Gui=Gui(self)
 

+ 0 - 0
servap/pakApp/pakGui/modGui.py → pakApp/pakGui/modGui.py


+ 0 - 0
servap/pakApp/pakGui/pakWinAddBot/modWinAddBot.py → pakApp/pakGui/pakWinAddBot/modWinAddBot.py


+ 0 - 0
servap/pakApp/pakGui/pakWinAddBot/pakFrmCmd/modFrmCmd.py → pakApp/pakGui/pakWinAddBot/pakFrmCmd/modFrmCmd.py


+ 0 - 0
servap/pakApp/pakGui/pakWinAddBot/pakFrmNewBot/mofFrmNewBot.py → pakApp/pakGui/pakWinAddBot/pakFrmNewBot/mofFrmNewBot.py


+ 6 - 1
servap/pakApp/pakGui/pakWinMain/modWinMain.py → pakApp/pakGui/pakWinMain/modWinMain.py

@@ -1,7 +1,7 @@
 """Главное окно приложения"""
 
 from typing import Any
-from tkinter import Tk
+from tkinter import Tk,LabelFrame
 
 from pakApp.pakGui.pakWinMain.pakFrmCmd.modFrmCmd import FrmCmd
 
@@ -15,8 +15,13 @@ class WinMain(Tk):
 
     def run(self)->None:
         self.frmCmd=FrmCmd(self)
+        self.frmListBot=LabelFrame(self,text="Список ботов")
+        self.frmListBot.pack(fill="both",expand=True)
+        self.update_list_bot()
         self.mainloop()
 
     def update_list_bot(self)->None:
         """Обновляет список ботоов с сервера"""
         print("WinMain.update_list_bot()")
+        list_bot:Any=self.app.logic.get_list_bot()
+        print(f"WinMain.update_list_bot(): list_bot={list_bot}")

+ 0 - 0
servap/pakApp/pakGui/pakWinMain/pakFrmCmd/modFrmCmd.py → pakApp/pakGui/pakWinMain/pakFrmCmd/modFrmCmd.py


+ 8 - 1
servap/pakApp/pakLogic/modLogic.py → pakApp/pakLogic/modLogic.py

@@ -2,7 +2,7 @@
 
 from typing import Any
 import requests
-
+import json
 
 
 class Logic():
@@ -23,3 +23,10 @@ class Logic():
         print(f"Logic.add_new_bot(): result={res}")
         self.app.gui.winMain.update_list_bot()
 
+    def get_list_bot(self):
+        """Возвращает список ботов"""
+        res=requests.get("http://localhost:"+self.port+"/list_bot/get")
+        print(f"Logic.add_new_bot(): result={res.text}")
+        content=json.loads(res.content)
+        return content
+

+ 24 - 0
requiments.txt

@@ -0,0 +1,24 @@
+Package            Version
+------------------ ---------
+astroid            2.15.7
+certifi            2023.7.22
+charset-normalizer 3.2.0
+dill               0.3.7
+flake8             6.1.0
+idna               3.4
+isort              5.12.0
+lazy-object-proxy  1.9.0
+mccabe             0.7.0
+pip                23.2.1
+platformdirs       3.10.0
+pycodestyle        2.11.0
+pydocstyle         6.3.0
+pyflakes           3.1.0
+pylama             8.4.1
+pylint             2.17.6
+requests           2.31.0
+setuptools         65.5.0
+snowballstemmer    2.2.0
+tomlkit            0.12.1
+urllib3            2.0.5
+wrapt              1.15.0

+ 0 - 127
servap/index.py

@@ -1,127 +0,0 @@
-# coding: utf8
-"""С этого начинается работа """
-
-from typing import Any
-from browser import document, ajax
-
-
-def bot_list_update(event:Any):
-    """Обновляет список ботов"""
-    def read(req: Any):
-        print(req.text)
-        document["bot_list"] = req.text
-    print(f"bot_list_update click, ev={event}!")
-    ajax.get("/bot_list/update", oncomplete=read)
-
-
-def bot_list_add(event: Any):
-    """Добавляет нового бота"""
-    print(f"bot_list_add click, ev={event}!")
-    FormBotNew()
-
-
-class FormBotNew():
-    """Показывает форму добавления бота"""
-
-    def __init__(self):
-        print("FormBotNew.__init__()")
-        self.div_new: Any = document["bot_list_new"]
-        self.div_new.clear()
-
-        self.box_new: Any = document.createElement("div")
-        self.box_new["class"] = "container bg-secondary text-white p-3 border"
-
-        self.ent_login: Any = document.createElement("input")
-        self.ent_login["class"] = "col"
-        self.ent_login["id"] = "bot_login"
-
-        self.ent_pass: Any = document.createElement("input")
-        self.ent_pass["class"] = "col"
-        self.ent_pass["id"] = "bot_pass"
-
-        self.add_container_new()
-
-    def add_container_new(self):
-        """Добавляет контейнер добавления нового бота"""
-        print("FormBotNew.add_container_new()")
-        self.add_row_login()
-        self.add_row_pass()
-        self.add_row_btn()
-        self.div_new.appendChild(self.box_new)
-
-    def add_row_login(self) -> None:
-        """Добавляет ряд логина"""
-        print("FormBotNew.add_row_login()")
-        row_login: Any = document.createElement("div")
-        row_login["class"] = "row p-3"
-        lbl_login: Any = document.createElement("label")
-        lbl_login["class"] = "col-2"
-        lbl_login.text = "Логин"
-        row_login.appendChild(lbl_login)
-
-        row_login.appendChild(self.ent_login)
-
-        self.box_new.appendChild(row_login)
-
-    def add_row_pass(self) -> None:
-        """Добавляет ряд пароля"""
-        print("FormBotNew.add_row_pass()")
-        row_pass: Any = document.createElement("div")
-        row_pass["class"] = "row p-3"
-        lbl_pass: Any = document.createElement("label")
-        lbl_pass["class"] = "col-2"
-        lbl_pass.text = "Пароль"
-        row_pass.appendChild(lbl_pass)
-
-        row_pass.appendChild(self.ent_pass)
-
-        self.box_new.appendChild(row_pass)
-
-    def add_row_btn(self) -> None:
-        """добавляет ряд кнопок формы"""
-        row_btn: Any = document.createElement("div")
-        row_btn["class"] = "row p-3"
-        lbl_btn: Any = document.createElement("label")
-        lbl_btn["class"] = "col-6"
-        row_btn.appendChild(lbl_btn)
-
-        btn_cancel: Any = document.createElement("button")
-        btn_cancel["class"] = "btn btn-dark col-3"
-        btn_cancel.text = "Отмена"
-        btn_cancel.bind("click", self.clear)
-        row_btn.appendChild(btn_cancel)
-
-        btn_add: Any = document.createElement("button")
-        btn_add["class"] = "btn btn-primary col-3"
-        btn_add.text = "Добавить"
-        btn_add.bind("click", self.add)
-        row_btn.appendChild(btn_add)
-
-        self.box_new.appendChild(row_btn)
-
-    def add(self, event: Any) -> None:
-        """Получает логин и пароль нового боты из формы"""
-        print("FormBotNew.clear()")
-        login: Any = self.ent_login.value
-        _pass: Any = self.ent_pass.value
-        print(f"FormBotNew.clear(): login={login}, " +
-              f"pass={_pass}, event={event}")
-        self.div_new.clear()
-        self.list_bot_update()
-
-    def list_bot_update(self)->None:
-        """Обновляет список ботов"""
-        def read(req: Any):
-            print(req.text)
-            document["bot_list"] = req.text
-        print(f"bot_list_update click, ev={event}!")
-        ajax.get("/bot_list/update", oncomplete=read)
-
-
-    def clear(self, event: Any) -> None:
-        """Зачищает форму логина"""
-        print("FormBotNew.clear()")
-        self.div_new.clear()
-
-document["bot_list_add"].bind("click", bot_list_add)
-document["bot_list_update"].bind("click", bot_list_update)

+ 0 - 21
servap/list_bot.py

@@ -1,21 +0,0 @@
-# coding: utf-8
-"""Обновляет список ботов в отдельном"""
-
-class ListBot():
-    """Обновляет список ботов"""
-    def __init__(self)->None:
-        """Создаёт обновлятор списка ботов"""
-        self.list_bot={}
-
-
-    def update(self)->None:
-        """Обновляет список ботов"""
-        def read(req: Any):
-            print(req.text)
-            document["bot_list"] = req.text
-        print(f"bot_list_update click, ev={event}!")
-        ajax.get("/bot_list/update", oncomplete=read)
-
-    def render(self)->None:
-        """Показывает список ботов"""
-        pass

+ 8 - 4
server/serv_web/serv_web.go

@@ -101,7 +101,7 @@ func НовСервВеб(серв types.ИСервер) (*СервВеб, error
 	// 	CacheDuration: 30 * time.Second,
 	// 	MaxAge:        3600,
 	// })
-	сам.роутер.Get("/list_bot/get", сам.getBotList)
+	сам.роутер.Get("/list_bot/get", сам.гетСписБот)
 	сам.роутер.Post("/list_bot/add", сам.постБотНов)
 	return сам, nil
 }
@@ -162,8 +162,12 @@ func (сам *СервВеб) постБотНов(кнт *fiber.Ctx) error {
 	return nil
 }
 
-// getBotList -- возвращает список ботов
-func (сам *СервВеб) getBotList(кнт *fiber.Ctx) error {
+// гетСписБот -- возвращает список ботов
+func (сам *СервВеб) гетСписБот(кнт *fiber.Ctx) error {
 	списБот := сам.серв.ServBots().ListBot()
-	return кнт.JSON(списБот)
+	рез := []string{}
+	for _, бот := range списБот {
+		рез = append(рез, бот.Имя())
+	}
+	return кнт.JSON(рез)
 }

+ 222 - 0
venv/lib/python3.11/site-packages/_distutils_hack/__init__.py

@@ -0,0 +1,222 @@
+# don't import any costly modules
+import sys
+import os
+
+
+is_pypy = '__pypy__' in sys.builtin_module_names
+
+
+def warn_distutils_present():
+    if 'distutils' not in sys.modules:
+        return
+    if is_pypy and sys.version_info < (3, 7):
+        # PyPy for 3.6 unconditionally imports distutils, so bypass the warning
+        # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
+        return
+    import warnings
+
+    warnings.warn(
+        "Distutils was imported before Setuptools, but importing Setuptools "
+        "also replaces the `distutils` module in `sys.modules`. This may lead "
+        "to undesirable behaviors or errors. To avoid these issues, avoid "
+        "using distutils directly, ensure that setuptools is installed in the "
+        "traditional way (e.g. not an editable install), and/or make sure "
+        "that setuptools is always imported before distutils."
+    )
+
+
+def clear_distutils():
+    if 'distutils' not in sys.modules:
+        return
+    import warnings
+
+    warnings.warn("Setuptools is replacing distutils.")
+    mods = [
+        name
+        for name in sys.modules
+        if name == "distutils" or name.startswith("distutils.")
+    ]
+    for name in mods:
+        del sys.modules[name]
+
+
+def enabled():
+    """
+    Allow selection of distutils by environment variable.
+    """
+    which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local')
+    return which == 'local'
+
+
+def ensure_local_distutils():
+    import importlib
+
+    clear_distutils()
+
+    # With the DistutilsMetaFinder in place,
+    # perform an import to cause distutils to be
+    # loaded from setuptools._distutils. Ref #2906.
+    with shim():
+        importlib.import_module('distutils')
+
+    # check that submodules load as expected
+    core = importlib.import_module('distutils.core')
+    assert '_distutils' in core.__file__, core.__file__
+    assert 'setuptools._distutils.log' not in sys.modules
+
+
+def do_override():
+    """
+    Ensure that the local copy of distutils is preferred over stdlib.
+
+    See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
+    for more motivation.
+    """
+    if enabled():
+        warn_distutils_present()
+        ensure_local_distutils()
+
+
+class _TrivialRe:
+    def __init__(self, *patterns):
+        self._patterns = patterns
+
+    def match(self, string):
+        return all(pat in string for pat in self._patterns)
+
+
+class DistutilsMetaFinder:
+    def find_spec(self, fullname, path, target=None):
+        # optimization: only consider top level modules and those
+        # found in the CPython test suite.
+        if path is not None and not fullname.startswith('test.'):
+            return
+
+        method_name = 'spec_for_{fullname}'.format(**locals())
+        method = getattr(self, method_name, lambda: None)
+        return method()
+
+    def spec_for_distutils(self):
+        if self.is_cpython():
+            return
+
+        import importlib
+        import importlib.abc
+        import importlib.util
+
+        try:
+            mod = importlib.import_module('setuptools._distutils')
+        except Exception:
+            # There are a couple of cases where setuptools._distutils
+            # may not be present:
+            # - An older Setuptools without a local distutils is
+            #   taking precedence. Ref #2957.
+            # - Path manipulation during sitecustomize removes
+            #   setuptools from the path but only after the hook
+            #   has been loaded. Ref #2980.
+            # In either case, fall back to stdlib behavior.
+            return
+
+        class DistutilsLoader(importlib.abc.Loader):
+            def create_module(self, spec):
+                mod.__name__ = 'distutils'
+                return mod
+
+            def exec_module(self, module):
+                pass
+
+        return importlib.util.spec_from_loader(
+            'distutils', DistutilsLoader(), origin=mod.__file__
+        )
+
+    @staticmethod
+    def is_cpython():
+        """
+        Suppress supplying distutils for CPython (build and tests).
+        Ref #2965 and #3007.
+        """
+        return os.path.isfile('pybuilddir.txt')
+
+    def spec_for_pip(self):
+        """
+        Ensure stdlib distutils when running under pip.
+        See pypa/pip#8761 for rationale.
+        """
+        if self.pip_imported_during_build():
+            return
+        clear_distutils()
+        self.spec_for_distutils = lambda: None
+
+    @classmethod
+    def pip_imported_during_build(cls):
+        """
+        Detect if pip is being imported in a build script. Ref #2355.
+        """
+        import traceback
+
+        return any(
+            cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None)
+        )
+
+    @staticmethod
+    def frame_file_is_setup(frame):
+        """
+        Return True if the indicated frame suggests a setup.py file.
+        """
+        # some frames may not have __file__ (#2940)
+        return frame.f_globals.get('__file__', '').endswith('setup.py')
+
+    def spec_for_sensitive_tests(self):
+        """
+        Ensure stdlib distutils when running select tests under CPython.
+
+        python/cpython#91169
+        """
+        clear_distutils()
+        self.spec_for_distutils = lambda: None
+
+    sensitive_tests = (
+        [
+            'test.test_distutils',
+            'test.test_peg_generator',
+            'test.test_importlib',
+        ]
+        if sys.version_info < (3, 10)
+        else [
+            'test.test_distutils',
+        ]
+    )
+
+
+for name in DistutilsMetaFinder.sensitive_tests:
+    setattr(
+        DistutilsMetaFinder,
+        f'spec_for_{name}',
+        DistutilsMetaFinder.spec_for_sensitive_tests,
+    )
+
+
+DISTUTILS_FINDER = DistutilsMetaFinder()
+
+
+def add_shim():
+    DISTUTILS_FINDER in sys.meta_path or insert_shim()
+
+
+class shim:
+    def __enter__(self):
+        insert_shim()
+
+    def __exit__(self, exc, value, tb):
+        remove_shim()
+
+
+def insert_shim():
+    sys.meta_path.insert(0, DISTUTILS_FINDER)
+
+
+def remove_shim():
+    try:
+        sys.meta_path.remove(DISTUTILS_FINDER)
+    except ValueError:
+        pass

+ 1 - 0
venv/lib/python3.11/site-packages/_distutils_hack/override.py

@@ -0,0 +1 @@
+__import__('_distutils_hack').do_override()

+ 199 - 0
venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/CONTRIBUTORS.txt

@@ -0,0 +1,199 @@
+# This file is autocompleted by 'contributors-txt',
+# using the configuration in 'script/.contributors_aliases.json'.
+# Do not add new persons manually and only add information without
+# using '-' as the line first character.
+# Please verify that your change are stable if you modify manually.
+
+Ex-maintainers
+--------------
+- Claudiu Popa <pcmanticore@gmail.com>
+- Sylvain Thénault <thenault@gmail.com>
+- Torsten Marek <shlomme@gmail.com>
+
+
+Maintainers
+-----------
+- Pierre Sassoulas <pierre.sassoulas@gmail.com>
+- Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
+- Hippo91 <guillaume.peillex@gmail.com>
+- Marc Mueller <30130371+cdce8p@users.noreply.github.com>
+- Jacob Walls <jacobtylerwalls@gmail.com>
+- Bryce Guinta <bryce.paul.guinta@gmail.com>
+- Ceridwen <ceridwenv@gmail.com>
+- Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com>
+- Łukasz Rogalski <rogalski.91@gmail.com>
+- Florian Bruhin <me@the-compiler.org>
+- Ashley Whetter <ashley@awhetter.co.uk>
+- Dimitri Prybysh <dmand@yandex.ru>
+- Areveny <areveny@protonmail.com>
+
+
+Contributors
+------------
+- Emile Anclin <emile.anclin@logilab.fr>
+- Nick Drozd <nicholasdrozd@gmail.com>
+- Andrew Haigh <hello@nelf.in>
+- Julien Cristau <julien.cristau@logilab.fr>
+- David Liu <david@cs.toronto.edu>
+- Alexandre Fayolle <alexandre.fayolle@logilab.fr>
+- Eevee (Alex Munroe) <amunroe@yelp.com>
+- David Gilman <davidgilman1@gmail.com>
+- Julien Jehannet <julien.jehannet@logilab.fr>
+- Calen Pennington <calen.pennington@gmail.com>
+- Tushar Sadhwani <86737547+tushar-deepsource@users.noreply.github.com>
+- Hugo van Kemenade <hugovk@users.noreply.github.com>
+- Tim Martin <tim@asymptotic.co.uk>
+- Phil Schaf <flying-sheep@web.de>
+- Alex Hall <alex.mojaki@gmail.com>
+- Raphael Gaschignard <raphael@makeleaps.com>
+- Radosław Ganczarek <radoslaw@ganczarek.in>
+- Paligot Gérard <androwiiid@gmail.com>
+- Ioana Tagirta <ioana.tagirta@gmail.com>
+- Derek Gustafson <degustaf@gmail.com>
+- David Shea <dshea@redhat.com>
+- Daniel Harding <dharding@gmail.com>
+- Christian Clauss <cclauss@me.com>
+- Ville Skyttä <ville.skytta@iki.fi>
+- Rene Zhang <rz99@cornell.edu>
+- Philip Lorenz <philip@bithub.de>
+- Nicolas Chauvat <nicolas.chauvat@logilab.fr>
+- Michael K <michael-k@users.noreply.github.com>
+- Mario Corchero <mariocj89@gmail.com>
+- Marien Zwart <marienz@gentoo.org>
+- Laura Médioni <laura.medioni@logilab.fr>
+- James Addison <55152140+jayaddison@users.noreply.github.com>
+- FELD Boris <lothiraldan@gmail.com>
+- Enji Cooper <yaneurabeya@gmail.com>
+- Dani Alcala <112832187+clavedeluna@users.noreply.github.com>
+- Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
+- tristanlatr <19967168+tristanlatr@users.noreply.github.com>
+- emile@crater.logilab.fr <emile@crater.logilab.fr>
+- doranid <ddandd@gmail.com>
+- brendanator <brendan.maginnis@gmail.com>
+- Tomas Gavenciak <gavento@ucw.cz>
+- Tim Paine <t.paine154@gmail.com>
+- Thomas Hisch <t.hisch@gmail.com>
+- Stefan Scherfke <stefan@sofa-rockers.org>
+- Sergei Lebedev <185856+superbobry@users.noreply.github.com>
+- Saugat Pachhai (सौगात) <suagatchhetri@outlook.com>
+- Ram Rachum <ram@rachum.com>
+- Pierre-Yves David <pierre-yves.david@logilab.fr>
+- Peter Pentchev <roam@ringlet.net>
+- Peter Kolbus <peter.kolbus@gmail.com>
+- Omer Katz <omer.drow@gmail.com>
+- Moises Lopez <moylop260@vauxoo.com>
+- Michal Vasilek <michal@vasilek.cz>
+- Keichi Takahashi <keichi.t@me.com>
+- Kavins Singh <kavinsingh@hotmail.com>
+- Karthikeyan Singaravelan <tir.karthi@gmail.com>
+- Joshua Cannon <joshdcannon@gmail.com>
+- John Vandenberg <jayvdb@gmail.com>
+- Jacob Bogdanov <jacob@bogdanov.dev>
+- Google, Inc. <no-reply@google.com>
+- David Euresti <github@euresti.com>
+- David Douard <david.douard@logilab.fr>
+- David Cain <davidjosephcain@gmail.com>
+- Anthony Truchet <anthony.truchet@logilab.fr>
+- Anthony Sottile <asottile@umich.edu>
+- Alexander Shadchin <alexandr.shadchin@gmail.com>
+- wgehalo <wgehalo@gmail.com>
+- rr- <rr-@sakuya.pl>
+- raylu <lurayl@gmail.com>
+- plucury <plucury@gmail.com>
+- ostr00000 <ostr00000@gmail.com>
+- noah-weingarden <33741795+noah-weingarden@users.noreply.github.com>
+- nathannaveen <42319948+nathannaveen@users.noreply.github.com>
+- mathieui <mathieui@users.noreply.github.com>
+- markmcclain <markmcclain@users.noreply.github.com>
+- ioanatia <ioanatia@users.noreply.github.com>
+- grayjk <grayjk@gmail.com>
+- adam-grant-hendry <59346180+adam-grant-hendry@users.noreply.github.com>
+- Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
+- Zac Hatfield-Dodds <Zac-HD@users.noreply.github.com>
+- Vilnis Termanis <vilnis.termanis@iotics.com>
+- Valentin Valls <valentin.valls@esrf.fr>
+- Uilian Ries <uilianries@gmail.com>
+- Tomas Novak <ext.Tomas.Novak@skoda-auto.cz>
+- Thirumal Venkat <me@thirumal.in>
+- SupImDos <62866982+SupImDos@users.noreply.github.com>
+- Stanislav Levin <slev@altlinux.org>
+- Simon Hewitt <si@sjhewitt.co.uk>
+- Serhiy Storchaka <storchaka@gmail.com>
+- Roy Wright <roy@wright.org>
+- Robin Jarry <robin.jarry@6wind.com>
+- René Fritze <47802+renefritze@users.noreply.github.com>
+- Redoubts <Redoubts@users.noreply.github.com>
+- Philipp Hörist <philipp@hoerist.com>
+- Peter de Blanc <peter@standard.ai>
+- Peter Talley <peterctalley@gmail.com>
+- Ovidiu Sabou <ovidiu@sabou.org>
+- Nicolas Noirbent <nicolas@noirbent.fr>
+- Neil Girdhar <mistersheik@gmail.com>
+- Michał Masłowski <m.maslowski@clearcode.cc>
+- Mateusz Bysiek <mb@mbdev.pl>
+- Leandro T. C. Melo <ltcmelo@gmail.com>
+- Konrad Weihmann <kweihmann@outlook.com>
+- Kian Meng, Ang <kianmeng.ang@gmail.com>
+- Kai Mueller <15907922+kasium@users.noreply.github.com>
+- Jörg Thalheim <Mic92@users.noreply.github.com>
+- Jonathan Striebel <jstriebel@users.noreply.github.com>
+- John Belmonte <john@neggie.net>
+- Jeff Widman <jeff@jeffwidman.com>
+- Jeff Quast <contact@jeffquast.com>
+- Jarrad Hope <me@jarradhope.com>
+- Jared Garst <jgarst@users.noreply.github.com>
+- Jakub Wilk <jwilk@jwilk.net>
+- Iva Miholic <ivamiho@gmail.com>
+- Ionel Maries Cristian <contact@ionelmc.ro>
+- HoverHell <hoverhell@gmail.com>
+- HQupgradeHQ <18361586+HQupgradeHQ@users.noreply.github.com>
+- Grygorii Iermolenko <gyermolenko@gmail.com>
+- Gregory P. Smith <greg@krypto.org>
+- Giuseppe Scrivano <gscrivan@redhat.com>
+- Frédéric Chapoton <fchapoton2@gmail.com>
+- Francis Charette Migneault <francis.charette.migneault@gmail.com>
+- Felix Mölder <felix.moelder@uni-due.de>
+- Federico Bond <federicobond@gmail.com>
+- DudeNr33 <3929834+DudeNr33@users.noreply.github.com>
+- Dmitry Shachnev <mitya57@users.noreply.github.com>
+- Denis Laxalde <denis.laxalde@logilab.fr>
+- Deepyaman Datta <deepyaman.datta@utexas.edu>
+- David Poirier <david-poirier-csn@users.noreply.github.com>
+- Dave Hirschfeld <dave.hirschfeld@gmail.com>
+- Dave Baum <dbaum@google.com>
+- Daniel Martin <daniel.martin@crowdstrike.com>
+- Daniel Colascione <dancol@dancol.org>
+- Damien Baty <damien@damienbaty.com>
+- Craig Franklin <craigjfranklin@gmail.com>
+- Colin Kennedy <colinvfx@gmail.com>
+- Cole Robinson <crobinso@redhat.com>
+- Christoph Reiter <reiter.christoph@gmail.com>
+- Chris Philip <chrisp533@gmail.com>
+- BioGeek <jeroen.vangoey@gmail.com>
+- Bianca Power <30207144+biancapower@users.noreply.github.com>
+- Benjamin Elven <25181435+S3ntinelX@users.noreply.github.com>
+- Ben Elliston <bje@air.net.au>
+- Becker Awqatty <bawqatty@mide.com>
+- Batuhan Taskaya <isidentical@gmail.com>
+- BasPH <BasPH@users.noreply.github.com>
+- Azeem Bande-Ali <A.BandeAli@gmail.com>
+- Avram Lubkin <aviso@rockhopper.net>
+- Aru Sahni <arusahni@gmail.com>
+- Artsiom Kaval <lezeroq@gmail.com>
+- Anubhav <35621759+anubh-v@users.noreply.github.com>
+- Antoine Boellinger <aboellinger@hotmail.com>
+- Alphadelta14 <alpha@alphaservcomputing.solutions>
+- Alexander Scheel <alexander.m.scheel@gmail.com>
+- Alexander Presnyakov <flagist0@gmail.com>
+- Ahmed Azzaoui <ahmed.azzaoui@engie.com>
+
+Co-Author
+---------
+The following persons were credited manually but did not commit themselves
+under this name, or we did not manage to find their commits in the history.
+
+- François Mockers
+- platings
+- carl
+- alain lefroy
+- Mark Gius

+ 1 - 0
venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/INSTALLER

@@ -0,0 +1 @@
+pip

+ 508 - 0
venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/LICENSE

@@ -0,0 +1,508 @@
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James
+  Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!

+ 127 - 0
venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/METADATA

@@ -0,0 +1,127 @@
+Metadata-Version: 2.1
+Name: astroid
+Version: 2.15.7
+Summary: An abstract syntax tree for Python with inference support.
+License: LGPL-2.1-or-later
+Project-URL: Docs, https://pylint.readthedocs.io/projects/astroid/en/latest/
+Project-URL: Source Code, https://github.com/PyCQA/astroid
+Project-URL: Bug tracker, https://github.com/PyCQA/astroid/issues
+Project-URL: Discord server, https://discord.gg/Egy6P8AMB5
+Keywords: static code analysis,python,abstract syntax tree
+Classifier: Development Status :: 6 - Mature
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Software Development :: Quality Assurance
+Classifier: Topic :: Software Development :: Testing
+Requires-Python: >=3.7.2
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+License-File: CONTRIBUTORS.txt
+Requires-Dist: lazy-object-proxy >=1.4.0
+Requires-Dist: typed-ast <2.0,>=1.4.0 ; implementation_name == "cpython" and python_version < "3.8"
+Requires-Dist: wrapt <2,>=1.11 ; python_version < "3.11"
+Requires-Dist: typing-extensions >=4.0.0 ; python_version < "3.11"
+Requires-Dist: wrapt <2,>=1.14 ; python_version >= "3.11"
+
+Astroid
+=======
+
+.. image:: https://codecov.io/gh/PyCQA/astroid/branch/main/graph/badge.svg?token=Buxy4WptLb
+    :target: https://codecov.io/gh/PyCQA/astroid
+    :alt: Coverage badge from codecov
+
+.. image:: https://readthedocs.org/projects/astroid/badge/?version=latest
+    :target: http://astroid.readthedocs.io/en/latest/?badge=latest
+    :alt: Documentation Status
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+    :target: https://github.com/ambv/black
+
+.. image:: https://results.pre-commit.ci/badge/github/PyCQA/astroid/main.svg
+   :target: https://results.pre-commit.ci/latest/github/PyCQA/astroid/main
+   :alt: pre-commit.ci status
+
+.. |tidelift_logo| image:: https://raw.githubusercontent.com/PyCQA/astroid/main/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png
+   :width: 200
+   :alt: Tidelift
+
+.. list-table::
+   :widths: 10 100
+
+   * - |tidelift_logo|
+     - Professional support for astroid is available as part of the
+       `Tidelift Subscription`_.  Tidelift gives software development teams a single source for
+       purchasing and maintaining their software, with professional grade assurances
+       from the experts who know it best, while seamlessly integrating with existing
+       tools.
+
+.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-astroid?utm_source=pypi-astroid&utm_medium=referral&utm_campaign=readme
+
+
+
+What's this?
+------------
+
+The aim of this module is to provide a common base representation of
+python source code. It is currently the library powering pylint's capabilities.
+
+It provides a compatible representation which comes from the `_ast`
+module.  It rebuilds the tree generated by the builtin _ast module by
+recursively walking down the AST and building an extended ast. The new
+node classes have additional methods and attributes for different
+usages. They include some support for static inference and local name
+scopes. Furthermore, astroid can also build partial trees by inspecting living
+objects.
+
+
+Installation
+------------
+
+Extract the tarball, jump into the created directory and run::
+
+    pip install .
+
+
+If you want to do an editable installation, you can run::
+
+    pip install -e .
+
+
+If you have any questions, please mail the code-quality@python.org
+mailing list for support. See
+http://mail.python.org/mailman/listinfo/code-quality for subscription
+information and archives.
+
+Documentation
+-------------
+http://astroid.readthedocs.io/en/latest/
+
+
+Python Versions
+---------------
+
+astroid 2.0 is currently available for Python 3 only. If you want Python 2
+support, use an older version of astroid (though note that these versions
+are no longer supported).
+
+Test
+----
+
+Tests are in the 'test' subdirectory. To launch the whole tests suite, you can use
+either `tox` or `pytest`::
+
+    tox
+    pytest

+ 209 - 0
venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/RECORD

@@ -0,0 +1,209 @@
+astroid-2.15.7.dist-info/CONTRIBUTORS.txt,sha256=_R56rA3okHgrKxY42SDN2zOaFZV49Oks0iFtQ_tIIBs,8038
+astroid-2.15.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+astroid-2.15.7.dist-info/LICENSE,sha256=_qFr2p5zTeoNnI2fW5CYeO9BcWJjVDKWCf_889tCyyQ,26516
+astroid-2.15.7.dist-info/METADATA,sha256=XZoh9NUD_8Z-4t50nMq5eAWcYRxrQ9erbttSp0xlg48,4706
+astroid-2.15.7.dist-info/RECORD,,
+astroid-2.15.7.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
+astroid-2.15.7.dist-info/top_level.txt,sha256=HsdW4O2x7ZXRj6k-agi3RaQybGLobI3VSE-jt4vQUXM,8
+astroid/__init__.py,sha256=6_rvD7Q7fukmOmd3ScMedXuk2Z26jjElFqb8-dCRtqc,5104
+astroid/__pkginfo__.py,sha256=SCiVhvKqJqIj74Xr99YuyMFKAClpIgoR2mlYKgtn92A,274
+astroid/__pycache__/__init__.cpython-311.pyc,,
+astroid/__pycache__/__pkginfo__.cpython-311.pyc,,
+astroid/__pycache__/_ast.cpython-311.pyc,,
+astroid/__pycache__/_backport_stdlib_names.cpython-311.pyc,,
+astroid/__pycache__/_cache.cpython-311.pyc,,
+astroid/__pycache__/arguments.cpython-311.pyc,,
+astroid/__pycache__/astroid_manager.cpython-311.pyc,,
+astroid/__pycache__/bases.cpython-311.pyc,,
+astroid/__pycache__/builder.cpython-311.pyc,,
+astroid/__pycache__/const.cpython-311.pyc,,
+astroid/__pycache__/constraint.cpython-311.pyc,,
+astroid/__pycache__/context.cpython-311.pyc,,
+astroid/__pycache__/decorators.cpython-311.pyc,,
+astroid/__pycache__/exceptions.cpython-311.pyc,,
+astroid/__pycache__/filter_statements.cpython-311.pyc,,
+astroid/__pycache__/helpers.cpython-311.pyc,,
+astroid/__pycache__/inference.cpython-311.pyc,,
+astroid/__pycache__/inference_tip.cpython-311.pyc,,
+astroid/__pycache__/manager.cpython-311.pyc,,
+astroid/__pycache__/mixins.cpython-311.pyc,,
+astroid/__pycache__/modutils.cpython-311.pyc,,
+astroid/__pycache__/node_classes.cpython-311.pyc,,
+astroid/__pycache__/objects.cpython-311.pyc,,
+astroid/__pycache__/protocols.cpython-311.pyc,,
+astroid/__pycache__/raw_building.cpython-311.pyc,,
+astroid/__pycache__/rebuilder.cpython-311.pyc,,
+astroid/__pycache__/scoped_nodes.cpython-311.pyc,,
+astroid/__pycache__/test_utils.cpython-311.pyc,,
+astroid/__pycache__/transforms.cpython-311.pyc,,
+astroid/__pycache__/typing.cpython-311.pyc,,
+astroid/__pycache__/util.cpython-311.pyc,,
+astroid/_ast.py,sha256=fx6vCj7YJJRQSf3GQYRllJ9hRKPh9-QczU3crjt3IrA,4146
+astroid/_backport_stdlib_names.py,sha256=pmMEGwionQpfJdJaJomDF6Qc6peNMxChcOsNa8U2IDo,7016
+astroid/_cache.py,sha256=ZuO1Kl9HXaxoJ_hJXbzWqkMGmQR7CGf-R4cPen9odzM,786
+astroid/arguments.py,sha256=9GeVion6IpISnxQGyXU6uHxfJ_kuDXOCYm3Jv6SJNUg,12958
+astroid/astroid_manager.py,sha256=02l_ltGWEglvkoL_qMGmYxuHpDA5-ySPf0r1cGwIQqU,572
+astroid/bases.py,sha256=9GT3_gPrLdlS3zmvW3Nmv2X2pQcY5_vywz1MVky4ero,25594
+astroid/brain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+astroid/brain/__pycache__/__init__.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_argparse.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_attrs.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_boto3.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_builtin_inference.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_collections.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_crypt.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_ctypes.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_curses.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_dataclasses.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_dateutil.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_fstrings.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_functools.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_gi.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_hashlib.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_http.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_hypothesis.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_io.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_mechanize.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_multiprocessing.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_namedtuple_enum.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_nose.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_einsumfunc.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_umath.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_ma.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_ndarray.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_numpy_utils.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_pathlib.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_pkg_resources.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_pytest.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_qt.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_random.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_re.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_regex.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_responses.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_scipy_signal.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_signal.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_six.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_sqlalchemy.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_ssl.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_subprocess.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_threading.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_type.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_typing.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_unittest.cpython-311.pyc,,
+astroid/brain/__pycache__/brain_uuid.cpython-311.pyc,,
+astroid/brain/__pycache__/helpers.cpython-311.pyc,,
+astroid/brain/brain_argparse.py,sha256=ozU4z8-HTSX6tl6g2ysoqFGn6b8jK4Bt_NlAKl_jYyQ,1557
+astroid/brain/brain_attrs.py,sha256=6xcXIat61Jr8o4QghNPLEZl-aYaKbujaOQM2WaYdrKU,3038
+astroid/brain/brain_boto3.py,sha256=gZOkDoY1YZk24EpOMVQVj0MBuNfBWn_XfDmmlH4jyHU,1012
+astroid/brain/brain_builtin_inference.py,sha256=N-B5FxLAID8wztOTdiZEoc42qrQh5M8MrtSH4BaQwZs,34254
+astroid/brain/brain_collections.py,sha256=__vh_SUp3ymY68i_UYW1OMlAjDN-Hiv_NHdDQs98HV4,4416
+astroid/brain/brain_crypt.py,sha256=Zo66z94cQOL-HNOcAQSPNs_Ib_xNLLY9qEtxZ1VkbAM,863
+astroid/brain/brain_ctypes.py,sha256=55flnAIj_Th_eqqG1HRMDR5ctt74ww1vDsSF42F26Es,2660
+astroid/brain/brain_curses.py,sha256=XXyCeLASFFksO2zSbrZgB_AImGBlIGZ7Pgjdm04x-jg,3477
+astroid/brain/brain_dataclasses.py,sha256=81fglaiPlsUkbImjLpSIpdVCdmmZa8vYUcCKQFo9YAI,22089
+astroid/brain/brain_dateutil.py,sha256=1INt87VBa9Qt0DzEuI1r7cEtdyg5CvNDUIdVCv-j_mc,767
+astroid/brain/brain_fstrings.py,sha256=VU14-7tVsJWlZt0r3UMB8lPS-Bb72dyYcDvd1PmTPJM,2471
+astroid/brain/brain_functools.py,sha256=Tx8SfFhQO4Ch1XxUMRtgURwY03L_yg2RCM-r0krB4mM,6103
+astroid/brain/brain_gi.py,sha256=LYtvbe48qmyugTP31CcrqfM6X0ra_h6ntTBuKGCIrcM,7543
+astroid/brain/brain_hashlib.py,sha256=WE7fQJCA3Rp1ZFDIseQjhrLEcsYhOfrka2PcvvuZ6Tg,2821
+astroid/brain/brain_http.py,sha256=mYLcMdh_ka5d11IpdWg6WrG3R7R2wN4bKOLVG0W3vPk,10640
+astroid/brain/brain_hypothesis.py,sha256=K4LfRb2KT6H5B7oGxAXQLcJFl9bkeKfFW8IEceGAbfw,1732
+astroid/brain/brain_io.py,sha256=l499fbVi5HUbPWoyQn1xTHaPgRcf0hsKjjI90BdHzyQ,1526
+astroid/brain/brain_mechanize.py,sha256=45syVjTGklYjS469vPsuHmyic09OTZtcJuM7FEXcMYI,2646
+astroid/brain/brain_multiprocessing.py,sha256=htHZ3zSyv4eqh6c-AFRPt2GlyyPb4Jg8OQfAolojR8Q,3211
+astroid/brain/brain_namedtuple_enum.py,sha256=Lku2wOGCIHWOok8plTBGbG1iJpdgZO7EI9hPIo4vco4,22412
+astroid/brain/brain_nose.py,sha256=DCUwKzR14Se5tF-s54AMwFoRvhsHH_nf6agrh966VpQ,2320
+astroid/brain/brain_numpy_core_einsumfunc.py,sha256=CuZtZYZGDhPzV46l1xyNl_7RfMCaQ4bYyZgJ9f8KxcQ,825
+astroid/brain/brain_numpy_core_fromnumeric.py,sha256=3UFRtK5U51AVQNxawf9ElhFv2tizhj5RfyCSnPT3u54,732
+astroid/brain/brain_numpy_core_function_base.py,sha256=_k-WRQ_x0QEHhvr9IwuTLnpGoCYOVO0GAH7e3DyoHNQ,1298
+astroid/brain/brain_numpy_core_multiarray.py,sha256=NIxnvno-mhp-P6IKfyRaKOFtiHx8H9cEchjgx69oht0,4238
+astroid/brain/brain_numpy_core_numeric.py,sha256=VAc51Ud8QDujsh29e-RQK5SBcKW_jzjZIFmntCslCKU,1629
+astroid/brain/brain_numpy_core_numerictypes.py,sha256=HVJ_paQwjh2V8qz0Juei6b2ZRm_dmDJodiDKfAcE-mE,8546
+astroid/brain/brain_numpy_core_umath.py,sha256=2EhXmtiw8wQcdUtbnlLLTjHAOHpQ1c9BXHdc6kHJGbA,4893
+astroid/brain/brain_numpy_ma.py,sha256=1XHvZH1C3CEXTiRH3BJxpFiztgqBYEGd9JDFquGsUMk,896
+astroid/brain/brain_numpy_ndarray.py,sha256=HohawmatzpNMOJAhYbEPJGZdCTNmYO1Oz-IPxYDd5mM,8998
+astroid/brain/brain_numpy_random_mtrand.py,sha256=Wrpm9SX8kH0Xsk9b3qpR0rhs-MEb0I5aN4E-66wkLkM,3436
+astroid/brain/brain_numpy_utils.py,sha256=wgSRUgGdn8Io0AoTe6avll35oceRq2S6ZXqNviljvGI,2637
+astroid/brain/brain_pathlib.py,sha256=Ec8Xuo-4IIG2wH4875c8c2Xe_I0fqPbPqoKCJOttVlE,1542
+astroid/brain/brain_pkg_resources.py,sha256=W3Q4G6mYTYtJY9iWQv3vEO8_lpmLFAin9aQLtBRLxAc,2200
+astroid/brain/brain_pytest.py,sha256=suTDBHCJ_p4F7TGXjwVJAFtiAP-VlkFE2gncXjLRQKM,2223
+astroid/brain/brain_qt.py,sha256=qvexV4IFa4YKL0Nyo6qxmz_DfDDNwFVwBTJGADtVkiI,2808
+astroid/brain/brain_random.py,sha256=grXJrYL3rZpzm8SmuEnWkLwWNqWKtd5YmwbBoV6mjN4,2890
+astroid/brain/brain_re.py,sha256=aSU-HyEWsCrc1seiFohZw54ivcNtC-_AyiChJOSU-7Q,2870
+astroid/brain/brain_regex.py,sha256=UzW2lF2ctbdD8BX4fkC7PFtXDdGNUcdBKZx7sJiO4Z0,3362
+astroid/brain/brain_responses.py,sha256=Jn7jS8L26T5hnUwOcHX469FTkVs3_Yz8OiVRPAx3Bjo,1868
+astroid/brain/brain_scipy_signal.py,sha256=Mi93D6-j94tqhqDHCbabVs3_eDAhti88CL1_rOvnG_I,2276
+astroid/brain/brain_signal.py,sha256=18rOCgLVmM1rtc6BFYPx4qziSrfPt6CrP-A2fJyMWBY,3880
+astroid/brain/brain_six.py,sha256=xrTEKMqcm8VqsLvYKlkFKj5oyGF9GQgZsI5ZrPdxKU0,7594
+astroid/brain/brain_sqlalchemy.py,sha256=onKAxgKY-GYLe2v6PVK3APBn_z8txokEZfDcXt33GuE,1009
+astroid/brain/brain_ssl.py,sha256=8MdXhl9ZvYVe6U_gtSTtgnm_Dned2Ic9MbgfSj56RHU,6554
+astroid/brain/brain_subprocess.py,sha256=Kayri4dMqwtXbZTOhB0E05p2ckKc7hIGPt1jA3zv2xA,2996
+astroid/brain/brain_threading.py,sha256=rN-dQ_4Zo0AZOOiPQigxF3NEgmDC7DY-r0ujLN6W52c,870
+astroid/brain/brain_type.py,sha256=VUwQsehFIShvKrm98ywa5-gGz7eLwJnBHCpQdreGOGM,2483
+astroid/brain/brain_typing.py,sha256=S7fylWBVGnsJaR1yZPk5QTTIhKNPAEEzJRxzZIX-b4E,14543
+astroid/brain/brain_unittest.py,sha256=X_xqI70br7JgIrUiUwP4Hm9mQwCx9jBfvNW5h0wmp-0,1147
+astroid/brain/brain_uuid.py,sha256=t0fj_QeNzbPga03GS0YP92sjjEmxN04rlgZ4dt82JHE,667
+astroid/brain/helpers.py,sha256=qtThXG2zllaFm8Ril8Mjbb6yuRPUqy-elXz4Qu8UKX0,908
+astroid/builder.py,sha256=bkCDciVprfLz3vyg6BfoTaUMe34tRB1n0z3hCXvoyLE,18788
+astroid/const.py,sha256=-C9gZEVAgp83YpjnG4k6BKBlTkn22J3UeR59IzPzC-s,1095
+astroid/constraint.py,sha256=RgYVUe0CiEXeXB0iGDd896CEDgCHpwALdzZEFogagwo,5043
+astroid/context.py,sha256=BwdZndGFMeP9ldxt-c4LP21N61ZGCFjySNCASiGHWhw,5994
+astroid/decorators.py,sha256=4_SEOBUSxU60_7SaOkACXvBiiwGxYVd7E6YJdhB-5AY,10090
+astroid/exceptions.py,sha256=wcJFoxkuWUslQMsjjBsvQadlTl-CCLin4I5Mg9EA4XQ,13089
+astroid/filter_statements.py,sha256=zd55vGal1W6k1DZRsZoTQodtTZCtKrmebUVkECeoUS4,9643
+astroid/helpers.py,sha256=X4mvwstWUR0fyLWYVjdLsvlWn00VxFeAyX4wdxcJKzw,11336
+astroid/inference.py,sha256=YrvDKAhJ_hhhRV4Xjh6c2Uct_fl7BjE8qRcnRsIZflA,45395
+astroid/inference_tip.py,sha256=4ims3HAlCZwVybV1CyLxreDtPVNEnvta_OKb_ELAbyA,2888
+astroid/interpreter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+astroid/interpreter/__pycache__/__init__.cpython-311.pyc,,
+astroid/interpreter/__pycache__/dunder_lookup.cpython-311.pyc,,
+astroid/interpreter/__pycache__/objectmodel.cpython-311.pyc,,
+astroid/interpreter/_import/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+astroid/interpreter/_import/__pycache__/__init__.cpython-311.pyc,,
+astroid/interpreter/_import/__pycache__/spec.cpython-311.pyc,,
+astroid/interpreter/_import/__pycache__/util.cpython-311.pyc,,
+astroid/interpreter/_import/spec.py,sha256=P4YdYxV380GVSmThsAj352y1SG2udWYlWASnjZbC_Hs,16525
+astroid/interpreter/_import/util.py,sha256=b5ggIRSth_I5Uwv3s44jMwf3OlnFAWIV-57wX3XK9yM,4592
+astroid/interpreter/dunder_lookup.py,sha256=77YNmgp6l2sPA7UIaUpjaP6-4x26NP93IihPR5EEv-c,2172
+astroid/interpreter/objectmodel.py,sha256=SVtDFz0kAlHgkRTcIp4r3t2UEjeJLE2jXSxZdo8QhXw,31177
+astroid/manager.py,sha256=ithriGjnMq5lCLbrCSHyysFVHdnk1L9bLFYKRf9Hspg,17960
+astroid/mixins.py,sha256=4fuPIi4N5ucNbocjVrS5UpEc_6Jkn3gsYD--JG85TII,1182
+astroid/modutils.py,sha256=VNNEBLqFP6NHFYFtL6i7KA7EmPb1ck1QWyNTqRe6qP4,23508
+astroid/node_classes.py,sha256=XQtchZSdAhAUShDW7ja3OghiPt0dEorOBEgRGQwqFEI,1840
+astroid/nodes/__init__.py,sha256=Q7No61D_MH5TS6mlz1vwSOXRRI3Kap90AwCd8593-SU,5071
+astroid/nodes/__pycache__/__init__.cpython-311.pyc,,
+astroid/nodes/__pycache__/_base_nodes.cpython-311.pyc,,
+astroid/nodes/__pycache__/as_string.cpython-311.pyc,,
+astroid/nodes/__pycache__/const.cpython-311.pyc,,
+astroid/nodes/__pycache__/node_classes.cpython-311.pyc,,
+astroid/nodes/__pycache__/node_ng.cpython-311.pyc,,
+astroid/nodes/__pycache__/utils.cpython-311.pyc,,
+astroid/nodes/_base_nodes.py,sha256=oQ4ueaxb5-JAnG1w17lK8TRo4xs3KatHrY985M0qgss,7398
+astroid/nodes/as_string.py,sha256=XBJt4Yo-hXrxRF_wpyCbP9nYMmaBU8jrQfydd6KULo0,25043
+astroid/nodes/const.py,sha256=iyQn_v-wEzK6uXc-dEMoliPVSRy0NZ8T3XmvvPXLuP4,797
+astroid/nodes/node_classes.py,sha256=vmiMXFjaRS7o8ag4yShavJxfyd3CIh9xOfJiORxxQ2M,172738
+astroid/nodes/node_ng.py,sha256=F_FVTRUCfVPIDDLT5IVV_ckgy7harh1Oqpq9ADpjK_I,28232
+astroid/nodes/scoped_nodes/__init__.py,sha256=CRdThe_LiW7SgcJz3pHSL8oFqHKNIJWg1WkQOkws994,1219
+astroid/nodes/scoped_nodes/__pycache__/__init__.cpython-311.pyc,,
+astroid/nodes/scoped_nodes/__pycache__/mixin.cpython-311.pyc,,
+astroid/nodes/scoped_nodes/__pycache__/scoped_nodes.cpython-311.pyc,,
+astroid/nodes/scoped_nodes/__pycache__/utils.cpython-311.pyc,,
+astroid/nodes/scoped_nodes/mixin.py,sha256=mN3oqKpS2AWOtrBjHDDfReVO7OxAMhl_es-jzsn89Js,6917
+astroid/nodes/scoped_nodes/scoped_nodes.py,sha256=WF_xNXWopJsEAoyKYnoPSqabqgY0FTmSgCpQMWU6o4w,107214
+astroid/nodes/scoped_nodes/utils.py,sha256=Nnjdkcbm9zs72HcoV9b_hE6P-SLZ7MCB9urU-WUDNVg,1216
+astroid/nodes/utils.py,sha256=Uy6xoownLyWwDtuZ5qUpjbq19P2pUIcj0J04khbH5HY,423
+astroid/objects.py,sha256=8roM53sWwFzusAIvz3nOj0LaQEXdzJw0ANfb6mDMy5w,12757
+astroid/protocols.py,sha256=34wj4PKOVK0Nsi4WxJPnrHoYqPJze-JOW39LUOHwf-w,32976
+astroid/raw_building.py,sha256=TI55TaZL35Zi3bk2DThToKEQ8rMOhZCgAMayfktxbAM,22875
+astroid/rebuilder.py,sha256=0J-B2fW7JEEmBTKoGT3C_9r1vYYj9wCOD8wvMAmihPI,79731
+astroid/scoped_nodes.py,sha256=j51jEOiN1rkMqmrhvVtYje8d-EWaGbzrgCwvg_s4TE0,958
+astroid/test_utils.py,sha256=7wyVL8A8WYQXbix1mT05lh8yO4kXk1gRjCCuqwfV4f8,2434
+astroid/transforms.py,sha256=eg7b0lehQu_GsFHCsLdUsNNvbxYBGcTC0p7rF2EKIXc,3271
+astroid/typing.py,sha256=OTOZbQ7tHSMq1tlASsVgRMC90GTjFoVSCgevZarAwhI,1983
+astroid/util.py,sha256=T5ux2LDjSNv2et5blQuUU7xCBr1id-CeU8lorr-yAVo,4729

+ 5 - 0
venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/WHEEL

@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.41.2)
+Root-Is-Purelib: true
+Tag: py3-none-any
+

+ 1 - 0
venv/lib/python3.11/site-packages/astroid-2.15.7.dist-info/top_level.txt

@@ -0,0 +1 @@
+astroid

+ 198 - 0
venv/lib/python3.11/site-packages/astroid/__init__.py

@@ -0,0 +1,198 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Python Abstract Syntax Tree New Generation.
+
+The aim of this module is to provide a common base representation of
+python source code for projects such as pychecker, pyreverse,
+pylint... Well, actually the development of this library is essentially
+governed by pylint's needs.
+
+It mimics the class defined in the python's _ast module with some
+additional methods and attributes. New nodes instances are not fully
+compatible with python's _ast.
+
+Instance attributes are added by a
+builder object, which can either generate extended ast (let's call
+them astroid ;) by visiting an existent ast tree or by inspecting living
+object. Methods are added by monkey patching ast classes.
+
+Main modules are:
+
+* nodes and scoped_nodes for more information about methods and
+  attributes added to different node classes
+
+* the manager contains a high level object to get astroid trees from
+  source files and living objects. It maintains a cache of previously
+  constructed tree for quick access
+
+* builder contains the class responsible to build astroid trees
+"""
+
+import functools
+import tokenize
+from importlib import import_module
+
+# isort: off
+# We have an isort: off on '__version__' because the packaging need to access
+# the version before the dependencies are installed (in particular 'wrapt'
+# that is imported in astroid.inference)
+from astroid.__pkginfo__ import __version__, version
+from astroid.nodes import node_classes, scoped_nodes
+
+# isort: on
+
+from astroid import inference, raw_building
+from astroid.astroid_manager import MANAGER
+from astroid.bases import BaseInstance, BoundMethod, Instance, UnboundMethod
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import extract_node, parse
+from astroid.const import BRAIN_MODULES_DIRECTORY, PY310_PLUS, Context, Del, Load, Store
+from astroid.exceptions import (
+    AstroidBuildingError,
+    AstroidBuildingException,
+    AstroidError,
+    AstroidImportError,
+    AstroidIndexError,
+    AstroidSyntaxError,
+    AstroidTypeError,
+    AstroidValueError,
+    AttributeInferenceError,
+    BinaryOperationError,
+    DuplicateBasesError,
+    InconsistentMroError,
+    InferenceError,
+    InferenceOverwriteError,
+    MroError,
+    NameInferenceError,
+    NoDefault,
+    NotFoundError,
+    OperationError,
+    ParentMissingError,
+    ResolveError,
+    StatementMissing,
+    SuperArgumentTypeError,
+    SuperError,
+    TooManyLevelsError,
+    UnaryOperationError,
+    UnresolvableName,
+    UseInferenceDefault,
+)
+from astroid.inference_tip import _inference_tip_cached, inference_tip
+from astroid.objects import ExceptionInstance
+
+# isort: off
+# It's impossible to import from astroid.nodes with a wildcard, because
+# there is a cyclic import that prevent creating an __all__ in astroid/nodes
+# and we need astroid/scoped_nodes and astroid/node_classes to work. So
+# importing with a wildcard would clash with astroid/nodes/scoped_nodes
+# and astroid/nodes/node_classes.
+from astroid.nodes import (  # pylint: disable=redefined-builtin (Ellipsis)
+    CONST_CLS,
+    AnnAssign,
+    Arguments,
+    Assert,
+    Assign,
+    AssignAttr,
+    AssignName,
+    AsyncFor,
+    AsyncFunctionDef,
+    AsyncWith,
+    Attribute,
+    AugAssign,
+    Await,
+    BinOp,
+    BoolOp,
+    Break,
+    Call,
+    ClassDef,
+    Compare,
+    Comprehension,
+    ComprehensionScope,
+    Const,
+    Continue,
+    Decorators,
+    DelAttr,
+    Delete,
+    DelName,
+    Dict,
+    DictComp,
+    DictUnpack,
+    Ellipsis,
+    EmptyNode,
+    EvaluatedObject,
+    ExceptHandler,
+    Expr,
+    ExtSlice,
+    For,
+    FormattedValue,
+    FunctionDef,
+    GeneratorExp,
+    Global,
+    If,
+    IfExp,
+    Import,
+    ImportFrom,
+    Index,
+    JoinedStr,
+    Keyword,
+    Lambda,
+    List,
+    ListComp,
+    Match,
+    MatchAs,
+    MatchCase,
+    MatchClass,
+    MatchMapping,
+    MatchOr,
+    MatchSequence,
+    MatchSingleton,
+    MatchStar,
+    MatchValue,
+    Module,
+    Name,
+    NamedExpr,
+    NodeNG,
+    Nonlocal,
+    Pass,
+    Raise,
+    Return,
+    Set,
+    SetComp,
+    Slice,
+    Starred,
+    Subscript,
+    TryExcept,
+    TryFinally,
+    TryStar,
+    Tuple,
+    UnaryOp,
+    Unknown,
+    While,
+    With,
+    Yield,
+    YieldFrom,
+    are_exclusive,
+    builtin_lookup,
+    unpack_infer,
+    function_to_method,
+)
+
+# isort: on
+
+from astroid.util import Uninferable
+
+# Performance hack for tokenize. See https://bugs.python.org/issue43014
+# Adapted from https://github.com/PyCQA/pycodestyle/pull/993
+if (
+    not PY310_PLUS
+    and callable(getattr(tokenize, "_compile", None))
+    and getattr(tokenize._compile, "__wrapped__", None) is None  # type: ignore[attr-defined]
+):
+    tokenize._compile = functools.lru_cache()(tokenize._compile)  # type: ignore[attr-defined]
+
+# load brain plugins
+for module in BRAIN_MODULES_DIRECTORY.iterdir():
+    if module.suffix == ".py":
+        import_module(f"astroid.brain.{module.stem}")

+ 6 - 0
venv/lib/python3.11/site-packages/astroid/__pkginfo__.py

@@ -0,0 +1,6 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+__version__ = "2.15.7"
+version = __version__

+ 141 - 0
venv/lib/python3.11/site-packages/astroid/_ast.py

@@ -0,0 +1,141 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+import ast
+import sys
+import types
+from collections.abc import Callable
+from functools import partial
+from typing import NamedTuple
+
+from astroid.const import PY38_PLUS, Context
+
+if sys.version_info >= (3, 8):
+    # On Python 3.8, typed_ast was merged back into `ast`
+    _ast_py3: types.ModuleType | None = ast
+else:
+    try:
+        import typed_ast.ast3 as _ast_py3
+    except ImportError:
+        _ast_py3 = None
+
+
+class FunctionType(NamedTuple):
+    argtypes: list[ast.expr]
+    returns: ast.expr
+
+
+class ParserModule(NamedTuple):
+    module: types.ModuleType
+    unary_op_classes: dict[type[ast.unaryop], str]
+    cmp_op_classes: dict[type[ast.cmpop], str]
+    bool_op_classes: dict[type[ast.boolop], str]
+    bin_op_classes: dict[type[ast.operator], str]
+    context_classes: dict[type[ast.expr_context], Context]
+
+    def parse(self, string: str, type_comments: bool = True) -> ast.Module:
+        parse_func: Callable[[str], ast.Module]
+        if self.module is _ast_py3:
+            if PY38_PLUS:
+                parse_func = partial(self.module.parse, type_comments=type_comments)
+            else:
+                parse_func = partial(
+                    self.module.parse, feature_version=sys.version_info.minor
+                )
+        else:
+            parse_func = self.module.parse
+        return parse_func(string)
+
+
+def parse_function_type_comment(type_comment: str) -> FunctionType | None:
+    """Given a correct type comment, obtain a FunctionType object."""
+    if _ast_py3 is None:
+        return None
+
+    func_type = _ast_py3.parse(type_comment, "<type_comment>", "func_type")  # type: ignore[attr-defined]
+    return FunctionType(argtypes=func_type.argtypes, returns=func_type.returns)
+
+
+def get_parser_module(type_comments: bool = True) -> ParserModule:
+    parser_module = ast
+    if type_comments and _ast_py3:
+        parser_module = _ast_py3
+
+    unary_op_classes = _unary_operators_from_module(parser_module)
+    cmp_op_classes = _compare_operators_from_module(parser_module)
+    bool_op_classes = _bool_operators_from_module(parser_module)
+    bin_op_classes = _binary_operators_from_module(parser_module)
+    context_classes = _contexts_from_module(parser_module)
+
+    return ParserModule(
+        parser_module,
+        unary_op_classes,
+        cmp_op_classes,
+        bool_op_classes,
+        bin_op_classes,
+        context_classes,
+    )
+
+
+def _unary_operators_from_module(
+    module: types.ModuleType,
+) -> dict[type[ast.unaryop], str]:
+    return {module.UAdd: "+", module.USub: "-", module.Not: "not", module.Invert: "~"}
+
+
+def _binary_operators_from_module(
+    module: types.ModuleType,
+) -> dict[type[ast.operator], str]:
+    binary_operators = {
+        module.Add: "+",
+        module.BitAnd: "&",
+        module.BitOr: "|",
+        module.BitXor: "^",
+        module.Div: "/",
+        module.FloorDiv: "//",
+        module.MatMult: "@",
+        module.Mod: "%",
+        module.Mult: "*",
+        module.Pow: "**",
+        module.Sub: "-",
+        module.LShift: "<<",
+        module.RShift: ">>",
+    }
+    return binary_operators
+
+
+def _bool_operators_from_module(
+    module: types.ModuleType,
+) -> dict[type[ast.boolop], str]:
+    return {module.And: "and", module.Or: "or"}
+
+
+def _compare_operators_from_module(
+    module: types.ModuleType,
+) -> dict[type[ast.cmpop], str]:
+    return {
+        module.Eq: "==",
+        module.Gt: ">",
+        module.GtE: ">=",
+        module.In: "in",
+        module.Is: "is",
+        module.IsNot: "is not",
+        module.Lt: "<",
+        module.LtE: "<=",
+        module.NotEq: "!=",
+        module.NotIn: "not in",
+    }
+
+
+def _contexts_from_module(
+    module: types.ModuleType,
+) -> dict[type[ast.expr_context], Context]:
+    return {
+        module.Load: Context.Load,
+        module.Store: Context.Store,
+        module.Del: Context.Del,
+        module.Param: Context.Store,
+    }

+ 356 - 0
venv/lib/python3.11/site-packages/astroid/_backport_stdlib_names.py

@@ -0,0 +1,356 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""
+Shim to support Python versions < 3.10 that don't have sys.stdlib_module_names
+
+These values were created by cherry-picking the commits from
+https://bugs.python.org/issue42955 into each version, but may be updated
+manually if changes are needed.
+"""
+
+import sys
+
+# TODO: Remove this file when Python 3.9 is no longer supported
+
+PY_3_7 = frozenset(
+    {
+        "__future__",
+        "_abc",
+        "_ast",
+        "_asyncio",
+        "_bisect",
+        "_blake2",
+        "_bootlocale",
+        "_bz2",
+        "_codecs",
+        "_codecs_cn",
+        "_codecs_hk",
+        "_codecs_iso2022",
+        "_codecs_jp",
+        "_codecs_kr",
+        "_codecs_tw",
+        "_collections",
+        "_collections_abc",
+        "_compat_pickle",
+        "_compression",
+        "_contextvars",
+        "_crypt",
+        "_csv",
+        "_ctypes",
+        "_curses",
+        "_curses_panel",
+        "_datetime",
+        "_dbm",
+        "_decimal",
+        "_dummy_thread",
+        "_elementtree",
+        "_functools",
+        "_gdbm",
+        "_hashlib",
+        "_heapq",
+        "_imp",
+        "_io",
+        "_json",
+        "_locale",
+        "_lsprof",
+        "_lzma",
+        "_markupbase",
+        "_md5",
+        "_msi",
+        "_multibytecodec",
+        "_multiprocessing",
+        "_opcode",
+        "_operator",
+        "_osx_support",
+        "_pickle",
+        "_posixsubprocess",
+        "_py_abc",
+        "_pydecimal",
+        "_pyio",
+        "_queue",
+        "_random",
+        "_sha1",
+        "_sha256",
+        "_sha3",
+        "_sha512",
+        "_signal",
+        "_sitebuiltins",
+        "_socket",
+        "_sqlite3",
+        "_sre",
+        "_ssl",
+        "_stat",
+        "_string",
+        "_strptime",
+        "_struct",
+        "_symtable",
+        "_thread",
+        "_threading_local",
+        "_tkinter",
+        "_tracemalloc",
+        "_uuid",
+        "_warnings",
+        "_weakref",
+        "_weakrefset",
+        "_winapi",
+        "abc",
+        "aifc",
+        "antigravity",
+        "argparse",
+        "array",
+        "ast",
+        "asynchat",
+        "asyncio",
+        "asyncore",
+        "atexit",
+        "audioop",
+        "base64",
+        "bdb",
+        "binascii",
+        "binhex",
+        "bisect",
+        "builtins",
+        "bz2",
+        "cProfile",
+        "calendar",
+        "cgi",
+        "cgitb",
+        "chunk",
+        "cmath",
+        "cmd",
+        "code",
+        "codecs",
+        "codeop",
+        "collections",
+        "colorsys",
+        "compileall",
+        "concurrent",
+        "configparser",
+        "contextlib",
+        "contextvars",
+        "copy",
+        "copyreg",
+        "crypt",
+        "csv",
+        "ctypes",
+        "curses",
+        "dataclasses",
+        "datetime",
+        "dbm",
+        "decimal",
+        "difflib",
+        "dis",
+        "distutils",
+        "doctest",
+        "dummy_threading",
+        "email",
+        "encodings",
+        "ensurepip",
+        "enum",
+        "errno",
+        "faulthandler",
+        "fcntl",
+        "filecmp",
+        "fileinput",
+        "fnmatch",
+        "formatter",
+        "fractions",
+        "ftplib",
+        "functools",
+        "gc",
+        "genericpath",
+        "getopt",
+        "getpass",
+        "gettext",
+        "glob",
+        "grp",
+        "gzip",
+        "hashlib",
+        "heapq",
+        "hmac",
+        "html",
+        "http",
+        "idlelib",
+        "imaplib",
+        "imghdr",
+        "imp",
+        "importlib",
+        "inspect",
+        "io",
+        "ipaddress",
+        "itertools",
+        "json",
+        "keyword",
+        "lib2to3",
+        "linecache",
+        "locale",
+        "logging",
+        "lzma",
+        "macpath",
+        "mailbox",
+        "mailcap",
+        "marshal",
+        "math",
+        "mimetypes",
+        "mmap",
+        "modulefinder",
+        "msilib",
+        "msvcrt",
+        "multiprocessing",
+        "netrc",
+        "nis",
+        "nntplib",
+        "nt",
+        "ntpath",
+        "nturl2path",
+        "numbers",
+        "opcode",
+        "operator",
+        "optparse",
+        "os",
+        "ossaudiodev",
+        "parser",
+        "pathlib",
+        "pdb",
+        "pickle",
+        "pickletools",
+        "pipes",
+        "pkgutil",
+        "platform",
+        "plistlib",
+        "poplib",
+        "posix",
+        "posixpath",
+        "pprint",
+        "profile",
+        "pstats",
+        "pty",
+        "pwd",
+        "py_compile",
+        "pyclbr",
+        "pydoc",
+        "pydoc_data",
+        "pyexpat",
+        "queue",
+        "quopri",
+        "random",
+        "re",
+        "readline",
+        "reprlib",
+        "resource",
+        "rlcompleter",
+        "runpy",
+        "sched",
+        "secrets",
+        "select",
+        "selectors",
+        "shelve",
+        "shlex",
+        "shutil",
+        "signal",
+        "site",
+        "smtpd",
+        "smtplib",
+        "sndhdr",
+        "socket",
+        "socketserver",
+        "spwd",
+        "sqlite3",
+        "sre_compile",
+        "sre_constants",
+        "sre_parse",
+        "ssl",
+        "stat",
+        "statistics",
+        "string",
+        "stringprep",
+        "struct",
+        "subprocess",
+        "sunau",
+        "symbol",
+        "symtable",
+        "sys",
+        "sysconfig",
+        "syslog",
+        "tabnanny",
+        "tarfile",
+        "telnetlib",
+        "tempfile",
+        "termios",
+        "textwrap",
+        "this",
+        "threading",
+        "time",
+        "timeit",
+        "tkinter",
+        "token",
+        "tokenize",
+        "trace",
+        "traceback",
+        "tracemalloc",
+        "tty",
+        "turtle",
+        "turtledemo",
+        "types",
+        "typing",
+        "unicodedata",
+        "unittest",
+        "urllib",
+        "uu",
+        "uuid",
+        "venv",
+        "warnings",
+        "wave",
+        "weakref",
+        "webbrowser",
+        "winreg",
+        "winsound",
+        "wsgiref",
+        "xdrlib",
+        "xml",
+        "xmlrpc",
+        "zipapp",
+        "zipfile",
+        "zipimport",
+        "zlib",
+    }
+)
+
+PY_3_8 = frozenset(
+    PY_3_7
+    - {
+        "macpath",
+    }
+    | {
+        "_posixshmem",
+        "_statistics",
+        "_xxsubinterpreters",
+    }
+)
+
+PY_3_9 = frozenset(
+    PY_3_8
+    - {
+        "_dummy_thread",
+        "dummy_threading",
+    }
+    | {
+        "_aix_support",
+        "_bootsubprocess",
+        "_peg_parser",
+        "_zoneinfo",
+        "graphlib",
+        "zoneinfo",
+    }
+)
+
+if sys.version_info[:2] == (3, 7):
+    stdlib_module_names = PY_3_7
+elif sys.version_info[:2] == (3, 8):
+    stdlib_module_names = PY_3_8
+elif sys.version_info[:2] == (3, 9):
+    stdlib_module_names = PY_3_9
+else:
+    raise AssertionError("This module is only intended as a backport for Python <= 3.9")

+ 26 - 0
venv/lib/python3.11/site-packages/astroid/_cache.py

@@ -0,0 +1,26 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+from typing import Any
+
+
+class CacheManager:
+    """Manager of caches, to be used as a singleton."""
+
+    def __init__(self) -> None:
+        self.dict_caches: list[dict[Any, Any]] = []
+
+    def clear_all_caches(self) -> None:
+        """Clear all caches."""
+        for dict_cache in self.dict_caches:
+            dict_cache.clear()
+
+    def add_dict_cache(self, cache: dict[Any, Any]) -> None:
+        """Add a dictionary cache to the manager."""
+        self.dict_caches.append(cache)
+
+
+CACHE_MANAGER = CacheManager()

+ 309 - 0
venv/lib/python3.11/site-packages/astroid/arguments.py

@@ -0,0 +1,309 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+from astroid import nodes
+from astroid.bases import Instance
+from astroid.context import CallContext, InferenceContext
+from astroid.exceptions import InferenceError, NoDefault
+from astroid.util import Uninferable, UninferableBase
+
+
+class CallSite:
+    """Class for understanding arguments passed into a call site.
+
+    It needs a call context, which contains the arguments and the
+    keyword arguments that were passed into a given call site.
+    In order to infer what an argument represents, call :meth:`infer_argument`
+    with the corresponding function node and the argument name.
+
+    :param callcontext:
+        An instance of :class:`astroid.context.CallContext`, that holds
+        the arguments for the call site.
+    :param argument_context_map:
+        Additional contexts per node, passed in from :attr:`astroid.context.Context.extra_context`
+    :param context:
+        An instance of :class:`astroid.context.Context`.
+    """
+
+    def __init__(
+        self,
+        callcontext: CallContext,
+        argument_context_map=None,
+        context: InferenceContext | None = None,
+    ):
+        if argument_context_map is None:
+            argument_context_map = {}
+        self.argument_context_map = argument_context_map
+        args = callcontext.args
+        keywords = callcontext.keywords
+        self.duplicated_keywords: set[str] = set()
+        self._unpacked_args = self._unpack_args(args, context=context)
+        self._unpacked_kwargs = self._unpack_keywords(keywords, context=context)
+
+        self.positional_arguments = [
+            arg for arg in self._unpacked_args if not isinstance(arg, UninferableBase)
+        ]
+        self.keyword_arguments = {
+            key: value
+            for key, value in self._unpacked_kwargs.items()
+            if not isinstance(value, UninferableBase)
+        }
+
+    @classmethod
+    def from_call(cls, call_node, context: InferenceContext | None = None):
+        """Get a CallSite object from the given Call node.
+
+        context will be used to force a single inference path.
+        """
+
+        # Determine the callcontext from the given `context` object if any.
+        context = context or InferenceContext()
+        callcontext = CallContext(call_node.args, call_node.keywords)
+        return cls(callcontext, context=context)
+
+    def has_invalid_arguments(self):
+        """Check if in the current CallSite were passed *invalid* arguments.
+
+        This can mean multiple things. For instance, if an unpacking
+        of an invalid object was passed, then this method will return True.
+        Other cases can be when the arguments can't be inferred by astroid,
+        for example, by passing objects which aren't known statically.
+        """
+        return len(self.positional_arguments) != len(self._unpacked_args)
+
+    def has_invalid_keywords(self) -> bool:
+        """Check if in the current CallSite were passed *invalid* keyword arguments.
+
+        For instance, unpacking a dictionary with integer keys is invalid
+        (**{1:2}), because the keys must be strings, which will make this
+        method to return True. Other cases where this might return True if
+        objects which can't be inferred were passed.
+        """
+        return len(self.keyword_arguments) != len(self._unpacked_kwargs)
+
+    def _unpack_keywords(self, keywords, context: InferenceContext | None = None):
+        values = {}
+        context = context or InferenceContext()
+        context.extra_context = self.argument_context_map
+        for name, value in keywords:
+            if name is None:
+                # Then it's an unpacking operation (**)
+                try:
+                    inferred = next(value.infer(context=context))
+                except InferenceError:
+                    values[name] = Uninferable
+                    continue
+                except StopIteration:
+                    continue
+
+                if not isinstance(inferred, nodes.Dict):
+                    # Not something we can work with.
+                    values[name] = Uninferable
+                    continue
+
+                for dict_key, dict_value in inferred.items:
+                    try:
+                        dict_key = next(dict_key.infer(context=context))
+                    except InferenceError:
+                        values[name] = Uninferable
+                        continue
+                    except StopIteration:
+                        continue
+                    if not isinstance(dict_key, nodes.Const):
+                        values[name] = Uninferable
+                        continue
+                    if not isinstance(dict_key.value, str):
+                        values[name] = Uninferable
+                        continue
+                    if dict_key.value in values:
+                        # The name is already in the dictionary
+                        values[dict_key.value] = Uninferable
+                        self.duplicated_keywords.add(dict_key.value)
+                        continue
+                    values[dict_key.value] = dict_value
+            else:
+                values[name] = value
+        return values
+
+    def _unpack_args(self, args, context: InferenceContext | None = None):
+        values = []
+        context = context or InferenceContext()
+        context.extra_context = self.argument_context_map
+        for arg in args:
+            if isinstance(arg, nodes.Starred):
+                try:
+                    inferred = next(arg.value.infer(context=context))
+                except InferenceError:
+                    values.append(Uninferable)
+                    continue
+                except StopIteration:
+                    continue
+
+                if isinstance(inferred, UninferableBase):
+                    values.append(Uninferable)
+                    continue
+                if not hasattr(inferred, "elts"):
+                    values.append(Uninferable)
+                    continue
+                values.extend(inferred.elts)
+            else:
+                values.append(arg)
+        return values
+
+    def infer_argument(self, funcnode, name, context):  # noqa: C901
+        """Infer a function argument value according to the call context.
+
+        Arguments:
+            funcnode: The function being called.
+            name: The name of the argument whose value is being inferred.
+            context: Inference context object
+        """
+        if name in self.duplicated_keywords:
+            raise InferenceError(
+                "The arguments passed to {func!r} have duplicate keywords.",
+                call_site=self,
+                func=funcnode,
+                arg=name,
+                context=context,
+            )
+
+        # Look into the keywords first, maybe it's already there.
+        try:
+            return self.keyword_arguments[name].infer(context)
+        except KeyError:
+            pass
+
+        # Too many arguments given and no variable arguments.
+        if len(self.positional_arguments) > len(funcnode.args.args):
+            if not funcnode.args.vararg and not funcnode.args.posonlyargs:
+                raise InferenceError(
+                    "Too many positional arguments "
+                    "passed to {func!r} that does "
+                    "not have *args.",
+                    call_site=self,
+                    func=funcnode,
+                    arg=name,
+                    context=context,
+                )
+
+        positional = self.positional_arguments[: len(funcnode.args.args)]
+        vararg = self.positional_arguments[len(funcnode.args.args) :]
+        argindex = funcnode.args.find_argname(name)[0]
+        kwonlyargs = {arg.name for arg in funcnode.args.kwonlyargs}
+        kwargs = {
+            key: value
+            for key, value in self.keyword_arguments.items()
+            if key not in kwonlyargs
+        }
+        # If there are too few positionals compared to
+        # what the function expects to receive, check to see
+        # if the missing positional arguments were passed
+        # as keyword arguments and if so, place them into the
+        # positional args list.
+        if len(positional) < len(funcnode.args.args):
+            for func_arg in funcnode.args.args:
+                if func_arg.name in kwargs:
+                    arg = kwargs.pop(func_arg.name)
+                    positional.append(arg)
+
+        if argindex is not None:
+            boundnode = getattr(context, "boundnode", None)
+            # 2. first argument of instance/class method
+            if argindex == 0 and funcnode.type in {"method", "classmethod"}:
+                # context.boundnode is None when an instance method is called with
+                # the class, e.g. MyClass.method(obj, ...). In this case, self
+                # is the first argument.
+                if boundnode is None and funcnode.type == "method" and positional:
+                    return positional[0].infer(context=context)
+                if boundnode is None:
+                    # XXX can do better ?
+                    boundnode = funcnode.parent.frame(future=True)
+
+                if isinstance(boundnode, nodes.ClassDef):
+                    # Verify that we're accessing a method
+                    # of the metaclass through a class, as in
+                    # `cls.metaclass_method`. In this case, the
+                    # first argument is always the class.
+                    method_scope = funcnode.parent.scope()
+                    if method_scope is boundnode.metaclass():
+                        return iter((boundnode,))
+
+                if funcnode.type == "method":
+                    if not isinstance(boundnode, Instance):
+                        boundnode = boundnode.instantiate_class()
+                    return iter((boundnode,))
+                if funcnode.type == "classmethod":
+                    return iter((boundnode,))
+            # if we have a method, extract one position
+            # from the index, so we'll take in account
+            # the extra parameter represented by `self` or `cls`
+            if funcnode.type in {"method", "classmethod"} and boundnode:
+                argindex -= 1
+            # 2. search arg index
+            try:
+                return self.positional_arguments[argindex].infer(context)
+            except IndexError:
+                pass
+
+        if funcnode.args.kwarg == name:
+            # It wants all the keywords that were passed into
+            # the call site.
+            if self.has_invalid_keywords():
+                raise InferenceError(
+                    "Inference failed to find values for all keyword arguments "
+                    "to {func!r}: {unpacked_kwargs!r} doesn't correspond to "
+                    "{keyword_arguments!r}.",
+                    keyword_arguments=self.keyword_arguments,
+                    unpacked_kwargs=self._unpacked_kwargs,
+                    call_site=self,
+                    func=funcnode,
+                    arg=name,
+                    context=context,
+                )
+            kwarg = nodes.Dict(
+                lineno=funcnode.args.lineno,
+                col_offset=funcnode.args.col_offset,
+                parent=funcnode.args,
+            )
+            kwarg.postinit(
+                [(nodes.const_factory(key), value) for key, value in kwargs.items()]
+            )
+            return iter((kwarg,))
+        if funcnode.args.vararg == name:
+            # It wants all the args that were passed into
+            # the call site.
+            if self.has_invalid_arguments():
+                raise InferenceError(
+                    "Inference failed to find values for all positional "
+                    "arguments to {func!r}: {unpacked_args!r} doesn't "
+                    "correspond to {positional_arguments!r}.",
+                    positional_arguments=self.positional_arguments,
+                    unpacked_args=self._unpacked_args,
+                    call_site=self,
+                    func=funcnode,
+                    arg=name,
+                    context=context,
+                )
+            args = nodes.Tuple(
+                lineno=funcnode.args.lineno,
+                col_offset=funcnode.args.col_offset,
+                parent=funcnode.args,
+            )
+            args.postinit(vararg)
+            return iter((args,))
+
+        # Check if it's a default parameter.
+        try:
+            return funcnode.args.default_value(name).infer(context)
+        except NoDefault:
+            pass
+        raise InferenceError(
+            "No value found for argument {arg} to {func!r}",
+            call_site=self,
+            func=funcnode,
+            arg=name,
+            context=context,
+        )

+ 17 - 0
venv/lib/python3.11/site-packages/astroid/astroid_manager.py

@@ -0,0 +1,17 @@
+"""
+This file contain the global astroid MANAGER.
+
+It prevents a circular import that happened
+when the only possibility to import it was from astroid.__init__.py.
+
+This AstroidManager is a singleton/borg so it's possible to instantiate an
+AstroidManager() directly.
+"""
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from astroid.manager import AstroidManager
+
+MANAGER = AstroidManager()

+ 711 - 0
venv/lib/python3.11/site-packages/astroid/bases.py

@@ -0,0 +1,711 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""This module contains base classes and functions for the nodes and some
+inference utils.
+"""
+from __future__ import annotations
+
+import collections
+import collections.abc
+import sys
+from collections.abc import Sequence
+from typing import TYPE_CHECKING, Any, ClassVar
+
+from astroid import decorators, nodes
+from astroid.const import PY310_PLUS
+from astroid.context import (
+    CallContext,
+    InferenceContext,
+    bind_context_to_node,
+    copy_context,
+)
+from astroid.exceptions import (
+    AstroidTypeError,
+    AttributeInferenceError,
+    InferenceError,
+    NameInferenceError,
+)
+from astroid.typing import InferBinaryOp, InferenceErrorInfo, InferenceResult
+from astroid.util import Uninferable, UninferableBase, lazy_descriptor, lazy_import
+
+if sys.version_info >= (3, 8):
+    from typing import Literal
+else:
+    from typing_extensions import Literal
+
+if TYPE_CHECKING:
+    from astroid.constraint import Constraint
+
+objectmodel = lazy_import("interpreter.objectmodel")
+helpers = lazy_import("helpers")
+manager = lazy_import("manager")
+
+
+# TODO: check if needs special treatment
+BOOL_SPECIAL_METHOD = "__bool__"
+BUILTINS = "builtins"  # TODO Remove in 2.8
+
+PROPERTIES = {"builtins.property", "abc.abstractproperty"}
+if PY310_PLUS:
+    PROPERTIES.add("enum.property")
+
+# List of possible property names. We use this list in order
+# to see if a method is a property or not. This should be
+# pretty reliable and fast, the alternative being to check each
+# decorator to see if its a real property-like descriptor, which
+# can be too complicated.
+# Also, these aren't qualified, because each project can
+# define them, we shouldn't expect to know every possible
+# property-like decorator!
+POSSIBLE_PROPERTIES = {
+    "cached_property",
+    "cachedproperty",
+    "lazyproperty",
+    "lazy_property",
+    "reify",
+    "lazyattribute",
+    "lazy_attribute",
+    "LazyProperty",
+    "lazy",
+    "cache_readonly",
+    "DynamicClassAttribute",
+}
+
+
+def _is_property(meth, context: InferenceContext | None = None) -> bool:
+    decoratornames = meth.decoratornames(context=context)
+    if PROPERTIES.intersection(decoratornames):
+        return True
+    stripped = {
+        name.split(".")[-1]
+        for name in decoratornames
+        if not isinstance(name, UninferableBase)
+    }
+    if any(name in stripped for name in POSSIBLE_PROPERTIES):
+        return True
+
+    # Lookup for subclasses of *property*
+    if not meth.decorators:
+        return False
+    for decorator in meth.decorators.nodes or ():
+        inferred = helpers.safe_infer(decorator, context=context)
+        if inferred is None or isinstance(inferred, UninferableBase):
+            continue
+        if inferred.__class__.__name__ == "ClassDef":
+            for base_class in inferred.bases:
+                if base_class.__class__.__name__ != "Name":
+                    continue
+                module, _ = base_class.lookup(base_class.name)
+                if module.name == "builtins" and base_class.name == "property":
+                    return True
+
+    return False
+
+
+class Proxy:
+    """A simple proxy object.
+
+    Note:
+
+    Subclasses of this object will need a custom __getattr__
+    if new instance attributes are created. See the Const class
+    """
+
+    _proxied: nodes.ClassDef | nodes.Lambda | Proxy | None = (
+        None  # proxied object may be set by class or by instance
+    )
+
+    def __init__(
+        self, proxied: nodes.ClassDef | nodes.Lambda | Proxy | None = None
+    ) -> None:
+        if proxied is None:
+            # This is a hack to allow calling this __init__ during bootstrapping of
+            # builtin classes and their docstrings.
+            # For Const, Generator, and UnionType nodes the _proxied attribute
+            # is set during bootstrapping
+            # as we first need to build the ClassDef that they can proxy.
+            # Thus, if proxied is None self should be a Const or Generator
+            # as that is the only way _proxied will be correctly set as a ClassDef.
+            assert isinstance(self, (nodes.Const, Generator, UnionType))
+        else:
+            self._proxied = proxied
+
+    def __getattr__(self, name):
+        if name == "_proxied":
+            return self.__class__._proxied
+        if name in self.__dict__:
+            return self.__dict__[name]
+        return getattr(self._proxied, name)
+
+    def infer(  # type: ignore[return]
+        self, context: InferenceContext | None = None, **kwargs: Any
+    ) -> collections.abc.Generator[InferenceResult, None, InferenceErrorInfo | None]:
+        yield self
+
+
+def _infer_stmts(
+    stmts: Sequence[nodes.NodeNG | UninferableBase | Instance],
+    context: InferenceContext | None,
+    frame: nodes.NodeNG | Instance | None = None,
+) -> collections.abc.Generator[InferenceResult, None, None]:
+    """Return an iterator on statements inferred by each statement in *stmts*."""
+    inferred = False
+    constraint_failed = False
+    if context is not None:
+        name = context.lookupname
+        context = context.clone()
+        constraints = context.constraints.get(name, {})
+    else:
+        name = None
+        constraints = {}
+        context = InferenceContext()
+
+    for stmt in stmts:
+        if isinstance(stmt, UninferableBase):
+            yield stmt
+            inferred = True
+            continue
+        # 'context' is always InferenceContext and Instances get '_infer_name' from ClassDef
+        context.lookupname = stmt._infer_name(frame, name)  # type: ignore[union-attr]
+        try:
+            stmt_constraints: set[Constraint] = set()
+            for constraint_stmt, potential_constraints in constraints.items():
+                if not constraint_stmt.parent_of(stmt):
+                    stmt_constraints.update(potential_constraints)
+            for inf in stmt.infer(context=context):
+                if all(constraint.satisfied_by(inf) for constraint in stmt_constraints):
+                    yield inf
+                    inferred = True
+                else:
+                    constraint_failed = True
+        except NameInferenceError:
+            continue
+        except InferenceError:
+            yield Uninferable
+            inferred = True
+
+    if not inferred and constraint_failed:
+        yield Uninferable
+    elif not inferred:
+        raise InferenceError(
+            "Inference failed for all members of {stmts!r}.",
+            stmts=stmts,
+            frame=frame,
+            context=context,
+        )
+
+
+def _infer_method_result_truth(instance, method_name, context):
+    # Get the method from the instance and try to infer
+    # its return's truth value.
+    meth = next(instance.igetattr(method_name, context=context), None)
+    if meth and hasattr(meth, "infer_call_result"):
+        if not meth.callable():
+            return Uninferable
+        try:
+            context.callcontext = CallContext(args=[], callee=meth)
+            for value in meth.infer_call_result(instance, context=context):
+                if isinstance(value, UninferableBase):
+                    return value
+                try:
+                    inferred = next(value.infer(context=context))
+                except StopIteration as e:
+                    raise InferenceError(context=context) from e
+                return inferred.bool_value()
+        except InferenceError:
+            pass
+    return Uninferable
+
+
+class BaseInstance(Proxy):
+    """An instance base class, which provides lookup methods for potential
+    instances.
+    """
+
+    special_attributes = None
+
+    def display_type(self) -> str:
+        return "Instance of"
+
+    def getattr(self, name, context: InferenceContext | None = None, lookupclass=True):
+        try:
+            values = self._proxied.instance_attr(name, context)
+        except AttributeInferenceError as exc:
+            if self.special_attributes and name in self.special_attributes:
+                return [self.special_attributes.lookup(name)]
+
+            if lookupclass:
+                # Class attributes not available through the instance
+                # unless they are explicitly defined.
+                return self._proxied.getattr(name, context, class_context=False)
+
+            raise AttributeInferenceError(
+                target=self, attribute=name, context=context
+            ) from exc
+        # since we've no context information, return matching class members as
+        # well
+        if lookupclass:
+            try:
+                return values + self._proxied.getattr(
+                    name, context, class_context=False
+                )
+            except AttributeInferenceError:
+                pass
+        return values
+
+    def igetattr(self, name, context: InferenceContext | None = None):
+        """Inferred getattr."""
+        if not context:
+            context = InferenceContext()
+        try:
+            context.lookupname = name
+            # avoid recursively inferring the same attr on the same class
+            if context.push(self._proxied):
+                raise InferenceError(
+                    message="Cannot infer the same attribute again",
+                    node=self,
+                    context=context,
+                )
+
+            # XXX frame should be self._proxied, or not ?
+            get_attr = self.getattr(name, context, lookupclass=False)
+            yield from _infer_stmts(
+                self._wrap_attr(get_attr, context), context, frame=self
+            )
+        except AttributeInferenceError:
+            try:
+                # fallback to class.igetattr since it has some logic to handle
+                # descriptors
+                # But only if the _proxied is the Class.
+                if self._proxied.__class__.__name__ != "ClassDef":
+                    raise
+                attrs = self._proxied.igetattr(name, context, class_context=False)
+                yield from self._wrap_attr(attrs, context)
+            except AttributeInferenceError as error:
+                raise InferenceError(**vars(error)) from error
+
+    def _wrap_attr(self, attrs, context: InferenceContext | None = None):
+        """Wrap bound methods of attrs in a InstanceMethod proxies."""
+        for attr in attrs:
+            if isinstance(attr, UnboundMethod):
+                if _is_property(attr):
+                    yield from attr.infer_call_result(self, context)
+                else:
+                    yield BoundMethod(attr, self)
+            elif hasattr(attr, "name") and attr.name == "<lambda>":
+                if attr.args.arguments and attr.args.arguments[0].name == "self":
+                    yield BoundMethod(attr, self)
+                    continue
+                yield attr
+            else:
+                yield attr
+
+    def infer_call_result(
+        self, caller: nodes.Call | Proxy, context: InferenceContext | None = None
+    ):
+        """Infer what a class instance is returning when called."""
+        context = bind_context_to_node(context, self)
+        inferred = False
+
+        # If the call is an attribute on the instance, we infer the attribute itself
+        if isinstance(caller, nodes.Call) and isinstance(caller.func, nodes.Attribute):
+            for res in self.igetattr(caller.func.attrname, context):
+                inferred = True
+                yield res
+
+        # Otherwise we infer the call to the __call__ dunder normally
+        for node in self._proxied.igetattr("__call__", context):
+            if isinstance(node, UninferableBase) or not node.callable():
+                continue
+            for res in node.infer_call_result(caller, context):
+                inferred = True
+                yield res
+        if not inferred:
+            raise InferenceError(node=self, caller=caller, context=context)
+
+
+class Instance(BaseInstance):
+    """A special node representing a class instance."""
+
+    _proxied: nodes.ClassDef
+
+    # pylint: disable=unnecessary-lambda
+    special_attributes = lazy_descriptor(lambda: objectmodel.InstanceModel())
+
+    def __init__(self, proxied: nodes.ClassDef | None) -> None:
+        super().__init__(proxied)
+
+    infer_binary_op: ClassVar[InferBinaryOp[Instance]]
+
+    def __repr__(self) -> str:
+        return "<Instance of {}.{} at 0x{}>".format(
+            self._proxied.root().name, self._proxied.name, id(self)
+        )
+
+    def __str__(self) -> str:
+        return f"Instance of {self._proxied.root().name}.{self._proxied.name}"
+
+    def callable(self) -> bool:
+        try:
+            self._proxied.getattr("__call__", class_context=False)
+            return True
+        except AttributeInferenceError:
+            return False
+
+    def pytype(self) -> str:
+        return self._proxied.qname()
+
+    def display_type(self) -> str:
+        return "Instance of"
+
+    def bool_value(self, context: InferenceContext | None = None):
+        """Infer the truth value for an Instance.
+
+        The truth value of an instance is determined by these conditions:
+
+           * if it implements __bool__ on Python 3 or __nonzero__
+             on Python 2, then its bool value will be determined by
+             calling this special method and checking its result.
+           * when this method is not defined, __len__() is called, if it
+             is defined, and the object is considered true if its result is
+             nonzero. If a class defines neither __len__() nor __bool__(),
+             all its instances are considered true.
+        """
+        context = context or InferenceContext()
+        context.boundnode = self
+
+        try:
+            result = _infer_method_result_truth(self, BOOL_SPECIAL_METHOD, context)
+        except (InferenceError, AttributeInferenceError):
+            # Fallback to __len__.
+            try:
+                result = _infer_method_result_truth(self, "__len__", context)
+            except (AttributeInferenceError, InferenceError):
+                return True
+        return result
+
+    def getitem(self, index, context: InferenceContext | None = None):
+        new_context = bind_context_to_node(context, self)
+        if not context:
+            context = new_context
+        method = next(self.igetattr("__getitem__", context=context), None)
+        # Create a new CallContext for providing index as an argument.
+        new_context.callcontext = CallContext(args=[index], callee=method)
+        if not isinstance(method, BoundMethod):
+            raise InferenceError(
+                "Could not find __getitem__ for {node!r}.", node=self, context=context
+            )
+        if len(method.args.arguments) != 2:  # (self, index)
+            raise AstroidTypeError(
+                "__getitem__ for {node!r} does not have correct signature",
+                node=self,
+                context=context,
+            )
+        return next(method.infer_call_result(self, new_context), None)
+
+
+class UnboundMethod(Proxy):
+    """A special node representing a method not bound to an instance."""
+
+    # pylint: disable=unnecessary-lambda
+    special_attributes = lazy_descriptor(lambda: objectmodel.UnboundMethodModel())
+
+    def __repr__(self) -> str:
+        frame = self._proxied.parent.frame(future=True)
+        return "<{} {} of {} at 0x{}".format(
+            self.__class__.__name__, self._proxied.name, frame.qname(), id(self)
+        )
+
+    def implicit_parameters(self) -> Literal[0]:
+        return 0
+
+    def is_bound(self) -> Literal[False]:
+        return False
+
+    def getattr(self, name, context: InferenceContext | None = None):
+        if name in self.special_attributes:
+            return [self.special_attributes.lookup(name)]
+        return self._proxied.getattr(name, context)
+
+    def igetattr(self, name, context: InferenceContext | None = None):
+        if name in self.special_attributes:
+            return iter((self.special_attributes.lookup(name),))
+        return self._proxied.igetattr(name, context)
+
+    def infer_call_result(self, caller, context):
+        """
+        The boundnode of the regular context with a function called
+        on ``object.__new__`` will be of type ``object``,
+        which is incorrect for the argument in general.
+        If no context is given the ``object.__new__`` call argument will
+        be correctly inferred except when inside a call that requires
+        the additional context (such as a classmethod) of the boundnode
+        to determine which class the method was called from
+        """
+
+        # If we're unbound method __new__ of a builtin, the result is an
+        # instance of the class given as first argument.
+        if self._proxied.name == "__new__":
+            qname = self._proxied.parent.frame(future=True).qname()
+            # Avoid checking builtins.type: _infer_type_new_call() does more validation
+            if qname.startswith("builtins.") and qname != "builtins.type":
+                return self._infer_builtin_new(caller, context)
+        return self._proxied.infer_call_result(caller, context)
+
+    def _infer_builtin_new(
+        self,
+        caller: nodes.Call,
+        context: InferenceContext,
+    ) -> collections.abc.Generator[
+        nodes.Const | Instance | UninferableBase, None, None
+    ]:
+        if not caller.args:
+            return
+        # Attempt to create a constant
+        if len(caller.args) > 1:
+            value = None
+            if isinstance(caller.args[1], nodes.Const):
+                value = caller.args[1].value
+            else:
+                inferred_arg = next(caller.args[1].infer(), None)
+                if isinstance(inferred_arg, nodes.Const):
+                    value = inferred_arg.value
+            if value is not None:
+                yield nodes.const_factory(value)
+                return
+
+        node_context = context.extra_context.get(caller.args[0])
+        for inferred in caller.args[0].infer(context=node_context):
+            if isinstance(inferred, UninferableBase):
+                yield inferred
+            if isinstance(inferred, nodes.ClassDef):
+                yield Instance(inferred)
+            raise InferenceError
+
+    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
+        return True
+
+
+class BoundMethod(UnboundMethod):
+    """A special node representing a method bound to an instance."""
+
+    # pylint: disable=unnecessary-lambda
+    special_attributes = lazy_descriptor(lambda: objectmodel.BoundMethodModel())
+
+    def __init__(self, proxy, bound):
+        super().__init__(proxy)
+        self.bound = bound
+
+    def implicit_parameters(self) -> Literal[0, 1]:
+        if self.name == "__new__":
+            # __new__ acts as a classmethod but the class argument is not implicit.
+            return 0
+        return 1
+
+    def is_bound(self) -> Literal[True]:
+        return True
+
+    def _infer_type_new_call(self, caller, context):  # noqa: C901
+        """Try to infer what type.__new__(mcs, name, bases, attrs) returns.
+
+        In order for such call to be valid, the metaclass needs to be
+        a subtype of ``type``, the name needs to be a string, the bases
+        needs to be a tuple of classes
+        """
+        # pylint: disable=import-outside-toplevel; circular import
+        from astroid.nodes import Pass
+
+        # Verify the metaclass
+        try:
+            mcs = next(caller.args[0].infer(context=context))
+        except StopIteration as e:
+            raise InferenceError(context=context) from e
+        if mcs.__class__.__name__ != "ClassDef":
+            # Not a valid first argument.
+            return None
+        if not mcs.is_subtype_of("builtins.type"):
+            # Not a valid metaclass.
+            return None
+
+        # Verify the name
+        try:
+            name = next(caller.args[1].infer(context=context))
+        except StopIteration as e:
+            raise InferenceError(context=context) from e
+        if name.__class__.__name__ != "Const":
+            # Not a valid name, needs to be a const.
+            return None
+        if not isinstance(name.value, str):
+            # Needs to be a string.
+            return None
+
+        # Verify the bases
+        try:
+            bases = next(caller.args[2].infer(context=context))
+        except StopIteration as e:
+            raise InferenceError(context=context) from e
+        if bases.__class__.__name__ != "Tuple":
+            # Needs to be a tuple.
+            return None
+        try:
+            inferred_bases = [next(elt.infer(context=context)) for elt in bases.elts]
+        except StopIteration as e:
+            raise InferenceError(context=context) from e
+        if any(base.__class__.__name__ != "ClassDef" for base in inferred_bases):
+            # All the bases needs to be Classes
+            return None
+
+        # Verify the attributes.
+        try:
+            attrs = next(caller.args[3].infer(context=context))
+        except StopIteration as e:
+            raise InferenceError(context=context) from e
+        if attrs.__class__.__name__ != "Dict":
+            # Needs to be a dictionary.
+            return None
+        cls_locals = collections.defaultdict(list)
+        for key, value in attrs.items:
+            try:
+                key = next(key.infer(context=context))
+            except StopIteration as e:
+                raise InferenceError(context=context) from e
+            try:
+                value = next(value.infer(context=context))
+            except StopIteration as e:
+                raise InferenceError(context=context) from e
+            # Ignore non string keys
+            if key.__class__.__name__ == "Const" and isinstance(key.value, str):
+                cls_locals[key.value].append(value)
+
+        # Build the class from now.
+        cls = mcs.__class__(
+            name=name.value,
+            lineno=caller.lineno,
+            col_offset=caller.col_offset,
+            parent=caller,
+        )
+        empty = Pass()
+        cls.postinit(
+            bases=bases.elts,
+            body=[empty],
+            decorators=[],
+            newstyle=True,
+            metaclass=mcs,
+            keywords=[],
+        )
+        cls.locals = cls_locals
+        return cls
+
+    def infer_call_result(self, caller, context: InferenceContext | None = None):
+        context = bind_context_to_node(context, self.bound)
+        if (
+            self.bound.__class__.__name__ == "ClassDef"
+            and self.bound.name == "type"
+            and self.name == "__new__"
+            and len(caller.args) == 4
+        ):
+            # Check if we have a ``type.__new__(mcs, name, bases, attrs)`` call.
+            new_cls = self._infer_type_new_call(caller, context)
+            if new_cls:
+                return iter((new_cls,))
+
+        return super().infer_call_result(caller, context)
+
+    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
+        return True
+
+
+class Generator(BaseInstance):
+    """A special node representing a generator.
+
+    Proxied class is set once for all in raw_building.
+    """
+
+    _proxied: nodes.ClassDef
+
+    special_attributes = lazy_descriptor(objectmodel.GeneratorModel)
+
+    def __init__(
+        self, parent=None, generator_initial_context: InferenceContext | None = None
+    ):
+        super().__init__()
+        self.parent = parent
+        self._call_context = copy_context(generator_initial_context)
+
+    @decorators.cached
+    def infer_yield_types(self):
+        yield from self.parent.infer_yield_result(self._call_context)
+
+    def callable(self) -> Literal[False]:
+        return False
+
+    def pytype(self) -> Literal["builtins.generator"]:
+        return "builtins.generator"
+
+    def display_type(self) -> str:
+        return "Generator"
+
+    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
+        return True
+
+    def __repr__(self) -> str:
+        return f"<Generator({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"
+
+    def __str__(self) -> str:
+        return f"Generator({self._proxied.name})"
+
+
+class AsyncGenerator(Generator):
+    """Special node representing an async generator."""
+
+    def pytype(self) -> Literal["builtins.async_generator"]:
+        return "builtins.async_generator"
+
+    def display_type(self) -> str:
+        return "AsyncGenerator"
+
+    def __repr__(self) -> str:
+        return f"<AsyncGenerator({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"
+
+    def __str__(self) -> str:
+        return f"AsyncGenerator({self._proxied.name})"
+
+
+class UnionType(BaseInstance):
+    """Special node representing new style typing unions.
+
+    Proxied class is set once for all in raw_building.
+    """
+
+    _proxied: nodes.ClassDef
+
+    def __init__(
+        self,
+        left: UnionType | nodes.ClassDef | nodes.Const,
+        right: UnionType | nodes.ClassDef | nodes.Const,
+        parent: nodes.NodeNG | None = None,
+    ) -> None:
+        super().__init__()
+        self.parent = parent
+        self.left = left
+        self.right = right
+
+    def callable(self) -> Literal[False]:
+        return False
+
+    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
+        return True
+
+    def pytype(self) -> Literal["types.UnionType"]:
+        return "types.UnionType"
+
+    def display_type(self) -> str:
+        return "UnionType"
+
+    def __repr__(self) -> str:
+        return f"<UnionType({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"
+
+    def __str__(self) -> str:
+        return f"UnionType({self._proxied.name})"

+ 0 - 0
venv/lib/python3.11/site-packages/astroid/brain/__init__.py


+ 44 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_argparse.py

@@ -0,0 +1,44 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+from astroid import arguments, inference_tip, nodes
+from astroid.context import InferenceContext
+from astroid.exceptions import UseInferenceDefault
+from astroid.manager import AstroidManager
+
+
+def infer_namespace(node, context: InferenceContext | None = None):
+    callsite = arguments.CallSite.from_call(node, context=context)
+    if not callsite.keyword_arguments:
+        # Cannot make sense of it.
+        raise UseInferenceDefault()
+
+    class_node = nodes.ClassDef("Namespace")
+    # Set parent manually until ClassDef constructor fixed:
+    # https://github.com/PyCQA/astroid/issues/1490
+    class_node.parent = node.parent
+    for attr in set(callsite.keyword_arguments):
+        fake_node = nodes.EmptyNode()
+        fake_node.parent = class_node
+        fake_node.attrname = attr
+        class_node.instance_attrs[attr] = [fake_node]
+    return iter((class_node.instantiate_class(),))
+
+
+def _looks_like_namespace(node) -> bool:
+    func = node.func
+    if isinstance(func, nodes.Attribute):
+        return (
+            func.attrname == "Namespace"
+            and isinstance(func.expr, nodes.Name)
+            and func.expr.name == "argparse"
+        )
+    return False
+
+
+AstroidManager().register_transform(
+    nodes.Call, inference_tip(infer_namespace), _looks_like_namespace
+)

+ 88 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_attrs.py

@@ -0,0 +1,88 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""
+Astroid hook for the attrs library
+
+Without this hook pylint reports unsupported-assignment-operation
+for attrs classes
+"""
+from astroid.helpers import safe_infer
+from astroid.manager import AstroidManager
+from astroid.nodes.node_classes import AnnAssign, Assign, AssignName, Call, Unknown
+from astroid.nodes.scoped_nodes import ClassDef
+
+ATTRIB_NAMES = frozenset(
+    ("attr.ib", "attrib", "attr.attrib", "attr.field", "attrs.field", "field")
+)
+ATTRS_NAMES = frozenset(
+    (
+        "attr.s",
+        "attrs",
+        "attr.attrs",
+        "attr.attributes",
+        "attr.define",
+        "attr.mutable",
+        "attr.frozen",
+        "attrs.define",
+        "attrs.mutable",
+        "attrs.frozen",
+    )
+)
+
+
+def is_decorated_with_attrs(node, decorator_names=ATTRS_NAMES) -> bool:
+    """Return whether a decorated node has an attr decorator applied."""
+    if not node.decorators:
+        return False
+    for decorator_attribute in node.decorators.nodes:
+        if isinstance(decorator_attribute, Call):  # decorator with arguments
+            decorator_attribute = decorator_attribute.func
+        if decorator_attribute.as_string() in decorator_names:
+            return True
+
+        inferred = safe_infer(decorator_attribute)
+        if inferred and inferred.root().name == "attr._next_gen":
+            return True
+    return False
+
+
+def attr_attributes_transform(node: ClassDef) -> None:
+    """Given that the ClassNode has an attr decorator,
+    rewrite class attributes as instance attributes
+    """
+    # Astroid can't infer this attribute properly
+    # Prevents https://github.com/PyCQA/pylint/issues/1884
+    node.locals["__attrs_attrs__"] = [Unknown(parent=node)]
+
+    for cdef_body_node in node.body:
+        if not isinstance(cdef_body_node, (Assign, AnnAssign)):
+            continue
+        if isinstance(cdef_body_node.value, Call):
+            if cdef_body_node.value.func.as_string() not in ATTRIB_NAMES:
+                continue
+        else:
+            continue
+        targets = (
+            cdef_body_node.targets
+            if hasattr(cdef_body_node, "targets")
+            else [cdef_body_node.target]
+        )
+        for target in targets:
+            rhs_node = Unknown(
+                lineno=cdef_body_node.lineno,
+                col_offset=cdef_body_node.col_offset,
+                parent=cdef_body_node,
+            )
+            if isinstance(target, AssignName):
+                # Could be a subscript if the code analysed is
+                # i = Optional[str] = ""
+                # See https://github.com/PyCQA/pylint/issues/4439
+                node.locals[target.name] = [rhs_node]
+                node.instance_attrs[target.name] = [rhs_node]
+
+
+AstroidManager().register_transform(
+    ClassDef, attr_attributes_transform, is_decorated_with_attrs
+)

+ 31 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_boto3.py

@@ -0,0 +1,31 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for understanding ``boto3.ServiceRequest()``."""
+
+from astroid import extract_node
+from astroid.manager import AstroidManager
+from astroid.nodes.scoped_nodes import ClassDef
+
+BOTO_SERVICE_FACTORY_QUALIFIED_NAME = "boto3.resources.base.ServiceResource"
+
+
+def service_request_transform(node):
+    """Transform ServiceResource to look like dynamic classes."""
+    code = """
+    def __getattr__(self, attr):
+        return 0
+    """
+    func_getattr = extract_node(code)
+    node.locals["__getattr__"] = [func_getattr]
+    return node
+
+
+def _looks_like_boto3_service_request(node) -> bool:
+    return node.qname() == BOTO_SERVICE_FACTORY_QUALIFIED_NAME
+
+
+AstroidManager().register_transform(
+    ClassDef, service_request_transform, _looks_like_boto3_service_request
+)

+ 1007 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_builtin_inference.py

@@ -0,0 +1,1007 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for various builtins."""
+
+from __future__ import annotations
+
+import itertools
+from collections.abc import Iterator
+from functools import partial
+
+from astroid import arguments, helpers, inference_tip, nodes, objects, util
+from astroid.builder import AstroidBuilder
+from astroid.context import InferenceContext
+from astroid.exceptions import (
+    AstroidTypeError,
+    AttributeInferenceError,
+    InferenceError,
+    MroError,
+    UseInferenceDefault,
+)
+from astroid.manager import AstroidManager
+from astroid.nodes import scoped_nodes
+
+OBJECT_DUNDER_NEW = "object.__new__"
+
+STR_CLASS = """
+class whatever(object):
+    def join(self, iterable):
+        return {rvalue}
+    def replace(self, old, new, count=None):
+        return {rvalue}
+    def format(self, *args, **kwargs):
+        return {rvalue}
+    def encode(self, encoding='ascii', errors=None):
+        return b''
+    def decode(self, encoding='ascii', errors=None):
+        return u''
+    def capitalize(self):
+        return {rvalue}
+    def title(self):
+        return {rvalue}
+    def lower(self):
+        return {rvalue}
+    def upper(self):
+        return {rvalue}
+    def swapcase(self):
+        return {rvalue}
+    def index(self, sub, start=None, end=None):
+        return 0
+    def find(self, sub, start=None, end=None):
+        return 0
+    def count(self, sub, start=None, end=None):
+        return 0
+    def strip(self, chars=None):
+        return {rvalue}
+    def lstrip(self, chars=None):
+        return {rvalue}
+    def rstrip(self, chars=None):
+        return {rvalue}
+    def rjust(self, width, fillchar=None):
+        return {rvalue}
+    def center(self, width, fillchar=None):
+        return {rvalue}
+    def ljust(self, width, fillchar=None):
+        return {rvalue}
+"""
+
+
+BYTES_CLASS = """
+class whatever(object):
+    def join(self, iterable):
+        return {rvalue}
+    def replace(self, old, new, count=None):
+        return {rvalue}
+    def decode(self, encoding='ascii', errors=None):
+        return u''
+    def capitalize(self):
+        return {rvalue}
+    def title(self):
+        return {rvalue}
+    def lower(self):
+        return {rvalue}
+    def upper(self):
+        return {rvalue}
+    def swapcase(self):
+        return {rvalue}
+    def index(self, sub, start=None, end=None):
+        return 0
+    def find(self, sub, start=None, end=None):
+        return 0
+    def count(self, sub, start=None, end=None):
+        return 0
+    def strip(self, chars=None):
+        return {rvalue}
+    def lstrip(self, chars=None):
+        return {rvalue}
+    def rstrip(self, chars=None):
+        return {rvalue}
+    def rjust(self, width, fillchar=None):
+        return {rvalue}
+    def center(self, width, fillchar=None):
+        return {rvalue}
+    def ljust(self, width, fillchar=None):
+        return {rvalue}
+"""
+
+
+def _extend_string_class(class_node, code, rvalue):
+    """Function to extend builtin str/unicode class."""
+    code = code.format(rvalue=rvalue)
+    fake = AstroidBuilder(AstroidManager()).string_build(code)["whatever"]
+    for method in fake.mymethods():
+        method.parent = class_node
+        method.lineno = None
+        method.col_offset = None
+        if "__class__" in method.locals:
+            method.locals["__class__"] = [class_node]
+        class_node.locals[method.name] = [method]
+        method.parent = class_node
+
+
+def _extend_builtins(class_transforms):
+    builtin_ast = AstroidManager().builtins_module
+    for class_name, transform in class_transforms.items():
+        transform(builtin_ast[class_name])
+
+
+_extend_builtins(
+    {
+        "bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"),
+        "str": partial(_extend_string_class, code=STR_CLASS, rvalue="''"),
+    }
+)
+
+
+def _builtin_filter_predicate(node, builtin_name) -> bool:
+    if (
+        builtin_name == "type"
+        and node.root().name == "re"
+        and isinstance(node.func, nodes.Name)
+        and node.func.name == "type"
+        and isinstance(node.parent, nodes.Assign)
+        and len(node.parent.targets) == 1
+        and isinstance(node.parent.targets[0], nodes.AssignName)
+        and node.parent.targets[0].name in {"Pattern", "Match"}
+    ):
+        # Handle re.Pattern and re.Match in brain_re
+        # Match these patterns from stdlib/re.py
+        # ```py
+        # Pattern = type(...)
+        # Match = type(...)
+        # ```
+        return False
+    if isinstance(node.func, nodes.Name) and node.func.name == builtin_name:
+        return True
+    if isinstance(node.func, nodes.Attribute):
+        return (
+            node.func.attrname == "fromkeys"
+            and isinstance(node.func.expr, nodes.Name)
+            and node.func.expr.name == "dict"
+        )
+    return False
+
+
+def register_builtin_transform(transform, builtin_name) -> None:
+    """Register a new transform function for the given *builtin_name*.
+
+    The transform function must accept two parameters, a node and
+    an optional context.
+    """
+
+    def _transform_wrapper(node, context: InferenceContext | None = None):
+        result = transform(node, context=context)
+        if result:
+            if not result.parent:
+                # Let the transformation function determine
+                # the parent for its result. Otherwise,
+                # we set it to be the node we transformed from.
+                result.parent = node
+
+            if result.lineno is None:
+                result.lineno = node.lineno
+            # Can be a 'Module' see https://github.com/PyCQA/pylint/issues/4671
+            # We don't have a regression test on this one: tread carefully
+            if hasattr(result, "col_offset") and result.col_offset is None:
+                result.col_offset = node.col_offset
+        return iter([result])
+
+    AstroidManager().register_transform(
+        nodes.Call,
+        inference_tip(_transform_wrapper),
+        partial(_builtin_filter_predicate, builtin_name=builtin_name),
+    )
+
+
+def _container_generic_inference(node, context, node_type, transform):
+    args = node.args
+    if not args:
+        return node_type()
+    if len(node.args) > 1:
+        raise UseInferenceDefault()
+
+    (arg,) = args
+    transformed = transform(arg)
+    if not transformed:
+        try:
+            inferred = next(arg.infer(context=context))
+        except (InferenceError, StopIteration) as exc:
+            raise UseInferenceDefault from exc
+        if isinstance(inferred, util.UninferableBase):
+            raise UseInferenceDefault
+        transformed = transform(inferred)
+    if not transformed or isinstance(transformed, util.UninferableBase):
+        raise UseInferenceDefault
+    return transformed
+
+
+def _container_generic_transform(  # pylint: disable=inconsistent-return-statements
+    arg, context, klass, iterables, build_elts
+):
+    if isinstance(arg, klass):
+        return arg
+    if isinstance(arg, iterables):
+        if all(isinstance(elt, nodes.Const) for elt in arg.elts):
+            elts = [elt.value for elt in arg.elts]
+        else:
+            # TODO: Does not handle deduplication for sets.
+            elts = []
+            for element in arg.elts:
+                if not element:
+                    continue
+                inferred = helpers.safe_infer(element, context=context)
+                if inferred:
+                    evaluated_object = nodes.EvaluatedObject(
+                        original=element, value=inferred
+                    )
+                    elts.append(evaluated_object)
+    elif isinstance(arg, nodes.Dict):
+        # Dicts need to have consts as strings already.
+        if not all(isinstance(elt[0], nodes.Const) for elt in arg.items):
+            raise UseInferenceDefault()
+        elts = [item[0].value for item in arg.items]
+    elif isinstance(arg, nodes.Const) and isinstance(arg.value, (str, bytes)):
+        elts = arg.value
+    else:
+        return
+    return klass.from_elements(elts=build_elts(elts))
+
+
+def _infer_builtin_container(
+    node, context, klass=None, iterables=None, build_elts=None
+):
+    transform_func = partial(
+        _container_generic_transform,
+        context=context,
+        klass=klass,
+        iterables=iterables,
+        build_elts=build_elts,
+    )
+
+    return _container_generic_inference(node, context, klass, transform_func)
+
+
+# pylint: disable=invalid-name
+infer_tuple = partial(
+    _infer_builtin_container,
+    klass=nodes.Tuple,
+    iterables=(
+        nodes.List,
+        nodes.Set,
+        objects.FrozenSet,
+        objects.DictItems,
+        objects.DictKeys,
+        objects.DictValues,
+    ),
+    build_elts=tuple,
+)
+
+infer_list = partial(
+    _infer_builtin_container,
+    klass=nodes.List,
+    iterables=(
+        nodes.Tuple,
+        nodes.Set,
+        objects.FrozenSet,
+        objects.DictItems,
+        objects.DictKeys,
+        objects.DictValues,
+    ),
+    build_elts=list,
+)
+
+infer_set = partial(
+    _infer_builtin_container,
+    klass=nodes.Set,
+    iterables=(nodes.List, nodes.Tuple, objects.FrozenSet, objects.DictKeys),
+    build_elts=set,
+)
+
+infer_frozenset = partial(
+    _infer_builtin_container,
+    klass=objects.FrozenSet,
+    iterables=(nodes.List, nodes.Tuple, nodes.Set, objects.FrozenSet, objects.DictKeys),
+    build_elts=frozenset,
+)
+
+
+def _get_elts(arg, context):
+    def is_iterable(n):
+        return isinstance(n, (nodes.List, nodes.Tuple, nodes.Set))
+
+    try:
+        inferred = next(arg.infer(context))
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+    if isinstance(inferred, nodes.Dict):
+        items = inferred.items
+    elif is_iterable(inferred):
+        items = []
+        for elt in inferred.elts:
+            # If an item is not a pair of two items,
+            # then fallback to the default inference.
+            # Also, take in consideration only hashable items,
+            # tuples and consts. We are choosing Names as well.
+            if not is_iterable(elt):
+                raise UseInferenceDefault()
+            if len(elt.elts) != 2:
+                raise UseInferenceDefault()
+            if not isinstance(elt.elts[0], (nodes.Tuple, nodes.Const, nodes.Name)):
+                raise UseInferenceDefault()
+            items.append(tuple(elt.elts))
+    else:
+        raise UseInferenceDefault()
+    return items
+
+
+def infer_dict(node, context: InferenceContext | None = None):
+    """Try to infer a dict call to a Dict node.
+
+    The function treats the following cases:
+
+        * dict()
+        * dict(mapping)
+        * dict(iterable)
+        * dict(iterable, **kwargs)
+        * dict(mapping, **kwargs)
+        * dict(**kwargs)
+
+    If a case can't be inferred, we'll fallback to default inference.
+    """
+    call = arguments.CallSite.from_call(node, context=context)
+    if call.has_invalid_arguments() or call.has_invalid_keywords():
+        raise UseInferenceDefault
+
+    args = call.positional_arguments
+    kwargs = list(call.keyword_arguments.items())
+
+    if not args and not kwargs:
+        # dict()
+        return nodes.Dict()
+    if kwargs and not args:
+        # dict(a=1, b=2, c=4)
+        items = [(nodes.Const(key), value) for key, value in kwargs]
+    elif len(args) == 1 and kwargs:
+        # dict(some_iterable, b=2, c=4)
+        elts = _get_elts(args[0], context)
+        keys = [(nodes.Const(key), value) for key, value in kwargs]
+        items = elts + keys
+    elif len(args) == 1:
+        items = _get_elts(args[0], context)
+    else:
+        raise UseInferenceDefault()
+    value = nodes.Dict(
+        col_offset=node.col_offset, lineno=node.lineno, parent=node.parent
+    )
+    value.postinit(items)
+    return value
+
+
+def infer_super(node, context: InferenceContext | None = None):
+    """Understand super calls.
+
+    There are some restrictions for what can be understood:
+
+        * unbounded super (one argument form) is not understood.
+
+        * if the super call is not inside a function (classmethod or method),
+          then the default inference will be used.
+
+        * if the super arguments can't be inferred, the default inference
+          will be used.
+    """
+    if len(node.args) == 1:
+        # Ignore unbounded super.
+        raise UseInferenceDefault
+
+    scope = node.scope()
+    if not isinstance(scope, nodes.FunctionDef):
+        # Ignore non-method uses of super.
+        raise UseInferenceDefault
+    if scope.type not in ("classmethod", "method"):
+        # Not interested in staticmethods.
+        raise UseInferenceDefault
+
+    cls = scoped_nodes.get_wrapping_class(scope)
+    if not node.args:
+        mro_pointer = cls
+        # In we are in a classmethod, the interpreter will fill
+        # automatically the class as the second argument, not an instance.
+        if scope.type == "classmethod":
+            mro_type = cls
+        else:
+            mro_type = cls.instantiate_class()
+    else:
+        try:
+            mro_pointer = next(node.args[0].infer(context=context))
+        except (InferenceError, StopIteration) as exc:
+            raise UseInferenceDefault from exc
+        try:
+            mro_type = next(node.args[1].infer(context=context))
+        except (InferenceError, StopIteration) as exc:
+            raise UseInferenceDefault from exc
+
+    if isinstance(mro_pointer, util.UninferableBase) or isinstance(
+        mro_type, util.UninferableBase
+    ):
+        # No way we could understand this.
+        raise UseInferenceDefault
+
+    super_obj = objects.Super(
+        mro_pointer=mro_pointer, mro_type=mro_type, self_class=cls, scope=scope
+    )
+    super_obj.parent = node
+    return super_obj
+
+
+def _infer_getattr_args(node, context):
+    if len(node.args) not in (2, 3):
+        # Not a valid getattr call.
+        raise UseInferenceDefault
+
+    try:
+        obj = next(node.args[0].infer(context=context))
+        attr = next(node.args[1].infer(context=context))
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+
+    if isinstance(obj, util.UninferableBase) or isinstance(attr, util.UninferableBase):
+        # If one of the arguments is something we can't infer,
+        # then also make the result of the getattr call something
+        # which is unknown.
+        return util.Uninferable, util.Uninferable
+
+    is_string = isinstance(attr, nodes.Const) and isinstance(attr.value, str)
+    if not is_string:
+        raise UseInferenceDefault
+
+    return obj, attr.value
+
+
+def infer_getattr(node, context: InferenceContext | None = None):
+    """Understand getattr calls.
+
+    If one of the arguments is an Uninferable object, then the
+    result will be an Uninferable object. Otherwise, the normal attribute
+    lookup will be done.
+    """
+    obj, attr = _infer_getattr_args(node, context)
+    if (
+        isinstance(obj, util.UninferableBase)
+        or isinstance(attr, util.UninferableBase)
+        or not hasattr(obj, "igetattr")
+    ):
+        return util.Uninferable
+
+    try:
+        return next(obj.igetattr(attr, context=context))
+    except (StopIteration, InferenceError, AttributeInferenceError):
+        if len(node.args) == 3:
+            # Try to infer the default and return it instead.
+            try:
+                return next(node.args[2].infer(context=context))
+            except (StopIteration, InferenceError) as exc:
+                raise UseInferenceDefault from exc
+
+    raise UseInferenceDefault
+
+
+def infer_hasattr(node, context: InferenceContext | None = None):
+    """Understand hasattr calls.
+
+    This always guarantees three possible outcomes for calling
+    hasattr: Const(False) when we are sure that the object
+    doesn't have the intended attribute, Const(True) when
+    we know that the object has the attribute and Uninferable
+    when we are unsure of the outcome of the function call.
+    """
+    try:
+        obj, attr = _infer_getattr_args(node, context)
+        if (
+            isinstance(obj, util.UninferableBase)
+            or isinstance(attr, util.UninferableBase)
+            or not hasattr(obj, "getattr")
+        ):
+            return util.Uninferable
+        obj.getattr(attr, context=context)
+    except UseInferenceDefault:
+        # Can't infer something from this function call.
+        return util.Uninferable
+    except AttributeInferenceError:
+        # Doesn't have it.
+        return nodes.Const(False)
+    return nodes.Const(True)
+
+
+def infer_callable(node, context: InferenceContext | None = None):
+    """Understand callable calls.
+
+    This follows Python's semantics, where an object
+    is callable if it provides an attribute __call__,
+    even though that attribute is something which can't be
+    called.
+    """
+    if len(node.args) != 1:
+        # Invalid callable call.
+        raise UseInferenceDefault
+
+    argument = node.args[0]
+    try:
+        inferred = next(argument.infer(context=context))
+    except (InferenceError, StopIteration):
+        return util.Uninferable
+    if isinstance(inferred, util.UninferableBase):
+        return util.Uninferable
+    return nodes.Const(inferred.callable())
+
+
+def infer_property(
+    node: nodes.Call, context: InferenceContext | None = None
+) -> objects.Property:
+    """Understand `property` class.
+
+    This only infers the output of `property`
+    call, not the arguments themselves.
+    """
+    if len(node.args) < 1:
+        # Invalid property call.
+        raise UseInferenceDefault
+
+    getter = node.args[0]
+    try:
+        inferred = next(getter.infer(context=context))
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+
+    if not isinstance(inferred, (nodes.FunctionDef, nodes.Lambda)):
+        raise UseInferenceDefault
+
+    prop_func = objects.Property(
+        function=inferred,
+        name=inferred.name,
+        lineno=node.lineno,
+        parent=node,
+        col_offset=node.col_offset,
+    )
+    prop_func.postinit(
+        body=[],
+        args=inferred.args,
+        doc_node=getattr(inferred, "doc_node", None),
+    )
+    return prop_func
+
+
+def infer_bool(node, context: InferenceContext | None = None):
+    """Understand bool calls."""
+    if len(node.args) > 1:
+        # Invalid bool call.
+        raise UseInferenceDefault
+
+    if not node.args:
+        return nodes.Const(False)
+
+    argument = node.args[0]
+    try:
+        inferred = next(argument.infer(context=context))
+    except (InferenceError, StopIteration):
+        return util.Uninferable
+    if isinstance(inferred, util.UninferableBase):
+        return util.Uninferable
+
+    bool_value = inferred.bool_value(context=context)
+    if isinstance(bool_value, util.UninferableBase):
+        return util.Uninferable
+    return nodes.Const(bool_value)
+
+
+def infer_type(node, context: InferenceContext | None = None):
+    """Understand the one-argument form of *type*."""
+    if len(node.args) != 1:
+        raise UseInferenceDefault
+
+    return helpers.object_type(node.args[0], context)
+
+
+def infer_slice(node, context: InferenceContext | None = None):
+    """Understand `slice` calls."""
+    args = node.args
+    if not 0 < len(args) <= 3:
+        raise UseInferenceDefault
+
+    infer_func = partial(helpers.safe_infer, context=context)
+    args = [infer_func(arg) for arg in args]
+    for arg in args:
+        if not arg or isinstance(arg, util.UninferableBase):
+            raise UseInferenceDefault
+        if not isinstance(arg, nodes.Const):
+            raise UseInferenceDefault
+        if not isinstance(arg.value, (type(None), int)):
+            raise UseInferenceDefault
+
+    if len(args) < 3:
+        # Make sure we have 3 arguments.
+        args.extend([None] * (3 - len(args)))
+
+    slice_node = nodes.Slice(
+        lineno=node.lineno, col_offset=node.col_offset, parent=node.parent
+    )
+    slice_node.postinit(*args)
+    return slice_node
+
+
+def _infer_object__new__decorator(node, context: InferenceContext | None = None):
+    # Instantiate class immediately
+    # since that's what @object.__new__ does
+    return iter((node.instantiate_class(),))
+
+
+def _infer_object__new__decorator_check(node) -> bool:
+    """Predicate before inference_tip.
+
+    Check if the given ClassDef has an @object.__new__ decorator
+    """
+    if not node.decorators:
+        return False
+
+    for decorator in node.decorators.nodes:
+        if isinstance(decorator, nodes.Attribute):
+            if decorator.as_string() == OBJECT_DUNDER_NEW:
+                return True
+    return False
+
+
+def infer_issubclass(callnode, context: InferenceContext | None = None):
+    """Infer issubclass() calls.
+
+    :param nodes.Call callnode: an `issubclass` call
+    :param InferenceContext context: the context for the inference
+    :rtype nodes.Const: Boolean Const value of the `issubclass` call
+    :raises UseInferenceDefault: If the node cannot be inferred
+    """
+    call = arguments.CallSite.from_call(callnode, context=context)
+    if call.keyword_arguments:
+        # issubclass doesn't support keyword arguments
+        raise UseInferenceDefault("TypeError: issubclass() takes no keyword arguments")
+    if len(call.positional_arguments) != 2:
+        raise UseInferenceDefault(
+            f"Expected two arguments, got {len(call.positional_arguments)}"
+        )
+    # The left hand argument is the obj to be checked
+    obj_node, class_or_tuple_node = call.positional_arguments
+
+    try:
+        obj_type = next(obj_node.infer(context=context))
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+    if not isinstance(obj_type, nodes.ClassDef):
+        raise UseInferenceDefault("TypeError: arg 1 must be class")
+
+    # The right hand argument is the class(es) that the given
+    # object is to be checked against.
+    try:
+        class_container = _class_or_tuple_to_container(
+            class_or_tuple_node, context=context
+        )
+    except InferenceError as exc:
+        raise UseInferenceDefault from exc
+    try:
+        issubclass_bool = helpers.object_issubclass(obj_type, class_container, context)
+    except AstroidTypeError as exc:
+        raise UseInferenceDefault("TypeError: " + str(exc)) from exc
+    except MroError as exc:
+        raise UseInferenceDefault from exc
+    return nodes.Const(issubclass_bool)
+
+
+def infer_isinstance(callnode, context: InferenceContext | None = None):
+    """Infer isinstance calls.
+
+    :param nodes.Call callnode: an isinstance call
+    :rtype nodes.Const: Boolean Const value of isinstance call
+
+    :raises UseInferenceDefault: If the node cannot be inferred
+    """
+    call = arguments.CallSite.from_call(callnode, context=context)
+    if call.keyword_arguments:
+        # isinstance doesn't support keyword arguments
+        raise UseInferenceDefault("TypeError: isinstance() takes no keyword arguments")
+    if len(call.positional_arguments) != 2:
+        raise UseInferenceDefault(
+            f"Expected two arguments, got {len(call.positional_arguments)}"
+        )
+    # The left hand argument is the obj to be checked
+    obj_node, class_or_tuple_node = call.positional_arguments
+    # The right hand argument is the class(es) that the given
+    # obj is to be check is an instance of
+    try:
+        class_container = _class_or_tuple_to_container(
+            class_or_tuple_node, context=context
+        )
+    except InferenceError as exc:
+        raise UseInferenceDefault from exc
+    try:
+        isinstance_bool = helpers.object_isinstance(obj_node, class_container, context)
+    except AstroidTypeError as exc:
+        raise UseInferenceDefault("TypeError: " + str(exc)) from exc
+    except MroError as exc:
+        raise UseInferenceDefault from exc
+    if isinstance(isinstance_bool, util.UninferableBase):
+        raise UseInferenceDefault
+    return nodes.Const(isinstance_bool)
+
+
+def _class_or_tuple_to_container(node, context: InferenceContext | None = None):
+    # Move inferences results into container
+    # to simplify later logic
+    # raises InferenceError if any of the inferences fall through
+    try:
+        node_infer = next(node.infer(context=context))
+    except StopIteration as e:
+        raise InferenceError(node=node, context=context) from e
+    # arg2 MUST be a type or a TUPLE of types
+    # for isinstance
+    if isinstance(node_infer, nodes.Tuple):
+        try:
+            class_container = [
+                next(node.infer(context=context)) for node in node_infer.elts
+            ]
+        except StopIteration as e:
+            raise InferenceError(node=node, context=context) from e
+        class_container = [
+            klass_node for klass_node in class_container if klass_node is not None
+        ]
+    else:
+        class_container = [node_infer]
+    return class_container
+
+
+def infer_len(node, context: InferenceContext | None = None):
+    """Infer length calls.
+
+    :param nodes.Call node: len call to infer
+    :param context.InferenceContext: node context
+    :rtype nodes.Const: a Const node with the inferred length, if possible
+    """
+    call = arguments.CallSite.from_call(node, context=context)
+    if call.keyword_arguments:
+        raise UseInferenceDefault("TypeError: len() must take no keyword arguments")
+    if len(call.positional_arguments) != 1:
+        raise UseInferenceDefault(
+            "TypeError: len() must take exactly one argument "
+            "({len}) given".format(len=len(call.positional_arguments))
+        )
+    [argument_node] = call.positional_arguments
+
+    try:
+        return nodes.Const(helpers.object_len(argument_node, context=context))
+    except (AstroidTypeError, InferenceError) as exc:
+        raise UseInferenceDefault(str(exc)) from exc
+
+
+def infer_str(node, context: InferenceContext | None = None):
+    """Infer str() calls.
+
+    :param nodes.Call node: str() call to infer
+    :param context.InferenceContext: node context
+    :rtype nodes.Const: a Const containing an empty string
+    """
+    call = arguments.CallSite.from_call(node, context=context)
+    if call.keyword_arguments:
+        raise UseInferenceDefault("TypeError: str() must take no keyword arguments")
+    try:
+        return nodes.Const("")
+    except (AstroidTypeError, InferenceError) as exc:
+        raise UseInferenceDefault(str(exc)) from exc
+
+
+def infer_int(node, context: InferenceContext | None = None):
+    """Infer int() calls.
+
+    :param nodes.Call node: int() call to infer
+    :param context.InferenceContext: node context
+    :rtype nodes.Const: a Const containing the integer value of the int() call
+    """
+    call = arguments.CallSite.from_call(node, context=context)
+    if call.keyword_arguments:
+        raise UseInferenceDefault("TypeError: int() must take no keyword arguments")
+
+    if call.positional_arguments:
+        try:
+            first_value = next(call.positional_arguments[0].infer(context=context))
+        except (InferenceError, StopIteration) as exc:
+            raise UseInferenceDefault(str(exc)) from exc
+
+        if isinstance(first_value, util.UninferableBase):
+            raise UseInferenceDefault
+
+        if isinstance(first_value, nodes.Const) and isinstance(
+            first_value.value, (int, str)
+        ):
+            try:
+                actual_value = int(first_value.value)
+            except ValueError:
+                return nodes.Const(0)
+            return nodes.Const(actual_value)
+
+    return nodes.Const(0)
+
+
+def infer_dict_fromkeys(node, context: InferenceContext | None = None):
+    """Infer dict.fromkeys.
+
+    :param nodes.Call node: dict.fromkeys() call to infer
+    :param context.InferenceContext context: node context
+    :rtype nodes.Dict:
+        a Dictionary containing the values that astroid was able to infer.
+        In case the inference failed for any reason, an empty dictionary
+        will be inferred instead.
+    """
+
+    def _build_dict_with_elements(elements):
+        new_node = nodes.Dict(
+            col_offset=node.col_offset, lineno=node.lineno, parent=node.parent
+        )
+        new_node.postinit(elements)
+        return new_node
+
+    call = arguments.CallSite.from_call(node, context=context)
+    if call.keyword_arguments:
+        raise UseInferenceDefault("TypeError: int() must take no keyword arguments")
+    if len(call.positional_arguments) not in {1, 2}:
+        raise UseInferenceDefault(
+            "TypeError: Needs between 1 and 2 positional arguments"
+        )
+
+    default = nodes.Const(None)
+    values = call.positional_arguments[0]
+    try:
+        inferred_values = next(values.infer(context=context))
+    except (InferenceError, StopIteration):
+        return _build_dict_with_elements([])
+    if inferred_values is util.Uninferable:
+        return _build_dict_with_elements([])
+
+    # Limit to a couple of potential values, as this can become pretty complicated
+    accepted_iterable_elements = (nodes.Const,)
+    if isinstance(inferred_values, (nodes.List, nodes.Set, nodes.Tuple)):
+        elements = inferred_values.elts
+        for element in elements:
+            if not isinstance(element, accepted_iterable_elements):
+                # Fallback to an empty dict
+                return _build_dict_with_elements([])
+
+        elements_with_value = [(element, default) for element in elements]
+        return _build_dict_with_elements(elements_with_value)
+    if isinstance(inferred_values, nodes.Const) and isinstance(
+        inferred_values.value, (str, bytes)
+    ):
+        elements = [
+            (nodes.Const(element), default) for element in inferred_values.value
+        ]
+        return _build_dict_with_elements(elements)
+    if isinstance(inferred_values, nodes.Dict):
+        keys = inferred_values.itered()
+        for key in keys:
+            if not isinstance(key, accepted_iterable_elements):
+                # Fallback to an empty dict
+                return _build_dict_with_elements([])
+
+        elements_with_value = [(element, default) for element in keys]
+        return _build_dict_with_elements(elements_with_value)
+
+    # Fallback to an empty dictionary
+    return _build_dict_with_elements([])
+
+
+def _infer_copy_method(
+    node: nodes.Call, context: InferenceContext | None = None
+) -> Iterator[nodes.NodeNG]:
+    assert isinstance(node.func, nodes.Attribute)
+    inferred_orig, inferred_copy = itertools.tee(node.func.expr.infer(context=context))
+    if all(
+        isinstance(
+            inferred_node, (nodes.Dict, nodes.List, nodes.Set, objects.FrozenSet)
+        )
+        for inferred_node in inferred_orig
+    ):
+        return inferred_copy
+
+    raise UseInferenceDefault()
+
+
+def _is_str_format_call(node: nodes.Call) -> bool:
+    """Catch calls to str.format()."""
+    if not isinstance(node.func, nodes.Attribute) or not node.func.attrname == "format":
+        return False
+
+    if isinstance(node.func.expr, nodes.Name):
+        value = helpers.safe_infer(node.func.expr)
+    else:
+        value = node.func.expr
+
+    return isinstance(value, nodes.Const) and isinstance(value.value, str)
+
+
+def _infer_str_format_call(
+    node: nodes.Call, context: InferenceContext | None = None
+) -> Iterator[nodes.Const | util.UninferableBase]:
+    """Return a Const node based on the template and passed arguments."""
+    call = arguments.CallSite.from_call(node, context=context)
+    if isinstance(node.func.expr, nodes.Name):
+        value: nodes.Const | None = helpers.safe_infer(node.func.expr)
+        if value is None:
+            return iter([util.Uninferable])
+    else:
+        value = node.func.expr
+
+    format_template = value.value
+
+    # Get the positional arguments passed
+    inferred_positional = [
+        helpers.safe_infer(i, context) for i in call.positional_arguments
+    ]
+    if not all(isinstance(i, nodes.Const) for i in inferred_positional):
+        return iter([util.Uninferable])
+    pos_values: list[str] = [i.value for i in inferred_positional]
+
+    # Get the keyword arguments passed
+    inferred_keyword = {
+        k: helpers.safe_infer(v, context) for k, v in call.keyword_arguments.items()
+    }
+    if not all(isinstance(i, nodes.Const) for i in inferred_keyword.values()):
+        return iter([util.Uninferable])
+    keyword_values: dict[str, str] = {k: v.value for k, v in inferred_keyword.items()}
+
+    try:
+        formatted_string = format_template.format(*pos_values, **keyword_values)
+    except (AttributeError, IndexError, KeyError, TypeError, ValueError):
+        # AttributeError: named field in format string was not found in the arguments
+        # IndexError: there are too few arguments to interpolate
+        # TypeError: Unsupported format string
+        # ValueError: Unknown format code
+        return iter([util.Uninferable])
+
+    return iter([nodes.const_factory(formatted_string)])
+
+
+# Builtins inference
+register_builtin_transform(infer_bool, "bool")
+register_builtin_transform(infer_super, "super")
+register_builtin_transform(infer_callable, "callable")
+register_builtin_transform(infer_property, "property")
+register_builtin_transform(infer_getattr, "getattr")
+register_builtin_transform(infer_hasattr, "hasattr")
+register_builtin_transform(infer_tuple, "tuple")
+register_builtin_transform(infer_set, "set")
+register_builtin_transform(infer_list, "list")
+register_builtin_transform(infer_dict, "dict")
+register_builtin_transform(infer_frozenset, "frozenset")
+register_builtin_transform(infer_type, "type")
+register_builtin_transform(infer_slice, "slice")
+register_builtin_transform(infer_isinstance, "isinstance")
+register_builtin_transform(infer_issubclass, "issubclass")
+register_builtin_transform(infer_len, "len")
+register_builtin_transform(infer_str, "str")
+register_builtin_transform(infer_int, "int")
+register_builtin_transform(infer_dict_fromkeys, "dict.fromkeys")
+
+
+# Infer object.__new__ calls
+AstroidManager().register_transform(
+    nodes.ClassDef,
+    inference_tip(_infer_object__new__decorator),
+    _infer_object__new__decorator_check,
+)
+
+AstroidManager().register_transform(
+    nodes.Call,
+    inference_tip(_infer_copy_method),
+    lambda node: isinstance(node.func, nodes.Attribute)
+    and node.func.attrname == "copy",
+)
+
+AstroidManager().register_transform(
+    nodes.Call, inference_tip(_infer_str_format_call), _is_str_format_call
+)

+ 126 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_collections.py

@@ -0,0 +1,126 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import extract_node, parse
+from astroid.const import PY39_PLUS
+from astroid.context import InferenceContext
+from astroid.exceptions import AttributeInferenceError
+from astroid.manager import AstroidManager
+from astroid.nodes.scoped_nodes import ClassDef
+
+
+def _collections_transform():
+    return parse(
+        """
+    class defaultdict(dict):
+        default_factory = None
+        def __missing__(self, key): pass
+        def __getitem__(self, key): return default_factory
+
+    """
+        + _deque_mock()
+        + _ordered_dict_mock()
+    )
+
+
+def _deque_mock():
+    base_deque_class = """
+    class deque(object):
+        maxlen = 0
+        def __init__(self, iterable=None, maxlen=None):
+            self.iterable = iterable or []
+        def append(self, x): pass
+        def appendleft(self, x): pass
+        def clear(self): pass
+        def count(self, x): return 0
+        def extend(self, iterable): pass
+        def extendleft(self, iterable): pass
+        def pop(self): return self.iterable[0]
+        def popleft(self): return self.iterable[0]
+        def remove(self, value): pass
+        def reverse(self): return reversed(self.iterable)
+        def rotate(self, n=1): return self
+        def __iter__(self): return self
+        def __reversed__(self): return self.iterable[::-1]
+        def __getitem__(self, index): return self.iterable[index]
+        def __setitem__(self, index, value): pass
+        def __delitem__(self, index): pass
+        def __bool__(self): return bool(self.iterable)
+        def __nonzero__(self): return bool(self.iterable)
+        def __contains__(self, o): return o in self.iterable
+        def __len__(self): return len(self.iterable)
+        def __copy__(self): return deque(self.iterable)
+        def copy(self): return deque(self.iterable)
+        def index(self, x, start=0, end=0): return 0
+        def insert(self, i, x): pass
+        def __add__(self, other): pass
+        def __iadd__(self, other): pass
+        def __mul__(self, other): pass
+        def __imul__(self, other): pass
+        def __rmul__(self, other): pass"""
+    if PY39_PLUS:
+        base_deque_class += """
+        @classmethod
+        def __class_getitem__(self, item): return cls"""
+    return base_deque_class
+
+
+def _ordered_dict_mock():
+    base_ordered_dict_class = """
+    class OrderedDict(dict):
+        def __reversed__(self): return self[::-1]
+        def move_to_end(self, key, last=False): pass"""
+    if PY39_PLUS:
+        base_ordered_dict_class += """
+        @classmethod
+        def __class_getitem__(cls, item): return cls"""
+    return base_ordered_dict_class
+
+
+register_module_extender(AstroidManager(), "collections", _collections_transform)
+
+
+def _looks_like_subscriptable(node: ClassDef) -> bool:
+    """
+    Returns True if the node corresponds to a ClassDef of the Collections.abc module
+    that supports subscripting.
+
+    :param node: ClassDef node
+    """
+    if node.qname().startswith("_collections") or node.qname().startswith(
+        "collections"
+    ):
+        try:
+            node.getattr("__class_getitem__")
+            return True
+        except AttributeInferenceError:
+            pass
+    return False
+
+
+CLASS_GET_ITEM_TEMPLATE = """
+@classmethod
+def __class_getitem__(cls, item):
+    return cls
+"""
+
+
+def easy_class_getitem_inference(node, context: InferenceContext | None = None):
+    # Here __class_getitem__ exists but is quite a mess to infer thus
+    # put an easy inference tip
+    func_to_add = extract_node(CLASS_GET_ITEM_TEMPLATE)
+    node.locals["__class_getitem__"] = [func_to_add]
+
+
+if PY39_PLUS:
+    # Starting with Python39 some objects of the collection module are subscriptable
+    # thanks to the __class_getitem__ method but the way it is implemented in
+    # _collection_abc makes it difficult to infer. (We would have to handle AssignName inference in the
+    # getitem method of the ClassDef class) Instead we put here a mock of the __class_getitem__ method
+    AstroidManager().register_transform(
+        ClassDef, easy_class_getitem_inference, _looks_like_subscriptable
+    )

+ 25 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_crypt.py

@@ -0,0 +1,25 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def _re_transform():
+    return parse(
+        """
+    from collections import namedtuple
+    _Method = namedtuple('_Method', 'name ident salt_chars total_size')
+
+    METHOD_SHA512 = _Method('SHA512', '6', 16, 106)
+    METHOD_SHA256 = _Method('SHA256', '5', 16, 63)
+    METHOD_BLOWFISH = _Method('BLOWFISH', 2, 'b', 22)
+    METHOD_MD5 = _Method('MD5', '1', 8, 34)
+    METHOD_CRYPT = _Method('CRYPT', None, 2, 13)
+    """
+    )
+
+
+register_module_extender(AstroidManager(), "crypt", _re_transform)

+ 84 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_ctypes.py

@@ -0,0 +1,84 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""
+Astroid hooks for ctypes module.
+
+Inside the ctypes module, the value class is defined inside
+the C coded module _ctypes.
+Thus astroid doesn't know that the value member is a builtin type
+among float, int, bytes or str.
+"""
+import sys
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def enrich_ctypes_redefined_types():
+    """
+    For each ctypes redefined types, overload 'value' and '_type_' members
+    definition.
+
+    Overloading 'value' is mandatory otherwise astroid cannot infer the correct type for it.
+    Overloading '_type_' is necessary because the class definition made here replaces the original
+    one, in which '_type_' member is defined. Luckily those original class definitions are very short
+    and contain only the '_type_' member definition.
+    """
+    c_class_to_type = (
+        ("c_byte", "int", "b"),
+        ("c_char", "bytes", "c"),
+        ("c_double", "float", "d"),
+        ("c_float", "float", "f"),
+        ("c_int", "int", "i"),
+        ("c_int16", "int", "h"),
+        ("c_int32", "int", "i"),
+        ("c_int64", "int", "l"),
+        ("c_int8", "int", "b"),
+        ("c_long", "int", "l"),
+        ("c_longdouble", "float", "g"),
+        ("c_longlong", "int", "l"),
+        ("c_short", "int", "h"),
+        ("c_size_t", "int", "L"),
+        ("c_ssize_t", "int", "l"),
+        ("c_ubyte", "int", "B"),
+        ("c_uint", "int", "I"),
+        ("c_uint16", "int", "H"),
+        ("c_uint32", "int", "I"),
+        ("c_uint64", "int", "L"),
+        ("c_uint8", "int", "B"),
+        ("c_ulong", "int", "L"),
+        ("c_ulonglong", "int", "L"),
+        ("c_ushort", "int", "H"),
+        ("c_wchar", "str", "u"),
+    )
+
+    src = [
+        """
+from _ctypes import _SimpleCData
+
+class c_bool(_SimpleCData):
+    def __init__(self, value):
+        self.value = True
+        self._type_ = '?'
+    """
+    ]
+
+    for c_type, builtin_type, type_code in c_class_to_type:
+        src.append(
+            f"""
+class {c_type}(_SimpleCData):
+    def __init__(self, value):
+        self.value = {builtin_type}(value)
+        self._type_ = '{type_code}'
+        """
+        )
+
+    return parse("\n".join(src))
+
+
+if not hasattr(sys, "pypy_version_info"):
+    # No need of this module in pypy where everything is written in python
+    register_module_extender(AstroidManager(), "ctypes", enrich_ctypes_redefined_types)

+ 183 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_curses.py

@@ -0,0 +1,183 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def _curses_transform():
+    return parse(
+        """
+    A_ALTCHARSET = 1
+    A_BLINK = 1
+    A_BOLD = 1
+    A_DIM = 1
+    A_INVIS = 1
+    A_ITALIC = 1
+    A_NORMAL = 1
+    A_PROTECT = 1
+    A_REVERSE = 1
+    A_STANDOUT = 1
+    A_UNDERLINE = 1
+    A_HORIZONTAL = 1
+    A_LEFT = 1
+    A_LOW = 1
+    A_RIGHT = 1
+    A_TOP = 1
+    A_VERTICAL = 1
+    A_CHARTEXT = 1
+    A_ATTRIBUTES = 1
+    A_CHARTEXT = 1
+    A_COLOR = 1
+    KEY_MIN = 1
+    KEY_BREAK = 1
+    KEY_DOWN = 1
+    KEY_UP = 1
+    KEY_LEFT = 1
+    KEY_RIGHT = 1
+    KEY_HOME = 1
+    KEY_BACKSPACE = 1
+    KEY_F0 = 1
+    KEY_Fn = 1
+    KEY_DL = 1
+    KEY_IL = 1
+    KEY_DC = 1
+    KEY_IC = 1
+    KEY_EIC = 1
+    KEY_CLEAR = 1
+    KEY_EOS = 1
+    KEY_EOL = 1
+    KEY_SF = 1
+    KEY_SR = 1
+    KEY_NPAGE = 1
+    KEY_PPAGE = 1
+    KEY_STAB = 1
+    KEY_CTAB = 1
+    KEY_CATAB = 1
+    KEY_ENTER = 1
+    KEY_SRESET = 1
+    KEY_RESET = 1
+    KEY_PRINT = 1
+    KEY_LL = 1
+    KEY_A1 = 1
+    KEY_A3 = 1
+    KEY_B2 = 1
+    KEY_C1 = 1
+    KEY_C3 = 1
+    KEY_BTAB = 1
+    KEY_BEG = 1
+    KEY_CANCEL = 1
+    KEY_CLOSE = 1
+    KEY_COMMAND = 1
+    KEY_COPY = 1
+    KEY_CREATE = 1
+    KEY_END = 1
+    KEY_EXIT = 1
+    KEY_FIND = 1
+    KEY_HELP = 1
+    KEY_MARK = 1
+    KEY_MESSAGE = 1
+    KEY_MOVE = 1
+    KEY_NEXT = 1
+    KEY_OPEN = 1
+    KEY_OPTIONS = 1
+    KEY_PREVIOUS = 1
+    KEY_REDO = 1
+    KEY_REFERENCE = 1
+    KEY_REFRESH = 1
+    KEY_REPLACE = 1
+    KEY_RESTART = 1
+    KEY_RESUME = 1
+    KEY_SAVE = 1
+    KEY_SBEG = 1
+    KEY_SCANCEL = 1
+    KEY_SCOMMAND = 1
+    KEY_SCOPY = 1
+    KEY_SCREATE = 1
+    KEY_SDC = 1
+    KEY_SDL = 1
+    KEY_SELECT = 1
+    KEY_SEND = 1
+    KEY_SEOL = 1
+    KEY_SEXIT = 1
+    KEY_SFIND = 1
+    KEY_SHELP = 1
+    KEY_SHOME = 1
+    KEY_SIC = 1
+    KEY_SLEFT = 1
+    KEY_SMESSAGE = 1
+    KEY_SMOVE = 1
+    KEY_SNEXT = 1
+    KEY_SOPTIONS = 1
+    KEY_SPREVIOUS = 1
+    KEY_SPRINT = 1
+    KEY_SREDO = 1
+    KEY_SREPLACE = 1
+    KEY_SRIGHT = 1
+    KEY_SRSUME = 1
+    KEY_SSAVE = 1
+    KEY_SSUSPEND = 1
+    KEY_SUNDO = 1
+    KEY_SUSPEND = 1
+    KEY_UNDO = 1
+    KEY_MOUSE = 1
+    KEY_RESIZE = 1
+    KEY_MAX = 1
+    ACS_BBSS = 1
+    ACS_BLOCK = 1
+    ACS_BOARD = 1
+    ACS_BSBS = 1
+    ACS_BSSB = 1
+    ACS_BSSS = 1
+    ACS_BTEE = 1
+    ACS_BULLET = 1
+    ACS_CKBOARD = 1
+    ACS_DARROW = 1
+    ACS_DEGREE = 1
+    ACS_DIAMOND = 1
+    ACS_GEQUAL = 1
+    ACS_HLINE = 1
+    ACS_LANTERN = 1
+    ACS_LARROW = 1
+    ACS_LEQUAL = 1
+    ACS_LLCORNER = 1
+    ACS_LRCORNER = 1
+    ACS_LTEE = 1
+    ACS_NEQUAL = 1
+    ACS_PI = 1
+    ACS_PLMINUS = 1
+    ACS_PLUS = 1
+    ACS_RARROW = 1
+    ACS_RTEE = 1
+    ACS_S1 = 1
+    ACS_S3 = 1
+    ACS_S7 = 1
+    ACS_S9 = 1
+    ACS_SBBS = 1
+    ACS_SBSB = 1
+    ACS_SBSS = 1
+    ACS_SSBB = 1
+    ACS_SSBS = 1
+    ACS_SSSB = 1
+    ACS_SSSS = 1
+    ACS_STERLING = 1
+    ACS_TTEE = 1
+    ACS_UARROW = 1
+    ACS_ULCORNER = 1
+    ACS_URCORNER = 1
+    ACS_VLINE = 1
+    COLOR_BLACK = 1
+    COLOR_BLUE = 1
+    COLOR_CYAN = 1
+    COLOR_GREEN = 1
+    COLOR_MAGENTA = 1
+    COLOR_RED = 1
+    COLOR_WHITE = 1
+    COLOR_YELLOW = 1
+        """
+    )
+
+
+register_module_extender(AstroidManager(), "curses", _curses_transform)

+ 636 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_dataclasses.py

@@ -0,0 +1,636 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""
+Astroid hook for the dataclasses library.
+
+Support built-in dataclasses, pydantic.dataclasses, and marshmallow_dataclass-annotated
+dataclasses. References:
+- https://docs.python.org/3/library/dataclasses.html
+- https://pydantic-docs.helpmanual.io/usage/dataclasses/
+- https://lovasoa.github.io/marshmallow_dataclass/
+"""
+
+from __future__ import annotations
+
+import sys
+from collections.abc import Iterator
+from typing import Tuple, Union
+
+from astroid import bases, context, helpers, nodes
+from astroid.builder import parse
+from astroid.const import PY39_PLUS, PY310_PLUS
+from astroid.exceptions import AstroidSyntaxError, InferenceError, UseInferenceDefault
+from astroid.inference_tip import inference_tip
+from astroid.manager import AstroidManager
+from astroid.typing import InferenceResult
+from astroid.util import Uninferable, UninferableBase
+
+if sys.version_info >= (3, 8):
+    from typing import Literal
+else:
+    from typing_extensions import Literal
+
+_FieldDefaultReturn = Union[
+    None,
+    Tuple[Literal["default"], nodes.NodeNG],
+    Tuple[Literal["default_factory"], nodes.Call],
+]
+
+DATACLASSES_DECORATORS = frozenset(("dataclass",))
+FIELD_NAME = "field"
+DATACLASS_MODULES = frozenset(
+    ("dataclasses", "marshmallow_dataclass", "pydantic.dataclasses")
+)
+DEFAULT_FACTORY = "_HAS_DEFAULT_FACTORY"  # based on typing.py
+
+
+def is_decorated_with_dataclass(
+    node: nodes.ClassDef, decorator_names: frozenset[str] = DATACLASSES_DECORATORS
+) -> bool:
+    """Return True if a decorated node has a `dataclass` decorator applied."""
+    if not isinstance(node, nodes.ClassDef) or not node.decorators:
+        return False
+
+    return any(
+        _looks_like_dataclass_decorator(decorator_attribute, decorator_names)
+        for decorator_attribute in node.decorators.nodes
+    )
+
+
+def dataclass_transform(node: nodes.ClassDef) -> None:
+    """Rewrite a dataclass to be easily understood by pylint."""
+    node.is_dataclass = True
+
+    for assign_node in _get_dataclass_attributes(node):
+        name = assign_node.target.name
+
+        rhs_node = nodes.Unknown(
+            lineno=assign_node.lineno,
+            col_offset=assign_node.col_offset,
+            parent=assign_node,
+        )
+        rhs_node = AstroidManager().visit_transforms(rhs_node)
+        node.instance_attrs[name] = [rhs_node]
+
+    if not _check_generate_dataclass_init(node):
+        return
+
+    kw_only_decorated = False
+    if PY310_PLUS and node.decorators.nodes:
+        for decorator in node.decorators.nodes:
+            if not isinstance(decorator, nodes.Call):
+                kw_only_decorated = False
+                break
+            for keyword in decorator.keywords:
+                if keyword.arg == "kw_only":
+                    kw_only_decorated = keyword.value.bool_value()
+
+    init_str = _generate_dataclass_init(
+        node,
+        list(_get_dataclass_attributes(node, init=True)),
+        kw_only_decorated,
+    )
+
+    try:
+        init_node = parse(init_str)["__init__"]
+    except AstroidSyntaxError:
+        pass
+    else:
+        init_node.parent = node
+        init_node.lineno, init_node.col_offset = None, None
+        node.locals["__init__"] = [init_node]
+
+        root = node.root()
+        if DEFAULT_FACTORY not in root.locals:
+            new_assign = parse(f"{DEFAULT_FACTORY} = object()").body[0]
+            new_assign.parent = root
+            root.locals[DEFAULT_FACTORY] = [new_assign.targets[0]]
+
+
+def _get_dataclass_attributes(
+    node: nodes.ClassDef, init: bool = False
+) -> Iterator[nodes.AnnAssign]:
+    """Yield the AnnAssign nodes of dataclass attributes for the node.
+
+    If init is True, also include InitVars.
+    """
+    for assign_node in node.body:
+        if not isinstance(assign_node, nodes.AnnAssign) or not isinstance(
+            assign_node.target, nodes.AssignName
+        ):
+            continue
+
+        # Annotation is never None
+        if _is_class_var(assign_node.annotation):  # type: ignore[arg-type]
+            continue
+
+        if _is_keyword_only_sentinel(assign_node.annotation):
+            continue
+
+        # Annotation is never None
+        if not init and _is_init_var(assign_node.annotation):  # type: ignore[arg-type]
+            continue
+
+        yield assign_node
+
+
+def _check_generate_dataclass_init(node: nodes.ClassDef) -> bool:
+    """Return True if we should generate an __init__ method for node.
+
+    This is True when:
+        - node doesn't define its own __init__ method
+        - the dataclass decorator was called *without* the keyword argument init=False
+    """
+    if "__init__" in node.locals:
+        return False
+
+    found = None
+
+    for decorator_attribute in node.decorators.nodes:
+        if not isinstance(decorator_attribute, nodes.Call):
+            continue
+
+        if _looks_like_dataclass_decorator(decorator_attribute):
+            found = decorator_attribute
+
+    if found is None:
+        return True
+
+    # Check for keyword arguments of the form init=False
+    return not any(
+        keyword.arg == "init"
+        and not keyword.value.bool_value()  # type: ignore[union-attr] # value is never None
+        for keyword in found.keywords
+    )
+
+
+def _find_arguments_from_base_classes(
+    node: nodes.ClassDef,
+) -> tuple[
+    dict[str, tuple[str | None, str | None]], dict[str, tuple[str | None, str | None]]
+]:
+    """Iterate through all bases and get their typing and defaults."""
+    pos_only_store: dict[str, tuple[str | None, str | None]] = {}
+    kw_only_store: dict[str, tuple[str | None, str | None]] = {}
+    # See TODO down below
+    # all_have_defaults = True
+
+    for base in reversed(node.mro()):
+        if not base.is_dataclass:
+            continue
+        try:
+            base_init: nodes.FunctionDef = base.locals["__init__"][0]
+        except KeyError:
+            continue
+
+        pos_only, kw_only = base_init.args._get_arguments_data()
+        for posarg, data in pos_only.items():
+            # if data[1] is None:
+            #     if all_have_defaults and pos_only_store:
+            #         # TODO: This should return an Uninferable as this would raise
+            #         # a TypeError at runtime. However, transforms can't return
+            #         # Uninferables currently.
+            #         pass
+            #     all_have_defaults = False
+            pos_only_store[posarg] = data
+
+        for kwarg, data in kw_only.items():
+            kw_only_store[kwarg] = data
+    return pos_only_store, kw_only_store
+
+
+def _parse_arguments_into_strings(
+    pos_only_store: dict[str, tuple[str | None, str | None]],
+    kw_only_store: dict[str, tuple[str | None, str | None]],
+) -> tuple[str, str]:
+    """Parse positional and keyword arguments into strings for an __init__ method."""
+    pos_only, kw_only = "", ""
+    for pos_arg, data in pos_only_store.items():
+        pos_only += pos_arg
+        if data[0]:
+            pos_only += ": " + data[0]
+        if data[1]:
+            pos_only += " = " + data[1]
+        pos_only += ", "
+    for kw_arg, data in kw_only_store.items():
+        kw_only += kw_arg
+        if data[0]:
+            kw_only += ": " + data[0]
+        if data[1]:
+            kw_only += " = " + data[1]
+        kw_only += ", "
+
+    return pos_only, kw_only
+
+
+def _get_previous_field_default(node: nodes.ClassDef, name: str) -> nodes.NodeNG | None:
+    """Get the default value of a previously defined field."""
+    for base in reversed(node.mro()):
+        if not base.is_dataclass:
+            continue
+        if name in base.locals:
+            for assign in base.locals[name]:
+                if (
+                    isinstance(assign.parent, nodes.AnnAssign)
+                    and assign.parent.value
+                    and isinstance(assign.parent.value, nodes.Call)
+                    and _looks_like_dataclass_field_call(assign.parent.value)
+                ):
+                    default = _get_field_default(assign.parent.value)
+                    if default:
+                        return default[1]
+    return None
+
+
+def _generate_dataclass_init(  # pylint: disable=too-many-locals
+    node: nodes.ClassDef, assigns: list[nodes.AnnAssign], kw_only_decorated: bool
+) -> str:
+    """Return an init method for a dataclass given the targets."""
+    params: list[str] = []
+    kw_only_params: list[str] = []
+    assignments: list[str] = []
+
+    prev_pos_only_store, prev_kw_only_store = _find_arguments_from_base_classes(node)
+
+    for assign in assigns:
+        name, annotation, value = assign.target.name, assign.annotation, assign.value
+
+        # Check whether this assign is overriden by a property assignment
+        property_node: nodes.FunctionDef | None = None
+        for additional_assign in node.locals[name]:
+            if not isinstance(additional_assign, nodes.FunctionDef):
+                continue
+            if not additional_assign.decorators:
+                continue
+            if "builtins.property" in additional_assign.decoratornames():
+                property_node = additional_assign
+                break
+
+        is_field = isinstance(value, nodes.Call) and _looks_like_dataclass_field_call(
+            value, check_scope=False
+        )
+
+        if is_field:
+            # Skip any fields that have `init=False`
+            if any(
+                keyword.arg == "init" and not keyword.value.bool_value()
+                for keyword in value.keywords  # type: ignore[union-attr] # value is never None
+            ):
+                # Also remove the name from the previous arguments to be inserted later
+                prev_pos_only_store.pop(name, None)
+                prev_kw_only_store.pop(name, None)
+                continue
+
+        if _is_init_var(annotation):  # type: ignore[arg-type] # annotation is never None
+            init_var = True
+            if isinstance(annotation, nodes.Subscript):
+                annotation = annotation.slice
+            else:
+                # Cannot determine type annotation for parameter from InitVar
+                annotation = None
+            assignment_str = ""
+        else:
+            init_var = False
+            assignment_str = f"self.{name} = {name}"
+
+        ann_str, default_str = None, None
+        if annotation is not None:
+            ann_str = annotation.as_string()
+
+        if value:
+            if is_field:
+                result = _get_field_default(value)  # type: ignore[arg-type]
+                if result:
+                    default_type, default_node = result
+                    if default_type == "default":
+                        default_str = default_node.as_string()
+                    elif default_type == "default_factory":
+                        default_str = DEFAULT_FACTORY
+                        assignment_str = (
+                            f"self.{name} = {default_node.as_string()} "
+                            f"if {name} is {DEFAULT_FACTORY} else {name}"
+                        )
+            else:
+                default_str = value.as_string()
+        elif property_node:
+            # We set the result of the property call as default
+            # This hides the fact that this would normally be a 'property object'
+            # But we can't represent those as string
+            try:
+                # Call str to make sure also Uninferable gets stringified
+                default_str = str(next(property_node.infer_call_result()).as_string())
+            except (InferenceError, StopIteration):
+                pass
+        else:
+            # Even with `init=False` the default value still can be propogated to
+            # later assignments. Creating weird signatures like:
+            # (self, a: str = 1) -> None
+            previous_default = _get_previous_field_default(node, name)
+            if previous_default:
+                default_str = previous_default.as_string()
+
+        # Construct the param string to add to the init if necessary
+        param_str = name
+        if ann_str is not None:
+            param_str += f": {ann_str}"
+        if default_str is not None:
+            param_str += f" = {default_str}"
+
+        # If the field is a kw_only field, we need to add it to the kw_only_params
+        # This overwrites whether or not the class is kw_only decorated
+        if is_field:
+            kw_only = [k for k in value.keywords if k.arg == "kw_only"]  # type: ignore[union-attr]
+            if kw_only:
+                if kw_only[0].value.bool_value():
+                    kw_only_params.append(param_str)
+                else:
+                    params.append(param_str)
+                continue
+        # If kw_only decorated, we need to add all parameters to the kw_only_params
+        if kw_only_decorated:
+            if name in prev_kw_only_store:
+                prev_kw_only_store[name] = (ann_str, default_str)
+            else:
+                kw_only_params.append(param_str)
+        else:
+            # If the name was previously seen, overwrite that data
+            # pylint: disable-next=else-if-used
+            if name in prev_pos_only_store:
+                prev_pos_only_store[name] = (ann_str, default_str)
+            elif name in prev_kw_only_store:
+                params = [name] + params
+                prev_kw_only_store.pop(name)
+            else:
+                params.append(param_str)
+
+        if not init_var:
+            assignments.append(assignment_str)
+
+    prev_pos_only, prev_kw_only = _parse_arguments_into_strings(
+        prev_pos_only_store, prev_kw_only_store
+    )
+
+    # Construct the new init method paramter string
+    # First we do the positional only parameters, making sure to add the
+    # the self parameter and the comma to allow adding keyword only parameters
+    params_string = "" if "self" in prev_pos_only else "self, "
+    params_string += prev_pos_only + ", ".join(params)
+    if not params_string.endswith(", "):
+        params_string += ", "
+
+    # Then we add the keyword only parameters
+    if prev_kw_only or kw_only_params:
+        params_string += "*, "
+    params_string += f"{prev_kw_only}{', '.join(kw_only_params)}"
+
+    assignments_string = "\n    ".join(assignments) if assignments else "pass"
+    return f"def __init__({params_string}) -> None:\n    {assignments_string}"
+
+
+def infer_dataclass_attribute(
+    node: nodes.Unknown, ctx: context.InferenceContext | None = None
+) -> Iterator[InferenceResult]:
+    """Inference tip for an Unknown node that was dynamically generated to
+    represent a dataclass attribute.
+
+    In the case that a default value is provided, that is inferred first.
+    Then, an Instance of the annotated class is yielded.
+    """
+    assign = node.parent
+    if not isinstance(assign, nodes.AnnAssign):
+        yield Uninferable
+        return
+
+    annotation, value = assign.annotation, assign.value
+    if value is not None:
+        yield from value.infer(context=ctx)
+    if annotation is not None:
+        yield from _infer_instance_from_annotation(annotation, ctx=ctx)
+    else:
+        yield Uninferable
+
+
+def infer_dataclass_field_call(
+    node: nodes.Call, ctx: context.InferenceContext | None = None
+) -> Iterator[InferenceResult]:
+    """Inference tip for dataclass field calls."""
+    if not isinstance(node.parent, (nodes.AnnAssign, nodes.Assign)):
+        raise UseInferenceDefault
+    result = _get_field_default(node)
+    if not result:
+        yield Uninferable
+    else:
+        default_type, default = result
+        if default_type == "default":
+            yield from default.infer(context=ctx)
+        else:
+            new_call = parse(default.as_string()).body[0].value
+            new_call.parent = node.parent
+            yield from new_call.infer(context=ctx)
+
+
+def _looks_like_dataclass_decorator(
+    node: nodes.NodeNG, decorator_names: frozenset[str] = DATACLASSES_DECORATORS
+) -> bool:
+    """Return True if node looks like a dataclass decorator.
+
+    Uses inference to lookup the value of the node, and if that fails,
+    matches against specific names.
+    """
+    if isinstance(node, nodes.Call):  # decorator with arguments
+        node = node.func
+    try:
+        inferred = next(node.infer())
+    except (InferenceError, StopIteration):
+        inferred = Uninferable
+
+    if isinstance(inferred, UninferableBase):
+        if isinstance(node, nodes.Name):
+            return node.name in decorator_names
+        if isinstance(node, nodes.Attribute):
+            return node.attrname in decorator_names
+
+        return False
+
+    return (
+        isinstance(inferred, nodes.FunctionDef)
+        and inferred.name in decorator_names
+        and inferred.root().name in DATACLASS_MODULES
+    )
+
+
+def _looks_like_dataclass_attribute(node: nodes.Unknown) -> bool:
+    """Return True if node was dynamically generated as the child of an AnnAssign
+    statement.
+    """
+    parent = node.parent
+    if not parent:
+        return False
+
+    scope = parent.scope()
+    return (
+        isinstance(parent, nodes.AnnAssign)
+        and isinstance(scope, nodes.ClassDef)
+        and is_decorated_with_dataclass(scope)
+    )
+
+
+def _looks_like_dataclass_field_call(
+    node: nodes.Call, check_scope: bool = True
+) -> bool:
+    """Return True if node is calling dataclasses field or Field
+    from an AnnAssign statement directly in the body of a ClassDef.
+
+    If check_scope is False, skips checking the statement and body.
+    """
+    if check_scope:
+        stmt = node.statement(future=True)
+        scope = stmt.scope()
+        if not (
+            isinstance(stmt, nodes.AnnAssign)
+            and stmt.value is not None
+            and isinstance(scope, nodes.ClassDef)
+            and is_decorated_with_dataclass(scope)
+        ):
+            return False
+
+    try:
+        inferred = next(node.func.infer())
+    except (InferenceError, StopIteration):
+        return False
+
+    if not isinstance(inferred, nodes.FunctionDef):
+        return False
+
+    return inferred.name == FIELD_NAME and inferred.root().name in DATACLASS_MODULES
+
+
+def _get_field_default(field_call: nodes.Call) -> _FieldDefaultReturn:
+    """Return a the default value of a field call, and the corresponding keyword
+    argument name.
+
+    field(default=...) results in the ... node
+    field(default_factory=...) results in a Call node with func ... and no arguments
+
+    If neither or both arguments are present, return ("", None) instead,
+    indicating that there is not a valid default value.
+    """
+    default, default_factory = None, None
+    for keyword in field_call.keywords:
+        if keyword.arg == "default":
+            default = keyword.value
+        elif keyword.arg == "default_factory":
+            default_factory = keyword.value
+
+    if default is not None and default_factory is None:
+        return "default", default
+
+    if default is None and default_factory is not None:
+        new_call = nodes.Call(
+            lineno=field_call.lineno,
+            col_offset=field_call.col_offset,
+            parent=field_call.parent,
+        )
+        new_call.postinit(func=default_factory)
+        return "default_factory", new_call
+
+    return None
+
+
+def _is_class_var(node: nodes.NodeNG) -> bool:
+    """Return True if node is a ClassVar, with or without subscripting."""
+    if PY39_PLUS:
+        try:
+            inferred = next(node.infer())
+        except (InferenceError, StopIteration):
+            return False
+
+        return getattr(inferred, "name", "") == "ClassVar"
+
+    # Before Python 3.9, inference returns typing._SpecialForm instead of ClassVar.
+    # Our backup is to inspect the node's structure.
+    return isinstance(node, nodes.Subscript) and (
+        isinstance(node.value, nodes.Name)
+        and node.value.name == "ClassVar"
+        or isinstance(node.value, nodes.Attribute)
+        and node.value.attrname == "ClassVar"
+    )
+
+
+def _is_keyword_only_sentinel(node: nodes.NodeNG) -> bool:
+    """Return True if node is the KW_ONLY sentinel."""
+    if not PY310_PLUS:
+        return False
+    inferred = helpers.safe_infer(node)
+    return (
+        isinstance(inferred, bases.Instance)
+        and inferred.qname() == "dataclasses._KW_ONLY_TYPE"
+    )
+
+
+def _is_init_var(node: nodes.NodeNG) -> bool:
+    """Return True if node is an InitVar, with or without subscripting."""
+    try:
+        inferred = next(node.infer())
+    except (InferenceError, StopIteration):
+        return False
+
+    return getattr(inferred, "name", "") == "InitVar"
+
+
+# Allowed typing classes for which we support inferring instances
+_INFERABLE_TYPING_TYPES = frozenset(
+    (
+        "Dict",
+        "FrozenSet",
+        "List",
+        "Set",
+        "Tuple",
+    )
+)
+
+
+def _infer_instance_from_annotation(
+    node: nodes.NodeNG, ctx: context.InferenceContext | None = None
+) -> Iterator[UninferableBase | bases.Instance]:
+    """Infer an instance corresponding to the type annotation represented by node.
+
+    Currently has limited support for the typing module.
+    """
+    klass = None
+    try:
+        klass = next(node.infer(context=ctx))
+    except (InferenceError, StopIteration):
+        yield Uninferable
+    if not isinstance(klass, nodes.ClassDef):
+        yield Uninferable
+    elif klass.root().name in {
+        "typing",
+        "_collections_abc",
+        "",
+    }:  # "" because of synthetic nodes in brain_typing.py
+        if klass.name in _INFERABLE_TYPING_TYPES:
+            yield klass.instantiate_class()
+        else:
+            yield Uninferable
+    else:
+        yield klass.instantiate_class()
+
+
+AstroidManager().register_transform(
+    nodes.ClassDef, dataclass_transform, is_decorated_with_dataclass
+)
+
+AstroidManager().register_transform(
+    nodes.Call,
+    inference_tip(infer_dataclass_field_call, raise_on_overwrite=True),
+    _looks_like_dataclass_field_call,
+)
+
+AstroidManager().register_transform(
+    nodes.Unknown,
+    inference_tip(infer_dataclass_attribute, raise_on_overwrite=True),
+    _looks_like_dataclass_attribute,
+)

+ 26 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_dateutil.py

@@ -0,0 +1,26 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for dateutil."""
+
+import textwrap
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import AstroidBuilder
+from astroid.manager import AstroidManager
+
+
+def dateutil_transform():
+    return AstroidBuilder(AstroidManager()).string_build(
+        textwrap.dedent(
+            """
+    import datetime
+    def parse(timestr, parserinfo=None, **kwargs):
+        return datetime.datetime()
+    """
+        )
+    )
+
+
+register_module_extender(AstroidManager(), "dateutil.parser", dateutil_transform)

+ 61 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_fstrings.py

@@ -0,0 +1,61 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+import collections.abc
+from typing import TypeVar
+
+from astroid import nodes
+from astroid.manager import AstroidManager
+
+_NodeT = TypeVar("_NodeT", bound=nodes.NodeNG)
+
+
+def _clone_node_with_lineno(
+    node: _NodeT, parent: nodes.NodeNG, lineno: int | None
+) -> _NodeT:
+    cls = node.__class__
+    other_fields = node._other_fields
+    _astroid_fields = node._astroid_fields
+    init_params = {"lineno": lineno, "col_offset": node.col_offset, "parent": parent}
+    postinit_params = {param: getattr(node, param) for param in _astroid_fields}
+    if other_fields:
+        init_params.update({param: getattr(node, param) for param in other_fields})
+    new_node = cls(**init_params)
+    if hasattr(node, "postinit") and _astroid_fields:
+        for param, child in postinit_params.items():
+            if child and not isinstance(child, collections.abc.Sequence):
+                cloned_child = _clone_node_with_lineno(
+                    node=child, lineno=new_node.lineno, parent=new_node
+                )
+                postinit_params[param] = cloned_child
+        new_node.postinit(**postinit_params)
+    return new_node
+
+
+def _transform_formatted_value(  # pylint: disable=inconsistent-return-statements
+    node: nodes.FormattedValue,
+) -> nodes.FormattedValue | None:
+    if node.value and node.value.lineno == 1:
+        if node.lineno != node.value.lineno:
+            new_node = nodes.FormattedValue(
+                lineno=node.lineno, col_offset=node.col_offset, parent=node.parent
+            )
+            new_value = _clone_node_with_lineno(
+                node=node.value, lineno=node.lineno, parent=new_node
+            )
+            new_node.postinit(
+                value=new_value,
+                conversion=node.conversion,
+                format_spec=node.format_spec,
+            )
+            return new_node
+
+
+# TODO: this fix tries to *patch* http://bugs.python.org/issue29051
+# The problem is that FormattedValue.value, which is a Name node,
+# has wrong line numbers, usually 1. This creates problems for pylint,
+# which expects correct line numbers for things such as message control.
+AstroidManager().register_transform(nodes.FormattedValue, _transform_formatted_value)

+ 166 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_functools.py

@@ -0,0 +1,166 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for understanding functools library module."""
+
+from __future__ import annotations
+
+from collections.abc import Iterator
+from functools import partial
+from itertools import chain
+
+from astroid import BoundMethod, arguments, extract_node, helpers, nodes, objects
+from astroid.context import InferenceContext
+from astroid.exceptions import InferenceError, UseInferenceDefault
+from astroid.inference_tip import inference_tip
+from astroid.interpreter import objectmodel
+from astroid.manager import AstroidManager
+from astroid.nodes.node_classes import AssignName, Attribute, Call, Name
+from astroid.nodes.scoped_nodes import FunctionDef
+from astroid.util import UninferableBase
+
+LRU_CACHE = "functools.lru_cache"
+
+
+class LruWrappedModel(objectmodel.FunctionModel):
+    """Special attribute model for functions decorated with functools.lru_cache.
+
+    The said decorators patches at decoration time some functions onto
+    the decorated function.
+    """
+
+    @property
+    def attr___wrapped__(self):
+        return self._instance
+
+    @property
+    def attr_cache_info(self):
+        cache_info = extract_node(
+            """
+        from functools import _CacheInfo
+        _CacheInfo(0, 0, 0, 0)
+        """
+        )
+
+        class CacheInfoBoundMethod(BoundMethod):
+            def infer_call_result(
+                self, caller, context: InferenceContext | None = None
+            ):
+                yield helpers.safe_infer(cache_info)
+
+        return CacheInfoBoundMethod(proxy=self._instance, bound=self._instance)
+
+    @property
+    def attr_cache_clear(self):
+        node = extract_node("""def cache_clear(self): pass""")
+        return BoundMethod(proxy=node, bound=self._instance.parent.scope())
+
+
+def _transform_lru_cache(node, context: InferenceContext | None = None) -> None:
+    # TODO: this is not ideal, since the node should be immutable,
+    # but due to https://github.com/PyCQA/astroid/issues/354,
+    # there's not much we can do now.
+    # Replacing the node would work partially, because,
+    # in pylint, the old node would still be available, leading
+    # to spurious false positives.
+    node.special_attributes = LruWrappedModel()(node)
+
+
+def _functools_partial_inference(
+    node: nodes.Call, context: InferenceContext | None = None
+) -> Iterator[objects.PartialFunction]:
+    call = arguments.CallSite.from_call(node, context=context)
+    number_of_positional = len(call.positional_arguments)
+    if number_of_positional < 1:
+        raise UseInferenceDefault("functools.partial takes at least one argument")
+    if number_of_positional == 1 and not call.keyword_arguments:
+        raise UseInferenceDefault(
+            "functools.partial needs at least to have some filled arguments"
+        )
+
+    partial_function = call.positional_arguments[0]
+    try:
+        inferred_wrapped_function = next(partial_function.infer(context=context))
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+    if isinstance(inferred_wrapped_function, UninferableBase):
+        raise UseInferenceDefault("Cannot infer the wrapped function")
+    if not isinstance(inferred_wrapped_function, FunctionDef):
+        raise UseInferenceDefault("The wrapped function is not a function")
+
+    # Determine if the passed keywords into the callsite are supported
+    # by the wrapped function.
+    if not inferred_wrapped_function.args:
+        function_parameters = []
+    else:
+        function_parameters = chain(
+            inferred_wrapped_function.args.args or (),
+            inferred_wrapped_function.args.posonlyargs or (),
+            inferred_wrapped_function.args.kwonlyargs or (),
+        )
+    parameter_names = {
+        param.name for param in function_parameters if isinstance(param, AssignName)
+    }
+    if set(call.keyword_arguments) - parameter_names:
+        raise UseInferenceDefault("wrapped function received unknown parameters")
+
+    partial_function = objects.PartialFunction(
+        call,
+        name=inferred_wrapped_function.name,
+        lineno=inferred_wrapped_function.lineno,
+        col_offset=inferred_wrapped_function.col_offset,
+        parent=node.parent,
+    )
+    partial_function.postinit(
+        args=inferred_wrapped_function.args,
+        body=inferred_wrapped_function.body,
+        decorators=inferred_wrapped_function.decorators,
+        returns=inferred_wrapped_function.returns,
+        type_comment_returns=inferred_wrapped_function.type_comment_returns,
+        type_comment_args=inferred_wrapped_function.type_comment_args,
+        doc_node=inferred_wrapped_function.doc_node,
+    )
+    return iter((partial_function,))
+
+
+def _looks_like_lru_cache(node) -> bool:
+    """Check if the given function node is decorated with lru_cache."""
+    if not node.decorators:
+        return False
+    for decorator in node.decorators.nodes:
+        if not isinstance(decorator, (Attribute, Call)):
+            continue
+        if _looks_like_functools_member(decorator, "lru_cache"):
+            return True
+    return False
+
+
+def _looks_like_functools_member(node: Attribute | Call, member: str) -> bool:
+    """Check if the given Call node is the wanted member of functools."""
+    if isinstance(node, Attribute):
+        return node.attrname == member
+    if isinstance(node.func, Name):
+        return node.func.name == member
+    if isinstance(node.func, Attribute):
+        return (
+            node.func.attrname == member
+            and isinstance(node.func.expr, Name)
+            and node.func.expr.name == "functools"
+        )
+    return False
+
+
+_looks_like_partial = partial(_looks_like_functools_member, member="partial")
+
+
+AstroidManager().register_transform(
+    FunctionDef, _transform_lru_cache, _looks_like_lru_cache
+)
+
+
+AstroidManager().register_transform(
+    Call,
+    inference_tip(_functools_partial_inference),
+    _looks_like_partial,
+)

+ 249 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_gi.py

@@ -0,0 +1,249 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for the Python 2 GObject introspection bindings.
+
+Helps with understanding everything imported from 'gi.repository'
+"""
+
+# pylint:disable=import-error,import-outside-toplevel
+
+import inspect
+import itertools
+import re
+import sys
+import warnings
+
+from astroid import nodes
+from astroid.builder import AstroidBuilder
+from astroid.exceptions import AstroidBuildingError
+from astroid.manager import AstroidManager
+
+_inspected_modules = {}
+
+_identifier_re = r"^[A-Za-z_]\w*$"
+
+_special_methods = frozenset(
+    {
+        "__lt__",
+        "__le__",
+        "__eq__",
+        "__ne__",
+        "__ge__",
+        "__gt__",
+        "__iter__",
+        "__getitem__",
+        "__setitem__",
+        "__delitem__",
+        "__len__",
+        "__bool__",
+        "__nonzero__",
+        "__next__",
+        "__str__",
+        "__contains__",
+        "__enter__",
+        "__exit__",
+        "__repr__",
+        "__getattr__",
+        "__setattr__",
+        "__delattr__",
+        "__del__",
+        "__hash__",
+    }
+)
+
+
+def _gi_build_stub(parent):  # noqa: C901
+    """
+    Inspect the passed module recursively and build stubs for functions,
+    classes, etc.
+    """
+    classes = {}
+    functions = {}
+    constants = {}
+    methods = {}
+    for name in dir(parent):
+        if name.startswith("__") and name not in _special_methods:
+            continue
+
+        # Check if this is a valid name in python
+        if not re.match(_identifier_re, name):
+            continue
+
+        try:
+            obj = getattr(parent, name)
+        except Exception:  # pylint: disable=broad-except
+            # gi.module.IntrospectionModule.__getattr__() can raise all kinds of things
+            # like ValueError, TypeError, NotImplementedError, RepositoryError, etc
+            continue
+
+        if inspect.isclass(obj):
+            classes[name] = obj
+        elif inspect.isfunction(obj) or inspect.isbuiltin(obj):
+            functions[name] = obj
+        elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj):
+            methods[name] = obj
+        elif (
+            str(obj).startswith("<flags")
+            or str(obj).startswith("<enum ")
+            or str(obj).startswith("<GType ")
+            or inspect.isdatadescriptor(obj)
+        ):
+            constants[name] = 0
+        elif isinstance(obj, (int, str)):
+            constants[name] = obj
+        elif callable(obj):
+            # Fall back to a function for anything callable
+            functions[name] = obj
+        else:
+            # Assume everything else is some manner of constant
+            constants[name] = 0
+
+    ret = ""
+
+    if constants:
+        ret += f"# {parent.__name__} constants\n\n"
+    for name in sorted(constants):
+        if name[0].isdigit():
+            # GDK has some busted constant names like
+            # Gdk.EventType.2BUTTON_PRESS
+            continue
+
+        val = constants[name]
+
+        strval = str(val)
+        if isinstance(val, str):
+            strval = '"%s"' % str(val).replace("\\", "\\\\")
+        ret += f"{name} = {strval}\n"
+
+    if ret:
+        ret += "\n\n"
+    if functions:
+        ret += f"# {parent.__name__} functions\n\n"
+    for name in sorted(functions):
+        ret += f"def {name}(*args, **kwargs):\n"
+        ret += "    pass\n"
+
+    if ret:
+        ret += "\n\n"
+    if methods:
+        ret += f"# {parent.__name__} methods\n\n"
+    for name in sorted(methods):
+        ret += f"def {name}(self, *args, **kwargs):\n"
+        ret += "    pass\n"
+
+    if ret:
+        ret += "\n\n"
+    if classes:
+        ret += f"# {parent.__name__} classes\n\n"
+    for name, obj in sorted(classes.items()):
+        base = "object"
+        if issubclass(obj, Exception):
+            base = "Exception"
+        ret += f"class {name}({base}):\n"
+
+        classret = _gi_build_stub(obj)
+        if not classret:
+            classret = "pass\n"
+
+        for line in classret.splitlines():
+            ret += "    " + line + "\n"
+        ret += "\n"
+
+    return ret
+
+
+def _import_gi_module(modname):
+    # we only consider gi.repository submodules
+    if not modname.startswith("gi.repository."):
+        raise AstroidBuildingError(modname=modname)
+    # build astroid representation unless we already tried so
+    if modname not in _inspected_modules:
+        modnames = [modname]
+        optional_modnames = []
+
+        # GLib and GObject may have some special case handling
+        # in pygobject that we need to cope with. However at
+        # least as of pygobject3-3.13.91 the _glib module doesn't
+        # exist anymore, so if treat these modules as optional.
+        if modname == "gi.repository.GLib":
+            optional_modnames.append("gi._glib")
+        elif modname == "gi.repository.GObject":
+            optional_modnames.append("gi._gobject")
+
+        try:
+            modcode = ""
+            for m in itertools.chain(modnames, optional_modnames):
+                try:
+                    with warnings.catch_warnings():
+                        # Just inspecting the code can raise gi deprecation
+                        # warnings, so ignore them.
+                        try:
+                            from gi import (  # pylint:disable=import-error
+                                PyGIDeprecationWarning,
+                                PyGIWarning,
+                            )
+
+                            warnings.simplefilter("ignore", PyGIDeprecationWarning)
+                            warnings.simplefilter("ignore", PyGIWarning)
+                        except Exception:  # pylint:disable=broad-except
+                            pass
+
+                        __import__(m)
+                        modcode += _gi_build_stub(sys.modules[m])
+                except ImportError:
+                    if m not in optional_modnames:
+                        raise
+        except ImportError:
+            astng = _inspected_modules[modname] = None
+        else:
+            astng = AstroidBuilder(AstroidManager()).string_build(modcode, modname)
+            _inspected_modules[modname] = astng
+    else:
+        astng = _inspected_modules[modname]
+    if astng is None:
+        raise AstroidBuildingError(modname=modname)
+    return astng
+
+
+def _looks_like_require_version(node) -> bool:
+    # Return whether this looks like a call to gi.require_version(<name>, <version>)
+    # Only accept function calls with two constant arguments
+    if len(node.args) != 2:
+        return False
+
+    if not all(isinstance(arg, nodes.Const) for arg in node.args):
+        return False
+
+    func = node.func
+    if isinstance(func, nodes.Attribute):
+        if func.attrname != "require_version":
+            return False
+        if isinstance(func.expr, nodes.Name) and func.expr.name == "gi":
+            return True
+
+        return False
+
+    if isinstance(func, nodes.Name):
+        return func.name == "require_version"
+
+    return False
+
+
+def _register_require_version(node):
+    # Load the gi.require_version locally
+    try:
+        import gi
+
+        gi.require_version(node.args[0].value, node.args[1].value)
+    except Exception:  # pylint:disable=broad-except
+        pass
+
+    return node
+
+
+AstroidManager().register_failed_import_hook(_import_gi_module)
+AstroidManager().register_transform(
+    nodes.Call, _register_require_version, _looks_like_require_version
+)

+ 96 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_hashlib.py

@@ -0,0 +1,96 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.const import PY39_PLUS
+from astroid.manager import AstroidManager
+
+
+def _hashlib_transform():
+    maybe_usedforsecurity = ", usedforsecurity=True" if PY39_PLUS else ""
+    init_signature = f"value=''{maybe_usedforsecurity}"
+    digest_signature = "self"
+    shake_digest_signature = "self, length"
+
+    template = """
+    class %(name)s:
+        def __init__(self, %(init_signature)s): pass
+        def digest(%(digest_signature)s):
+            return %(digest)s
+        def copy(self):
+            return self
+        def update(self, value): pass
+        def hexdigest(%(digest_signature)s):
+            return ''
+        @property
+        def name(self):
+            return %(name)r
+        @property
+        def block_size(self):
+            return 1
+        @property
+        def digest_size(self):
+            return 1
+    """
+
+    algorithms_with_signature = dict.fromkeys(
+        [
+            "md5",
+            "sha1",
+            "sha224",
+            "sha256",
+            "sha384",
+            "sha512",
+            "sha3_224",
+            "sha3_256",
+            "sha3_384",
+            "sha3_512",
+        ],
+        (init_signature, digest_signature),
+    )
+
+    blake2b_signature = (
+        "data=b'', *, digest_size=64, key=b'', salt=b'', "
+        "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, "
+        f"node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}"
+    )
+
+    blake2s_signature = (
+        "data=b'', *, digest_size=32, key=b'', salt=b'', "
+        "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, "
+        f"node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}"
+    )
+
+    shake_algorithms = dict.fromkeys(
+        ["shake_128", "shake_256"],
+        (init_signature, shake_digest_signature),
+    )
+    algorithms_with_signature.update(shake_algorithms)
+
+    algorithms_with_signature.update(
+        {
+            "blake2b": (blake2b_signature, digest_signature),
+            "blake2s": (blake2s_signature, digest_signature),
+        }
+    )
+
+    classes = "".join(
+        template
+        % {
+            "name": hashfunc,
+            "digest": 'b""',
+            "init_signature": init_signature,
+            "digest_signature": digest_signature,
+        }
+        for hashfunc, (
+            init_signature,
+            digest_signature,
+        ) in algorithms_with_signature.items()
+    )
+
+    return parse(classes)
+
+
+register_module_extender(AstroidManager(), "hashlib", _hashlib_transform)

+ 212 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_http.py

@@ -0,0 +1,212 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid brain hints for some of the `http` module."""
+import textwrap
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import AstroidBuilder
+from astroid.manager import AstroidManager
+
+
+def _http_transform():
+    code = textwrap.dedent(
+        """
+    from enum import IntEnum
+    from collections import namedtuple
+    _HTTPStatus = namedtuple('_HTTPStatus', 'value phrase description')
+
+    class HTTPStatus(IntEnum):
+
+        @property
+        def phrase(self):
+            return ""
+        @property
+        def value(self):
+            return 0
+        @property
+        def description(self):
+            return ""
+
+        # informational
+        CONTINUE = _HTTPStatus(100, 'Continue', 'Request received, please continue')
+        SWITCHING_PROTOCOLS = _HTTPStatus(101, 'Switching Protocols',
+                'Switching to new protocol; obey Upgrade header')
+        PROCESSING = _HTTPStatus(102, 'Processing', '')
+        OK = _HTTPStatus(200, 'OK', 'Request fulfilled, document follows')
+        CREATED = _HTTPStatus(201, 'Created', 'Document created, URL follows')
+        ACCEPTED = _HTTPStatus(202, 'Accepted',
+            'Request accepted, processing continues off-line')
+        NON_AUTHORITATIVE_INFORMATION = _HTTPStatus(203,
+            'Non-Authoritative Information', 'Request fulfilled from cache')
+        NO_CONTENT = _HTTPStatus(204, 'No Content', 'Request fulfilled, nothing follows')
+        RESET_CONTENT =_HTTPStatus(205, 'Reset Content', 'Clear input form for further input')
+        PARTIAL_CONTENT = _HTTPStatus(206, 'Partial Content', 'Partial content follows')
+        MULTI_STATUS = _HTTPStatus(207, 'Multi-Status', '')
+        ALREADY_REPORTED = _HTTPStatus(208, 'Already Reported', '')
+        IM_USED = _HTTPStatus(226, 'IM Used', '')
+        MULTIPLE_CHOICES = _HTTPStatus(300, 'Multiple Choices',
+            'Object has several resources -- see URI list')
+        MOVED_PERMANENTLY = _HTTPStatus(301, 'Moved Permanently',
+            'Object moved permanently -- see URI list')
+        FOUND = _HTTPStatus(302, 'Found', 'Object moved temporarily -- see URI list')
+        SEE_OTHER = _HTTPStatus(303, 'See Other', 'Object moved -- see Method and URL list')
+        NOT_MODIFIED = _HTTPStatus(304, 'Not Modified',
+            'Document has not changed since given time')
+        USE_PROXY = _HTTPStatus(305, 'Use Proxy',
+            'You must use proxy specified in Location to access this resource')
+        TEMPORARY_REDIRECT = _HTTPStatus(307, 'Temporary Redirect',
+            'Object moved temporarily -- see URI list')
+        PERMANENT_REDIRECT = _HTTPStatus(308, 'Permanent Redirect',
+            'Object moved permanently -- see URI list')
+        BAD_REQUEST = _HTTPStatus(400, 'Bad Request',
+            'Bad request syntax or unsupported method')
+        UNAUTHORIZED = _HTTPStatus(401, 'Unauthorized',
+            'No permission -- see authorization schemes')
+        PAYMENT_REQUIRED = _HTTPStatus(402, 'Payment Required',
+            'No payment -- see charging schemes')
+        FORBIDDEN = _HTTPStatus(403, 'Forbidden',
+            'Request forbidden -- authorization will not help')
+        NOT_FOUND = _HTTPStatus(404, 'Not Found',
+            'Nothing matches the given URI')
+        METHOD_NOT_ALLOWED = _HTTPStatus(405, 'Method Not Allowed',
+            'Specified method is invalid for this resource')
+        NOT_ACCEPTABLE = _HTTPStatus(406, 'Not Acceptable',
+            'URI not available in preferred format')
+        PROXY_AUTHENTICATION_REQUIRED = _HTTPStatus(407,
+            'Proxy Authentication Required',
+            'You must authenticate with this proxy before proceeding')
+        REQUEST_TIMEOUT = _HTTPStatus(408, 'Request Timeout',
+            'Request timed out; try again later')
+        CONFLICT = _HTTPStatus(409, 'Conflict', 'Request conflict')
+        GONE = _HTTPStatus(410, 'Gone',
+            'URI no longer exists and has been permanently removed')
+        LENGTH_REQUIRED = _HTTPStatus(411, 'Length Required',
+            'Client must specify Content-Length')
+        PRECONDITION_FAILED = _HTTPStatus(412, 'Precondition Failed',
+            'Precondition in headers is false')
+        REQUEST_ENTITY_TOO_LARGE = _HTTPStatus(413, 'Request Entity Too Large',
+            'Entity is too large')
+        REQUEST_URI_TOO_LONG = _HTTPStatus(414, 'Request-URI Too Long',
+            'URI is too long')
+        UNSUPPORTED_MEDIA_TYPE = _HTTPStatus(415, 'Unsupported Media Type',
+            'Entity body in unsupported format')
+        REQUESTED_RANGE_NOT_SATISFIABLE = _HTTPStatus(416,
+            'Requested Range Not Satisfiable',
+            'Cannot satisfy request range')
+        EXPECTATION_FAILED = _HTTPStatus(417, 'Expectation Failed',
+            'Expect condition could not be satisfied')
+        MISDIRECTED_REQUEST = _HTTPStatus(421, 'Misdirected Request',
+            'Server is not able to produce a response')
+        UNPROCESSABLE_ENTITY = _HTTPStatus(422, 'Unprocessable Entity')
+        LOCKED = _HTTPStatus(423, 'Locked')
+        FAILED_DEPENDENCY = _HTTPStatus(424, 'Failed Dependency')
+        UPGRADE_REQUIRED = _HTTPStatus(426, 'Upgrade Required')
+        PRECONDITION_REQUIRED = _HTTPStatus(428, 'Precondition Required',
+            'The origin server requires the request to be conditional')
+        TOO_MANY_REQUESTS = _HTTPStatus(429, 'Too Many Requests',
+            'The user has sent too many requests in '
+            'a given amount of time ("rate limiting")')
+        REQUEST_HEADER_FIELDS_TOO_LARGE = _HTTPStatus(431,
+            'Request Header Fields Too Large',
+            'The server is unwilling to process the request because its header '
+            'fields are too large')
+        UNAVAILABLE_FOR_LEGAL_REASONS = _HTTPStatus(451,
+            'Unavailable For Legal Reasons',
+            'The server is denying access to the '
+            'resource as a consequence of a legal demand')
+        INTERNAL_SERVER_ERROR = _HTTPStatus(500, 'Internal Server Error',
+            'Server got itself in trouble')
+        NOT_IMPLEMENTED = _HTTPStatus(501, 'Not Implemented',
+            'Server does not support this operation')
+        BAD_GATEWAY = _HTTPStatus(502, 'Bad Gateway',
+            'Invalid responses from another server/proxy')
+        SERVICE_UNAVAILABLE = _HTTPStatus(503, 'Service Unavailable',
+            'The server cannot process the request due to a high load')
+        GATEWAY_TIMEOUT = _HTTPStatus(504, 'Gateway Timeout',
+            'The gateway server did not receive a timely response')
+        HTTP_VERSION_NOT_SUPPORTED = _HTTPStatus(505, 'HTTP Version Not Supported',
+            'Cannot fulfill request')
+        VARIANT_ALSO_NEGOTIATES = _HTTPStatus(506, 'Variant Also Negotiates')
+        INSUFFICIENT_STORAGE = _HTTPStatus(507, 'Insufficient Storage')
+        LOOP_DETECTED = _HTTPStatus(508, 'Loop Detected')
+        NOT_EXTENDED = _HTTPStatus(510, 'Not Extended')
+        NETWORK_AUTHENTICATION_REQUIRED = _HTTPStatus(511,
+            'Network Authentication Required',
+            'The client needs to authenticate to gain network access')
+    """
+    )
+    return AstroidBuilder(AstroidManager()).string_build(code)
+
+
+def _http_client_transform():
+    return AstroidBuilder(AstroidManager()).string_build(
+        textwrap.dedent(
+            """
+    from http import HTTPStatus
+
+    CONTINUE = HTTPStatus.CONTINUE
+    SWITCHING_PROTOCOLS = HTTPStatus.SWITCHING_PROTOCOLS
+    PROCESSING = HTTPStatus.PROCESSING
+    OK = HTTPStatus.OK
+    CREATED = HTTPStatus.CREATED
+    ACCEPTED = HTTPStatus.ACCEPTED
+    NON_AUTHORITATIVE_INFORMATION = HTTPStatus.NON_AUTHORITATIVE_INFORMATION
+    NO_CONTENT = HTTPStatus.NO_CONTENT
+    RESET_CONTENT = HTTPStatus.RESET_CONTENT
+    PARTIAL_CONTENT = HTTPStatus.PARTIAL_CONTENT
+    MULTI_STATUS = HTTPStatus.MULTI_STATUS
+    ALREADY_REPORTED = HTTPStatus.ALREADY_REPORTED
+    IM_USED = HTTPStatus.IM_USED
+    MULTIPLE_CHOICES = HTTPStatus.MULTIPLE_CHOICES
+    MOVED_PERMANENTLY = HTTPStatus.MOVED_PERMANENTLY
+    FOUND = HTTPStatus.FOUND
+    SEE_OTHER = HTTPStatus.SEE_OTHER
+    NOT_MODIFIED = HTTPStatus.NOT_MODIFIED
+    USE_PROXY = HTTPStatus.USE_PROXY
+    TEMPORARY_REDIRECT = HTTPStatus.TEMPORARY_REDIRECT
+    PERMANENT_REDIRECT = HTTPStatus.PERMANENT_REDIRECT
+    BAD_REQUEST = HTTPStatus.BAD_REQUEST
+    UNAUTHORIZED = HTTPStatus.UNAUTHORIZED
+    PAYMENT_REQUIRED = HTTPStatus.PAYMENT_REQUIRED
+    FORBIDDEN = HTTPStatus.FORBIDDEN
+    NOT_FOUND = HTTPStatus.NOT_FOUND
+    METHOD_NOT_ALLOWED = HTTPStatus.METHOD_NOT_ALLOWED
+    NOT_ACCEPTABLE = HTTPStatus.NOT_ACCEPTABLE
+    PROXY_AUTHENTICATION_REQUIRED = HTTPStatus.PROXY_AUTHENTICATION_REQUIRED
+    REQUEST_TIMEOUT = HTTPStatus.REQUEST_TIMEOUT
+    CONFLICT = HTTPStatus.CONFLICT
+    GONE = HTTPStatus.GONE
+    LENGTH_REQUIRED = HTTPStatus.LENGTH_REQUIRED
+    PRECONDITION_FAILED = HTTPStatus.PRECONDITION_FAILED
+    REQUEST_ENTITY_TOO_LARGE = HTTPStatus.REQUEST_ENTITY_TOO_LARGE
+    REQUEST_URI_TOO_LONG = HTTPStatus.REQUEST_URI_TOO_LONG
+    UNSUPPORTED_MEDIA_TYPE = HTTPStatus.UNSUPPORTED_MEDIA_TYPE
+    REQUESTED_RANGE_NOT_SATISFIABLE = HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE
+    EXPECTATION_FAILED = HTTPStatus.EXPECTATION_FAILED
+    UNPROCESSABLE_ENTITY = HTTPStatus.UNPROCESSABLE_ENTITY
+    LOCKED = HTTPStatus.LOCKED
+    FAILED_DEPENDENCY = HTTPStatus.FAILED_DEPENDENCY
+    UPGRADE_REQUIRED = HTTPStatus.UPGRADE_REQUIRED
+    PRECONDITION_REQUIRED = HTTPStatus.PRECONDITION_REQUIRED
+    TOO_MANY_REQUESTS = HTTPStatus.TOO_MANY_REQUESTS
+    REQUEST_HEADER_FIELDS_TOO_LARGE = HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE
+    INTERNAL_SERVER_ERROR = HTTPStatus.INTERNAL_SERVER_ERROR
+    NOT_IMPLEMENTED = HTTPStatus.NOT_IMPLEMENTED
+    BAD_GATEWAY = HTTPStatus.BAD_GATEWAY
+    SERVICE_UNAVAILABLE = HTTPStatus.SERVICE_UNAVAILABLE
+    GATEWAY_TIMEOUT = HTTPStatus.GATEWAY_TIMEOUT
+    HTTP_VERSION_NOT_SUPPORTED = HTTPStatus.HTTP_VERSION_NOT_SUPPORTED
+    VARIANT_ALSO_NEGOTIATES = HTTPStatus.VARIANT_ALSO_NEGOTIATES
+    INSUFFICIENT_STORAGE = HTTPStatus.INSUFFICIENT_STORAGE
+    LOOP_DETECTED = HTTPStatus.LOOP_DETECTED
+    NOT_EXTENDED = HTTPStatus.NOT_EXTENDED
+    NETWORK_AUTHENTICATION_REQUIRED = HTTPStatus.NETWORK_AUTHENTICATION_REQUIRED
+    """
+        )
+    )
+
+
+register_module_extender(AstroidManager(), "http", _http_transform)
+register_module_extender(AstroidManager(), "http.client", _http_client_transform)

+ 54 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_hypothesis.py

@@ -0,0 +1,54 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""
+Astroid hook for the Hypothesis library.
+
+Without this hook pylint reports no-value-for-parameter for use of strategies
+defined using the `@hypothesis.strategies.composite` decorator.  For example:
+
+    from hypothesis import strategies as st
+
+    @st.composite
+    def a_strategy(draw):
+        return draw(st.integers())
+
+    a_strategy()
+"""
+from astroid.manager import AstroidManager
+from astroid.nodes.scoped_nodes import FunctionDef
+
+COMPOSITE_NAMES = (
+    "composite",
+    "st.composite",
+    "strategies.composite",
+    "hypothesis.strategies.composite",
+)
+
+
+def is_decorated_with_st_composite(node) -> bool:
+    """Return whether a decorated node has @st.composite applied."""
+    if node.decorators and node.args.args and node.args.args[0].name == "draw":
+        for decorator_attribute in node.decorators.nodes:
+            if decorator_attribute.as_string() in COMPOSITE_NAMES:
+                return True
+    return False
+
+
+def remove_draw_parameter_from_composite_strategy(node):
+    """Given that the FunctionDef is decorated with @st.composite, remove the
+    first argument (`draw`) - it's always supplied by Hypothesis so we don't
+    need to emit the no-value-for-parameter lint.
+    """
+    del node.args.args[0]
+    del node.args.annotations[0]
+    del node.args.type_comment_args[0]
+    return node
+
+
+AstroidManager().register_transform(
+    node_class=FunctionDef,
+    transform=remove_draw_parameter_from_composite_strategy,
+    predicate=is_decorated_with_st_composite,
+)

+ 43 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_io.py

@@ -0,0 +1,43 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid brain hints for some of the _io C objects."""
+from astroid.manager import AstroidManager
+from astroid.nodes import ClassDef
+
+BUFFERED = {"BufferedWriter", "BufferedReader"}
+TextIOWrapper = "TextIOWrapper"
+FileIO = "FileIO"
+BufferedWriter = "BufferedWriter"
+
+
+def _generic_io_transform(node, name, cls):
+    """Transform the given name, by adding the given *class* as a member of the
+    node.
+    """
+
+    io_module = AstroidManager().ast_from_module_name("_io")
+    attribute_object = io_module[cls]
+    instance = attribute_object.instantiate_class()
+    node.locals[name] = [instance]
+
+
+def _transform_text_io_wrapper(node):
+    # This is not always correct, since it can vary with the type of the descriptor,
+    # being stdout, stderr or stdin. But we cannot get access to the name of the
+    # stream, which is why we are using the BufferedWriter class as a default
+    # value
+    return _generic_io_transform(node, name="buffer", cls=BufferedWriter)
+
+
+def _transform_buffered(node):
+    return _generic_io_transform(node, name="raw", cls=FileIO)
+
+
+AstroidManager().register_transform(
+    ClassDef, _transform_buffered, lambda node: node.name in BUFFERED
+)
+AstroidManager().register_transform(
+    ClassDef, _transform_text_io_wrapper, lambda node: node.name == TextIOWrapper
+)

+ 123 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_mechanize.py

@@ -0,0 +1,123 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import AstroidBuilder
+from astroid.manager import AstroidManager
+
+
+def mechanize_transform():
+    return AstroidBuilder(AstroidManager()).string_build(
+        """class Browser(object):
+    def __getattr__(self, name):
+        return None
+
+    def __getitem__(self, name):
+        return None
+
+    def __setitem__(self, name, val):
+        return None
+
+    def back(self, n=1):
+        return None
+
+    def clear_history(self):
+        return None
+
+    def click(self, *args, **kwds):
+        return None
+
+    def click_link(self, link=None, **kwds):
+        return None
+
+    def close(self):
+        return None
+
+    def encoding(self):
+        return None
+
+    def find_link(
+        self,
+        text=None,
+        text_regex=None,
+        name=None,
+        name_regex=None,
+        url=None,
+        url_regex=None,
+        tag=None,
+        predicate=None,
+        nr=0,
+    ):
+        return None
+
+    def follow_link(self, link=None, **kwds):
+        return None
+
+    def forms(self):
+        return None
+
+    def geturl(self):
+        return None
+
+    def global_form(self):
+        return None
+
+    def links(self, **kwds):
+        return None
+
+    def open_local_file(self, filename):
+        return None
+
+    def open(self, url, data=None, timeout=None):
+        return None
+
+    def open_novisit(self, url, data=None, timeout=None):
+        return None
+
+    def open_local_file(self, filename):
+        return None
+
+    def reload(self):
+        return None
+
+    def response(self):
+        return None
+
+    def select_form(self, name=None, predicate=None, nr=None, **attrs):
+        return None
+
+    def set_cookie(self, cookie_string):
+        return None
+
+    def set_handle_referer(self, handle):
+        return None
+
+    def set_header(self, header, value=None):
+        return None
+
+    def set_html(self, html, url="http://example.com/"):
+        return None
+
+    def set_response(self, response):
+        return None
+
+    def set_simple_cookie(self, name, value, domain, path="/"):
+        return None
+
+    def submit(self, *args, **kwds):
+        return None
+
+    def title(self):
+        return None
+
+    def viewing_html(self):
+        return None
+
+    def visit_response(self, response, request=None):
+        return None
+"""
+    )
+
+
+register_module_extender(AstroidManager(), "mechanize", mechanize_transform)

+ 107 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_multiprocessing.py

@@ -0,0 +1,107 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from astroid.bases import BoundMethod
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.exceptions import InferenceError
+from astroid.manager import AstroidManager
+from astroid.nodes.scoped_nodes import FunctionDef
+
+
+def _multiprocessing_transform():
+    module = parse(
+        """
+    from multiprocessing.managers import SyncManager
+    def Manager():
+        return SyncManager()
+    """
+    )
+    # Multiprocessing uses a getattr lookup inside contexts,
+    # in order to get the attributes they need. Since it's extremely
+    # dynamic, we use this approach to fake it.
+    node = parse(
+        """
+    from multiprocessing.context import DefaultContext, BaseContext
+    default = DefaultContext()
+    base = BaseContext()
+    """
+    )
+    try:
+        context = next(node["default"].infer())
+        base = next(node["base"].infer())
+    except (InferenceError, StopIteration):
+        return module
+
+    for node in (context, base):
+        for key, value in node.locals.items():
+            if key.startswith("_"):
+                continue
+
+            value = value[0]
+            if isinstance(value, FunctionDef):
+                # We need to rebound this, since otherwise
+                # it will have an extra argument (self).
+                value = BoundMethod(value, node)
+            module[key] = value
+    return module
+
+
+def _multiprocessing_managers_transform():
+    return parse(
+        """
+    import array
+    import threading
+    import multiprocessing.pool as pool
+    import queue
+
+    class Namespace(object):
+        pass
+
+    class Value(object):
+        def __init__(self, typecode, value, lock=True):
+            self._typecode = typecode
+            self._value = value
+        def get(self):
+            return self._value
+        def set(self, value):
+            self._value = value
+        def __repr__(self):
+            return '%s(%r, %r)'%(type(self).__name__, self._typecode, self._value)
+        value = property(get, set)
+
+    def Array(typecode, sequence, lock=True):
+        return array.array(typecode, sequence)
+
+    class SyncManager(object):
+        Queue = JoinableQueue = queue.Queue
+        Event = threading.Event
+        RLock = threading.RLock
+        Lock = threading.Lock
+        BoundedSemaphore = threading.BoundedSemaphore
+        Condition = threading.Condition
+        Barrier = threading.Barrier
+        Pool = pool.Pool
+        list = list
+        dict = dict
+        Value = Value
+        Array = Array
+        Namespace = Namespace
+        __enter__ = lambda self: self
+        __exit__ = lambda *args: args
+
+        def start(self, initializer=None, initargs=None):
+            pass
+        def shutdown(self):
+            pass
+    """
+    )
+
+
+register_module_extender(
+    AstroidManager(), "multiprocessing.managers", _multiprocessing_managers_transform
+)
+register_module_extender(
+    AstroidManager(), "multiprocessing", _multiprocessing_transform
+)

+ 623 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_namedtuple_enum.py

@@ -0,0 +1,623 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for the Python standard library."""
+
+from __future__ import annotations
+
+import functools
+import keyword
+import sys
+from collections.abc import Iterator
+from textwrap import dedent
+
+import astroid
+from astroid import arguments, bases, inference_tip, nodes, util
+from astroid.builder import AstroidBuilder, _extract_single_node, extract_node
+from astroid.context import InferenceContext
+from astroid.exceptions import (
+    AstroidTypeError,
+    AstroidValueError,
+    InferenceError,
+    UseInferenceDefault,
+)
+from astroid.manager import AstroidManager
+
+if sys.version_info >= (3, 8):
+    from typing import Final
+else:
+    from typing_extensions import Final
+
+
+ENUM_QNAME: Final[str] = "enum.Enum"
+TYPING_NAMEDTUPLE_QUALIFIED: Final = {
+    "typing.NamedTuple",
+    "typing_extensions.NamedTuple",
+}
+TYPING_NAMEDTUPLE_BASENAMES: Final = {
+    "NamedTuple",
+    "typing.NamedTuple",
+    "typing_extensions.NamedTuple",
+}
+
+
+def _infer_first(node, context):
+    if isinstance(node, util.UninferableBase):
+        raise UseInferenceDefault
+    try:
+        value = next(node.infer(context=context))
+    except StopIteration as exc:
+        raise InferenceError from exc
+    if isinstance(value, util.UninferableBase):
+        raise UseInferenceDefault()
+    return value
+
+
+def _find_func_form_arguments(node, context):
+    def _extract_namedtuple_arg_or_keyword(  # pylint: disable=inconsistent-return-statements
+        position, key_name=None
+    ):
+        if len(args) > position:
+            return _infer_first(args[position], context)
+        if key_name and key_name in found_keywords:
+            return _infer_first(found_keywords[key_name], context)
+
+    args = node.args
+    keywords = node.keywords
+    found_keywords = (
+        {keyword.arg: keyword.value for keyword in keywords} if keywords else {}
+    )
+
+    name = _extract_namedtuple_arg_or_keyword(position=0, key_name="typename")
+    names = _extract_namedtuple_arg_or_keyword(position=1, key_name="field_names")
+    if name and names:
+        return name.value, names
+
+    raise UseInferenceDefault()
+
+
+def infer_func_form(
+    node: nodes.Call,
+    base_type: list[nodes.NodeNG],
+    context: InferenceContext | None = None,
+    enum: bool = False,
+) -> tuple[nodes.ClassDef, str, list[str]]:
+    """Specific inference function for namedtuple or Python 3 enum."""
+    # node is a Call node, class name as first argument and generated class
+    # attributes as second argument
+
+    # namedtuple or enums list of attributes can be a list of strings or a
+    # whitespace-separate string
+    try:
+        name, names = _find_func_form_arguments(node, context)
+        try:
+            attributes: list[str] = names.value.replace(",", " ").split()
+        except AttributeError as exc:
+            # Handle attributes of NamedTuples
+            if not enum:
+                attributes = []
+                fields = _get_namedtuple_fields(node)
+                if fields:
+                    fields_node = extract_node(fields)
+                    attributes = [
+                        _infer_first(const, context).value for const in fields_node.elts
+                    ]
+
+            # Handle attributes of Enums
+            else:
+                # Enums supports either iterator of (name, value) pairs
+                # or mappings.
+                if hasattr(names, "items") and isinstance(names.items, list):
+                    attributes = [
+                        _infer_first(const[0], context).value
+                        for const in names.items
+                        if isinstance(const[0], nodes.Const)
+                    ]
+                elif hasattr(names, "elts"):
+                    # Enums can support either ["a", "b", "c"]
+                    # or [("a", 1), ("b", 2), ...], but they can't
+                    # be mixed.
+                    if all(isinstance(const, nodes.Tuple) for const in names.elts):
+                        attributes = [
+                            _infer_first(const.elts[0], context).value
+                            for const in names.elts
+                            if isinstance(const, nodes.Tuple)
+                        ]
+                    else:
+                        attributes = [
+                            _infer_first(const, context).value for const in names.elts
+                        ]
+                else:
+                    raise AttributeError from exc
+                if not attributes:
+                    raise AttributeError from exc
+    except (AttributeError, InferenceError) as exc:
+        raise UseInferenceDefault from exc
+
+    if not enum:
+        # namedtuple maps sys.intern(str()) over over field_names
+        attributes = [str(attr) for attr in attributes]
+        # XXX this should succeed *unless* __str__/__repr__ is incorrect or throws
+        # in which case we should not have inferred these values and raised earlier
+    attributes = [attr for attr in attributes if " " not in attr]
+
+    # If we can't infer the name of the class, don't crash, up to this point
+    # we know it is a namedtuple anyway.
+    name = name or "Uninferable"
+    # we want to return a Class node instance with proper attributes set
+    class_node = nodes.ClassDef(name)
+    # A typical ClassDef automatically adds its name to the parent scope,
+    # but doing so causes problems, so defer setting parent until after init
+    # see: https://github.com/PyCQA/pylint/issues/5982
+    class_node.parent = node.parent
+    class_node.postinit(
+        # set base class=tuple
+        bases=base_type,
+        body=[],
+        decorators=None,
+    )
+    # XXX add __init__(*attributes) method
+    for attr in attributes:
+        fake_node = nodes.EmptyNode()
+        fake_node.parent = class_node
+        fake_node.attrname = attr
+        class_node.instance_attrs[attr] = [fake_node]
+    return class_node, name, attributes
+
+
+def _has_namedtuple_base(node):
+    """Predicate for class inference tip.
+
+    :type node: ClassDef
+    :rtype: bool
+    """
+    return set(node.basenames) & TYPING_NAMEDTUPLE_BASENAMES
+
+
+def _looks_like(node, name) -> bool:
+    func = node.func
+    if isinstance(func, nodes.Attribute):
+        return func.attrname == name
+    if isinstance(func, nodes.Name):
+        return func.name == name
+    return False
+
+
+_looks_like_namedtuple = functools.partial(_looks_like, name="namedtuple")
+_looks_like_enum = functools.partial(_looks_like, name="Enum")
+_looks_like_typing_namedtuple = functools.partial(_looks_like, name="NamedTuple")
+
+
+def infer_named_tuple(
+    node: nodes.Call, context: InferenceContext | None = None
+) -> Iterator[nodes.ClassDef]:
+    """Specific inference function for namedtuple Call node."""
+    tuple_base_name: list[nodes.NodeNG] = [nodes.Name(name="tuple", parent=node.root())]
+    class_node, name, attributes = infer_func_form(
+        node, tuple_base_name, context=context
+    )
+    call_site = arguments.CallSite.from_call(node, context=context)
+    node = extract_node("import collections; collections.namedtuple")
+    try:
+        func = next(node.infer())
+    except StopIteration as e:
+        raise InferenceError(node=node) from e
+    try:
+        rename = next(call_site.infer_argument(func, "rename", context)).bool_value()
+    except (InferenceError, StopIteration):
+        rename = False
+
+    try:
+        attributes = _check_namedtuple_attributes(name, attributes, rename)
+    except AstroidTypeError as exc:
+        raise UseInferenceDefault("TypeError: " + str(exc)) from exc
+    except AstroidValueError as exc:
+        raise UseInferenceDefault("ValueError: " + str(exc)) from exc
+
+    replace_args = ", ".join(f"{arg}=None" for arg in attributes)
+    field_def = (
+        "    {name} = property(lambda self: self[{index:d}], "
+        "doc='Alias for field number {index:d}')"
+    )
+    field_defs = "\n".join(
+        field_def.format(name=name, index=index)
+        for index, name in enumerate(attributes)
+    )
+    fake = AstroidBuilder(AstroidManager()).string_build(
+        f"""
+class {name}(tuple):
+    __slots__ = ()
+    _fields = {attributes!r}
+    def _asdict(self):
+        return self.__dict__
+    @classmethod
+    def _make(cls, iterable, new=tuple.__new__, len=len):
+        return new(cls, iterable)
+    def _replace(self, {replace_args}):
+        return self
+    def __getnewargs__(self):
+        return tuple(self)
+{field_defs}
+    """
+    )
+    class_node.locals["_asdict"] = fake.body[0].locals["_asdict"]
+    class_node.locals["_make"] = fake.body[0].locals["_make"]
+    class_node.locals["_replace"] = fake.body[0].locals["_replace"]
+    class_node.locals["_fields"] = fake.body[0].locals["_fields"]
+    for attr in attributes:
+        class_node.locals[attr] = fake.body[0].locals[attr]
+    # we use UseInferenceDefault, we can't be a generator so return an iterator
+    return iter([class_node])
+
+
+def _get_renamed_namedtuple_attributes(field_names):
+    names = list(field_names)
+    seen = set()
+    for i, name in enumerate(field_names):
+        if (
+            not all(c.isalnum() or c == "_" for c in name)
+            or keyword.iskeyword(name)
+            or not name
+            or name[0].isdigit()
+            or name.startswith("_")
+            or name in seen
+        ):
+            names[i] = "_%d" % i
+        seen.add(name)
+    return tuple(names)
+
+
+def _check_namedtuple_attributes(typename, attributes, rename=False):
+    attributes = tuple(attributes)
+    if rename:
+        attributes = _get_renamed_namedtuple_attributes(attributes)
+
+    # The following snippet is derived from the CPython Lib/collections/__init__.py sources
+    # <snippet>
+    for name in (typename,) + attributes:
+        if not isinstance(name, str):
+            raise AstroidTypeError("Type names and field names must be strings")
+        if not name.isidentifier():
+            raise AstroidValueError(
+                "Type names and field names must be valid" + f"identifiers: {name!r}"
+            )
+        if keyword.iskeyword(name):
+            raise AstroidValueError(
+                f"Type names and field names cannot be a keyword: {name!r}"
+            )
+
+    seen = set()
+    for name in attributes:
+        if name.startswith("_") and not rename:
+            raise AstroidValueError(
+                f"Field names cannot start with an underscore: {name!r}"
+            )
+        if name in seen:
+            raise AstroidValueError(f"Encountered duplicate field name: {name!r}")
+        seen.add(name)
+    # </snippet>
+
+    return attributes
+
+
+def infer_enum(
+    node: nodes.Call, context: InferenceContext | None = None
+) -> Iterator[bases.Instance]:
+    """Specific inference function for enum Call node."""
+    # Raise `UseInferenceDefault` if `node` is a call to a a user-defined Enum.
+    try:
+        inferred = node.func.infer(context)
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+
+    if not any(
+        isinstance(item, nodes.ClassDef) and item.qname() == ENUM_QNAME
+        for item in inferred
+    ):
+        raise UseInferenceDefault
+
+    enum_meta = _extract_single_node(
+        """
+    class EnumMeta(object):
+        'docstring'
+        def __call__(self, node):
+            class EnumAttribute(object):
+                name = ''
+                value = 0
+            return EnumAttribute()
+        def __iter__(self):
+            class EnumAttribute(object):
+                name = ''
+                value = 0
+            return [EnumAttribute()]
+        def __reversed__(self):
+            class EnumAttribute(object):
+                name = ''
+                value = 0
+            return (EnumAttribute, )
+        def __next__(self):
+            return next(iter(self))
+        def __getitem__(self, attr):
+            class Value(object):
+                @property
+                def name(self):
+                    return ''
+                @property
+                def value(self):
+                    return attr
+
+            return Value()
+        __members__ = ['']
+    """
+    )
+    class_node = infer_func_form(node, [enum_meta], context=context, enum=True)[0]
+    return iter([class_node.instantiate_class()])
+
+
+INT_FLAG_ADDITION_METHODS = """
+    def __or__(self, other):
+        return {name}(self.value | other.value)
+    def __and__(self, other):
+        return {name}(self.value & other.value)
+    def __xor__(self, other):
+        return {name}(self.value ^ other.value)
+    def __add__(self, other):
+        return {name}(self.value + other.value)
+    def __div__(self, other):
+        return {name}(self.value / other.value)
+    def __invert__(self):
+        return {name}(~self.value)
+    def __mul__(self, other):
+        return {name}(self.value * other.value)
+"""
+
+
+def infer_enum_class(node: nodes.ClassDef) -> nodes.ClassDef:
+    """Specific inference for enums."""
+    for basename in (b for cls in node.mro() for b in cls.basenames):
+        if node.root().name == "enum":
+            # Skip if the class is directly from enum module.
+            break
+        dunder_members = {}
+        target_names = set()
+        for local, values in node.locals.items():
+            if any(not isinstance(value, nodes.AssignName) for value in values):
+                continue
+
+            stmt = values[0].statement(future=True)
+            if isinstance(stmt, nodes.Assign):
+                if isinstance(stmt.targets[0], nodes.Tuple):
+                    targets = stmt.targets[0].itered()
+                else:
+                    targets = stmt.targets
+            elif isinstance(stmt, nodes.AnnAssign):
+                targets = [stmt.target]
+            else:
+                continue
+
+            inferred_return_value = None
+            if stmt.value is not None:
+                if isinstance(stmt.value, nodes.Const):
+                    if isinstance(stmt.value.value, str):
+                        inferred_return_value = repr(stmt.value.value)
+                    else:
+                        inferred_return_value = stmt.value.value
+                else:
+                    inferred_return_value = stmt.value.as_string()
+
+            new_targets = []
+            for target in targets:
+                if isinstance(target, nodes.Starred):
+                    continue
+                target_names.add(target.name)
+                # Replace all the assignments with our mocked class.
+                classdef = dedent(
+                    """
+                class {name}({types}):
+                    @property
+                    def value(self):
+                        return {return_value}
+                    @property
+                    def name(self):
+                        return "{name}"
+                """.format(
+                        name=target.name,
+                        types=", ".join(node.basenames),
+                        return_value=inferred_return_value,
+                    )
+                )
+                if "IntFlag" in basename:
+                    # Alright, we need to add some additional methods.
+                    # Unfortunately we still can't infer the resulting objects as
+                    # Enum members, but once we'll be able to do that, the following
+                    # should result in some nice symbolic execution
+                    classdef += INT_FLAG_ADDITION_METHODS.format(name=target.name)
+
+                fake = AstroidBuilder(
+                    AstroidManager(), apply_transforms=False
+                ).string_build(classdef)[target.name]
+                fake.parent = target.parent
+                for method in node.mymethods():
+                    fake.locals[method.name] = [method]
+                new_targets.append(fake.instantiate_class())
+                dunder_members[local] = fake
+            node.locals[local] = new_targets
+
+        # The undocumented `_value2member_map_` member:
+        node.locals["_value2member_map_"] = [nodes.Dict(parent=node)]
+
+        members = nodes.Dict(parent=node)
+        members.postinit(
+            [
+                (nodes.Const(k, parent=members), nodes.Name(v.name, parent=members))
+                for k, v in dunder_members.items()
+            ]
+        )
+        node.locals["__members__"] = [members]
+        # The enum.Enum class itself defines two @DynamicClassAttribute data-descriptors
+        # "name" and "value" (which we override in the mocked class for each enum member
+        # above). When dealing with inference of an arbitrary instance of the enum
+        # class, e.g. in a method defined in the class body like:
+        #     class SomeEnum(enum.Enum):
+        #         def method(self):
+        #             self.name  # <- here
+        # In the absence of an enum member called "name" or "value", these attributes
+        # should resolve to the descriptor on that particular instance, i.e. enum member.
+        # For "value", we have no idea what that should be, but for "name", we at least
+        # know that it should be a string, so infer that as a guess.
+        if "name" not in target_names:
+            code = dedent(
+                """
+            @property
+            def name(self):
+                return ''
+            """
+            )
+            name_dynamicclassattr = AstroidBuilder(AstroidManager()).string_build(code)[
+                "name"
+            ]
+            node.locals["name"] = [name_dynamicclassattr]
+        break
+    return node
+
+
+def infer_typing_namedtuple_class(class_node, context: InferenceContext | None = None):
+    """Infer a subclass of typing.NamedTuple."""
+    # Check if it has the corresponding bases
+    annassigns_fields = [
+        annassign.target.name
+        for annassign in class_node.body
+        if isinstance(annassign, nodes.AnnAssign)
+    ]
+    code = dedent(
+        """
+    from collections import namedtuple
+    namedtuple({typename!r}, {fields!r})
+    """
+    ).format(typename=class_node.name, fields=",".join(annassigns_fields))
+    node = extract_node(code)
+    try:
+        generated_class_node = next(infer_named_tuple(node, context))
+    except StopIteration as e:
+        raise InferenceError(node=node, context=context) from e
+    for method in class_node.mymethods():
+        generated_class_node.locals[method.name] = [method]
+
+    for body_node in class_node.body:
+        if isinstance(body_node, nodes.Assign):
+            for target in body_node.targets:
+                attr = target.name
+                generated_class_node.locals[attr] = class_node.locals[attr]
+        elif isinstance(body_node, nodes.ClassDef):
+            generated_class_node.locals[body_node.name] = [body_node]
+
+    return iter((generated_class_node,))
+
+
+def infer_typing_namedtuple_function(node, context: InferenceContext | None = None):
+    """
+    Starting with python3.9, NamedTuple is a function of the typing module.
+    The class NamedTuple is build dynamically through a call to `type` during
+    initialization of the `_NamedTuple` variable.
+    """
+    klass = extract_node(
+        """
+        from typing import _NamedTuple
+        _NamedTuple
+        """
+    )
+    return klass.infer(context)
+
+
+def infer_typing_namedtuple(
+    node: nodes.Call, context: InferenceContext | None = None
+) -> Iterator[nodes.ClassDef]:
+    """Infer a typing.NamedTuple(...) call."""
+    # This is essentially a namedtuple with different arguments
+    # so we extract the args and infer a named tuple.
+    try:
+        func = next(node.func.infer())
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+
+    if func.qname() not in TYPING_NAMEDTUPLE_QUALIFIED:
+        raise UseInferenceDefault
+
+    if len(node.args) != 2:
+        raise UseInferenceDefault
+
+    if not isinstance(node.args[1], (nodes.List, nodes.Tuple)):
+        raise UseInferenceDefault
+
+    return infer_named_tuple(node, context)
+
+
+def _get_namedtuple_fields(node: nodes.Call) -> str:
+    """Get and return fields of a NamedTuple in code-as-a-string.
+
+    Because the fields are represented in their code form we can
+    extract a node from them later on.
+    """
+    names = []
+    container = None
+    try:
+        container = next(node.args[1].infer())
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+    # We pass on IndexError as we'll try to infer 'field_names' from the keywords
+    except IndexError:
+        pass
+    if not container:
+        for keyword_node in node.keywords:
+            if keyword_node.arg == "field_names":
+                try:
+                    container = next(keyword_node.value.infer())
+                except (InferenceError, StopIteration) as exc:
+                    raise UseInferenceDefault from exc
+                break
+    if not isinstance(container, nodes.BaseContainer):
+        raise UseInferenceDefault
+    for elt in container.elts:
+        if isinstance(elt, nodes.Const):
+            names.append(elt.as_string())
+            continue
+        if not isinstance(elt, (nodes.List, nodes.Tuple)):
+            raise UseInferenceDefault
+        if len(elt.elts) != 2:
+            raise UseInferenceDefault
+        names.append(elt.elts[0].as_string())
+
+    if names:
+        field_names = f"({','.join(names)},)"
+    else:
+        field_names = ""
+    return field_names
+
+
+def _is_enum_subclass(cls: astroid.ClassDef) -> bool:
+    """Return whether cls is a subclass of an Enum."""
+    return cls.is_subtype_of("enum.Enum")
+
+
+AstroidManager().register_transform(
+    nodes.Call, inference_tip(infer_named_tuple), _looks_like_namedtuple
+)
+AstroidManager().register_transform(
+    nodes.Call, inference_tip(infer_enum), _looks_like_enum
+)
+AstroidManager().register_transform(
+    nodes.ClassDef, infer_enum_class, predicate=_is_enum_subclass
+)
+AstroidManager().register_transform(
+    nodes.ClassDef, inference_tip(infer_typing_namedtuple_class), _has_namedtuple_base
+)
+AstroidManager().register_transform(
+    nodes.FunctionDef,
+    inference_tip(infer_typing_namedtuple_function),
+    lambda node: node.name == "NamedTuple"
+    and getattr(node.root(), "name", None) == "typing",
+)
+AstroidManager().register_transform(
+    nodes.Call, inference_tip(infer_typing_namedtuple), _looks_like_typing_namedtuple
+)

+ 79 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_nose.py

@@ -0,0 +1,79 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Hooks for nose library."""
+
+import re
+import textwrap
+
+import astroid.builder
+from astroid.brain.helpers import register_module_extender
+from astroid.exceptions import InferenceError
+from astroid.manager import AstroidManager
+
+_BUILDER = astroid.builder.AstroidBuilder(AstroidManager())
+
+
+CAPITALS = re.compile("([A-Z])")
+
+
+def _pep8(name, caps=CAPITALS):
+    return caps.sub(lambda m: "_" + m.groups()[0].lower(), name)
+
+
+def _nose_tools_functions():
+    """Get an iterator of names and bound methods."""
+    module = _BUILDER.string_build(
+        textwrap.dedent(
+            """
+    import unittest
+
+    class Test(unittest.TestCase):
+        pass
+    a = Test()
+    """
+        )
+    )
+    try:
+        case = next(module["a"].infer())
+    except (InferenceError, StopIteration):
+        return
+    for method in case.methods():
+        if method.name.startswith("assert") and "_" not in method.name:
+            pep8_name = _pep8(method.name)
+            yield pep8_name, astroid.BoundMethod(method, case)
+        if method.name == "assertEqual":
+            # nose also exports assert_equals.
+            yield "assert_equals", astroid.BoundMethod(method, case)
+
+
+def _nose_tools_transform(node):
+    for method_name, method in _nose_tools_functions():
+        node.locals[method_name] = [method]
+
+
+def _nose_tools_trivial_transform():
+    """Custom transform for the nose.tools module."""
+    stub = _BUILDER.string_build("""__all__ = []""")
+    all_entries = ["ok_", "eq_"]
+
+    for pep8_name, method in _nose_tools_functions():
+        all_entries.append(pep8_name)
+        stub[pep8_name] = method
+
+    # Update the __all__ variable, since nose.tools
+    # does this manually with .append.
+    all_assign = stub["__all__"].parent
+    all_object = astroid.List(all_entries)
+    all_object.parent = all_assign
+    all_assign.value = all_object
+    return stub
+
+
+register_module_extender(
+    AstroidManager(), "nose.tools.trivial", _nose_tools_trivial_transform
+)
+AstroidManager().register_transform(
+    astroid.Module, _nose_tools_transform, lambda n: n.name == "nose.tools"
+)

+ 27 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py

@@ -0,0 +1,27 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""
+Astroid hooks for numpy.core.einsumfunc module:
+https://github.com/numpy/numpy/blob/main/numpy/core/einsumfunc.py.
+"""
+
+from astroid import nodes
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def numpy_core_einsumfunc_transform() -> nodes.Module:
+    return parse(
+        """
+    def einsum(*operands, out=None, optimize=False, **kwargs):
+        return numpy.ndarray([0, 0])
+    """
+    )
+
+
+register_module_extender(
+    AstroidManager(), "numpy.core.einsumfunc", numpy_core_einsumfunc_transform
+)

+ 22 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py

@@ -0,0 +1,22 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for numpy.core.fromnumeric module."""
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def numpy_core_fromnumeric_transform():
+    return parse(
+        """
+    def sum(a, axis=None, dtype=None, out=None, keepdims=None, initial=None):
+        return numpy.ndarray([0, 0])
+    """
+    )
+
+
+register_module_extender(
+    AstroidManager(), "numpy.core.fromnumeric", numpy_core_fromnumeric_transform
+)

+ 29 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_function_base.py

@@ -0,0 +1,29 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for numpy.core.function_base module."""
+
+import functools
+
+from astroid.brain.brain_numpy_utils import infer_numpy_member, looks_like_numpy_member
+from astroid.inference_tip import inference_tip
+from astroid.manager import AstroidManager
+from astroid.nodes.node_classes import Attribute
+
+METHODS_TO_BE_INFERRED = {
+    "linspace": """def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0):
+            return numpy.ndarray([0, 0])""",
+    "logspace": """def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0):
+            return numpy.ndarray([0, 0])""",
+    "geomspace": """def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0):
+            return numpy.ndarray([0, 0])""",
+}
+
+for func_name, func_src in METHODS_TO_BE_INFERRED.items():
+    inference_function = functools.partial(infer_numpy_member, func_src)
+    AstroidManager().register_transform(
+        Attribute,
+        inference_tip(inference_function),
+        functools.partial(looks_like_numpy_member, func_name),
+    )

+ 100 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_multiarray.py

@@ -0,0 +1,100 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for numpy.core.multiarray module."""
+
+import functools
+
+from astroid.brain.brain_numpy_utils import infer_numpy_member, looks_like_numpy_member
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.inference_tip import inference_tip
+from astroid.manager import AstroidManager
+from astroid.nodes.node_classes import Attribute, Name
+
+
+def numpy_core_multiarray_transform():
+    return parse(
+        """
+    # different functions defined in multiarray.py
+    def inner(a, b):
+        return numpy.ndarray([0, 0])
+
+    def vdot(a, b):
+        return numpy.ndarray([0, 0])
+        """
+    )
+
+
+register_module_extender(
+    AstroidManager(), "numpy.core.multiarray", numpy_core_multiarray_transform
+)
+
+
+METHODS_TO_BE_INFERRED = {
+    "array": """def array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0):
+            return numpy.ndarray([0, 0])""",
+    "dot": """def dot(a, b, out=None):
+            return numpy.ndarray([0, 0])""",
+    "empty_like": """def empty_like(a, dtype=None, order='K', subok=True):
+            return numpy.ndarray((0, 0))""",
+    "concatenate": """def concatenate(arrays, axis=None, out=None):
+            return numpy.ndarray((0, 0))""",
+    "where": """def where(condition, x=None, y=None):
+            return numpy.ndarray([0, 0])""",
+    "empty": """def empty(shape, dtype=float, order='C'):
+            return numpy.ndarray([0, 0])""",
+    "bincount": """def bincount(x, weights=None, minlength=0):
+            return numpy.ndarray([0, 0])""",
+    "busday_count": """def busday_count(
+        begindates, enddates, weekmask='1111100', holidays=[], busdaycal=None, out=None
+    ):
+        return numpy.ndarray([0, 0])""",
+    "busday_offset": """def busday_offset(
+        dates, offsets, roll='raise', weekmask='1111100', holidays=None,
+        busdaycal=None, out=None
+    ):
+        return numpy.ndarray([0, 0])""",
+    "can_cast": """def can_cast(from_, to, casting='safe'):
+            return True""",
+    "copyto": """def copyto(dst, src, casting='same_kind', where=True):
+            return None""",
+    "datetime_as_string": """def datetime_as_string(arr, unit=None, timezone='naive', casting='same_kind'):
+            return numpy.ndarray([0, 0])""",
+    "is_busday": """def is_busday(dates, weekmask='1111100', holidays=None, busdaycal=None, out=None):
+            return numpy.ndarray([0, 0])""",
+    "lexsort": """def lexsort(keys, axis=-1):
+            return numpy.ndarray([0, 0])""",
+    "may_share_memory": """def may_share_memory(a, b, max_work=None):
+            return True""",
+    # Not yet available because dtype is not yet present in those brains
+    #     "min_scalar_type": """def min_scalar_type(a):
+    #             return numpy.dtype('int16')""",
+    "packbits": """def packbits(a, axis=None, bitorder='big'):
+            return numpy.ndarray([0, 0])""",
+    # Not yet available because dtype is not yet present in those brains
+    #     "result_type": """def result_type(*arrays_and_dtypes):
+    #             return numpy.dtype('int16')""",
+    "shares_memory": """def shares_memory(a, b, max_work=None):
+            return True""",
+    "unpackbits": """def unpackbits(a, axis=None, count=None, bitorder='big'):
+            return numpy.ndarray([0, 0])""",
+    "unravel_index": """def unravel_index(indices, shape, order='C'):
+            return (numpy.ndarray([0, 0]),)""",
+    "zeros": """def zeros(shape, dtype=float, order='C'):
+            return numpy.ndarray([0, 0])""",
+}
+
+for method_name, function_src in METHODS_TO_BE_INFERRED.items():
+    inference_function = functools.partial(infer_numpy_member, function_src)
+    AstroidManager().register_transform(
+        Attribute,
+        inference_tip(inference_function),
+        functools.partial(looks_like_numpy_member, method_name),
+    )
+    AstroidManager().register_transform(
+        Name,
+        inference_tip(inference_function),
+        functools.partial(looks_like_numpy_member, method_name),
+    )

+ 46 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_numeric.py

@@ -0,0 +1,46 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for numpy.core.numeric module."""
+
+import functools
+
+from astroid.brain.brain_numpy_utils import infer_numpy_member, looks_like_numpy_member
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.inference_tip import inference_tip
+from astroid.manager import AstroidManager
+from astroid.nodes.node_classes import Attribute
+
+
+def numpy_core_numeric_transform():
+    return parse(
+        """
+    # different functions defined in numeric.py
+    import numpy
+    def zeros_like(a, dtype=None, order='K', subok=True, shape=None): return numpy.ndarray((0, 0))
+    def ones_like(a, dtype=None, order='K', subok=True, shape=None): return numpy.ndarray((0, 0))
+    def full_like(a, fill_value, dtype=None, order='K', subok=True, shape=None): return numpy.ndarray((0, 0))
+        """
+    )
+
+
+register_module_extender(
+    AstroidManager(), "numpy.core.numeric", numpy_core_numeric_transform
+)
+
+
+METHODS_TO_BE_INFERRED = {
+    "ones": """def ones(shape, dtype=None, order='C'):
+            return numpy.ndarray([0, 0])"""
+}
+
+
+for method_name, function_src in METHODS_TO_BE_INFERRED.items():
+    inference_function = functools.partial(infer_numpy_member, function_src)
+    AstroidManager().register_transform(
+        Attribute,
+        inference_tip(inference_function),
+        functools.partial(looks_like_numpy_member, method_name),
+    )

+ 263 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_numerictypes.py

@@ -0,0 +1,263 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+# TODO(hippo91) : correct the methods signature.
+
+"""Astroid hooks for numpy.core.numerictypes module."""
+from astroid.brain.brain_numpy_utils import numpy_supports_type_hints
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def numpy_core_numerictypes_transform():
+    # TODO: Uniformize the generic API with the ndarray one.
+    #       According to numpy doc the generic object should expose
+    #       the same API than ndarray. This has been done here partially
+    #       through the astype method.
+    generic_src = """
+    class generic(object):
+        def __init__(self, value):
+            self.T = np.ndarray([0, 0])
+            self.base = None
+            self.data = None
+            self.dtype = None
+            self.flags = None
+            # Should be a numpy.flatiter instance but not available for now
+            # Putting an array instead so that iteration and indexing are authorized
+            self.flat = np.ndarray([0, 0])
+            self.imag = None
+            self.itemsize = None
+            self.nbytes = None
+            self.ndim = None
+            self.real = None
+            self.size = None
+            self.strides = None
+
+        def all(self): return uninferable
+        def any(self): return uninferable
+        def argmax(self): return uninferable
+        def argmin(self): return uninferable
+        def argsort(self): return uninferable
+        def astype(self, dtype, order='K', casting='unsafe', subok=True, copy=True): return np.ndarray([0, 0])
+        def base(self): return uninferable
+        def byteswap(self): return uninferable
+        def choose(self): return uninferable
+        def clip(self): return uninferable
+        def compress(self): return uninferable
+        def conj(self): return uninferable
+        def conjugate(self): return uninferable
+        def copy(self): return uninferable
+        def cumprod(self): return uninferable
+        def cumsum(self): return uninferable
+        def data(self): return uninferable
+        def diagonal(self): return uninferable
+        def dtype(self): return uninferable
+        def dump(self): return uninferable
+        def dumps(self): return uninferable
+        def fill(self): return uninferable
+        def flags(self): return uninferable
+        def flat(self): return uninferable
+        def flatten(self): return uninferable
+        def getfield(self): return uninferable
+        def imag(self): return uninferable
+        def item(self): return uninferable
+        def itemset(self): return uninferable
+        def itemsize(self): return uninferable
+        def max(self): return uninferable
+        def mean(self): return uninferable
+        def min(self): return uninferable
+        def nbytes(self): return uninferable
+        def ndim(self): return uninferable
+        def newbyteorder(self): return uninferable
+        def nonzero(self): return uninferable
+        def prod(self): return uninferable
+        def ptp(self): return uninferable
+        def put(self): return uninferable
+        def ravel(self): return uninferable
+        def real(self): return uninferable
+        def repeat(self): return uninferable
+        def reshape(self): return uninferable
+        def resize(self): return uninferable
+        def round(self): return uninferable
+        def searchsorted(self): return uninferable
+        def setfield(self): return uninferable
+        def setflags(self): return uninferable
+        def shape(self): return uninferable
+        def size(self): return uninferable
+        def sort(self): return uninferable
+        def squeeze(self): return uninferable
+        def std(self): return uninferable
+        def strides(self): return uninferable
+        def sum(self): return uninferable
+        def swapaxes(self): return uninferable
+        def take(self): return uninferable
+        def tobytes(self): return uninferable
+        def tofile(self): return uninferable
+        def tolist(self): return uninferable
+        def tostring(self): return uninferable
+        def trace(self): return uninferable
+        def transpose(self): return uninferable
+        def var(self): return uninferable
+        def view(self): return uninferable
+        """
+    if numpy_supports_type_hints():
+        generic_src += """
+        @classmethod
+        def __class_getitem__(cls, value):
+            return cls
+        """
+    return parse(
+        generic_src
+        + """
+    class dtype(object):
+        def __init__(self, obj, align=False, copy=False):
+            self.alignment = None
+            self.base = None
+            self.byteorder = None
+            self.char = None
+            self.descr = None
+            self.fields = None
+            self.flags = None
+            self.hasobject = None
+            self.isalignedstruct = None
+            self.isbuiltin = None
+            self.isnative = None
+            self.itemsize = None
+            self.kind = None
+            self.metadata = None
+            self.name = None
+            self.names = None
+            self.num = None
+            self.shape = None
+            self.str = None
+            self.subdtype = None
+            self.type = None
+
+        def newbyteorder(self, new_order='S'): return uninferable
+        def __neg__(self): return uninferable
+
+    class busdaycalendar(object):
+        def __init__(self, weekmask='1111100', holidays=None):
+            self.holidays = None
+            self.weekmask = None
+
+    class flexible(generic): pass
+    class bool_(generic): pass
+    class number(generic):
+        def __neg__(self): return uninferable
+    class datetime64(generic):
+        def __init__(self, nb, unit=None): pass
+
+
+    class void(flexible):
+        def __init__(self, *args, **kwargs):
+            self.base = None
+            self.dtype = None
+            self.flags = None
+        def getfield(self): return uninferable
+        def setfield(self): return uninferable
+
+
+    class character(flexible): pass
+
+
+    class integer(number):
+        def __init__(self, value):
+           self.denominator = None
+           self.numerator = None
+
+
+    class inexact(number): pass
+
+
+    class str_(str, character):
+        def maketrans(self, x, y=None, z=None): return uninferable
+
+
+    class bytes_(bytes, character):
+        def fromhex(self, string): return uninferable
+        def maketrans(self, frm, to): return uninferable
+
+
+    class signedinteger(integer): pass
+
+
+    class unsignedinteger(integer): pass
+
+
+    class complexfloating(inexact): pass
+
+
+    class floating(inexact): pass
+
+
+    class float64(floating, float):
+        def fromhex(self, string): return uninferable
+
+
+    class uint64(unsignedinteger): pass
+    class complex64(complexfloating): pass
+    class int16(signedinteger): pass
+    class float96(floating): pass
+    class int8(signedinteger): pass
+    class uint32(unsignedinteger): pass
+    class uint8(unsignedinteger): pass
+    class _typedict(dict): pass
+    class complex192(complexfloating): pass
+    class timedelta64(signedinteger):
+        def __init__(self, nb, unit=None): pass
+    class int32(signedinteger): pass
+    class uint16(unsignedinteger): pass
+    class float32(floating): pass
+    class complex128(complexfloating, complex): pass
+    class float16(floating): pass
+    class int64(signedinteger): pass
+
+    buffer_type = memoryview
+    bool8 = bool_
+    byte = int8
+    bytes0 = bytes_
+    cdouble = complex128
+    cfloat = complex128
+    clongdouble = complex192
+    clongfloat = complex192
+    complex_ = complex128
+    csingle = complex64
+    double = float64
+    float_ = float64
+    half = float16
+    int0 = int32
+    int_ = int32
+    intc = int32
+    intp = int32
+    long = int32
+    longcomplex = complex192
+    longdouble = float96
+    longfloat = float96
+    longlong = int64
+    object0 = object_
+    object_ = object_
+    short = int16
+    single = float32
+    singlecomplex = complex64
+    str0 = str_
+    string_ = bytes_
+    ubyte = uint8
+    uint = uint32
+    uint0 = uint32
+    uintc = uint32
+    uintp = uint32
+    ulonglong = uint64
+    unicode = str_
+    unicode_ = str_
+    ushort = uint16
+    void0 = void
+    """
+    )
+
+
+register_module_extender(
+    AstroidManager(), "numpy.core.numerictypes", numpy_core_numerictypes_transform
+)

+ 154 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_core_umath.py

@@ -0,0 +1,154 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+# Note: starting with version 1.18 numpy module has `__getattr__` method which prevent
+# `pylint` to emit `no-member` message for all numpy's attributes. (see pylint's module
+# typecheck in `_emit_no_member` function)
+
+"""Astroid hooks for numpy.core.umath module."""
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def numpy_core_umath_transform():
+    ufunc_optional_keyword_arguments = (
+        """out=None, where=True, casting='same_kind', order='K', """
+        """dtype=None, subok=True"""
+    )
+    return parse(
+        """
+    class FakeUfunc:
+        def __init__(self):
+            self.__doc__ = str()
+            self.__name__ = str()
+            self.nin = 0
+            self.nout = 0
+            self.nargs = 0
+            self.ntypes = 0
+            self.types = None
+            self.identity = None
+            self.signature = None
+
+        @classmethod
+        def reduce(cls, a, axis=None, dtype=None, out=None):
+            return numpy.ndarray([0, 0])
+
+        @classmethod
+        def accumulate(cls, array, axis=None, dtype=None, out=None):
+            return numpy.ndarray([0, 0])
+
+        @classmethod
+        def reduceat(cls, a, indices, axis=None, dtype=None, out=None):
+            return numpy.ndarray([0, 0])
+
+        @classmethod
+        def outer(cls, A, B, **kwargs):
+            return numpy.ndarray([0, 0])
+
+        @classmethod
+        def at(cls, a, indices, b=None):
+            return numpy.ndarray([0, 0])
+
+    class FakeUfuncOneArg(FakeUfunc):
+        def __call__(self, x, {opt_args:s}):
+            return numpy.ndarray([0, 0])
+
+    class FakeUfuncOneArgBis(FakeUfunc):
+        def __call__(self, x, {opt_args:s}):
+            return numpy.ndarray([0, 0]), numpy.ndarray([0, 0])
+
+    class FakeUfuncTwoArgs(FakeUfunc):
+        def __call__(self, x1, x2, {opt_args:s}):
+            return numpy.ndarray([0, 0])
+
+    # Constants
+    e = 2.718281828459045
+    euler_gamma = 0.5772156649015329
+
+    # One arg functions with optional kwargs
+    arccos = FakeUfuncOneArg()
+    arccosh = FakeUfuncOneArg()
+    arcsin = FakeUfuncOneArg()
+    arcsinh = FakeUfuncOneArg()
+    arctan = FakeUfuncOneArg()
+    arctanh = FakeUfuncOneArg()
+    cbrt = FakeUfuncOneArg()
+    conj = FakeUfuncOneArg()
+    conjugate = FakeUfuncOneArg()
+    cosh = FakeUfuncOneArg()
+    deg2rad = FakeUfuncOneArg()
+    degrees = FakeUfuncOneArg()
+    exp2 = FakeUfuncOneArg()
+    expm1 = FakeUfuncOneArg()
+    fabs = FakeUfuncOneArg()
+    frexp = FakeUfuncOneArgBis()
+    isfinite = FakeUfuncOneArg()
+    isinf = FakeUfuncOneArg()
+    log = FakeUfuncOneArg()
+    log1p = FakeUfuncOneArg()
+    log2 = FakeUfuncOneArg()
+    logical_not = FakeUfuncOneArg()
+    modf = FakeUfuncOneArgBis()
+    negative = FakeUfuncOneArg()
+    positive = FakeUfuncOneArg()
+    rad2deg = FakeUfuncOneArg()
+    radians = FakeUfuncOneArg()
+    reciprocal = FakeUfuncOneArg()
+    rint = FakeUfuncOneArg()
+    sign = FakeUfuncOneArg()
+    signbit = FakeUfuncOneArg()
+    sinh = FakeUfuncOneArg()
+    spacing = FakeUfuncOneArg()
+    square = FakeUfuncOneArg()
+    tan = FakeUfuncOneArg()
+    tanh = FakeUfuncOneArg()
+    trunc = FakeUfuncOneArg()
+
+    # Two args functions with optional kwargs
+    add = FakeUfuncTwoArgs()
+    bitwise_and = FakeUfuncTwoArgs()
+    bitwise_or = FakeUfuncTwoArgs()
+    bitwise_xor = FakeUfuncTwoArgs()
+    copysign = FakeUfuncTwoArgs()
+    divide = FakeUfuncTwoArgs()
+    divmod = FakeUfuncTwoArgs()
+    equal = FakeUfuncTwoArgs()
+    float_power = FakeUfuncTwoArgs()
+    floor_divide = FakeUfuncTwoArgs()
+    fmax = FakeUfuncTwoArgs()
+    fmin = FakeUfuncTwoArgs()
+    fmod = FakeUfuncTwoArgs()
+    greater = FakeUfuncTwoArgs()
+    gcd = FakeUfuncTwoArgs()
+    hypot = FakeUfuncTwoArgs()
+    heaviside = FakeUfuncTwoArgs()
+    lcm = FakeUfuncTwoArgs()
+    ldexp = FakeUfuncTwoArgs()
+    left_shift = FakeUfuncTwoArgs()
+    less = FakeUfuncTwoArgs()
+    logaddexp = FakeUfuncTwoArgs()
+    logaddexp2 = FakeUfuncTwoArgs()
+    logical_and = FakeUfuncTwoArgs()
+    logical_or = FakeUfuncTwoArgs()
+    logical_xor = FakeUfuncTwoArgs()
+    maximum = FakeUfuncTwoArgs()
+    minimum = FakeUfuncTwoArgs()
+    multiply = FakeUfuncTwoArgs()
+    nextafter = FakeUfuncTwoArgs()
+    not_equal = FakeUfuncTwoArgs()
+    power = FakeUfuncTwoArgs()
+    remainder = FakeUfuncTwoArgs()
+    right_shift = FakeUfuncTwoArgs()
+    subtract = FakeUfuncTwoArgs()
+    true_divide = FakeUfuncTwoArgs()
+    """.format(
+            opt_args=ufunc_optional_keyword_arguments
+        )
+    )
+
+
+register_module_extender(
+    AstroidManager(), "numpy.core.umath", numpy_core_umath_transform
+)

+ 31 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_ma.py

@@ -0,0 +1,31 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for numpy ma module."""
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def numpy_ma_transform():
+    """
+    Infer the call of various numpy.ma functions.
+
+    :param node: node to infer
+    :param context: inference context
+    """
+    return parse(
+        """
+    import numpy.ma
+    def masked_where(condition, a, copy=True):
+        return numpy.ma.masked_array(a, mask=[])
+
+    def masked_invalid(a, copy=True):
+        return numpy.ma.masked_array(a, mask=[])
+    """
+    )
+
+
+register_module_extender(AstroidManager(), "numpy.ma", numpy_ma_transform)

+ 162 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_ndarray.py

@@ -0,0 +1,162 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for numpy ndarray class."""
+from __future__ import annotations
+
+from astroid.brain.brain_numpy_utils import numpy_supports_type_hints
+from astroid.builder import extract_node
+from astroid.context import InferenceContext
+from astroid.inference_tip import inference_tip
+from astroid.manager import AstroidManager
+from astroid.nodes.node_classes import Attribute
+
+
+def infer_numpy_ndarray(node, context: InferenceContext | None = None):
+    ndarray = """
+    class ndarray(object):
+        def __init__(self, shape, dtype=float, buffer=None, offset=0,
+                     strides=None, order=None):
+            self.T = numpy.ndarray([0, 0])
+            self.base = None
+            self.ctypes = None
+            self.data = None
+            self.dtype = None
+            self.flags = None
+            # Should be a numpy.flatiter instance but not available for now
+            # Putting an array instead so that iteration and indexing are authorized
+            self.flat = np.ndarray([0, 0])
+            self.imag = np.ndarray([0, 0])
+            self.itemsize = None
+            self.nbytes = None
+            self.ndim = None
+            self.real = np.ndarray([0, 0])
+            self.shape = numpy.ndarray([0, 0])
+            self.size = None
+            self.strides = None
+
+        def __abs__(self): return numpy.ndarray([0, 0])
+        def __add__(self, value): return numpy.ndarray([0, 0])
+        def __and__(self, value): return numpy.ndarray([0, 0])
+        def __array__(self, dtype=None): return numpy.ndarray([0, 0])
+        def __array_wrap__(self, obj): return numpy.ndarray([0, 0])
+        def __contains__(self, key): return True
+        def __copy__(self): return numpy.ndarray([0, 0])
+        def __deepcopy__(self, memo): return numpy.ndarray([0, 0])
+        def __divmod__(self, value): return (numpy.ndarray([0, 0]), numpy.ndarray([0, 0]))
+        def __eq__(self, value): return numpy.ndarray([0, 0])
+        def __float__(self): return 0.
+        def __floordiv__(self): return numpy.ndarray([0, 0])
+        def __ge__(self, value): return numpy.ndarray([0, 0])
+        def __getitem__(self, key): return uninferable
+        def __gt__(self, value): return numpy.ndarray([0, 0])
+        def __iadd__(self, value): return numpy.ndarray([0, 0])
+        def __iand__(self, value): return numpy.ndarray([0, 0])
+        def __ifloordiv__(self, value): return numpy.ndarray([0, 0])
+        def __ilshift__(self, value): return numpy.ndarray([0, 0])
+        def __imod__(self, value): return numpy.ndarray([0, 0])
+        def __imul__(self, value): return numpy.ndarray([0, 0])
+        def __int__(self): return 0
+        def __invert__(self): return numpy.ndarray([0, 0])
+        def __ior__(self, value): return numpy.ndarray([0, 0])
+        def __ipow__(self, value): return numpy.ndarray([0, 0])
+        def __irshift__(self, value): return numpy.ndarray([0, 0])
+        def __isub__(self, value): return numpy.ndarray([0, 0])
+        def __itruediv__(self, value): return numpy.ndarray([0, 0])
+        def __ixor__(self, value): return numpy.ndarray([0, 0])
+        def __le__(self, value): return numpy.ndarray([0, 0])
+        def __len__(self): return 1
+        def __lshift__(self, value): return numpy.ndarray([0, 0])
+        def __lt__(self, value): return numpy.ndarray([0, 0])
+        def __matmul__(self, value): return numpy.ndarray([0, 0])
+        def __mod__(self, value): return numpy.ndarray([0, 0])
+        def __mul__(self, value): return numpy.ndarray([0, 0])
+        def __ne__(self, value): return numpy.ndarray([0, 0])
+        def __neg__(self): return numpy.ndarray([0, 0])
+        def __or__(self, value): return numpy.ndarray([0, 0])
+        def __pos__(self): return numpy.ndarray([0, 0])
+        def __pow__(self): return numpy.ndarray([0, 0])
+        def __repr__(self): return str()
+        def __rshift__(self): return numpy.ndarray([0, 0])
+        def __setitem__(self, key, value): return uninferable
+        def __str__(self): return str()
+        def __sub__(self, value): return numpy.ndarray([0, 0])
+        def __truediv__(self, value): return numpy.ndarray([0, 0])
+        def __xor__(self, value): return numpy.ndarray([0, 0])
+        def all(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0])
+        def any(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0])
+        def argmax(self, axis=None, out=None): return np.ndarray([0, 0])
+        def argmin(self, axis=None, out=None): return np.ndarray([0, 0])
+        def argpartition(self, kth, axis=-1, kind='introselect', order=None): return np.ndarray([0, 0])
+        def argsort(self, axis=-1, kind='quicksort', order=None): return np.ndarray([0, 0])
+        def astype(self, dtype, order='K', casting='unsafe', subok=True, copy=True): return np.ndarray([0, 0])
+        def byteswap(self, inplace=False): return np.ndarray([0, 0])
+        def choose(self, choices, out=None, mode='raise'): return np.ndarray([0, 0])
+        def clip(self, min=None, max=None, out=None): return np.ndarray([0, 0])
+        def compress(self, condition, axis=None, out=None): return np.ndarray([0, 0])
+        def conj(self): return np.ndarray([0, 0])
+        def conjugate(self): return np.ndarray([0, 0])
+        def copy(self, order='C'): return np.ndarray([0, 0])
+        def cumprod(self, axis=None, dtype=None, out=None): return np.ndarray([0, 0])
+        def cumsum(self, axis=None, dtype=None, out=None): return np.ndarray([0, 0])
+        def diagonal(self, offset=0, axis1=0, axis2=1): return np.ndarray([0, 0])
+        def dot(self, b, out=None): return np.ndarray([0, 0])
+        def dump(self, file): return None
+        def dumps(self): return str()
+        def fill(self, value): return None
+        def flatten(self, order='C'): return np.ndarray([0, 0])
+        def getfield(self, dtype, offset=0): return np.ndarray([0, 0])
+        def item(self, *args): return uninferable
+        def itemset(self, *args): return None
+        def max(self, axis=None, out=None): return np.ndarray([0, 0])
+        def mean(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0])
+        def min(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0])
+        def newbyteorder(self, new_order='S'): return np.ndarray([0, 0])
+        def nonzero(self): return (1,)
+        def partition(self, kth, axis=-1, kind='introselect', order=None): return None
+        def prod(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0])
+        def ptp(self, axis=None, out=None): return np.ndarray([0, 0])
+        def put(self, indices, values, mode='raise'): return None
+        def ravel(self, order='C'): return np.ndarray([0, 0])
+        def repeat(self, repeats, axis=None): return np.ndarray([0, 0])
+        def reshape(self, shape, order='C'): return np.ndarray([0, 0])
+        def resize(self, new_shape, refcheck=True): return None
+        def round(self, decimals=0, out=None): return np.ndarray([0, 0])
+        def searchsorted(self, v, side='left', sorter=None): return np.ndarray([0, 0])
+        def setfield(self, val, dtype, offset=0): return None
+        def setflags(self, write=None, align=None, uic=None): return None
+        def sort(self, axis=-1, kind='quicksort', order=None): return None
+        def squeeze(self, axis=None): return np.ndarray([0, 0])
+        def std(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return np.ndarray([0, 0])
+        def sum(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0])
+        def swapaxes(self, axis1, axis2): return np.ndarray([0, 0])
+        def take(self, indices, axis=None, out=None, mode='raise'): return np.ndarray([0, 0])
+        def tobytes(self, order='C'): return b''
+        def tofile(self, fid, sep="", format="%s"): return None
+        def tolist(self, ): return []
+        def tostring(self, order='C'): return b''
+        def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): return np.ndarray([0, 0])
+        def transpose(self, *axes): return np.ndarray([0, 0])
+        def var(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return np.ndarray([0, 0])
+        def view(self, dtype=None, type=None): return np.ndarray([0, 0])
+    """
+    if numpy_supports_type_hints():
+        ndarray += """
+        @classmethod
+        def __class_getitem__(cls, value):
+            return cls
+        """
+    node = extract_node(ndarray)
+    return node.infer(context=context)
+
+
+def _looks_like_numpy_ndarray(node) -> bool:
+    return isinstance(node, Attribute) and node.attrname == "ndarray"
+
+
+AstroidManager().register_transform(
+    Attribute,
+    inference_tip(infer_numpy_ndarray),
+    _looks_like_numpy_ndarray,
+)

+ 71 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_random_mtrand.py

@@ -0,0 +1,71 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+# TODO(hippo91) : correct the functions return types
+"""Astroid hooks for numpy.random.mtrand module."""
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def numpy_random_mtrand_transform():
+    return parse(
+        """
+    def beta(a, b, size=None): return uninferable
+    def binomial(n, p, size=None): return uninferable
+    def bytes(length): return uninferable
+    def chisquare(df, size=None): return uninferable
+    def choice(a, size=None, replace=True, p=None): return uninferable
+    def dirichlet(alpha, size=None): return uninferable
+    def exponential(scale=1.0, size=None): return uninferable
+    def f(dfnum, dfden, size=None): return uninferable
+    def gamma(shape, scale=1.0, size=None): return uninferable
+    def geometric(p, size=None): return uninferable
+    def get_state(): return uninferable
+    def gumbel(loc=0.0, scale=1.0, size=None): return uninferable
+    def hypergeometric(ngood, nbad, nsample, size=None): return uninferable
+    def laplace(loc=0.0, scale=1.0, size=None): return uninferable
+    def logistic(loc=0.0, scale=1.0, size=None): return uninferable
+    def lognormal(mean=0.0, sigma=1.0, size=None): return uninferable
+    def logseries(p, size=None): return uninferable
+    def multinomial(n, pvals, size=None): return uninferable
+    def multivariate_normal(mean, cov, size=None): return uninferable
+    def negative_binomial(n, p, size=None): return uninferable
+    def noncentral_chisquare(df, nonc, size=None): return uninferable
+    def noncentral_f(dfnum, dfden, nonc, size=None): return uninferable
+    def normal(loc=0.0, scale=1.0, size=None): return uninferable
+    def pareto(a, size=None): return uninferable
+    def permutation(x): return uninferable
+    def poisson(lam=1.0, size=None): return uninferable
+    def power(a, size=None): return uninferable
+    def rand(*args): return uninferable
+    def randint(low, high=None, size=None, dtype='l'):
+        import numpy
+        return numpy.ndarray((1,1))
+    def randn(*args): return uninferable
+    def random(size=None): return uninferable
+    def random_integers(low, high=None, size=None): return uninferable
+    def random_sample(size=None): return uninferable
+    def rayleigh(scale=1.0, size=None): return uninferable
+    def seed(seed=None): return uninferable
+    def set_state(state): return uninferable
+    def shuffle(x): return uninferable
+    def standard_cauchy(size=None): return uninferable
+    def standard_exponential(size=None): return uninferable
+    def standard_gamma(shape, size=None): return uninferable
+    def standard_normal(size=None): return uninferable
+    def standard_t(df, size=None): return uninferable
+    def triangular(left, mode, right, size=None): return uninferable
+    def uniform(low=0.0, high=1.0, size=None): return uninferable
+    def vonmises(mu, kappa, size=None): return uninferable
+    def wald(mean, scale, size=None): return uninferable
+    def weibull(a, size=None): return uninferable
+    def zipf(a, size=None): return uninferable
+    """
+    )
+
+
+register_module_extender(
+    AstroidManager(), "numpy.random.mtrand", numpy_random_mtrand_transform
+)

+ 86 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_numpy_utils.py

@@ -0,0 +1,86 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Different utilities for the numpy brains."""
+
+from __future__ import annotations
+
+from astroid.builder import extract_node
+from astroid.context import InferenceContext
+from astroid.nodes.node_classes import Attribute, Import, Name, NodeNG
+
+# Class subscript is available in numpy starting with version 1.20.0
+NUMPY_VERSION_TYPE_HINTS_SUPPORT = ("1", "20", "0")
+
+
+def numpy_supports_type_hints() -> bool:
+    """Returns True if numpy supports type hints."""
+    np_ver = _get_numpy_version()
+    return np_ver and np_ver > NUMPY_VERSION_TYPE_HINTS_SUPPORT
+
+
+def _get_numpy_version() -> tuple[str, str, str]:
+    """
+    Return the numpy version number if numpy can be imported.
+
+    Otherwise returns ('0', '0', '0')
+    """
+    try:
+        import numpy  # pylint: disable=import-outside-toplevel
+
+        return tuple(numpy.version.version.split("."))
+    except (ImportError, AttributeError):
+        return ("0", "0", "0")
+
+
+def infer_numpy_member(src, node, context: InferenceContext | None = None):
+    node = extract_node(src)
+    return node.infer(context=context)
+
+
+def _is_a_numpy_module(node: Name) -> bool:
+    """
+    Returns True if the node is a representation of a numpy module.
+
+    For example in :
+        import numpy as np
+        x = np.linspace(1, 2)
+    The node <Name.np> is a representation of the numpy module.
+
+    :param node: node to test
+    :return: True if the node is a representation of the numpy module.
+    """
+    module_nickname = node.name
+    potential_import_target = [
+        x for x in node.lookup(module_nickname)[1] if isinstance(x, Import)
+    ]
+    return any(
+        ("numpy", module_nickname) in target.names or ("numpy", None) in target.names
+        for target in potential_import_target
+    )
+
+
+def looks_like_numpy_member(member_name: str, node: NodeNG) -> bool:
+    """
+    Returns True if the node is a member of numpy whose
+    name is member_name.
+
+    :param member_name: name of the member
+    :param node: node to test
+    :return: True if the node is a member of numpy
+    """
+    if (
+        isinstance(node, Attribute)
+        and node.attrname == member_name
+        and isinstance(node.expr, Name)
+        and _is_a_numpy_module(node.expr)
+    ):
+        return True
+    if (
+        isinstance(node, Name)
+        and node.name == member_name
+        and node.root().name.startswith("numpy")
+    ):
+        return True
+    return False

+ 51 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_pathlib.py

@@ -0,0 +1,51 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+from collections.abc import Iterator
+
+from astroid import bases, context, inference_tip, nodes
+from astroid.builder import _extract_single_node
+from astroid.exceptions import InferenceError, UseInferenceDefault
+from astroid.manager import AstroidManager
+
+PATH_TEMPLATE = """
+from pathlib import Path
+Path
+"""
+
+
+def _looks_like_parents_subscript(node: nodes.Subscript) -> bool:
+    if not (
+        isinstance(node.value, nodes.Attribute) and node.value.attrname == "parents"
+    ):
+        return False
+
+    try:
+        value = next(node.value.infer())
+    except (InferenceError, StopIteration):
+        return False
+    return (
+        isinstance(value, bases.Instance)
+        and isinstance(value._proxied, nodes.ClassDef)
+        and value.qname() == "pathlib._PathParents"
+    )
+
+
+def infer_parents_subscript(
+    subscript_node: nodes.Subscript, ctx: context.InferenceContext | None = None
+) -> Iterator[bases.Instance]:
+    if isinstance(subscript_node.slice, nodes.Const):
+        path_cls = next(_extract_single_node(PATH_TEMPLATE).infer())
+        return iter([path_cls.instantiate_class()])
+
+    raise UseInferenceDefault
+
+
+AstroidManager().register_transform(
+    nodes.Subscript,
+    inference_tip(infer_parents_subscript),
+    _looks_like_parents_subscript,
+)

+ 70 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_pkg_resources.py

@@ -0,0 +1,70 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from astroid import parse
+from astroid.brain.helpers import register_module_extender
+from astroid.manager import AstroidManager
+
+
+def pkg_resources_transform():
+    return parse(
+        """
+def require(*requirements):
+    return pkg_resources.working_set.require(*requirements)
+
+def run_script(requires, script_name):
+    return pkg_resources.working_set.run_script(requires, script_name)
+
+def iter_entry_points(group, name=None):
+    return pkg_resources.working_set.iter_entry_points(group, name)
+
+def resource_exists(package_or_requirement, resource_name):
+    return get_provider(package_or_requirement).has_resource(resource_name)
+
+def resource_isdir(package_or_requirement, resource_name):
+    return get_provider(package_or_requirement).resource_isdir(
+        resource_name)
+
+def resource_filename(package_or_requirement, resource_name):
+    return get_provider(package_or_requirement).get_resource_filename(
+        self, resource_name)
+
+def resource_stream(package_or_requirement, resource_name):
+    return get_provider(package_or_requirement).get_resource_stream(
+        self, resource_name)
+
+def resource_string(package_or_requirement, resource_name):
+    return get_provider(package_or_requirement).get_resource_string(
+        self, resource_name)
+
+def resource_listdir(package_or_requirement, resource_name):
+    return get_provider(package_or_requirement).resource_listdir(
+        resource_name)
+
+def extraction_error():
+    pass
+
+def get_cache_path(archive_name, names=()):
+    extract_path = self.extraction_path or get_default_cache()
+    target_path = os.path.join(extract_path, archive_name+'-tmp', *names)
+    return target_path
+
+def postprocess(tempname, filename):
+    pass
+
+def set_extraction_path(path):
+    pass
+
+def cleanup_resources(force=False):
+    pass
+
+def get_distribution(dist):
+    return Distribution(dist)
+
+_namespace_packages = {}
+"""
+    )
+
+
+register_module_extender(AstroidManager(), "pkg_resources", pkg_resources_transform)

+ 83 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_pytest.py

@@ -0,0 +1,83 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for pytest."""
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import AstroidBuilder
+from astroid.manager import AstroidManager
+
+
+def pytest_transform():
+    return AstroidBuilder(AstroidManager()).string_build(
+        """
+
+try:
+    import _pytest.mark
+    import _pytest.recwarn
+    import _pytest.runner
+    import _pytest.python
+    import _pytest.skipping
+    import _pytest.assertion
+except ImportError:
+    pass
+else:
+    deprecated_call = _pytest.recwarn.deprecated_call
+    warns = _pytest.recwarn.warns
+
+    exit = _pytest.runner.exit
+    fail = _pytest.runner.fail
+    skip = _pytest.runner.skip
+    importorskip = _pytest.runner.importorskip
+
+    xfail = _pytest.skipping.xfail
+    mark = _pytest.mark.MarkGenerator()
+    raises = _pytest.python.raises
+
+    # New in pytest 3.0
+    try:
+        approx = _pytest.python.approx
+        register_assert_rewrite = _pytest.assertion.register_assert_rewrite
+    except AttributeError:
+        pass
+
+
+# Moved in pytest 3.0
+
+try:
+    import _pytest.freeze_support
+    freeze_includes = _pytest.freeze_support.freeze_includes
+except ImportError:
+    try:
+        import _pytest.genscript
+        freeze_includes = _pytest.genscript.freeze_includes
+    except ImportError:
+        pass
+
+try:
+    import _pytest.debugging
+    set_trace = _pytest.debugging.pytestPDB().set_trace
+except ImportError:
+    try:
+        import _pytest.pdb
+        set_trace = _pytest.pdb.pytestPDB().set_trace
+    except ImportError:
+        pass
+
+try:
+    import _pytest.fixtures
+    fixture = _pytest.fixtures.fixture
+    yield_fixture = _pytest.fixtures.yield_fixture
+except ImportError:
+    try:
+        import _pytest.python
+        fixture = _pytest.python.fixture
+        yield_fixture = _pytest.python.yield_fixture
+    except ImportError:
+        pass
+"""
+    )
+
+
+register_module_extender(AstroidManager(), "pytest", pytest_transform)
+register_module_extender(AstroidManager(), "py.test", pytest_transform)

+ 88 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_qt.py

@@ -0,0 +1,88 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for the PyQT library."""
+
+from astroid import nodes, parse
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import AstroidBuilder
+from astroid.manager import AstroidManager
+
+
+def _looks_like_signal(
+    node: nodes.FunctionDef, signal_name: str = "pyqtSignal"
+) -> bool:
+    """Detect a Signal node."""
+    klasses = node.instance_attrs.get("__class__", [])
+    # On PySide2 or PySide6 (since  Qt 5.15.2) the Signal class changed locations
+    if node.qname().partition(".")[0] in {"PySide2", "PySide6"}:
+        return any(cls.qname() == "Signal" for cls in klasses)  # pragma: no cover
+    if klasses:
+        try:
+            return klasses[0].name == signal_name
+        except AttributeError:  # pragma: no cover
+            # return False if the cls does not have a name attribute
+            pass
+    return False
+
+
+def transform_pyqt_signal(node: nodes.FunctionDef) -> None:
+    module = parse(
+        """
+    _UNSET = object()
+
+    class pyqtSignal(object):
+        def connect(self, slot, type=None, no_receiver_check=False):
+            pass
+        def disconnect(self, slot=_UNSET):
+            pass
+        def emit(self, *args):
+            pass
+    """
+    )
+    signal_cls: nodes.ClassDef = module["pyqtSignal"]
+    node.instance_attrs["emit"] = [signal_cls["emit"]]
+    node.instance_attrs["disconnect"] = [signal_cls["disconnect"]]
+    node.instance_attrs["connect"] = [signal_cls["connect"]]
+
+
+def transform_pyside_signal(node: nodes.FunctionDef) -> None:
+    module = parse(
+        """
+    class NotPySideSignal(object):
+        def connect(self, receiver, type=None):
+            pass
+        def disconnect(self, receiver):
+            pass
+        def emit(self, *args):
+            pass
+    """
+    )
+    signal_cls: nodes.ClassDef = module["NotPySideSignal"]
+    node.instance_attrs["connect"] = [signal_cls["connect"]]
+    node.instance_attrs["disconnect"] = [signal_cls["disconnect"]]
+    node.instance_attrs["emit"] = [signal_cls["emit"]]
+
+
+def pyqt4_qtcore_transform():
+    return AstroidBuilder(AstroidManager()).string_build(
+        """
+
+def SIGNAL(signal_name): pass
+
+class QObject(object):
+    def emit(self, signal): pass
+"""
+    )
+
+
+register_module_extender(AstroidManager(), "PyQt4.QtCore", pyqt4_qtcore_transform)
+AstroidManager().register_transform(
+    nodes.FunctionDef, transform_pyqt_signal, _looks_like_signal
+)
+AstroidManager().register_transform(
+    nodes.ClassDef,
+    transform_pyside_signal,
+    lambda node: node.qname() in {"PySide.QtCore.Signal", "PySide2.QtCore.Signal"},
+)

+ 90 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_random.py

@@ -0,0 +1,90 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+import random
+
+from astroid import helpers
+from astroid.context import InferenceContext
+from astroid.exceptions import UseInferenceDefault
+from astroid.inference_tip import inference_tip
+from astroid.manager import AstroidManager
+from astroid.nodes.node_classes import (
+    Attribute,
+    Call,
+    Const,
+    EvaluatedObject,
+    List,
+    Name,
+    Set,
+    Tuple,
+)
+
+ACCEPTED_ITERABLES_FOR_SAMPLE = (List, Set, Tuple)
+
+
+def _clone_node_with_lineno(node, parent, lineno):
+    if isinstance(node, EvaluatedObject):
+        node = node.original
+    cls = node.__class__
+    other_fields = node._other_fields
+    _astroid_fields = node._astroid_fields
+    init_params = {"lineno": lineno, "col_offset": node.col_offset, "parent": parent}
+    postinit_params = {param: getattr(node, param) for param in _astroid_fields}
+    if other_fields:
+        init_params.update({param: getattr(node, param) for param in other_fields})
+    new_node = cls(**init_params)
+    if hasattr(node, "postinit") and _astroid_fields:
+        new_node.postinit(**postinit_params)
+    return new_node
+
+
+def infer_random_sample(node, context: InferenceContext | None = None):
+    if len(node.args) != 2:
+        raise UseInferenceDefault
+
+    inferred_length = helpers.safe_infer(node.args[1], context=context)
+    if not isinstance(inferred_length, Const):
+        raise UseInferenceDefault
+    if not isinstance(inferred_length.value, int):
+        raise UseInferenceDefault
+
+    inferred_sequence = helpers.safe_infer(node.args[0], context=context)
+    if not inferred_sequence:
+        raise UseInferenceDefault
+
+    if not isinstance(inferred_sequence, ACCEPTED_ITERABLES_FOR_SAMPLE):
+        raise UseInferenceDefault
+
+    if inferred_length.value > len(inferred_sequence.elts):
+        # In this case, this will raise a ValueError
+        raise UseInferenceDefault
+
+    try:
+        elts = random.sample(inferred_sequence.elts, inferred_length.value)
+    except ValueError as exc:
+        raise UseInferenceDefault from exc
+
+    new_node = List(lineno=node.lineno, col_offset=node.col_offset, parent=node.scope())
+    new_elts = [
+        _clone_node_with_lineno(elt, parent=new_node, lineno=new_node.lineno)
+        for elt in elts
+    ]
+    new_node.postinit(new_elts)
+    return iter((new_node,))
+
+
+def _looks_like_random_sample(node) -> bool:
+    func = node.func
+    if isinstance(func, Attribute):
+        return func.attrname == "sample"
+    if isinstance(func, Name):
+        return func.name == "sample"
+    return False
+
+
+AstroidManager().register_transform(
+    Call, inference_tip(infer_random_sample), _looks_like_random_sample
+)

+ 96 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_re.py

@@ -0,0 +1,96 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+from astroid import context, inference_tip, nodes
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import _extract_single_node, parse
+from astroid.const import PY39_PLUS, PY311_PLUS
+from astroid.manager import AstroidManager
+
+
+def _re_transform() -> nodes.Module:
+    # The RegexFlag enum exposes all its entries by updating globals()
+    # In 3.6-3.10 all flags come from sre_compile
+    # On 3.11+ all flags come from re._compiler
+    if PY311_PLUS:
+        import_compiler = "import re._compiler as _compiler"
+    else:
+        import_compiler = "import sre_compile as _compiler"
+    return parse(
+        f"""
+    {import_compiler}
+    NOFLAG = 0
+    ASCII = _compiler.SRE_FLAG_ASCII
+    IGNORECASE = _compiler.SRE_FLAG_IGNORECASE
+    LOCALE = _compiler.SRE_FLAG_LOCALE
+    UNICODE = _compiler.SRE_FLAG_UNICODE
+    MULTILINE = _compiler.SRE_FLAG_MULTILINE
+    DOTALL = _compiler.SRE_FLAG_DOTALL
+    VERBOSE = _compiler.SRE_FLAG_VERBOSE
+    TEMPLATE = _compiler.SRE_FLAG_TEMPLATE
+    DEBUG = _compiler.SRE_FLAG_DEBUG
+    A = ASCII
+    I = IGNORECASE
+    L = LOCALE
+    U = UNICODE
+    M = MULTILINE
+    S = DOTALL
+    X = VERBOSE
+    T = TEMPLATE
+    """
+    )
+
+
+register_module_extender(AstroidManager(), "re", _re_transform)
+
+
+CLASS_GETITEM_TEMPLATE = """
+@classmethod
+def __class_getitem__(cls, item):
+    return cls
+"""
+
+
+def _looks_like_pattern_or_match(node: nodes.Call) -> bool:
+    """Check for re.Pattern or re.Match call in stdlib.
+
+    Match these patterns from stdlib/re.py
+    ```py
+    Pattern = type(...)
+    Match = type(...)
+    ```
+    """
+    return (
+        node.root().name == "re"
+        and isinstance(node.func, nodes.Name)
+        and node.func.name == "type"
+        and isinstance(node.parent, nodes.Assign)
+        and len(node.parent.targets) == 1
+        and isinstance(node.parent.targets[0], nodes.AssignName)
+        and node.parent.targets[0].name in {"Pattern", "Match"}
+    )
+
+
+def infer_pattern_match(node: nodes.Call, ctx: context.InferenceContext | None = None):
+    """Infer re.Pattern and re.Match as classes.
+
+    For PY39+ add `__class_getitem__`.
+    """
+    class_def = nodes.ClassDef(
+        name=node.parent.targets[0].name,
+        lineno=node.lineno,
+        col_offset=node.col_offset,
+        parent=node.parent,
+    )
+    if PY39_PLUS:
+        func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
+        class_def.locals["__class_getitem__"] = [func_to_add]
+    return iter([class_def])
+
+
+AstroidManager().register_transform(
+    nodes.Call, inference_tip(infer_pattern_match), _looks_like_pattern_or_match
+)

+ 95 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_regex.py

@@ -0,0 +1,95 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+from astroid import context, inference_tip, nodes
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import _extract_single_node, parse
+from astroid.const import PY39_PLUS
+from astroid.manager import AstroidManager
+
+
+def _regex_transform() -> nodes.Module:
+    """The RegexFlag enum exposes all its entries by updating globals().
+
+    We hard-code the flags for now.
+    # pylint: disable-next=line-too-long
+    See https://github.com/mrabarnett/mrab-regex/blob/2022.10.31/regex_3/regex.py#L200
+    """
+    return parse(
+        """
+    A = ASCII = 0x80          # Assume ASCII locale.
+    B = BESTMATCH = 0x1000    # Best fuzzy match.
+    D = DEBUG = 0x200         # Print parsed pattern.
+    E = ENHANCEMATCH = 0x8000 # Attempt to improve the fit after finding the first
+                              # fuzzy match.
+    F = FULLCASE = 0x4000     # Unicode full case-folding.
+    I = IGNORECASE = 0x2      # Ignore case.
+    L = LOCALE = 0x4          # Assume current 8-bit locale.
+    M = MULTILINE = 0x8       # Make anchors look for newline.
+    P = POSIX = 0x10000       # POSIX-style matching (leftmost longest).
+    R = REVERSE = 0x400       # Search backwards.
+    S = DOTALL = 0x10         # Make dot match newline.
+    U = UNICODE = 0x20        # Assume Unicode locale.
+    V0 = VERSION0 = 0x2000    # Old legacy behaviour.
+    DEFAULT_VERSION = V0
+    V1 = VERSION1 = 0x100     # New enhanced behaviour.
+    W = WORD = 0x800          # Default Unicode word breaks.
+    X = VERBOSE = 0x40        # Ignore whitespace and comments.
+    T = TEMPLATE = 0x1        # Template (present because re module has it).
+    """
+    )
+
+
+register_module_extender(AstroidManager(), "regex", _regex_transform)
+
+
+CLASS_GETITEM_TEMPLATE = """
+@classmethod
+def __class_getitem__(cls, item):
+    return cls
+"""
+
+
+def _looks_like_pattern_or_match(node: nodes.Call) -> bool:
+    """Check for regex.Pattern or regex.Match call in stdlib.
+
+    Match these patterns from stdlib/re.py
+    ```py
+    Pattern = type(...)
+    Match = type(...)
+    ```
+    """
+    return (
+        node.root().name == "regex.regex"
+        and isinstance(node.func, nodes.Name)
+        and node.func.name == "type"
+        and isinstance(node.parent, nodes.Assign)
+        and len(node.parent.targets) == 1
+        and isinstance(node.parent.targets[0], nodes.AssignName)
+        and node.parent.targets[0].name in {"Pattern", "Match"}
+    )
+
+
+def infer_pattern_match(node: nodes.Call, ctx: context.InferenceContext | None = None):
+    """Infer regex.Pattern and regex.Match as classes.
+
+    For PY39+ add `__class_getitem__`.
+    """
+    class_def = nodes.ClassDef(
+        name=node.parent.targets[0].name,
+        lineno=node.lineno,
+        col_offset=node.col_offset,
+        parent=node.parent,
+    )
+    if PY39_PLUS:
+        func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
+        class_def.locals["__class_getitem__"] = [func_to_add]
+    return iter([class_def])
+
+
+AstroidManager().register_transform(
+    nodes.Call, inference_tip(infer_pattern_match), _looks_like_pattern_or_match
+)

+ 78 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_responses.py

@@ -0,0 +1,78 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""
+Astroid hooks for responses.
+
+It might need to be manually updated from the public methods of
+:class:`responses.RequestsMock`.
+
+See: https://github.com/getsentry/responses/blob/master/responses.py
+"""
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def responses_funcs():
+    return parse(
+        """
+        DELETE = "DELETE"
+        GET = "GET"
+        HEAD = "HEAD"
+        OPTIONS = "OPTIONS"
+        PATCH = "PATCH"
+        POST = "POST"
+        PUT = "PUT"
+        response_callback = None
+
+        def reset():
+            return
+
+        def add(
+            method=None,  # method or ``Response``
+            url=None,
+            body="",
+            adding_headers=None,
+            *args,
+            **kwargs
+        ):
+            return
+
+        def add_passthru(prefix):
+            return
+
+        def remove(method_or_response=None, url=None):
+            return
+
+        def replace(method_or_response=None, url=None, body="", *args, **kwargs):
+            return
+
+        def add_callback(
+            method, url, callback, match_querystring=False, content_type="text/plain"
+        ):
+            return
+
+        calls = []
+
+        def __enter__():
+            return
+
+        def __exit__(type, value, traceback):
+            success = type is None
+            return success
+
+        def activate(func):
+            return func
+
+        def start():
+            return
+
+        def stop(allow_assert=True):
+            return
+        """
+    )
+
+
+register_module_extender(AstroidManager(), "responses", responses_funcs)

+ 88 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_scipy_signal.py

@@ -0,0 +1,88 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for scipy.signal module."""
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def scipy_signal():
+    return parse(
+        """
+    # different functions defined in scipy.signals
+
+    def barthann(M, sym=True):
+        return numpy.ndarray([0])
+
+    def bartlett(M, sym=True):
+        return numpy.ndarray([0])
+
+    def blackman(M, sym=True):
+        return numpy.ndarray([0])
+
+    def blackmanharris(M, sym=True):
+        return numpy.ndarray([0])
+
+    def bohman(M, sym=True):
+        return numpy.ndarray([0])
+
+    def boxcar(M, sym=True):
+        return numpy.ndarray([0])
+
+    def chebwin(M, at, sym=True):
+        return numpy.ndarray([0])
+
+    def cosine(M, sym=True):
+        return numpy.ndarray([0])
+
+    def exponential(M, center=None, tau=1.0, sym=True):
+        return numpy.ndarray([0])
+
+    def flattop(M, sym=True):
+        return numpy.ndarray([0])
+
+    def gaussian(M, std, sym=True):
+        return numpy.ndarray([0])
+
+    def general_gaussian(M, p, sig, sym=True):
+        return numpy.ndarray([0])
+
+    def hamming(M, sym=True):
+        return numpy.ndarray([0])
+
+    def hann(M, sym=True):
+        return numpy.ndarray([0])
+
+    def hanning(M, sym=True):
+        return numpy.ndarray([0])
+
+    def impulse2(system, X0=None, T=None, N=None, **kwargs):
+        return numpy.ndarray([0]), numpy.ndarray([0])
+
+    def kaiser(M, beta, sym=True):
+        return numpy.ndarray([0])
+
+    def nuttall(M, sym=True):
+        return numpy.ndarray([0])
+
+    def parzen(M, sym=True):
+        return numpy.ndarray([0])
+
+    def slepian(M, width, sym=True):
+        return numpy.ndarray([0])
+
+    def step2(system, X0=None, T=None, N=None, **kwargs):
+        return numpy.ndarray([0]), numpy.ndarray([0])
+
+    def triang(M, sym=True):
+        return numpy.ndarray([0])
+
+    def tukey(M, alpha=0.5, sym=True):
+        return numpy.ndarray([0])
+        """
+    )
+
+
+register_module_extender(AstroidManager(), "scipy.signal", scipy_signal)

+ 119 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_signal.py

@@ -0,0 +1,119 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for the signal library.
+
+The signal module generates the 'Signals', 'Handlers' and 'Sigmasks' IntEnums
+dynamically using the IntEnum._convert() classmethod, which modifies the module
+globals. Astroid is unable to handle this type of code.
+
+Without these hooks, the following are erroneously triggered by Pylint:
+    * E1101: Module 'signal' has no 'Signals' member (no-member)
+    * E1101: Module 'signal' has no 'Handlers' member (no-member)
+    * E1101: Module 'signal' has no 'Sigmasks' member (no-member)
+
+These enums are defined slightly differently depending on the user's operating
+system and platform. These platform differences should follow the current
+Python typeshed stdlib `signal.pyi` stub file, available at:
+
+* https://github.com/python/typeshed/blob/master/stdlib/signal.pyi
+
+Note that the enum.auto() values defined here for the Signals, Handlers and
+Sigmasks IntEnums are just dummy integer values, and do not correspond to the
+actual standard signal numbers - which may vary depending on the system.
+"""
+
+
+import sys
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def _signals_enums_transform():
+    """Generates the AST for 'Signals', 'Handlers' and 'Sigmasks' IntEnums."""
+    return parse(_signals_enum() + _handlers_enum() + _sigmasks_enum())
+
+
+def _signals_enum() -> str:
+    """Generates the source code for the Signals int enum."""
+    signals_enum = """
+    import enum
+    class Signals(enum.IntEnum):
+        SIGABRT   = enum.auto()
+        SIGEMT    = enum.auto()
+        SIGFPE    = enum.auto()
+        SIGILL    = enum.auto()
+        SIGINFO   = enum.auto()
+        SIGINT    = enum.auto()
+        SIGSEGV   = enum.auto()
+        SIGTERM   = enum.auto()
+    """
+    if sys.platform != "win32":
+        signals_enum += """
+        SIGALRM   = enum.auto()
+        SIGBUS    = enum.auto()
+        SIGCHLD   = enum.auto()
+        SIGCONT   = enum.auto()
+        SIGHUP    = enum.auto()
+        SIGIO     = enum.auto()
+        SIGIOT    = enum.auto()
+        SIGKILL   = enum.auto()
+        SIGPIPE   = enum.auto()
+        SIGPROF   = enum.auto()
+        SIGQUIT   = enum.auto()
+        SIGSTOP   = enum.auto()
+        SIGSYS    = enum.auto()
+        SIGTRAP   = enum.auto()
+        SIGTSTP   = enum.auto()
+        SIGTTIN   = enum.auto()
+        SIGTTOU   = enum.auto()
+        SIGURG    = enum.auto()
+        SIGUSR1   = enum.auto()
+        SIGUSR2   = enum.auto()
+        SIGVTALRM = enum.auto()
+        SIGWINCH  = enum.auto()
+        SIGXCPU   = enum.auto()
+        SIGXFSZ   = enum.auto()
+        """
+    if sys.platform == "win32":
+        signals_enum += """
+        SIGBREAK  = enum.auto()
+        """
+    if sys.platform not in ("darwin", "win32"):
+        signals_enum += """
+        SIGCLD    = enum.auto()
+        SIGPOLL   = enum.auto()
+        SIGPWR    = enum.auto()
+        SIGRTMAX  = enum.auto()
+        SIGRTMIN  = enum.auto()
+        """
+    return signals_enum
+
+
+def _handlers_enum() -> str:
+    """Generates the source code for the Handlers int enum."""
+    return """
+    import enum
+    class Handlers(enum.IntEnum):
+        SIG_DFL = enum.auto()
+        SIG_IGN = eunm.auto()
+    """
+
+
+def _sigmasks_enum() -> str:
+    """Generates the source code for the Sigmasks int enum."""
+    if sys.platform != "win32":
+        return """
+    import enum
+    class Sigmasks(enum.IntEnum):
+        SIG_BLOCK   = enum.auto()
+        SIG_UNBLOCK = enum.auto()
+        SIG_SETMASK = enum.auto()
+        """
+    return ""
+
+
+register_module_extender(AstroidManager(), "signal", _signals_enums_transform)

+ 239 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_six.py

@@ -0,0 +1,239 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for six module."""
+
+from textwrap import dedent
+
+from astroid import nodes
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import AstroidBuilder
+from astroid.exceptions import (
+    AstroidBuildingError,
+    AttributeInferenceError,
+    InferenceError,
+)
+from astroid.manager import AstroidManager
+
+SIX_ADD_METACLASS = "six.add_metaclass"
+SIX_WITH_METACLASS = "six.with_metaclass"
+
+
+def default_predicate(line):
+    return line.strip()
+
+
+def _indent(text, prefix, predicate=default_predicate) -> str:
+    """Adds 'prefix' to the beginning of selected lines in 'text'.
+
+    If 'predicate' is provided, 'prefix' will only be added to the lines
+    where 'predicate(line)' is True. If 'predicate' is not provided,
+    it will default to adding 'prefix' to all non-empty lines that do not
+    consist solely of whitespace characters.
+    """
+
+    def prefixed_lines():
+        for line in text.splitlines(True):
+            yield prefix + line if predicate(line) else line
+
+    return "".join(prefixed_lines())
+
+
+_IMPORTS = """
+import _io
+cStringIO = _io.StringIO
+filter = filter
+from itertools import filterfalse
+input = input
+from sys import intern
+map = map
+range = range
+from importlib import reload
+reload_module = lambda module: reload(module)
+from functools import reduce
+from shlex import quote as shlex_quote
+from io import StringIO
+from collections import UserDict, UserList, UserString
+xrange = range
+zip = zip
+from itertools import zip_longest
+import builtins
+import configparser
+import copyreg
+import _dummy_thread
+import http.cookiejar as http_cookiejar
+import http.cookies as http_cookies
+import html.entities as html_entities
+import html.parser as html_parser
+import http.client as http_client
+import http.server as http_server
+BaseHTTPServer = CGIHTTPServer = SimpleHTTPServer = http.server
+import pickle as cPickle
+import queue
+import reprlib
+import socketserver
+import _thread
+import winreg
+import xmlrpc.server as xmlrpc_server
+import xmlrpc.client as xmlrpc_client
+import urllib.robotparser as urllib_robotparser
+import email.mime.multipart as email_mime_multipart
+import email.mime.nonmultipart as email_mime_nonmultipart
+import email.mime.text as email_mime_text
+import email.mime.base as email_mime_base
+import urllib.parse as urllib_parse
+import urllib.error as urllib_error
+import tkinter
+import tkinter.dialog as tkinter_dialog
+import tkinter.filedialog as tkinter_filedialog
+import tkinter.scrolledtext as tkinter_scrolledtext
+import tkinter.simpledialog as tkinder_simpledialog
+import tkinter.tix as tkinter_tix
+import tkinter.ttk as tkinter_ttk
+import tkinter.constants as tkinter_constants
+import tkinter.dnd as tkinter_dnd
+import tkinter.colorchooser as tkinter_colorchooser
+import tkinter.commondialog as tkinter_commondialog
+import tkinter.filedialog as tkinter_tkfiledialog
+import tkinter.font as tkinter_font
+import tkinter.messagebox as tkinter_messagebox
+import urllib
+import urllib.request as urllib_request
+import urllib.robotparser as urllib_robotparser
+import urllib.parse as urllib_parse
+import urllib.error as urllib_error
+"""
+
+
+def six_moves_transform():
+    code = dedent(
+        """
+    class Moves(object):
+    {}
+    moves = Moves()
+    """
+    ).format(_indent(_IMPORTS, "    "))
+    module = AstroidBuilder(AstroidManager()).string_build(code)
+    module.name = "six.moves"
+    return module
+
+
+def _six_fail_hook(modname):
+    """Fix six.moves imports due to the dynamic nature of this
+    class.
+
+    Construct a pseudo-module which contains all the necessary imports
+    for six
+
+    :param modname: Name of failed module
+    :type modname: str
+
+    :return: An astroid module
+    :rtype: nodes.Module
+    """
+
+    attribute_of = modname != "six.moves" and modname.startswith("six.moves")
+    if modname != "six.moves" and not attribute_of:
+        raise AstroidBuildingError(modname=modname)
+    module = AstroidBuilder(AstroidManager()).string_build(_IMPORTS)
+    module.name = "six.moves"
+    if attribute_of:
+        # Facilitate import of submodules in Moves
+        start_index = len(module.name)
+        attribute = modname[start_index:].lstrip(".").replace(".", "_")
+        try:
+            import_attr = module.getattr(attribute)[0]
+        except AttributeInferenceError as exc:
+            raise AstroidBuildingError(modname=modname) from exc
+        if isinstance(import_attr, nodes.Import):
+            submodule = AstroidManager().ast_from_module_name(import_attr.names[0][0])
+            return submodule
+    # Let dummy submodule imports pass through
+    # This will cause an Uninferable result, which is okay
+    return module
+
+
+def _looks_like_decorated_with_six_add_metaclass(node) -> bool:
+    if not node.decorators:
+        return False
+
+    for decorator in node.decorators.nodes:
+        if not isinstance(decorator, nodes.Call):
+            continue
+        if decorator.func.as_string() == SIX_ADD_METACLASS:
+            return True
+    return False
+
+
+def transform_six_add_metaclass(node):  # pylint: disable=inconsistent-return-statements
+    """Check if the given class node is decorated with *six.add_metaclass*.
+
+    If so, inject its argument as the metaclass of the underlying class.
+    """
+    if not node.decorators:
+        return
+
+    for decorator in node.decorators.nodes:
+        if not isinstance(decorator, nodes.Call):
+            continue
+
+        try:
+            func = next(decorator.func.infer())
+        except (InferenceError, StopIteration):
+            continue
+        if func.qname() == SIX_ADD_METACLASS and decorator.args:
+            metaclass = decorator.args[0]
+            node._metaclass = metaclass
+            return node
+    return
+
+
+def _looks_like_nested_from_six_with_metaclass(node) -> bool:
+    if len(node.bases) != 1:
+        return False
+    base = node.bases[0]
+    if not isinstance(base, nodes.Call):
+        return False
+    try:
+        if hasattr(base.func, "expr"):
+            # format when explicit 'six.with_metaclass' is used
+            mod = base.func.expr.name
+            func = base.func.attrname
+            func = f"{mod}.{func}"
+        else:
+            # format when 'with_metaclass' is used directly (local import from six)
+            # check reference module to avoid 'with_metaclass' name clashes
+            mod = base.parent.parent
+            import_from = mod.locals["with_metaclass"][0]
+            func = f"{import_from.modname}.{base.func.name}"
+    except (AttributeError, KeyError, IndexError):
+        return False
+    return func == SIX_WITH_METACLASS
+
+
+def transform_six_with_metaclass(node):
+    """Check if the given class node is defined with *six.with_metaclass*.
+
+    If so, inject its argument as the metaclass of the underlying class.
+    """
+    call = node.bases[0]
+    node._metaclass = call.args[0]
+    return node
+
+
+register_module_extender(AstroidManager(), "six", six_moves_transform)
+register_module_extender(
+    AstroidManager(), "requests.packages.urllib3.packages.six", six_moves_transform
+)
+AstroidManager().register_failed_import_hook(_six_fail_hook)
+AstroidManager().register_transform(
+    nodes.ClassDef,
+    transform_six_add_metaclass,
+    _looks_like_decorated_with_six_add_metaclass,
+)
+AstroidManager().register_transform(
+    nodes.ClassDef,
+    transform_six_with_metaclass,
+    _looks_like_nested_from_six_with_metaclass,
+)

+ 39 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_sqlalchemy.py

@@ -0,0 +1,39 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def _session_transform():
+    return parse(
+        """
+    from sqlalchemy.orm.session import Session
+
+    class sessionmaker:
+        def __init__(
+            self,
+            bind=None,
+            class_=Session,
+            autoflush=True,
+            autocommit=False,
+            expire_on_commit=True,
+            info=None,
+            **kw
+        ):
+            return
+
+        def __call__(self, **local_kw):
+            return Session()
+
+        def configure(self, **new_kw):
+            return
+
+        return Session()
+    """
+    )
+
+
+register_module_extender(AstroidManager(), "sqlalchemy.orm.session", _session_transform)

+ 160 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_ssl.py

@@ -0,0 +1,160 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for the ssl library."""
+
+from astroid import parse
+from astroid.brain.helpers import register_module_extender
+from astroid.const import PY38_PLUS, PY310_PLUS
+from astroid.manager import AstroidManager
+
+
+def _verifyflags_enum() -> str:
+    enum = """
+    class VerifyFlags(_IntFlag):
+        VERIFY_DEFAULT = 0
+        VERIFY_CRL_CHECK_LEAF = 1
+        VERIFY_CRL_CHECK_CHAIN = 2
+        VERIFY_X509_STRICT = 3
+        VERIFY_X509_TRUSTED_FIRST = 4"""
+    if PY310_PLUS:
+        enum += """
+        VERIFY_ALLOW_PROXY_CERTS = 5
+        VERIFY_X509_PARTIAL_CHAIN = 6
+        """
+    return enum
+
+
+def _options_enum() -> str:
+    enum = """
+    class Options(_IntFlag):
+        OP_ALL = 1
+        OP_NO_SSLv2 = 2
+        OP_NO_SSLv3 = 3
+        OP_NO_TLSv1 = 4
+        OP_NO_TLSv1_1 = 5
+        OP_NO_TLSv1_2 = 6
+        OP_NO_TLSv1_3 = 7
+        OP_CIPHER_SERVER_PREFERENCE = 8
+        OP_SINGLE_DH_USE = 9
+        OP_SINGLE_ECDH_USE = 10
+        OP_NO_COMPRESSION = 11
+        OP_NO_TICKET = 12
+        OP_NO_RENEGOTIATION = 13"""
+    if PY38_PLUS:
+        enum += """
+        OP_ENABLE_MIDDLEBOX_COMPAT = 14"""
+    return enum
+
+
+def ssl_transform():
+    return parse(
+        """
+    # Import necessary for conversion of objects defined in C into enums
+    from enum import IntEnum as _IntEnum, IntFlag as _IntFlag
+
+    from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
+    from _ssl import _SSLContext, MemoryBIO
+    from _ssl import (
+        SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError,
+        SSLSyscallError, SSLEOFError,
+        )
+    from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
+    from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj
+    from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes
+    try:
+        from _ssl import RAND_egd
+    except ImportError:
+        # LibreSSL does not provide RAND_egd
+        pass
+    from _ssl import (OP_ALL, OP_CIPHER_SERVER_PREFERENCE,
+                      OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3,
+                      OP_NO_TLSv1, OP_NO_TLSv1_1, OP_NO_TLSv1_2,
+                      OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE)
+
+    from _ssl import (ALERT_DESCRIPTION_ACCESS_DENIED, ALERT_DESCRIPTION_BAD_CERTIFICATE,
+                      ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE,
+                      ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE,
+                      ALERT_DESCRIPTION_BAD_RECORD_MAC,
+                      ALERT_DESCRIPTION_CERTIFICATE_EXPIRED,
+                      ALERT_DESCRIPTION_CERTIFICATE_REVOKED,
+                      ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN,
+                      ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE,
+                      ALERT_DESCRIPTION_CLOSE_NOTIFY, ALERT_DESCRIPTION_DECODE_ERROR,
+                      ALERT_DESCRIPTION_DECOMPRESSION_FAILURE,
+                      ALERT_DESCRIPTION_DECRYPT_ERROR,
+                      ALERT_DESCRIPTION_HANDSHAKE_FAILURE,
+                      ALERT_DESCRIPTION_ILLEGAL_PARAMETER,
+                      ALERT_DESCRIPTION_INSUFFICIENT_SECURITY,
+                      ALERT_DESCRIPTION_INTERNAL_ERROR,
+                      ALERT_DESCRIPTION_NO_RENEGOTIATION,
+                      ALERT_DESCRIPTION_PROTOCOL_VERSION,
+                      ALERT_DESCRIPTION_RECORD_OVERFLOW,
+                      ALERT_DESCRIPTION_UNEXPECTED_MESSAGE,
+                      ALERT_DESCRIPTION_UNKNOWN_CA,
+                      ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY,
+                      ALERT_DESCRIPTION_UNRECOGNIZED_NAME,
+                      ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE,
+                      ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION,
+                      ALERT_DESCRIPTION_USER_CANCELLED)
+    from _ssl import (SSL_ERROR_EOF, SSL_ERROR_INVALID_ERROR_CODE, SSL_ERROR_SSL,
+                      SSL_ERROR_SYSCALL, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_READ,
+                      SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_ZERO_RETURN)
+    from _ssl import VERIFY_CRL_CHECK_CHAIN, VERIFY_CRL_CHECK_LEAF, VERIFY_DEFAULT, VERIFY_X509_STRICT
+    from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN
+    from _ssl import _OPENSSL_API_VERSION
+    from _ssl import PROTOCOL_SSLv23, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2
+    from _ssl import PROTOCOL_TLS, PROTOCOL_TLS_CLIENT, PROTOCOL_TLS_SERVER
+
+    class AlertDescription(_IntEnum):
+        ALERT_DESCRIPTION_ACCESS_DENIED = 0
+        ALERT_DESCRIPTION_BAD_CERTIFICATE = 1
+        ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE = 2
+        ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE = 3
+        ALERT_DESCRIPTION_BAD_RECORD_MAC = 4
+        ALERT_DESCRIPTION_CERTIFICATE_EXPIRED = 5
+        ALERT_DESCRIPTION_CERTIFICATE_REVOKED = 6
+        ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN = 7
+        ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE = 8
+        ALERT_DESCRIPTION_CLOSE_NOTIFY = 9
+        ALERT_DESCRIPTION_DECODE_ERROR = 10
+        ALERT_DESCRIPTION_DECOMPRESSION_FAILURE = 11
+        ALERT_DESCRIPTION_DECRYPT_ERROR = 12
+        ALERT_DESCRIPTION_HANDSHAKE_FAILURE = 13
+        ALERT_DESCRIPTION_ILLEGAL_PARAMETER = 14
+        ALERT_DESCRIPTION_INSUFFICIENT_SECURITY = 15
+        ALERT_DESCRIPTION_INTERNAL_ERROR = 16
+        ALERT_DESCRIPTION_NO_RENEGOTIATION = 17
+        ALERT_DESCRIPTION_PROTOCOL_VERSION = 18
+        ALERT_DESCRIPTION_RECORD_OVERFLOW = 19
+        ALERT_DESCRIPTION_UNEXPECTED_MESSAGE = 20
+        ALERT_DESCRIPTION_UNKNOWN_CA = 21
+        ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY = 22
+        ALERT_DESCRIPTION_UNRECOGNIZED_NAME = 23
+        ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE = 24
+        ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION = 25
+        ALERT_DESCRIPTION_USER_CANCELLED = 26
+
+    class SSLErrorNumber(_IntEnum):
+        SSL_ERROR_EOF = 0
+        SSL_ERROR_INVALID_ERROR_CODE = 1
+        SSL_ERROR_SSL = 2
+        SSL_ERROR_SYSCALL = 3
+        SSL_ERROR_WANT_CONNECT = 4
+        SSL_ERROR_WANT_READ = 5
+        SSL_ERROR_WANT_WRITE = 6
+        SSL_ERROR_WANT_X509_LOOKUP = 7
+        SSL_ERROR_ZERO_RETURN = 8
+
+    class VerifyMode(_IntEnum):
+        CERT_NONE = 0
+        CERT_OPTIONAL = 1
+        CERT_REQUIRED = 2
+    """
+        + _verifyflags_enum()
+        + _options_enum()
+    )
+
+
+register_module_extender(AstroidManager(), "ssl", ssl_transform)

+ 105 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_subprocess.py

@@ -0,0 +1,105 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+import textwrap
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.const import PY39_PLUS, PY310_PLUS, PY311_PLUS
+from astroid.manager import AstroidManager
+
+
+def _subprocess_transform():
+    communicate = (bytes("string", "ascii"), bytes("string", "ascii"))
+    communicate_signature = "def communicate(self, input=None, timeout=None)"
+    args = """\
+        self, args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None,
+        preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None,
+        universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True,
+        start_new_session=False, pass_fds=(), *, encoding=None, errors=None, text=None"""
+
+    if PY39_PLUS:
+        args += ", user=None, group=None, extra_groups=None, umask=-1"
+    if PY310_PLUS:
+        args += ", pipesize=-1"
+    if PY311_PLUS:
+        args += ", process_group=None"
+
+    init = f"""
+        def __init__({args}):
+            pass"""
+    wait_signature = "def wait(self, timeout=None)"
+    ctx_manager = """
+        def __enter__(self): return self
+        def __exit__(self, *args): pass
+    """
+    py3_args = "args = []"
+
+    check_output_signature = """
+    check_output(
+        args, *,
+        stdin=None,
+        stderr=None,
+        shell=False,
+        cwd=None,
+        encoding=None,
+        errors=None,
+        universal_newlines=False,
+        timeout=None,
+        env=None,
+        text=None,
+        restore_signals=True,
+        preexec_fn=None,
+        pass_fds=(),
+        input=None,
+        bufsize=0,
+        executable=None,
+        close_fds=False,
+        startupinfo=None,
+        creationflags=0,
+        start_new_session=False
+    ):
+    """.strip()
+
+    code = textwrap.dedent(
+        f"""
+    def {check_output_signature}
+        if universal_newlines:
+            return ""
+        return b""
+
+    class Popen(object):
+        returncode = pid = 0
+        stdin = stdout = stderr = file()
+        {py3_args}
+
+        {communicate_signature}:
+            return {communicate!r}
+        {wait_signature}:
+            return self.returncode
+        def poll(self):
+            return self.returncode
+        def send_signal(self, signal):
+            pass
+        def terminate(self):
+            pass
+        def kill(self):
+            pass
+        {ctx_manager}
+       """
+    )
+    if PY39_PLUS:
+        code += """
+    @classmethod
+    def __class_getitem__(cls, item):
+        pass
+        """
+
+    init_lines = textwrap.dedent(init).splitlines()
+    indented_init = "\n".join(" " * 4 + line for line in init_lines)
+    code += indented_init
+    return parse(code)
+
+
+register_module_extender(AstroidManager(), "subprocess", _subprocess_transform)

+ 31 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_threading.py

@@ -0,0 +1,31 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.manager import AstroidManager
+
+
+def _thread_transform():
+    return parse(
+        """
+    class lock(object):
+        def acquire(self, blocking=True, timeout=-1):
+            return False
+        def release(self):
+            pass
+        def __enter__(self):
+            return True
+        def __exit__(self, *args):
+            pass
+        def locked(self):
+            return False
+
+    def Lock(*args, **kwargs):
+        return lock()
+    """
+    )
+
+
+register_module_extender(AstroidManager(), "threading", _thread_transform)

+ 69 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_type.py

@@ -0,0 +1,69 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""
+Astroid hooks for type support.
+
+Starting from python3.9, type object behaves as it had __class_getitem__ method.
+However it was not possible to simply add this method inside type's body, otherwise
+all types would also have this method. In this case it would have been possible
+to write str[int].
+Guido Van Rossum proposed a hack to handle this in the interpreter:
+https://github.com/python/cpython/blob/67e394562d67cbcd0ac8114e5439494e7645b8f5/Objects/abstract.c#L181-L184
+
+This brain follows the same logic. It is no wise to add permanently the __class_getitem__ method
+to the type object. Instead we choose to add it only in the case of a subscript node
+which inside name node is type.
+Doing this type[int] is allowed whereas str[int] is not.
+
+Thanks to Lukasz Langa for fruitful discussion.
+"""
+
+from __future__ import annotations
+
+from astroid import extract_node, inference_tip, nodes
+from astroid.const import PY39_PLUS
+from astroid.context import InferenceContext
+from astroid.exceptions import UseInferenceDefault
+from astroid.manager import AstroidManager
+
+
+def _looks_like_type_subscript(node) -> bool:
+    """
+    Try to figure out if a Name node is used inside a type related subscript.
+
+    :param node: node to check
+    :type node: astroid.nodes.node_classes.NodeNG
+    :return: whether the node is a Name node inside a type related subscript
+    """
+    if isinstance(node, nodes.Name) and isinstance(node.parent, nodes.Subscript):
+        return node.name == "type"
+    return False
+
+
+def infer_type_sub(node, context: InferenceContext | None = None):
+    """
+    Infer a type[...] subscript.
+
+    :param node: node to infer
+    :type node: astroid.nodes.node_classes.NodeNG
+    :return: the inferred node
+    :rtype: nodes.NodeNG
+    """
+    node_scope, _ = node.scope().lookup("type")
+    if not isinstance(node_scope, nodes.Module) or node_scope.qname() != "builtins":
+        raise UseInferenceDefault()
+    class_src = """
+    class type:
+        def __class_getitem__(cls, key):
+            return cls
+     """
+    node = extract_node(class_src)
+    return node.infer(context=context)
+
+
+if PY39_PLUS:
+    AstroidManager().register_transform(
+        nodes.Name, inference_tip(infer_type_sub), _looks_like_type_subscript
+    )

+ 447 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_typing.py

@@ -0,0 +1,447 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for typing.py support."""
+
+from __future__ import annotations
+
+import sys
+import typing
+from collections.abc import Iterator
+from functools import partial
+
+from astroid import context, extract_node, inference_tip
+from astroid.builder import _extract_single_node
+from astroid.const import PY38_PLUS, PY39_PLUS
+from astroid.exceptions import (
+    AttributeInferenceError,
+    InferenceError,
+    UseInferenceDefault,
+)
+from astroid.manager import AstroidManager
+from astroid.nodes.node_classes import (
+    Assign,
+    AssignName,
+    Attribute,
+    Call,
+    Const,
+    JoinedStr,
+    Name,
+    NodeNG,
+    Subscript,
+    Tuple,
+)
+from astroid.nodes.scoped_nodes import ClassDef, FunctionDef
+
+if sys.version_info >= (3, 8):
+    from typing import Final
+else:
+    from typing_extensions import Final
+
+TYPING_TYPEVARS = {"TypeVar", "NewType"}
+TYPING_TYPEVARS_QUALIFIED: Final = {
+    "typing.TypeVar",
+    "typing.NewType",
+    "typing_extensions.TypeVar",
+}
+TYPING_TYPEDDICT_QUALIFIED: Final = {"typing.TypedDict", "typing_extensions.TypedDict"}
+TYPING_TYPE_TEMPLATE = """
+class Meta(type):
+    def __getitem__(self, item):
+        return self
+
+    @property
+    def __args__(self):
+        return ()
+
+class {0}(metaclass=Meta):
+    pass
+"""
+TYPING_MEMBERS = set(getattr(typing, "__all__", []))
+
+TYPING_ALIAS = frozenset(
+    (
+        "typing.Hashable",
+        "typing.Awaitable",
+        "typing.Coroutine",
+        "typing.AsyncIterable",
+        "typing.AsyncIterator",
+        "typing.Iterable",
+        "typing.Iterator",
+        "typing.Reversible",
+        "typing.Sized",
+        "typing.Container",
+        "typing.Collection",
+        "typing.Callable",
+        "typing.AbstractSet",
+        "typing.MutableSet",
+        "typing.Mapping",
+        "typing.MutableMapping",
+        "typing.Sequence",
+        "typing.MutableSequence",
+        "typing.ByteString",
+        "typing.Tuple",
+        "typing.List",
+        "typing.Deque",
+        "typing.Set",
+        "typing.FrozenSet",
+        "typing.MappingView",
+        "typing.KeysView",
+        "typing.ItemsView",
+        "typing.ValuesView",
+        "typing.ContextManager",
+        "typing.AsyncContextManager",
+        "typing.Dict",
+        "typing.DefaultDict",
+        "typing.OrderedDict",
+        "typing.Counter",
+        "typing.ChainMap",
+        "typing.Generator",
+        "typing.AsyncGenerator",
+        "typing.Type",
+        "typing.Pattern",
+        "typing.Match",
+    )
+)
+
+CLASS_GETITEM_TEMPLATE = """
+@classmethod
+def __class_getitem__(cls, item):
+    return cls
+"""
+
+
+def looks_like_typing_typevar_or_newtype(node) -> bool:
+    func = node.func
+    if isinstance(func, Attribute):
+        return func.attrname in TYPING_TYPEVARS
+    if isinstance(func, Name):
+        return func.name in TYPING_TYPEVARS
+    return False
+
+
+def infer_typing_typevar_or_newtype(
+    node: Call, context_itton: context.InferenceContext | None = None
+) -> Iterator[ClassDef]:
+    """Infer a typing.TypeVar(...) or typing.NewType(...) call."""
+    try:
+        func = next(node.func.infer(context=context_itton))
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+
+    if func.qname() not in TYPING_TYPEVARS_QUALIFIED:
+        raise UseInferenceDefault
+    if not node.args:
+        raise UseInferenceDefault
+    # Cannot infer from a dynamic class name (f-string)
+    if isinstance(node.args[0], JoinedStr):
+        raise UseInferenceDefault
+
+    typename = node.args[0].as_string().strip("'")
+    node = ClassDef(
+        name=typename,
+        lineno=node.lineno,
+        col_offset=node.col_offset,
+        parent=node.parent,
+        end_lineno=node.end_lineno,
+        end_col_offset=node.end_col_offset,
+    )
+    return node.infer(context=context_itton)
+
+
+def _looks_like_typing_subscript(node) -> bool:
+    """Try to figure out if a Subscript node *might* be a typing-related subscript."""
+    if isinstance(node, Name):
+        return node.name in TYPING_MEMBERS
+    if isinstance(node, Attribute):
+        return node.attrname in TYPING_MEMBERS
+    if isinstance(node, Subscript):
+        return _looks_like_typing_subscript(node.value)
+    return False
+
+
+def infer_typing_attr(
+    node: Subscript, ctx: context.InferenceContext | None = None
+) -> Iterator[ClassDef]:
+    """Infer a typing.X[...] subscript."""
+    try:
+        value = next(node.value.infer())  # type: ignore[union-attr] # value shouldn't be None for Subscript.
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+
+    if not value.qname().startswith("typing.") or value.qname() in TYPING_ALIAS:
+        # If typing subscript belongs to an alias handle it separately.
+        raise UseInferenceDefault
+
+    if isinstance(value, ClassDef) and value.qname() in {
+        "typing.Generic",
+        "typing.Annotated",
+        "typing_extensions.Annotated",
+    }:
+        # typing.Generic and typing.Annotated (PY39) are subscriptable
+        # through __class_getitem__. Since astroid can't easily
+        # infer the native methods, replace them for an easy inference tip
+        func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
+        value.locals["__class_getitem__"] = [func_to_add]
+        if (
+            isinstance(node.parent, ClassDef)
+            and node in node.parent.bases
+            and getattr(node.parent, "__cache", None)
+        ):
+            # node.parent.slots is evaluated and cached before the inference tip
+            # is first applied. Remove the last result to allow a recalculation of slots
+            cache = node.parent.__cache  # type: ignore[attr-defined] # Unrecognized getattr
+            if cache.get(node.parent.slots) is not None:
+                del cache[node.parent.slots]
+        return iter([value])
+
+    node = extract_node(TYPING_TYPE_TEMPLATE.format(value.qname().split(".")[-1]))
+    return node.infer(context=ctx)
+
+
+def _looks_like_typedDict(  # pylint: disable=invalid-name
+    node: FunctionDef | ClassDef,
+) -> bool:
+    """Check if node is TypedDict FunctionDef."""
+    return node.qname() in TYPING_TYPEDDICT_QUALIFIED
+
+
+def infer_old_typedDict(  # pylint: disable=invalid-name
+    node: ClassDef, ctx: context.InferenceContext | None = None
+) -> Iterator[ClassDef]:
+    func_to_add = _extract_single_node("dict")
+    node.locals["__call__"] = [func_to_add]
+    return iter([node])
+
+
+def infer_typedDict(  # pylint: disable=invalid-name
+    node: FunctionDef, ctx: context.InferenceContext | None = None
+) -> Iterator[ClassDef]:
+    """Replace TypedDict FunctionDef with ClassDef."""
+    class_def = ClassDef(
+        name="TypedDict",
+        lineno=node.lineno,
+        col_offset=node.col_offset,
+        parent=node.parent,
+    )
+    class_def.postinit(bases=[extract_node("dict")], body=[], decorators=None)
+    func_to_add = _extract_single_node("dict")
+    class_def.locals["__call__"] = [func_to_add]
+    return iter([class_def])
+
+
+def _looks_like_typing_alias(node: Call) -> bool:
+    """
+    Returns True if the node corresponds to a call to _alias function.
+
+    For example :
+
+    MutableSet = _alias(collections.abc.MutableSet, T)
+
+    :param node: call node
+    """
+    return (
+        isinstance(node.func, Name)
+        and node.func.name == "_alias"
+        and (
+            # _alias function works also for builtins object such as list and dict
+            isinstance(node.args[0], (Attribute, Name))
+        )
+    )
+
+
+def _forbid_class_getitem_access(node: ClassDef) -> None:
+    """Disable the access to __class_getitem__ method for the node in parameters."""
+
+    def full_raiser(origin_func, attr, *args, **kwargs):
+        """
+        Raises an AttributeInferenceError in case of access to __class_getitem__ method.
+        Otherwise, just call origin_func.
+        """
+        if attr == "__class_getitem__":
+            raise AttributeInferenceError("__class_getitem__ access is not allowed")
+        return origin_func(attr, *args, **kwargs)
+
+    try:
+        node.getattr("__class_getitem__")
+        # If we are here, then we are sure to modify an object that does have
+        # __class_getitem__ method (which origin is the protocol defined in
+        # collections module) whereas the typing module considers it should not.
+        # We do not want __class_getitem__ to be found in the classdef
+        partial_raiser = partial(full_raiser, node.getattr)
+        node.getattr = partial_raiser
+    except AttributeInferenceError:
+        pass
+
+
+def infer_typing_alias(
+    node: Call, ctx: context.InferenceContext | None = None
+) -> Iterator[ClassDef]:
+    """
+    Infers the call to _alias function
+    Insert ClassDef, with same name as aliased class,
+    in mro to simulate _GenericAlias.
+
+    :param node: call node
+    :param context: inference context
+    """
+    if (
+        not isinstance(node.parent, Assign)
+        or not len(node.parent.targets) == 1
+        or not isinstance(node.parent.targets[0], AssignName)
+    ):
+        raise UseInferenceDefault
+    try:
+        res = next(node.args[0].infer(context=ctx))
+    except StopIteration as e:
+        raise InferenceError(node=node.args[0], context=ctx) from e
+
+    assign_name = node.parent.targets[0]
+
+    class_def = ClassDef(
+        name=assign_name.name,
+        lineno=assign_name.lineno,
+        col_offset=assign_name.col_offset,
+        parent=node.parent,
+    )
+    if isinstance(res, ClassDef):
+        # Only add `res` as base if it's a `ClassDef`
+        # This isn't the case for `typing.Pattern` and `typing.Match`
+        class_def.postinit(bases=[res], body=[], decorators=None)
+
+    maybe_type_var = node.args[1]
+    if (
+        not PY39_PLUS
+        and not (isinstance(maybe_type_var, Tuple) and not maybe_type_var.elts)
+        or PY39_PLUS
+        and isinstance(maybe_type_var, Const)
+        and maybe_type_var.value > 0
+    ):
+        # If typing alias is subscriptable, add `__class_getitem__` to ClassDef
+        func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
+        class_def.locals["__class_getitem__"] = [func_to_add]
+    else:
+        # If not, make sure that `__class_getitem__` access is forbidden.
+        # This is an issue in cases where the aliased class implements it,
+        # but the typing alias isn't subscriptable. E.g., `typing.ByteString` for PY39+
+        _forbid_class_getitem_access(class_def)
+    return iter([class_def])
+
+
+def _looks_like_special_alias(node: Call) -> bool:
+    """Return True if call is for Tuple or Callable alias.
+
+    In PY37 and PY38 the call is to '_VariadicGenericAlias' with 'tuple' as
+    first argument. In PY39+ it is replaced by a call to '_TupleType'.
+
+    PY37: Tuple = _VariadicGenericAlias(tuple, (), inst=False, special=True)
+    PY39: Tuple = _TupleType(tuple, -1, inst=False, name='Tuple')
+
+    PY37: Callable = _VariadicGenericAlias(collections.abc.Callable, (), special=True)
+    PY39: Callable = _CallableType(collections.abc.Callable, 2)
+    """
+    return isinstance(node.func, Name) and (
+        not PY39_PLUS
+        and node.func.name == "_VariadicGenericAlias"
+        and (
+            isinstance(node.args[0], Name)
+            and node.args[0].name == "tuple"
+            or isinstance(node.args[0], Attribute)
+            and node.args[0].as_string() == "collections.abc.Callable"
+        )
+        or PY39_PLUS
+        and (
+            node.func.name == "_TupleType"
+            and isinstance(node.args[0], Name)
+            and node.args[0].name == "tuple"
+            or node.func.name == "_CallableType"
+            and isinstance(node.args[0], Attribute)
+            and node.args[0].as_string() == "collections.abc.Callable"
+        )
+    )
+
+
+def infer_special_alias(
+    node: Call, ctx: context.InferenceContext | None = None
+) -> Iterator[ClassDef]:
+    """Infer call to tuple alias as new subscriptable class typing.Tuple."""
+    if not (
+        isinstance(node.parent, Assign)
+        and len(node.parent.targets) == 1
+        and isinstance(node.parent.targets[0], AssignName)
+    ):
+        raise UseInferenceDefault
+    try:
+        res = next(node.args[0].infer(context=ctx))
+    except StopIteration as e:
+        raise InferenceError(node=node.args[0], context=ctx) from e
+
+    assign_name = node.parent.targets[0]
+    class_def = ClassDef(
+        name=assign_name.name,
+        parent=node.parent,
+    )
+    class_def.postinit(bases=[res], body=[], decorators=None)
+    func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
+    class_def.locals["__class_getitem__"] = [func_to_add]
+    return iter([class_def])
+
+
+def _looks_like_typing_cast(node: Call) -> bool:
+    return isinstance(node, Call) and (
+        isinstance(node.func, Name)
+        and node.func.name == "cast"
+        or isinstance(node.func, Attribute)
+        and node.func.attrname == "cast"
+    )
+
+
+def infer_typing_cast(
+    node: Call, ctx: context.InferenceContext | None = None
+) -> Iterator[NodeNG]:
+    """Infer call to cast() returning same type as casted-from var."""
+    if not isinstance(node.func, (Name, Attribute)):
+        raise UseInferenceDefault
+
+    try:
+        func = next(node.func.infer(context=ctx))
+    except (InferenceError, StopIteration) as exc:
+        raise UseInferenceDefault from exc
+    if (
+        not isinstance(func, FunctionDef)
+        or func.qname() != "typing.cast"
+        or len(node.args) != 2
+    ):
+        raise UseInferenceDefault
+
+    return node.args[1].infer(context=ctx)
+
+
+AstroidManager().register_transform(
+    Call,
+    inference_tip(infer_typing_typevar_or_newtype),
+    looks_like_typing_typevar_or_newtype,
+)
+AstroidManager().register_transform(
+    Subscript, inference_tip(infer_typing_attr), _looks_like_typing_subscript
+)
+AstroidManager().register_transform(
+    Call, inference_tip(infer_typing_cast), _looks_like_typing_cast
+)
+
+if PY39_PLUS:
+    AstroidManager().register_transform(
+        FunctionDef, inference_tip(infer_typedDict), _looks_like_typedDict
+    )
+elif PY38_PLUS:
+    AstroidManager().register_transform(
+        ClassDef, inference_tip(infer_old_typedDict), _looks_like_typedDict
+    )
+
+AstroidManager().register_transform(
+    Call, inference_tip(infer_typing_alias), _looks_like_typing_alias
+)
+AstroidManager().register_transform(
+    Call, inference_tip(infer_special_alias), _looks_like_special_alias
+)

+ 33 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_unittest.py

@@ -0,0 +1,33 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for unittest module."""
+from astroid.brain.helpers import register_module_extender
+from astroid.builder import parse
+from astroid.const import PY38_PLUS
+from astroid.manager import AstroidManager
+
+
+def IsolatedAsyncioTestCaseImport():
+    """
+    In the unittest package, the IsolatedAsyncioTestCase class is imported lazily.
+
+    I.E. only when the ``__getattr__`` method of the unittest module is called with
+    'IsolatedAsyncioTestCase' as argument. Thus the IsolatedAsyncioTestCase
+    is not imported statically (during import time).
+    This function mocks a classical static import of the IsolatedAsyncioTestCase.
+
+    (see https://github.com/PyCQA/pylint/issues/4060)
+    """
+    return parse(
+        """
+    from .async_case import IsolatedAsyncioTestCase
+    """
+    )
+
+
+if PY38_PLUS:
+    register_module_extender(
+        AstroidManager(), "unittest", IsolatedAsyncioTestCaseImport
+    )

+ 18 - 0
venv/lib/python3.11/site-packages/astroid/brain/brain_uuid.py

@@ -0,0 +1,18 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Astroid hooks for the UUID module."""
+from astroid.manager import AstroidManager
+from astroid.nodes.node_classes import Const
+from astroid.nodes.scoped_nodes import ClassDef
+
+
+def _patch_uuid_class(node: ClassDef) -> None:
+    # The .int member is patched using __dict__
+    node.locals["int"] = [Const(0, parent=node)]
+
+
+AstroidManager().register_transform(
+    ClassDef, _patch_uuid_class, lambda node: node.qname() == "uuid.UUID"
+)

+ 24 - 0
venv/lib/python3.11/site-packages/astroid/brain/helpers.py

@@ -0,0 +1,24 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+from collections.abc import Callable
+
+from astroid.manager import AstroidManager
+from astroid.nodes.scoped_nodes import Module
+
+
+def register_module_extender(
+    manager: AstroidManager, module_name: str, get_extension_mod: Callable[[], Module]
+) -> None:
+    def transform(node: Module) -> None:
+        extension_module = get_extension_mod()
+        for name, objs in extension_module.locals.items():
+            node.locals[name] = objs
+            for obj in objs:
+                if obj.parent is extension_module:
+                    obj.parent = node
+
+    manager.register_transform(Module, transform, lambda n: n.name == module_name)

+ 494 - 0
venv/lib/python3.11/site-packages/astroid/builder.py

@@ -0,0 +1,494 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""The AstroidBuilder makes astroid from living object and / or from _ast.
+
+The builder is not thread safe and can't be used to parse different sources
+at the same time.
+"""
+
+from __future__ import annotations
+
+import ast
+import os
+import textwrap
+import types
+from collections.abc import Iterator, Sequence
+from io import TextIOWrapper
+from tokenize import detect_encoding
+from typing import TYPE_CHECKING
+
+from astroid import bases, modutils, nodes, raw_building, rebuilder, util
+from astroid._ast import ParserModule, get_parser_module
+from astroid.exceptions import AstroidBuildingError, AstroidSyntaxError, InferenceError
+from astroid.manager import AstroidManager
+
+if TYPE_CHECKING:
+    from astroid import objects
+else:
+    objects = util.lazy_import("objects")
+
+
+# The name of the transient function that is used to
+# wrap expressions to be extracted when calling
+# extract_node.
+_TRANSIENT_FUNCTION = "__"
+
+# The comment used to select a statement to be extracted
+# when calling extract_node.
+_STATEMENT_SELECTOR = "#@"
+MISPLACED_TYPE_ANNOTATION_ERROR = "misplaced type annotation"
+
+
+def open_source_file(filename: str) -> tuple[TextIOWrapper, str, str]:
+    # pylint: disable=consider-using-with
+    with open(filename, "rb") as byte_stream:
+        encoding = detect_encoding(byte_stream.readline)[0]
+    stream = open(filename, newline=None, encoding=encoding)
+    data = stream.read()
+    return stream, encoding, data
+
+
+def _can_assign_attr(node: nodes.ClassDef, attrname: str | None) -> bool:
+    try:
+        slots = node.slots()
+    except NotImplementedError:
+        pass
+    else:
+        if slots and attrname not in {slot.value for slot in slots}:
+            return False
+    return node.qname() != "builtins.object"
+
+
+class AstroidBuilder(raw_building.InspectBuilder):
+    """Class for building an astroid tree from source code or from a live module.
+
+    The param *manager* specifies the manager class which should be used.
+    If no manager is given, then the default one will be used. The
+    param *apply_transforms* determines if the transforms should be
+    applied after the tree was built from source or from a live object,
+    by default being True.
+    """
+
+    def __init__(
+        self, manager: AstroidManager | None = None, apply_transforms: bool = True
+    ) -> None:
+        super().__init__(manager)
+        self._apply_transforms = apply_transforms
+
+    def module_build(
+        self, module: types.ModuleType, modname: str | None = None
+    ) -> nodes.Module:
+        """Build an astroid from a living module instance."""
+        node = None
+        path = getattr(module, "__file__", None)
+        loader = getattr(module, "__loader__", None)
+        # Prefer the loader to get the source rather than assuming we have a
+        # filesystem to read the source file from ourselves.
+        if loader:
+            modname = modname or module.__name__
+            source = loader.get_source(modname)
+            if source:
+                node = self.string_build(source, modname, path=path)
+        if node is None and path is not None:
+            path_, ext = os.path.splitext(modutils._path_from_filename(path))
+            if ext in {".py", ".pyc", ".pyo"} and os.path.exists(path_ + ".py"):
+                node = self.file_build(path_ + ".py", modname)
+        if node is None:
+            # this is a built-in module
+            # get a partial representation by introspection
+            node = self.inspect_build(module, modname=modname, path=path)
+            if self._apply_transforms:
+                # We have to handle transformation by ourselves since the
+                # rebuilder isn't called for builtin nodes
+                node = self._manager.visit_transforms(node)
+        assert isinstance(node, nodes.Module)
+        return node
+
+    def file_build(self, path: str, modname: str | None = None) -> nodes.Module:
+        """Build astroid from a source code file (i.e. from an ast).
+
+        *path* is expected to be a python source file
+        """
+        try:
+            stream, encoding, data = open_source_file(path)
+        except OSError as exc:
+            raise AstroidBuildingError(
+                "Unable to load file {path}:\n{error}",
+                modname=modname,
+                path=path,
+                error=exc,
+            ) from exc
+        except (SyntaxError, LookupError) as exc:
+            raise AstroidSyntaxError(
+                "Python 3 encoding specification error or unknown encoding:\n"
+                "{error}",
+                modname=modname,
+                path=path,
+                error=exc,
+            ) from exc
+        except UnicodeError as exc:  # wrong encoding
+            # detect_encoding returns utf-8 if no encoding specified
+            raise AstroidBuildingError(
+                "Wrong or no encoding specified for {filename}.", filename=path
+            ) from exc
+        with stream:
+            # get module name if necessary
+            if modname is None:
+                try:
+                    modname = ".".join(modutils.modpath_from_file(path))
+                except ImportError:
+                    modname = os.path.splitext(os.path.basename(path))[0]
+            # build astroid representation
+            module, builder = self._data_build(data, modname, path)
+            return self._post_build(module, builder, encoding)
+
+    def string_build(
+        self, data: str, modname: str = "", path: str | None = None
+    ) -> nodes.Module:
+        """Build astroid from source code string."""
+        module, builder = self._data_build(data, modname, path)
+        module.file_bytes = data.encode("utf-8")
+        return self._post_build(module, builder, "utf-8")
+
+    def _post_build(
+        self, module: nodes.Module, builder: rebuilder.TreeRebuilder, encoding: str
+    ) -> nodes.Module:
+        """Handles encoding and delayed nodes after a module has been built."""
+        module.file_encoding = encoding
+        self._manager.cache_module(module)
+        # post tree building steps after we stored the module in the cache:
+        for from_node in builder._import_from_nodes:
+            if from_node.modname == "__future__":
+                for symbol, _ in from_node.names:
+                    module.future_imports.add(symbol)
+            self.add_from_names_to_locals(from_node)
+        # handle delayed assattr nodes
+        for delayed in builder._delayed_assattr:
+            self.delayed_assattr(delayed)
+
+        # Visit the transforms
+        if self._apply_transforms:
+            module = self._manager.visit_transforms(module)
+        return module
+
+    def _data_build(
+        self, data: str, modname: str, path: str | None
+    ) -> tuple[nodes.Module, rebuilder.TreeRebuilder]:
+        """Build tree node from data and add some informations."""
+        try:
+            node, parser_module = _parse_string(data, type_comments=True)
+        except (TypeError, ValueError, SyntaxError) as exc:
+            raise AstroidSyntaxError(
+                "Parsing Python code failed:\n{error}",
+                source=data,
+                modname=modname,
+                path=path,
+                error=exc,
+            ) from exc
+
+        if path is not None:
+            node_file = os.path.abspath(path)
+        else:
+            node_file = "<?>"
+        if modname.endswith(".__init__"):
+            modname = modname[:-9]
+            package = True
+        else:
+            package = (
+                path is not None
+                and os.path.splitext(os.path.basename(path))[0] == "__init__"
+            )
+        builder = rebuilder.TreeRebuilder(self._manager, parser_module, data)
+        module = builder.visit_module(node, modname, node_file, package)
+        return module, builder
+
+    def add_from_names_to_locals(self, node: nodes.ImportFrom) -> None:
+        """Store imported names to the locals.
+
+        Resort the locals if coming from a delayed node
+        """
+
+        def _key_func(node: nodes.NodeNG) -> int:
+            return node.fromlineno or 0
+
+        def sort_locals(my_list: list[nodes.NodeNG]) -> None:
+            my_list.sort(key=_key_func)
+
+        assert node.parent  # It should always default to the module
+        for name, asname in node.names:
+            if name == "*":
+                try:
+                    imported = node.do_import_module()
+                except AstroidBuildingError:
+                    continue
+                for name in imported.public_names():
+                    node.parent.set_local(name, node)
+                    sort_locals(node.parent.scope().locals[name])  # type: ignore[arg-type]
+            else:
+                node.parent.set_local(asname or name, node)
+                sort_locals(node.parent.scope().locals[asname or name])  # type: ignore[arg-type]
+
+    def delayed_assattr(self, node: nodes.AssignAttr) -> None:
+        """Visit a AssAttr node.
+
+        This adds name to locals and handle members definition.
+        """
+        try:
+            frame = node.frame(future=True)
+            for inferred in node.expr.infer():
+                if isinstance(inferred, util.UninferableBase):
+                    continue
+                try:
+                    # pylint: disable=unidiomatic-typecheck # We want a narrow check on the
+                    # parent type, not all of its subclasses
+                    if (
+                        type(inferred) == bases.Instance
+                        or type(inferred) == objects.ExceptionInstance
+                    ):
+                        inferred = inferred._proxied
+                        iattrs = inferred.instance_attrs
+                        if not _can_assign_attr(inferred, node.attrname):
+                            continue
+                    elif isinstance(inferred, bases.Instance):
+                        # Const, Tuple or other containers that inherit from
+                        # `Instance`
+                        continue
+                    elif isinstance(inferred, (bases.Proxy, util.UninferableBase)):
+                        continue
+                    elif inferred.is_function:
+                        iattrs = inferred.instance_attrs
+                    else:
+                        iattrs = inferred.locals
+                except AttributeError:
+                    # XXX log error
+                    continue
+                values = iattrs.setdefault(node.attrname, [])
+                if node in values:
+                    continue
+                # get assign in __init__ first XXX useful ?
+                if (
+                    frame.name == "__init__"
+                    and values
+                    and values[0].frame(future=True).name != "__init__"
+                ):
+                    values.insert(0, node)
+                else:
+                    values.append(node)
+        except InferenceError:
+            pass
+
+
+def build_namespace_package_module(name: str, path: Sequence[str]) -> nodes.Module:
+    # TODO: Typing: Remove the cast to list and just update typing to accept Sequence
+    return nodes.Module(name, path=list(path), package=True)
+
+
+def parse(
+    code: str,
+    module_name: str = "",
+    path: str | None = None,
+    apply_transforms: bool = True,
+) -> nodes.Module:
+    """Parses a source string in order to obtain an astroid AST from it.
+
+    :param str code: The code for the module.
+    :param str module_name: The name for the module, if any
+    :param str path: The path for the module
+    :param bool apply_transforms:
+        Apply the transforms for the give code. Use it if you
+        don't want the default transforms to be applied.
+    """
+    code = textwrap.dedent(code)
+    builder = AstroidBuilder(
+        manager=AstroidManager(), apply_transforms=apply_transforms
+    )
+    return builder.string_build(code, modname=module_name, path=path)
+
+
+def _extract_expressions(node: nodes.NodeNG) -> Iterator[nodes.NodeNG]:
+    """Find expressions in a call to _TRANSIENT_FUNCTION and extract them.
+
+    The function walks the AST recursively to search for expressions that
+    are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an
+    expression, it completely removes the function call node from the tree,
+    replacing it by the wrapped expression inside the parent.
+
+    :param node: An astroid node.
+    :type node:  astroid.bases.NodeNG
+    :yields: The sequence of wrapped expressions on the modified tree
+    expression can be found.
+    """
+    if (
+        isinstance(node, nodes.Call)
+        and isinstance(node.func, nodes.Name)
+        and node.func.name == _TRANSIENT_FUNCTION
+    ):
+        real_expr = node.args[0]
+        assert node.parent
+        real_expr.parent = node.parent
+        # Search for node in all _astng_fields (the fields checked when
+        # get_children is called) of its parent. Some of those fields may
+        # be lists or tuples, in which case the elements need to be checked.
+        # When we find it, replace it by real_expr, so that the AST looks
+        # like no call to _TRANSIENT_FUNCTION ever took place.
+        for name in node.parent._astroid_fields:
+            child = getattr(node.parent, name)
+            if isinstance(child, list):
+                for idx, compound_child in enumerate(child):
+                    if compound_child is node:
+                        child[idx] = real_expr
+            elif child is node:
+                setattr(node.parent, name, real_expr)
+        yield real_expr
+    else:
+        for child in node.get_children():
+            yield from _extract_expressions(child)
+
+
+def _find_statement_by_line(node: nodes.NodeNG, line: int) -> nodes.NodeNG | None:
+    """Extracts the statement on a specific line from an AST.
+
+    If the line number of node matches line, it will be returned;
+    otherwise its children are iterated and the function is called
+    recursively.
+
+    :param node: An astroid node.
+    :type node: astroid.bases.NodeNG
+    :param line: The line number of the statement to extract.
+    :type line: int
+    :returns: The statement on the line, or None if no statement for the line
+      can be found.
+    :rtype:  astroid.bases.NodeNG or None
+    """
+    if isinstance(node, (nodes.ClassDef, nodes.FunctionDef, nodes.MatchCase)):
+        # This is an inaccuracy in the AST: the nodes that can be
+        # decorated do not carry explicit information on which line
+        # the actual definition (class/def), but .fromline seems to
+        # be close enough.
+        node_line = node.fromlineno
+    else:
+        node_line = node.lineno
+
+    if node_line == line:
+        return node
+
+    for child in node.get_children():
+        result = _find_statement_by_line(child, line)
+        if result:
+            return result
+
+    return None
+
+
+def extract_node(code: str, module_name: str = "") -> nodes.NodeNG | list[nodes.NodeNG]:
+    """Parses some Python code as a module and extracts a designated AST node.
+
+    Statements:
+     To extract one or more statement nodes, append #@ to the end of the line
+
+     Examples:
+       >>> def x():
+       >>>   def y():
+       >>>     return 1 #@
+
+       The return statement will be extracted.
+
+       >>> class X(object):
+       >>>   def meth(self): #@
+       >>>     pass
+
+      The function object 'meth' will be extracted.
+
+    Expressions:
+     To extract arbitrary expressions, surround them with the fake
+     function call __(...). After parsing, the surrounded expression
+     will be returned and the whole AST (accessible via the returned
+     node's parent attribute) will look like the function call was
+     never there in the first place.
+
+     Examples:
+       >>> a = __(1)
+
+       The const node will be extracted.
+
+       >>> def x(d=__(foo.bar)): pass
+
+       The node containing the default argument will be extracted.
+
+       >>> def foo(a, b):
+       >>>   return 0 < __(len(a)) < b
+
+       The node containing the function call 'len' will be extracted.
+
+    If no statements or expressions are selected, the last toplevel
+    statement will be returned.
+
+    If the selected statement is a discard statement, (i.e. an expression
+    turned into a statement), the wrapped expression is returned instead.
+
+    For convenience, singleton lists are unpacked.
+
+    :param str code: A piece of Python code that is parsed as
+    a module. Will be passed through textwrap.dedent first.
+    :param str module_name: The name of the module.
+    :returns: The designated node from the parse tree, or a list of nodes.
+    """
+
+    def _extract(node: nodes.NodeNG | None) -> nodes.NodeNG | None:
+        if isinstance(node, nodes.Expr):
+            return node.value
+
+        return node
+
+    requested_lines: list[int] = []
+    for idx, line in enumerate(code.splitlines()):
+        if line.strip().endswith(_STATEMENT_SELECTOR):
+            requested_lines.append(idx + 1)
+
+    tree = parse(code, module_name=module_name)
+    if not tree.body:
+        raise ValueError("Empty tree, cannot extract from it")
+
+    extracted: list[nodes.NodeNG | None] = []
+    if requested_lines:
+        extracted = [_find_statement_by_line(tree, line) for line in requested_lines]
+
+    # Modifies the tree.
+    extracted.extend(_extract_expressions(tree))
+
+    if not extracted:
+        extracted.append(tree.body[-1])
+
+    extracted = [_extract(node) for node in extracted]
+    extracted_without_none = [node for node in extracted if node is not None]
+    if len(extracted_without_none) == 1:
+        return extracted_without_none[0]
+    return extracted_without_none
+
+
+def _extract_single_node(code: str, module_name: str = "") -> nodes.NodeNG:
+    """Call extract_node while making sure that only one value is returned."""
+    ret = extract_node(code, module_name)
+    if isinstance(ret, list):
+        return ret[0]
+    return ret
+
+
+def _parse_string(
+    data: str, type_comments: bool = True
+) -> tuple[ast.Module, ParserModule]:
+    parser_module = get_parser_module(type_comments=type_comments)
+    try:
+        parsed = parser_module.parse(data + "\n", type_comments=type_comments)
+    except SyntaxError as exc:
+        # If the type annotations are misplaced for some reason, we do not want
+        # to fail the entire parsing of the file, so we need to retry the parsing without
+        # type comment support.
+        if exc.args[0] != MISPLACED_TYPE_ANNOTATION_ERROR or not type_comments:
+            raise
+
+        parser_module = get_parser_module(type_comments=False)
+        parsed = parser_module.parse(data + "\n", type_comments=False)
+    return parsed, parser_module

+ 41 - 0
venv/lib/python3.11/site-packages/astroid/const.py

@@ -0,0 +1,41 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+import enum
+import sys
+from pathlib import Path
+
+PY38 = sys.version_info[:2] == (3, 8)
+PY38_PLUS = sys.version_info >= (3, 8)
+PY39_PLUS = sys.version_info >= (3, 9)
+PY310_PLUS = sys.version_info >= (3, 10)
+PY311_PLUS = sys.version_info >= (3, 11)
+BUILTINS = "builtins"  # TODO Remove in 2.8
+
+WIN32 = sys.platform == "win32"
+
+IS_PYPY = sys.implementation.name == "pypy"
+IS_JYTHON = sys.implementation.name == "jython"
+
+# pylint: disable-next=no-member
+PYPY_7_3_11_PLUS = IS_PYPY and sys.pypy_version_info >= (7, 3, 11)  # type: ignore[attr-defined]
+
+
+class Context(enum.Enum):
+    Load = 1
+    Store = 2
+    Del = 3
+
+
+# TODO Remove in 3.0 in favor of Context
+Load = Context.Load
+Store = Context.Store
+Del = Context.Del
+
+
+ASTROID_INSTALL_DIRECTORY = Path(__file__).parent
+BRAIN_MODULES_DIRECTORY = ASTROID_INSTALL_DIRECTORY / "brain"
+
+
+_EMPTY_OBJECT_MARKER = object()

+ 137 - 0
venv/lib/python3.11/site-packages/astroid/constraint.py

@@ -0,0 +1,137 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Classes representing different types of constraints on inference values."""
+from __future__ import annotations
+
+import sys
+from abc import ABC, abstractmethod
+from collections.abc import Iterator
+from typing import Union
+
+from astroid import bases, nodes, util
+from astroid.typing import InferenceResult
+
+if sys.version_info >= (3, 11):
+    from typing import Self
+else:
+    from typing_extensions import Self
+
+_NameNodes = Union[nodes.AssignAttr, nodes.Attribute, nodes.AssignName, nodes.Name]
+
+
+class Constraint(ABC):
+    """Represents a single constraint on a variable."""
+
+    def __init__(self, node: nodes.NodeNG, negate: bool) -> None:
+        self.node = node
+        """The node that this constraint applies to."""
+        self.negate = negate
+        """True if this constraint is negated. E.g., "is not" instead of "is"."""
+
+    @classmethod
+    @abstractmethod
+    def match(
+        cls: type[Self], node: _NameNodes, expr: nodes.NodeNG, negate: bool = False
+    ) -> Self | None:
+        """Return a new constraint for node matched from expr, if expr matches
+        the constraint pattern.
+
+        If negate is True, negate the constraint.
+        """
+
+    @abstractmethod
+    def satisfied_by(self, inferred: InferenceResult) -> bool:
+        """Return True if this constraint is satisfied by the given inferred value."""
+
+
+class NoneConstraint(Constraint):
+    """Represents an "is None" or "is not None" constraint."""
+
+    CONST_NONE: nodes.Const = nodes.Const(None)
+
+    @classmethod
+    def match(
+        cls: type[Self], node: _NameNodes, expr: nodes.NodeNG, negate: bool = False
+    ) -> Self | None:
+        """Return a new constraint for node matched from expr, if expr matches
+        the constraint pattern.
+
+        Negate the constraint based on the value of negate.
+        """
+        if isinstance(expr, nodes.Compare) and len(expr.ops) == 1:
+            left = expr.left
+            op, right = expr.ops[0]
+            if op in {"is", "is not"} and (
+                _matches(left, node) and _matches(right, cls.CONST_NONE)
+            ):
+                negate = (op == "is" and negate) or (op == "is not" and not negate)
+                return cls(node=node, negate=negate)
+
+        return None
+
+    def satisfied_by(self, inferred: InferenceResult) -> bool:
+        """Return True if this constraint is satisfied by the given inferred value."""
+        # Assume true if uninferable
+        if isinstance(inferred, util.UninferableBase):
+            return True
+
+        # Return the XOR of self.negate and matches(inferred, self.CONST_NONE)
+        return self.negate ^ _matches(inferred, self.CONST_NONE)
+
+
+def get_constraints(
+    expr: _NameNodes, frame: nodes.LocalsDictNodeNG
+) -> dict[nodes.If, set[Constraint]]:
+    """Returns the constraints for the given expression.
+
+    The returned dictionary maps the node where the constraint was generated to the
+    corresponding constraint(s).
+
+    Constraints are computed statically by analysing the code surrounding expr.
+    Currently this only supports constraints generated from if conditions.
+    """
+    current_node: nodes.NodeNG | None = expr
+    constraints_mapping: dict[nodes.If, set[Constraint]] = {}
+    while current_node is not None and current_node is not frame:
+        parent = current_node.parent
+        if isinstance(parent, nodes.If):
+            branch, _ = parent.locate_child(current_node)
+            constraints: set[Constraint] | None = None
+            if branch == "body":
+                constraints = set(_match_constraint(expr, parent.test))
+            elif branch == "orelse":
+                constraints = set(_match_constraint(expr, parent.test, invert=True))
+
+            if constraints:
+                constraints_mapping[parent] = constraints
+        current_node = parent
+
+    return constraints_mapping
+
+
+ALL_CONSTRAINT_CLASSES = frozenset((NoneConstraint,))
+"""All supported constraint types."""
+
+
+def _matches(node1: nodes.NodeNG | bases.Proxy, node2: nodes.NodeNG) -> bool:
+    """Returns True if the two nodes match."""
+    if isinstance(node1, nodes.Name) and isinstance(node2, nodes.Name):
+        return node1.name == node2.name
+    if isinstance(node1, nodes.Attribute) and isinstance(node2, nodes.Attribute):
+        return node1.attrname == node2.attrname and _matches(node1.expr, node2.expr)
+    if isinstance(node1, nodes.Const) and isinstance(node2, nodes.Const):
+        return node1.value == node2.value
+
+    return False
+
+
+def _match_constraint(
+    node: _NameNodes, expr: nodes.NodeNG, invert: bool = False
+) -> Iterator[Constraint]:
+    """Yields all constraint patterns for node that match."""
+    for constraint_cls in ALL_CONSTRAINT_CLASSES:
+        constraint = constraint_cls.match(node, expr, invert)
+        if constraint:
+            yield constraint

+ 205 - 0
venv/lib/python3.11/site-packages/astroid/context.py

@@ -0,0 +1,205 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Various context related utilities, including inference and call contexts."""
+
+from __future__ import annotations
+
+import contextlib
+import pprint
+from typing import TYPE_CHECKING, Dict, Optional, Sequence, Tuple
+
+if TYPE_CHECKING:
+    from astroid import constraint, nodes
+    from astroid.nodes.node_classes import Keyword, NodeNG
+
+_InferenceCache = Dict[
+    Tuple["NodeNG", Optional[str], Optional[str], Optional[str]], Sequence["NodeNG"]
+]
+
+_INFERENCE_CACHE: _InferenceCache = {}
+
+
+def _invalidate_cache() -> None:
+    _INFERENCE_CACHE.clear()
+
+
+class InferenceContext:
+    """Provide context for inference.
+
+    Store already inferred nodes to save time
+    Account for already visited nodes to stop infinite recursion
+    """
+
+    __slots__ = (
+        "path",
+        "lookupname",
+        "callcontext",
+        "boundnode",
+        "extra_context",
+        "constraints",
+        "_nodes_inferred",
+    )
+
+    max_inferred = 100
+
+    def __init__(
+        self,
+        path=None,
+        nodes_inferred: list[int] | None = None,
+    ):
+        if nodes_inferred is None:
+            self._nodes_inferred = [0]
+        else:
+            self._nodes_inferred = nodes_inferred
+
+        self.path = path or set()
+        """
+        :type: set(tuple(NodeNG, optional(str)))
+
+        Path of visited nodes and their lookupname
+
+        Currently this key is ``(node, context.lookupname)``
+        """
+        self.lookupname: str | None = None
+        """The original name of the node.
+
+        e.g.
+        foo = 1
+        The inference of 'foo' is nodes.Const(1) but the lookup name is 'foo'
+        """
+        self.callcontext: CallContext | None = None
+        """The call arguments and keywords for the given context."""
+        self.boundnode = None
+        """
+        :type: optional[NodeNG]
+
+        The bound node of the given context
+
+        e.g. the bound node of object.__new__(cls) is the object node
+        """
+        self.extra_context = {}
+        """
+        :type: dict(NodeNG, Context)
+
+        Context that needs to be passed down through call stacks
+        for call arguments
+        """
+
+        self.constraints: dict[str, dict[nodes.If, set[constraint.Constraint]]] = {}
+        """The constraints on nodes."""
+
+    @property
+    def nodes_inferred(self) -> int:
+        """
+        Number of nodes inferred in this context and all its clones/descendents.
+
+        Wrap inner value in a mutable cell to allow for mutating a class
+        variable in the presence of __slots__
+        """
+        return self._nodes_inferred[0]
+
+    @nodes_inferred.setter
+    def nodes_inferred(self, value: int) -> None:
+        self._nodes_inferred[0] = value
+
+    @property
+    def inferred(self) -> _InferenceCache:
+        """
+        Inferred node contexts to their mapped results.
+
+        Currently the key is ``(node, lookupname, callcontext, boundnode)``
+        and the value is tuple of the inferred results
+        """
+        return _INFERENCE_CACHE
+
+    def push(self, node) -> bool:
+        """Push node into inference path.
+
+        :return: Whether node is already in context path.
+
+        Allows one to see if the given node has already
+        been looked at for this inference context
+        """
+        name = self.lookupname
+        if (node, name) in self.path:
+            return True
+
+        self.path.add((node, name))
+        return False
+
+    def clone(self) -> InferenceContext:
+        """Clone inference path.
+
+        For example, each side of a binary operation (BinOp)
+        starts with the same context but diverge as each side is inferred
+        so the InferenceContext will need be cloned
+        """
+        # XXX copy lookupname/callcontext ?
+        clone = InferenceContext(self.path.copy(), nodes_inferred=self._nodes_inferred)
+        clone.callcontext = self.callcontext
+        clone.boundnode = self.boundnode
+        clone.extra_context = self.extra_context
+        clone.constraints = self.constraints.copy()
+        return clone
+
+    @contextlib.contextmanager
+    def restore_path(self):
+        path = set(self.path)
+        yield
+        self.path = path
+
+    def __str__(self) -> str:
+        state = (
+            f"{field}={pprint.pformat(getattr(self, field), width=80 - len(field))}"
+            for field in self.__slots__
+        )
+        return "{}({})".format(type(self).__name__, ",\n    ".join(state))
+
+
+class CallContext:
+    """Holds information for a call site."""
+
+    __slots__ = ("args", "keywords", "callee")
+
+    def __init__(
+        self,
+        args: list[NodeNG],
+        keywords: list[Keyword] | None = None,
+        callee: NodeNG | None = None,
+    ):
+        self.args = args  # Call positional arguments
+        if keywords:
+            arg_value_pairs = [(arg.arg, arg.value) for arg in keywords]
+        else:
+            arg_value_pairs = []
+        self.keywords = arg_value_pairs  # Call keyword arguments
+        self.callee = callee  # Function being called
+
+
+def copy_context(context: InferenceContext | None) -> InferenceContext:
+    """Clone a context if given, or return a fresh context."""
+    if context is not None:
+        return context.clone()
+
+    return InferenceContext()
+
+
+def bind_context_to_node(context: InferenceContext | None, node) -> InferenceContext:
+    """Give a context a boundnode
+    to retrieve the correct function name or attribute value
+    with from further inference.
+
+    Do not use an existing context since the boundnode could then
+    be incorrectly propagated higher up in the call stack.
+
+    :param node: Node to do name lookups from
+    :type node NodeNG:
+
+    :returns: A new context
+    :rtype: InferenceContext
+    """
+    context = copy_context(context)
+    context.boundnode = node
+    return context

+ 290 - 0
venv/lib/python3.11/site-packages/astroid/decorators.py

@@ -0,0 +1,290 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""A few useful function/method decorators."""
+
+from __future__ import annotations
+
+import functools
+import inspect
+import sys
+import warnings
+from collections.abc import Callable, Generator
+from typing import TypeVar
+
+import wrapt
+
+from astroid import _cache, util
+from astroid.context import InferenceContext
+from astroid.exceptions import InferenceError
+
+if sys.version_info >= (3, 10):
+    from typing import ParamSpec
+else:
+    from typing_extensions import ParamSpec
+
+_R = TypeVar("_R")
+_P = ParamSpec("_P")
+
+
+@wrapt.decorator
+def cached(func, instance, args, kwargs):
+    """Simple decorator to cache result of method calls without args."""
+    cache = getattr(instance, "__cache", None)
+    if cache is None:
+        instance.__cache = cache = {}
+        _cache.CACHE_MANAGER.add_dict_cache(cache)
+    try:
+        return cache[func]
+    except KeyError:
+        cache[func] = result = func(*args, **kwargs)
+        return result
+
+
+# TODO: Remove when support for 3.7 is dropped
+# TODO: astroid 3.0 -> move class behind sys.version_info < (3, 8) guard
+class cachedproperty:
+    """Provides a cached property equivalent to the stacking of
+    @cached and @property, but more efficient.
+
+    After first usage, the <property_name> becomes part of the object's
+    __dict__. Doing:
+
+      del obj.<property_name> empties the cache.
+
+    Idea taken from the pyramid_ framework and the mercurial_ project.
+
+    .. _pyramid: http://pypi.python.org/pypi/pyramid
+    .. _mercurial: http://pypi.python.org/pypi/Mercurial
+    """
+
+    __slots__ = ("wrapped",)
+
+    def __init__(self, wrapped):
+        if sys.version_info >= (3, 8):
+            warnings.warn(
+                "cachedproperty has been deprecated and will be removed in astroid 3.0 for Python 3.8+. "
+                "Use functools.cached_property instead.",
+                DeprecationWarning,
+                stacklevel=2,
+            )
+        try:
+            wrapped.__name__
+        except AttributeError as exc:
+            raise TypeError(f"{wrapped} must have a __name__ attribute") from exc
+        self.wrapped = wrapped
+
+    @property
+    def __doc__(self):
+        doc = getattr(self.wrapped, "__doc__", None)
+        return "<wrapped by the cachedproperty decorator>%s" % (
+            "\n%s" % doc if doc else ""
+        )
+
+    def __get__(self, inst, objtype=None):
+        if inst is None:
+            return self
+        val = self.wrapped(inst)
+        setattr(inst, self.wrapped.__name__, val)
+        return val
+
+
+def path_wrapper(func):
+    """Return the given infer function wrapped to handle the path.
+
+    Used to stop inference if the node has already been looked
+    at for a given `InferenceContext` to prevent infinite recursion
+    """
+
+    @functools.wraps(func)
+    def wrapped(
+        node, context: InferenceContext | None = None, _func=func, **kwargs
+    ) -> Generator:
+        """Wrapper function handling context."""
+        if context is None:
+            context = InferenceContext()
+        if context.push(node):
+            return
+
+        yielded = set()
+
+        for res in _func(node, context, **kwargs):
+            # unproxy only true instance, not const, tuple, dict...
+            if res.__class__.__name__ == "Instance":
+                ares = res._proxied
+            else:
+                ares = res
+            if ares not in yielded:
+                yield res
+                yielded.add(ares)
+
+    return wrapped
+
+
+@wrapt.decorator
+def yes_if_nothing_inferred(func, instance, args, kwargs):
+    generator = func(*args, **kwargs)
+
+    try:
+        yield next(generator)
+    except StopIteration:
+        # generator is empty
+        yield util.Uninferable
+        return
+
+    yield from generator
+
+
+@wrapt.decorator
+def raise_if_nothing_inferred(func, instance, args, kwargs):
+    generator = func(*args, **kwargs)
+    try:
+        yield next(generator)
+    except StopIteration as error:
+        # generator is empty
+        if error.args:
+            # pylint: disable=not-a-mapping
+            raise InferenceError(**error.args[0]) from error
+        raise InferenceError(
+            "StopIteration raised without any error information."
+        ) from error
+    except RecursionError as error:
+        raise InferenceError(
+            f"RecursionError raised with limit {sys.getrecursionlimit()}."
+        ) from error
+
+    yield from generator
+
+
+# Expensive decorators only used to emit Deprecation warnings.
+# If no other than the default DeprecationWarning are enabled,
+# fall back to passthrough implementations.
+if util.check_warnings_filter():  # noqa: C901
+
+    def deprecate_default_argument_values(
+        astroid_version: str = "3.0", **arguments: str
+    ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
+        """Decorator which emits a DeprecationWarning if any arguments specified
+        are None or not passed at all.
+
+        Arguments should be a key-value mapping, with the key being the argument to check
+        and the value being a type annotation as string for the value of the argument.
+
+        To improve performance, only used when DeprecationWarnings other than
+        the default one are enabled.
+        """
+        # Helpful links
+        # Decorator for DeprecationWarning: https://stackoverflow.com/a/49802489
+        # Typing of stacked decorators: https://stackoverflow.com/a/68290080
+
+        def deco(func: Callable[_P, _R]) -> Callable[_P, _R]:
+            """Decorator function."""
+
+            @functools.wraps(func)
+            def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:
+                """Emit DeprecationWarnings if conditions are met."""
+
+                keys = list(inspect.signature(func).parameters.keys())
+                for arg, type_annotation in arguments.items():
+                    try:
+                        index = keys.index(arg)
+                    except ValueError:
+                        raise ValueError(
+                            f"Can't find argument '{arg}' for '{args[0].__class__.__qualname__}'"
+                        ) from None
+                    if (
+                        # Check kwargs
+                        # - if found, check it's not None
+                        (arg in kwargs and kwargs[arg] is None)
+                        # Check args
+                        # - make sure not in kwargs
+                        # - len(args) needs to be long enough, if too short
+                        #   arg can't be in args either
+                        # - args[index] should not be None
+                        or arg not in kwargs
+                        and (
+                            index == -1
+                            or len(args) <= index
+                            or (len(args) > index and args[index] is None)
+                        )
+                    ):
+                        warnings.warn(
+                            f"'{arg}' will be a required argument for "
+                            f"'{args[0].__class__.__qualname__}.{func.__name__}'"
+                            f" in astroid {astroid_version} "
+                            f"('{arg}' should be of type: '{type_annotation}')",
+                            DeprecationWarning,
+                            stacklevel=2,
+                        )
+                return func(*args, **kwargs)
+
+            return wrapper
+
+        return deco
+
+    def deprecate_arguments(
+        astroid_version: str = "3.0", **arguments: str
+    ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
+        """Decorator which emits a DeprecationWarning if any arguments specified
+        are passed.
+
+        Arguments should be a key-value mapping, with the key being the argument to check
+        and the value being a string that explains what to do instead of passing the argument.
+
+        To improve performance, only used when DeprecationWarnings other than
+        the default one are enabled.
+        """
+
+        def deco(func: Callable[_P, _R]) -> Callable[_P, _R]:
+            @functools.wraps(func)
+            def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:
+                keys = list(inspect.signature(func).parameters.keys())
+                for arg, note in arguments.items():
+                    try:
+                        index = keys.index(arg)
+                    except ValueError:
+                        raise ValueError(
+                            f"Can't find argument '{arg}' for '{args[0].__class__.__qualname__}'"
+                        ) from None
+                    if arg in kwargs or len(args) > index:
+                        warnings.warn(
+                            f"The argument '{arg}' for "
+                            f"'{args[0].__class__.__qualname__}.{func.__name__}' is deprecated "
+                            f"and will be removed in astroid {astroid_version} ({note})",
+                            DeprecationWarning,
+                            stacklevel=2,
+                        )
+                return func(*args, **kwargs)
+
+            return wrapper
+
+        return deco
+
+else:
+
+    def deprecate_default_argument_values(
+        astroid_version: str = "3.0", **arguments: str
+    ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
+        """Passthrough decorator to improve performance if DeprecationWarnings are
+        disabled.
+        """
+
+        def deco(func: Callable[_P, _R]) -> Callable[_P, _R]:
+            """Decorator function."""
+            return func
+
+        return deco
+
+    def deprecate_arguments(
+        astroid_version: str = "3.0", **arguments: str
+    ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
+        """Passthrough decorator to improve performance if DeprecationWarnings are
+        disabled.
+        """
+
+        def deco(func: Callable[_P, _R]) -> Callable[_P, _R]:
+            """Decorator function."""
+            return func
+
+        return deco

+ 426 - 0
venv/lib/python3.11/site-packages/astroid/exceptions.py

@@ -0,0 +1,426 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""This module contains exceptions used in the astroid library."""
+
+from __future__ import annotations
+
+from collections.abc import Sequence
+from typing import TYPE_CHECKING, Any
+
+from astroid import util
+
+if TYPE_CHECKING:
+    from astroid import arguments, bases, nodes, objects
+    from astroid.context import InferenceContext
+
+__all__ = (
+    "AstroidBuildingError",
+    "AstroidBuildingException",
+    "AstroidError",
+    "AstroidImportError",
+    "AstroidIndexError",
+    "AstroidSyntaxError",
+    "AstroidTypeError",
+    "AstroidValueError",
+    "AttributeInferenceError",
+    "BinaryOperationError",
+    "DuplicateBasesError",
+    "InconsistentMroError",
+    "InferenceError",
+    "InferenceOverwriteError",
+    "MroError",
+    "NameInferenceError",
+    "NoDefault",
+    "NotFoundError",
+    "OperationError",
+    "ParentMissingError",
+    "ResolveError",
+    "StatementMissing",
+    "SuperArgumentTypeError",
+    "SuperError",
+    "TooManyLevelsError",
+    "UnaryOperationError",
+    "UnresolvableName",
+    "UseInferenceDefault",
+)
+
+
+class AstroidError(Exception):
+    """Base exception class for all astroid related exceptions.
+
+    AstroidError and its subclasses are structured, intended to hold
+    objects representing state when the exception is thrown.  Field
+    values are passed to the constructor as keyword-only arguments.
+    Each subclass has its own set of standard fields, but use your
+    best judgment to decide whether a specific exception instance
+    needs more or fewer fields for debugging.  Field values may be
+    used to lazily generate the error message: self.message.format()
+    will be called with the field names and values supplied as keyword
+    arguments.
+    """
+
+    def __init__(self, message: str = "", **kws: Any) -> None:
+        super().__init__(message)
+        self.message = message
+        for key, value in kws.items():
+            setattr(self, key, value)
+
+    def __str__(self) -> str:
+        return self.message.format(**vars(self))
+
+
+class AstroidBuildingError(AstroidError):
+    """Exception class when we are unable to build an astroid representation.
+
+    Standard attributes:
+        modname: Name of the module that AST construction failed for.
+        error: Exception raised during construction.
+    """
+
+    def __init__(
+        self,
+        message: str = "Failed to import module {modname}.",
+        modname: str | None = None,
+        error: Exception | None = None,
+        source: str | None = None,
+        path: str | None = None,
+        cls: type | None = None,
+        class_repr: str | None = None,
+        **kws: Any,
+    ) -> None:
+        self.modname = modname
+        self.error = error
+        self.source = source
+        self.path = path
+        self.cls = cls
+        self.class_repr = class_repr
+        super().__init__(message, **kws)
+
+
+class AstroidImportError(AstroidBuildingError):
+    """Exception class used when a module can't be imported by astroid."""
+
+
+class TooManyLevelsError(AstroidImportError):
+    """Exception class which is raised when a relative import was beyond the top-level.
+
+    Standard attributes:
+        level: The level which was attempted.
+        name: the name of the module on which the relative import was attempted.
+    """
+
+    def __init__(
+        self,
+        message: str = "Relative import with too many levels "
+        "({level}) for module {name!r}",
+        level: int | None = None,
+        name: str | None = None,
+        **kws: Any,
+    ) -> None:
+        self.level = level
+        self.name = name
+        super().__init__(message, **kws)
+
+
+class AstroidSyntaxError(AstroidBuildingError):
+    """Exception class used when a module can't be parsed."""
+
+    def __init__(
+        self,
+        message: str,
+        modname: str | None,
+        error: Exception,
+        path: str | None,
+        source: str | None = None,
+    ) -> None:
+        super().__init__(message, modname, error, source, path)
+
+
+class NoDefault(AstroidError):
+    """Raised by function's `default_value` method when an argument has
+    no default value.
+
+    Standard attributes:
+        func: Function node.
+        name: Name of argument without a default.
+    """
+
+    def __init__(
+        self,
+        message: str = "{func!r} has no default for {name!r}.",
+        func: nodes.FunctionDef | None = None,
+        name: str | None = None,
+        **kws: Any,
+    ) -> None:
+        self.func = func
+        self.name = name
+        super().__init__(message, **kws)
+
+
+class ResolveError(AstroidError):
+    """Base class of astroid resolution/inference error.
+
+    ResolveError is not intended to be raised.
+
+    Standard attributes:
+        context: InferenceContext object.
+    """
+
+    def __init__(
+        self, message: str = "", context: InferenceContext | None = None, **kws: Any
+    ) -> None:
+        self.context = context
+        super().__init__(message, **kws)
+
+
+class MroError(ResolveError):
+    """Error raised when there is a problem with method resolution of a class.
+
+    Standard attributes:
+        mros: A sequence of sequences containing ClassDef nodes.
+        cls: ClassDef node whose MRO resolution failed.
+        context: InferenceContext object.
+    """
+
+    def __init__(
+        self,
+        message: str,
+        mros: list[nodes.ClassDef],
+        cls: nodes.ClassDef,
+        context: InferenceContext | None = None,
+        **kws: Any,
+    ) -> None:
+        self.mros = mros
+        self.cls = cls
+        self.context = context
+        super().__init__(message, **kws)
+
+    def __str__(self) -> str:
+        mro_names = ", ".join(f"({', '.join(b.name for b in m)})" for m in self.mros)
+        return self.message.format(mros=mro_names, cls=self.cls)
+
+
+class DuplicateBasesError(MroError):
+    """Error raised when there are duplicate bases in the same class bases."""
+
+
+class InconsistentMroError(MroError):
+    """Error raised when a class's MRO is inconsistent."""
+
+
+class SuperError(ResolveError):
+    """Error raised when there is a problem with a *super* call.
+
+    Standard attributes:
+        *super_*: The Super instance that raised the exception.
+        context: InferenceContext object.
+    """
+
+    def __init__(self, message: str, super_: objects.Super, **kws: Any) -> None:
+        self.super_ = super_
+        super().__init__(message, **kws)
+
+    def __str__(self) -> str:
+        return self.message.format(**vars(self.super_))
+
+
+class InferenceError(ResolveError):  # pylint: disable=too-many-instance-attributes
+    """Raised when we are unable to infer a node.
+
+    Standard attributes:
+        node: The node inference was called on.
+        context: InferenceContext object.
+    """
+
+    def __init__(  # pylint: disable=too-many-arguments
+        self,
+        message: str = "Inference failed for {node!r}.",
+        node: nodes.NodeNG | bases.Instance | None = None,
+        context: InferenceContext | None = None,
+        target: nodes.NodeNG | bases.Instance | None = None,
+        targets: nodes.Tuple | None = None,
+        attribute: str | None = None,
+        unknown: nodes.NodeNG | bases.Instance | None = None,
+        assign_path: list[int] | None = None,
+        caller: nodes.Call | None = None,
+        stmts: Sequence[nodes.NodeNG | bases.Instance] | None = None,
+        frame: nodes.LocalsDictNodeNG | None = None,
+        call_site: arguments.CallSite | None = None,
+        func: nodes.FunctionDef | None = None,
+        arg: str | None = None,
+        positional_arguments: list | None = None,
+        unpacked_args: list | None = None,
+        keyword_arguments: dict | None = None,
+        unpacked_kwargs: dict | None = None,
+        **kws: Any,
+    ) -> None:
+        self.node = node
+        self.context = context
+        self.target = target
+        self.targets = targets
+        self.attribute = attribute
+        self.unknown = unknown
+        self.assign_path = assign_path
+        self.caller = caller
+        self.stmts = stmts
+        self.frame = frame
+        self.call_site = call_site
+        self.func = func
+        self.arg = arg
+        self.positional_arguments = positional_arguments
+        self.unpacked_args = unpacked_args
+        self.keyword_arguments = keyword_arguments
+        self.unpacked_kwargs = unpacked_kwargs
+        super().__init__(message, **kws)
+
+
+# Why does this inherit from InferenceError rather than ResolveError?
+# Changing it causes some inference tests to fail.
+class NameInferenceError(InferenceError):
+    """Raised when a name lookup fails, corresponds to NameError.
+
+    Standard attributes:
+        name: The name for which lookup failed, as a string.
+        scope: The node representing the scope in which the lookup occurred.
+        context: InferenceContext object.
+    """
+
+    def __init__(
+        self,
+        message: str = "{name!r} not found in {scope!r}.",
+        name: str | None = None,
+        scope: nodes.LocalsDictNodeNG | None = None,
+        context: InferenceContext | None = None,
+        **kws: Any,
+    ) -> None:
+        self.name = name
+        self.scope = scope
+        self.context = context
+        super().__init__(message, **kws)
+
+
+class AttributeInferenceError(ResolveError):
+    """Raised when an attribute lookup fails, corresponds to AttributeError.
+
+    Standard attributes:
+        target: The node for which lookup failed.
+        attribute: The attribute for which lookup failed, as a string.
+        context: InferenceContext object.
+    """
+
+    def __init__(
+        self,
+        message: str = "{attribute!r} not found on {target!r}.",
+        attribute: str = "",
+        target: nodes.NodeNG | bases.Instance | None = None,
+        context: InferenceContext | None = None,
+        mros: list[nodes.ClassDef] | None = None,
+        super_: nodes.ClassDef | None = None,
+        cls: nodes.ClassDef | None = None,
+        **kws: Any,
+    ) -> None:
+        self.attribute = attribute
+        self.target = target
+        self.context = context
+        self.mros = mros
+        self.super_ = super_
+        self.cls = cls
+        super().__init__(message, **kws)
+
+
+class UseInferenceDefault(Exception):
+    """Exception to be raised in custom inference function to indicate that it
+    should go back to the default behaviour.
+    """
+
+
+class _NonDeducibleTypeHierarchy(Exception):
+    """Raised when is_subtype / is_supertype can't deduce the relation between two
+    types.
+    """
+
+
+class AstroidIndexError(AstroidError):
+    """Raised when an Indexable / Mapping does not have an index / key."""
+
+    def __init__(
+        self,
+        message: str = "",
+        node: nodes.NodeNG | bases.Instance | None = None,
+        index: nodes.Subscript | None = None,
+        context: InferenceContext | None = None,
+        **kws: Any,
+    ) -> None:
+        self.node = node
+        self.index = index
+        self.context = context
+        super().__init__(message, **kws)
+
+
+class AstroidTypeError(AstroidError):
+    """Raised when a TypeError would be expected in Python code."""
+
+    def __init__(
+        self,
+        message: str = "",
+        node: nodes.NodeNG | bases.Instance | None = None,
+        index: nodes.Subscript | None = None,
+        context: InferenceContext | None = None,
+        **kws: Any,
+    ) -> None:
+        self.node = node
+        self.index = index
+        self.context = context
+        super().__init__(message, **kws)
+
+
+class AstroidValueError(AstroidError):
+    """Raised when a ValueError would be expected in Python code."""
+
+
+class InferenceOverwriteError(AstroidError):
+    """Raised when an inference tip is overwritten.
+
+    Currently only used for debugging.
+    """
+
+
+class ParentMissingError(AstroidError):
+    """Raised when a node which is expected to have a parent attribute is missing one.
+
+    Standard attributes:
+        target: The node for which the parent lookup failed.
+    """
+
+    def __init__(self, target: nodes.NodeNG) -> None:
+        self.target = target
+        super().__init__(message=f"Parent not found on {target!r}.")
+
+
+class StatementMissing(ParentMissingError):
+    """Raised when a call to node.statement() does not return a node.
+
+    This is because a node in the chain does not have a parent attribute
+    and therefore does not return a node for statement().
+
+    Standard attributes:
+        target: The node for which the parent lookup failed.
+    """
+
+    def __init__(self, target: nodes.NodeNG) -> None:
+        super(ParentMissingError, self).__init__(
+            message=f"Statement not found on {target!r}"
+        )
+
+
+# Backwards-compatibility aliases
+OperationError = util.BadOperationMessage
+UnaryOperationError = util.BadUnaryOperationMessage
+BinaryOperationError = util.BadBinaryOperationMessage
+
+SuperArgumentTypeError = SuperError
+UnresolvableName = NameInferenceError
+NotFoundError = AttributeInferenceError
+AstroidBuildingException = AstroidBuildingError

+ 241 - 0
venv/lib/python3.11/site-packages/astroid/filter_statements.py

@@ -0,0 +1,241 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""_filter_stmts and helper functions.
+
+This method gets used in LocalsDictnodes.NodeNG._scope_lookup.
+It is not considered public.
+"""
+
+from __future__ import annotations
+
+from astroid import nodes
+
+
+def _get_filtered_node_statements(
+    base_node: nodes.NodeNG, stmt_nodes: list[nodes.NodeNG]
+) -> list[tuple[nodes.NodeNG, nodes.Statement]]:
+    statements = [(node, node.statement(future=True)) for node in stmt_nodes]
+    # Next we check if we have ExceptHandlers that are parent
+    # of the underlying variable, in which case the last one survives
+    if len(statements) > 1 and all(
+        isinstance(stmt, nodes.ExceptHandler) for _, stmt in statements
+    ):
+        statements = [
+            (node, stmt) for node, stmt in statements if stmt.parent_of(base_node)
+        ]
+    return statements
+
+
+def _is_from_decorator(node) -> bool:
+    """Return whether the given node is the child of a decorator."""
+    return any(isinstance(parent, nodes.Decorators) for parent in node.node_ancestors())
+
+
+def _get_if_statement_ancestor(node: nodes.NodeNG) -> nodes.If | None:
+    """Return the first parent node that is an If node (or None)."""
+    for parent in node.node_ancestors():
+        if isinstance(parent, nodes.If):
+            return parent
+    return None
+
+
+def _filter_stmts(base_node: nodes.NodeNG, stmts, frame, offset):
+    """Filter the given list of statements to remove ignorable statements.
+
+    If base_node is not a frame itself and the name is found in the inner
+    frame locals, statements will be filtered to remove ignorable
+    statements according to base_node's location.
+
+    :param stmts: The statements to filter.
+    :type stmts: list(nodes.NodeNG)
+
+    :param frame: The frame that all of the given statements belong to.
+    :type frame: nodes.NodeNG
+
+    :param offset: The line offset to filter statements up to.
+    :type offset: int
+
+    :returns: The filtered statements.
+    :rtype: list(nodes.NodeNG)
+    """
+    # if offset == -1, my actual frame is not the inner frame but its parent
+    #
+    # class A(B): pass
+    #
+    # we need this to resolve B correctly
+    if offset == -1:
+        myframe = base_node.frame().parent.frame()
+    else:
+        myframe = base_node.frame()
+        # If the frame of this node is the same as the statement
+        # of this node, then the node is part of a class or
+        # a function definition and the frame of this node should be the
+        # the upper frame, not the frame of the definition.
+        # For more information why this is important,
+        # see Pylint issue #295.
+        # For example, for 'b', the statement is the same
+        # as the frame / scope:
+        #
+        # def test(b=1):
+        #     ...
+        if (
+            base_node.parent
+            and base_node.statement(future=True) is myframe
+            and myframe.parent
+        ):
+            myframe = myframe.parent.frame()
+
+    mystmt: nodes.Statement | None = None
+    if base_node.parent:
+        mystmt = base_node.statement(future=True)
+
+    # line filtering if we are in the same frame
+    #
+    # take care node may be missing lineno information (this is the case for
+    # nodes inserted for living objects)
+    if myframe is frame and mystmt and mystmt.fromlineno is not None:
+        assert mystmt.fromlineno is not None, mystmt
+        mylineno = mystmt.fromlineno + offset
+    else:
+        # disabling lineno filtering
+        mylineno = 0
+
+    _stmts = []
+    _stmt_parents = []
+    statements = _get_filtered_node_statements(base_node, stmts)
+    for node, stmt in statements:
+        # line filtering is on and we have reached our location, break
+        if stmt.fromlineno and stmt.fromlineno > mylineno > 0:
+            break
+        # Ignore decorators with the same name as the
+        # decorated function
+        # Fixes issue #375
+        if mystmt is stmt and _is_from_decorator(base_node):
+            continue
+        if node.has_base(base_node):
+            break
+
+        if isinstance(node, nodes.EmptyNode):
+            # EmptyNode does not have assign_type(), so just add it and move on
+            _stmts.append(node)
+            continue
+
+        assign_type = node.assign_type()
+        _stmts, done = assign_type._get_filtered_stmts(base_node, node, _stmts, mystmt)
+        if done:
+            break
+
+        optional_assign = assign_type.optional_assign
+        if optional_assign and assign_type.parent_of(base_node):
+            # we are inside a loop, loop var assignment is hiding previous
+            # assignment
+            _stmts = [node]
+            _stmt_parents = [stmt.parent]
+            continue
+
+        if isinstance(assign_type, nodes.NamedExpr):
+            # If the NamedExpr is in an if statement we do some basic control flow inference
+            if_parent = _get_if_statement_ancestor(assign_type)
+            if if_parent:
+                # If the if statement is within another if statement we append the node
+                # to possible statements
+                if _get_if_statement_ancestor(if_parent):
+                    optional_assign = False
+                    _stmts.append(node)
+                    _stmt_parents.append(stmt.parent)
+                # If the if statement is first-level and not within an orelse block
+                # we know that it will be evaluated
+                elif not if_parent.is_orelse:
+                    _stmts = [node]
+                    _stmt_parents = [stmt.parent]
+                # Else we do not known enough about the control flow to be 100% certain
+                # and we append to possible statements
+                else:
+                    _stmts.append(node)
+                    _stmt_parents.append(stmt.parent)
+            else:
+                _stmts = [node]
+                _stmt_parents = [stmt.parent]
+
+        # XXX comment various branches below!!!
+        try:
+            pindex = _stmt_parents.index(stmt.parent)
+        except ValueError:
+            pass
+        else:
+            # we got a parent index, this means the currently visited node
+            # is at the same block level as a previously visited node
+            if _stmts[pindex].assign_type().parent_of(assign_type):
+                # both statements are not at the same block level
+                continue
+            # if currently visited node is following previously considered
+            # assignment and both are not exclusive, we can drop the
+            # previous one. For instance in the following code ::
+            #
+            #   if a:
+            #     x = 1
+            #   else:
+            #     x = 2
+            #   print x
+            #
+            # we can't remove neither x = 1 nor x = 2 when looking for 'x'
+            # of 'print x'; while in the following ::
+            #
+            #   x = 1
+            #   x = 2
+            #   print x
+            #
+            # we can remove x = 1 when we see x = 2
+            #
+            # moreover, on loop assignment types, assignment won't
+            # necessarily be done if the loop has no iteration, so we don't
+            # want to clear previous assignments if any (hence the test on
+            # optional_assign)
+            if not (optional_assign or nodes.are_exclusive(_stmts[pindex], node)):
+                del _stmt_parents[pindex]
+                del _stmts[pindex]
+
+        # If base_node and node are exclusive, then we can ignore node
+        if nodes.are_exclusive(base_node, node):
+            continue
+
+        # An AssignName node overrides previous assignments if:
+        #   1. node's statement always assigns
+        #   2. node and base_node are in the same block (i.e., has the same parent as base_node)
+        if isinstance(node, (nodes.NamedExpr, nodes.AssignName)):
+            if isinstance(stmt, nodes.ExceptHandler):
+                # If node's statement is an ExceptHandler, then it is the variable
+                # bound to the caught exception. If base_node is not contained within
+                # the exception handler block, node should override previous assignments;
+                # otherwise, node should be ignored, as an exception variable
+                # is local to the handler block.
+                if stmt.parent_of(base_node):
+                    _stmts = []
+                    _stmt_parents = []
+                else:
+                    continue
+            elif not optional_assign and mystmt and stmt.parent is mystmt.parent:
+                _stmts = []
+                _stmt_parents = []
+        elif isinstance(node, nodes.DelName):
+            # Remove all previously stored assignments
+            _stmts = []
+            _stmt_parents = []
+            continue
+        # Add the new assignment
+        _stmts.append(node)
+        if isinstance(node, nodes.Arguments) or isinstance(
+            node.parent, nodes.Arguments
+        ):
+            # Special case for _stmt_parents when node is a function parameter;
+            # in this case, stmt is the enclosing FunctionDef, which is what we
+            # want to add to _stmt_parents, not stmt.parent. This case occurs when
+            # node is an Arguments node (representing varargs or kwargs parameter),
+            # and when node.parent is an Arguments node (other parameters).
+            # See issue #180.
+            _stmt_parents.append(stmt)
+        else:
+            _stmt_parents.append(stmt.parent)
+    return _stmts

+ 320 - 0
venv/lib/python3.11/site-packages/astroid/helpers.py

@@ -0,0 +1,320 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Various helper utilities."""
+
+from __future__ import annotations
+
+from collections.abc import Generator
+
+from astroid import bases, manager, nodes, objects, raw_building, util
+from astroid.context import CallContext, InferenceContext
+from astroid.exceptions import (
+    AstroidTypeError,
+    AttributeInferenceError,
+    InferenceError,
+    MroError,
+    _NonDeducibleTypeHierarchy,
+)
+from astroid.nodes import scoped_nodes
+from astroid.typing import InferenceResult, SuccessfulInferenceResult
+
+
+def _build_proxy_class(cls_name: str, builtins: nodes.Module) -> nodes.ClassDef:
+    proxy = raw_building.build_class(cls_name)
+    proxy.parent = builtins
+    return proxy
+
+
+def _function_type(
+    function: nodes.Lambda | bases.UnboundMethod, builtins: nodes.Module
+) -> nodes.ClassDef:
+    if isinstance(function, scoped_nodes.Lambda):
+        if function.root().name == "builtins":
+            cls_name = "builtin_function_or_method"
+        else:
+            cls_name = "function"
+    elif isinstance(function, bases.BoundMethod):
+        cls_name = "method"
+    else:
+        cls_name = "function"
+    return _build_proxy_class(cls_name, builtins)
+
+
+def _object_type(
+    node: SuccessfulInferenceResult, context: InferenceContext | None = None
+) -> Generator[InferenceResult | None, None, None]:
+    astroid_manager = manager.AstroidManager()
+    builtins = astroid_manager.builtins_module
+    context = context or InferenceContext()
+
+    for inferred in node.infer(context=context):
+        if isinstance(inferred, scoped_nodes.ClassDef):
+            if inferred.newstyle:
+                metaclass = inferred.metaclass(context=context)
+                if metaclass:
+                    yield metaclass
+                    continue
+            yield builtins.getattr("type")[0]
+        elif isinstance(inferred, (scoped_nodes.Lambda, bases.UnboundMethod)):
+            yield _function_type(inferred, builtins)
+        elif isinstance(inferred, scoped_nodes.Module):
+            yield _build_proxy_class("module", builtins)
+        elif isinstance(inferred, nodes.Unknown):
+            raise InferenceError
+        elif isinstance(inferred, util.UninferableBase):
+            yield inferred
+        elif isinstance(inferred, (bases.Proxy, nodes.Slice, objects.Super)):
+            yield inferred._proxied
+        else:  # pragma: no cover
+            raise AssertionError(f"We don't handle {type(inferred)} currently")
+
+
+def object_type(
+    node: SuccessfulInferenceResult, context: InferenceContext | None = None
+) -> InferenceResult | None:
+    """Obtain the type of the given node.
+
+    This is used to implement the ``type`` builtin, which means that it's
+    used for inferring type calls, as well as used in a couple of other places
+    in the inference.
+    The node will be inferred first, so this function can support all
+    sorts of objects, as long as they support inference.
+    """
+
+    try:
+        types = set(_object_type(node, context))
+    except InferenceError:
+        return util.Uninferable
+    if len(types) > 1 or not types:
+        return util.Uninferable
+    return list(types)[0]
+
+
+def _object_type_is_subclass(
+    obj_type, class_or_seq, context: InferenceContext | None = None
+):
+    if not isinstance(class_or_seq, (tuple, list)):
+        class_seq = (class_or_seq,)
+    else:
+        class_seq = class_or_seq
+
+    if isinstance(obj_type, util.UninferableBase):
+        return util.Uninferable
+
+    # Instances are not types
+    class_seq = [
+        item if not isinstance(item, bases.Instance) else util.Uninferable
+        for item in class_seq
+    ]
+    # strict compatibility with issubclass
+    # issubclass(type, (object, 1)) evaluates to true
+    # issubclass(object, (1, type)) raises TypeError
+    for klass in class_seq:
+        if isinstance(klass, util.UninferableBase):
+            raise AstroidTypeError("arg 2 must be a type or tuple of types")
+
+        for obj_subclass in obj_type.mro():
+            if obj_subclass == klass:
+                return True
+    return False
+
+
+def object_isinstance(node, class_or_seq, context: InferenceContext | None = None):
+    """Check if a node 'isinstance' any node in class_or_seq.
+
+    :param node: A given node
+    :param class_or_seq: Union[nodes.NodeNG, Sequence[nodes.NodeNG]]
+    :rtype: bool
+
+    :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
+    """
+    obj_type = object_type(node, context)
+    if isinstance(obj_type, util.UninferableBase):
+        return util.Uninferable
+    return _object_type_is_subclass(obj_type, class_or_seq, context=context)
+
+
+def object_issubclass(node, class_or_seq, context: InferenceContext | None = None):
+    """Check if a type is a subclass of any node in class_or_seq.
+
+    :param node: A given node
+    :param class_or_seq: Union[Nodes.NodeNG, Sequence[nodes.NodeNG]]
+    :rtype: bool
+
+    :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
+    :raises AstroidError: if the type of the given node cannot be inferred
+        or its type's mro doesn't work
+    """
+    if not isinstance(node, nodes.ClassDef):
+        raise TypeError(f"{node} needs to be a ClassDef node")
+    return _object_type_is_subclass(node, class_or_seq, context=context)
+
+
+def safe_infer(
+    node: nodes.NodeNG | bases.Proxy, context: InferenceContext | None = None
+) -> InferenceResult | None:
+    """Return the inferred value for the given node.
+
+    Return None if inference failed or if there is some ambiguity (more than
+    one node has been inferred).
+    """
+    try:
+        inferit = node.infer(context=context)
+        value = next(inferit)
+    except (InferenceError, StopIteration):
+        return None
+    try:
+        next(inferit)
+        return None  # None if there is ambiguity on the inferred node
+    except InferenceError:
+        return None  # there is some kind of ambiguity
+    except StopIteration:
+        return value
+
+
+def has_known_bases(klass, context: InferenceContext | None = None) -> bool:
+    """Return whether all base classes of a class could be inferred."""
+    try:
+        return klass._all_bases_known
+    except AttributeError:
+        pass
+    for base in klass.bases:
+        result = safe_infer(base, context=context)
+        # TODO: check for A->B->A->B pattern in class structure too?
+        if (
+            not isinstance(result, scoped_nodes.ClassDef)
+            or result is klass
+            or not has_known_bases(result, context=context)
+        ):
+            klass._all_bases_known = False
+            return False
+    klass._all_bases_known = True
+    return True
+
+
+def _type_check(type1, type2) -> bool:
+    if not all(map(has_known_bases, (type1, type2))):
+        raise _NonDeducibleTypeHierarchy
+
+    if not all([type1.newstyle, type2.newstyle]):
+        return False
+    try:
+        return type1 in type2.mro()[:-1]
+    except MroError as e:
+        # The MRO is invalid.
+        raise _NonDeducibleTypeHierarchy from e
+
+
+def is_subtype(type1, type2) -> bool:
+    """Check if *type1* is a subtype of *type2*."""
+    return _type_check(type1=type2, type2=type1)
+
+
+def is_supertype(type1, type2) -> bool:
+    """Check if *type2* is a supertype of *type1*."""
+    return _type_check(type1, type2)
+
+
+def class_instance_as_index(node: SuccessfulInferenceResult) -> nodes.Const | None:
+    """Get the value as an index for the given instance.
+
+    If an instance provides an __index__ method, then it can
+    be used in some scenarios where an integer is expected,
+    for instance when multiplying or subscripting a list.
+    """
+    context = InferenceContext()
+    try:
+        for inferred in node.igetattr("__index__", context=context):
+            if not isinstance(inferred, bases.BoundMethod):
+                continue
+
+            context.boundnode = node
+            context.callcontext = CallContext(args=[], callee=inferred)
+            for result in inferred.infer_call_result(node, context=context):
+                if isinstance(result, nodes.Const) and isinstance(result.value, int):
+                    return result
+    except InferenceError:
+        pass
+    return None
+
+
+def object_len(node, context: InferenceContext | None = None):
+    """Infer length of given node object.
+
+    :param Union[nodes.ClassDef, nodes.Instance] node:
+    :param node: Node to infer length of
+
+    :raises AstroidTypeError: If an invalid node is returned
+        from __len__ method or no __len__ method exists
+    :raises InferenceError: If the given node cannot be inferred
+        or if multiple nodes are inferred or if the code executed in python
+        would result in a infinite recursive check for length
+    :rtype int: Integer length of node
+    """
+    # pylint: disable=import-outside-toplevel; circular import
+    from astroid.objects import FrozenSet
+
+    inferred_node = safe_infer(node, context=context)
+
+    # prevent self referential length calls from causing a recursion error
+    # see https://github.com/PyCQA/astroid/issues/777
+    node_frame = node.frame(future=True)
+    if (
+        isinstance(node_frame, scoped_nodes.FunctionDef)
+        and node_frame.name == "__len__"
+        and hasattr(inferred_node, "_proxied")
+        and inferred_node._proxied == node_frame.parent
+    ):
+        message = (
+            "Self referential __len__ function will "
+            "cause a RecursionError on line {} of {}".format(
+                node.lineno, node.root().file
+            )
+        )
+        raise InferenceError(message)
+
+    if inferred_node is None or isinstance(inferred_node, util.UninferableBase):
+        raise InferenceError(node=node)
+    if isinstance(inferred_node, nodes.Const) and isinstance(
+        inferred_node.value, (bytes, str)
+    ):
+        return len(inferred_node.value)
+    if isinstance(inferred_node, (nodes.List, nodes.Set, nodes.Tuple, FrozenSet)):
+        return len(inferred_node.elts)
+    if isinstance(inferred_node, nodes.Dict):
+        return len(inferred_node.items)
+
+    node_type = object_type(inferred_node, context=context)
+    if not node_type:
+        raise InferenceError(node=node)
+
+    try:
+        len_call = next(node_type.igetattr("__len__", context=context))
+    except StopIteration as e:
+        raise AstroidTypeError(str(e)) from e
+    except AttributeInferenceError as e:
+        raise AstroidTypeError(
+            f"object of type '{node_type.pytype()}' has no len()"
+        ) from e
+
+    inferred = len_call.infer_call_result(node, context)
+    if isinstance(inferred, util.UninferableBase):
+        raise InferenceError(node=node, context=context)
+    result_of_len = next(inferred, None)
+    if (
+        isinstance(result_of_len, nodes.Const)
+        and result_of_len.pytype() == "builtins.int"
+    ):
+        return result_of_len.value
+    if (
+        result_of_len is None
+        or isinstance(result_of_len, bases.Instance)
+        and result_of_len.is_subtype_of("builtins.int")
+    ):
+        # Fake a result as we don't know the arguments of the instance call.
+        return 0
+    raise AstroidTypeError(
+        f"'{result_of_len}' object cannot be interpreted as an integer"
+    )

+ 1278 - 0
venv/lib/python3.11/site-packages/astroid/inference.py

@@ -0,0 +1,1278 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""This module contains a set of functions to handle inference on astroid trees."""
+
+from __future__ import annotations
+
+import ast
+import functools
+import itertools
+import operator
+import typing
+from collections.abc import Callable, Generator, Iterable, Iterator
+from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
+
+from astroid import bases, constraint, decorators, helpers, nodes, protocols, util
+from astroid.const import PY310_PLUS
+from astroid.context import (
+    CallContext,
+    InferenceContext,
+    bind_context_to_node,
+    copy_context,
+)
+from astroid.exceptions import (
+    AstroidBuildingError,
+    AstroidError,
+    AstroidIndexError,
+    AstroidTypeError,
+    AstroidValueError,
+    AttributeInferenceError,
+    InferenceError,
+    NameInferenceError,
+    _NonDeducibleTypeHierarchy,
+)
+from astroid.interpreter import dunder_lookup
+from astroid.manager import AstroidManager
+from astroid.typing import (
+    InferenceErrorInfo,
+    InferenceResult,
+    SuccessfulInferenceResult,
+)
+
+if TYPE_CHECKING:
+    from astroid.objects import Property
+
+# Prevents circular imports
+objects = util.lazy_import("objects")
+
+_T = TypeVar("_T")
+_BaseContainerT = TypeVar("_BaseContainerT", bound=nodes.BaseContainer)
+_FunctionDefT = TypeVar("_FunctionDefT", bound=nodes.FunctionDef)
+
+GetFlowFactory = typing.Callable[
+    [
+        InferenceResult,
+        Optional[InferenceResult],
+        Union[nodes.AugAssign, nodes.BinOp],
+        InferenceResult,
+        Optional[InferenceResult],
+        InferenceContext,
+        InferenceContext,
+    ],
+    "list[functools.partial[Generator[InferenceResult, None, None]]]",
+]
+
+# .infer method ###############################################################
+
+
+def infer_end(
+    self: _T, context: InferenceContext | None = None, **kwargs: Any
+) -> Iterator[_T]:
+    """Inference's end for nodes that yield themselves on inference.
+
+    These are objects for which inference does not have any semantic,
+    such as Module or Consts.
+    """
+    yield self
+
+
+# We add ignores to all assignments to methods
+# See https://github.com/python/mypy/issues/2427
+nodes.Module._infer = infer_end
+nodes.ClassDef._infer = infer_end
+nodes.Lambda._infer = infer_end  # type: ignore[assignment]
+nodes.Const._infer = infer_end  # type: ignore[assignment]
+nodes.Slice._infer = infer_end  # type: ignore[assignment]
+
+
+def _infer_sequence_helper(
+    node: _BaseContainerT, context: InferenceContext | None = None
+) -> list[SuccessfulInferenceResult]:
+    """Infer all values based on _BaseContainer.elts."""
+    values = []
+
+    for elt in node.elts:
+        if isinstance(elt, nodes.Starred):
+            starred = helpers.safe_infer(elt.value, context)
+            if not starred:
+                raise InferenceError(node=node, context=context)
+            if not hasattr(starred, "elts"):
+                raise InferenceError(node=node, context=context)
+            values.extend(_infer_sequence_helper(starred))
+        elif isinstance(elt, nodes.NamedExpr):
+            value = helpers.safe_infer(elt.value, context)
+            if not value:
+                raise InferenceError(node=node, context=context)
+            values.append(value)
+        else:
+            values.append(elt)
+    return values
+
+
+@decorators.raise_if_nothing_inferred
+def infer_sequence(
+    self: _BaseContainerT,
+    context: InferenceContext | None = None,
+    **kwargs: Any,
+) -> Iterator[_BaseContainerT]:
+    has_starred_named_expr = any(
+        isinstance(e, (nodes.Starred, nodes.NamedExpr)) for e in self.elts
+    )
+    if has_starred_named_expr:
+        values = _infer_sequence_helper(self, context)
+        new_seq = type(self)(
+            lineno=self.lineno, col_offset=self.col_offset, parent=self.parent
+        )
+        new_seq.postinit(values)
+
+        yield new_seq
+    else:
+        yield self
+
+
+nodes.List._infer = infer_sequence  # type: ignore[assignment]
+nodes.Tuple._infer = infer_sequence  # type: ignore[assignment]
+nodes.Set._infer = infer_sequence  # type: ignore[assignment]
+
+
+def infer_map(
+    self: nodes.Dict, context: InferenceContext | None = None
+) -> Iterator[nodes.Dict]:
+    if not any(isinstance(k, nodes.DictUnpack) for k, _ in self.items):
+        yield self
+    else:
+        items = _infer_map(self, context)
+        new_seq = type(self)(self.lineno, self.col_offset, self.parent)
+        new_seq.postinit(list(items.items()))
+        yield new_seq
+
+
+def _update_with_replacement(
+    lhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
+    rhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
+) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
+    """Delete nodes that equate to duplicate keys.
+
+    Since an astroid node doesn't 'equal' another node with the same value,
+    this function uses the as_string method to make sure duplicate keys
+    don't get through
+
+    Note that both the key and the value are astroid nodes
+
+    Fixes issue with DictUnpack causing duplicate keys
+    in inferred Dict items
+
+    :param lhs_dict: Dictionary to 'merge' nodes into
+    :param rhs_dict: Dictionary with nodes to pull from
+    :return : merged dictionary of nodes
+    """
+    combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items())
+    # Overwrite keys which have the same string values
+    string_map = {key.as_string(): (key, value) for key, value in combined_dict}
+    # Return to dictionary
+    return dict(string_map.values())
+
+
+def _infer_map(
+    node: nodes.Dict, context: InferenceContext | None
+) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
+    """Infer all values based on Dict.items."""
+    values: dict[SuccessfulInferenceResult, SuccessfulInferenceResult] = {}
+    for name, value in node.items:
+        if isinstance(name, nodes.DictUnpack):
+            double_starred = helpers.safe_infer(value, context)
+            if not double_starred:
+                raise InferenceError
+            if not isinstance(double_starred, nodes.Dict):
+                raise InferenceError(node=node, context=context)
+            unpack_items = _infer_map(double_starred, context)
+            values = _update_with_replacement(values, unpack_items)
+        else:
+            key = helpers.safe_infer(name, context=context)
+            safe_value = helpers.safe_infer(value, context=context)
+            if any(not elem for elem in (key, safe_value)):
+                raise InferenceError(node=node, context=context)
+            # safe_value is SuccessfulInferenceResult as bool(Uninferable) == False
+            values = _update_with_replacement(values, {key: safe_value})
+    return values
+
+
+nodes.Dict._infer = infer_map  # type: ignore[assignment]
+
+
+def _higher_function_scope(node: nodes.NodeNG) -> nodes.FunctionDef | None:
+    """Search for the first function which encloses the given
+    scope. This can be used for looking up in that function's
+    scope, in case looking up in a lower scope for a particular
+    name fails.
+
+    :param node: A scope node.
+    :returns:
+        ``None``, if no parent function scope was found,
+        otherwise an instance of :class:`astroid.nodes.scoped_nodes.Function`,
+        which encloses the given node.
+    """
+    current = node
+    while current.parent and not isinstance(current.parent, nodes.FunctionDef):
+        current = current.parent
+    if current and current.parent:
+        return current.parent  # type: ignore[no-any-return]
+    return None
+
+
+def infer_name(
+    self: nodes.Name | nodes.AssignName,
+    context: InferenceContext | None = None,
+    **kwargs: Any,
+) -> Generator[InferenceResult, None, None]:
+    """Infer a Name: use name lookup rules."""
+    frame, stmts = self.lookup(self.name)
+    if not stmts:
+        # Try to see if the name is enclosed in a nested function
+        # and use the higher (first function) scope for searching.
+        parent_function = _higher_function_scope(self.scope())
+        if parent_function:
+            _, stmts = parent_function.lookup(self.name)
+
+        if not stmts:
+            raise NameInferenceError(
+                name=self.name, scope=self.scope(), context=context
+            )
+    context = copy_context(context)
+    context.lookupname = self.name
+    context.constraints[self.name] = constraint.get_constraints(self, frame)
+
+    return bases._infer_stmts(stmts, context, frame)
+
+
+# pylint: disable=no-value-for-parameter
+# The order of the decorators here is important
+# See https://github.com/PyCQA/astroid/commit/0a8a75db30da060a24922e05048bc270230f5
+nodes.Name._infer = decorators.raise_if_nothing_inferred(
+    decorators.path_wrapper(infer_name)
+)
+nodes.AssignName.infer_lhs = infer_name  # won't work with a path wrapper
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_call(
+    self: nodes.Call, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, InferenceErrorInfo]:
+    """Infer a Call node by trying to guess what the function returns."""
+    callcontext = copy_context(context)
+    callcontext.boundnode = None
+    if context is not None:
+        callcontext.extra_context = _populate_context_lookup(self, context.clone())
+
+    for callee in self.func.infer(context):
+        if isinstance(callee, util.UninferableBase):
+            yield callee
+            continue
+        try:
+            if hasattr(callee, "infer_call_result"):
+                callcontext.callcontext = CallContext(
+                    args=self.args, keywords=self.keywords, callee=callee
+                )
+                yield from callee.infer_call_result(caller=self, context=callcontext)
+        except InferenceError:
+            continue
+    return InferenceErrorInfo(node=self, context=context)
+
+
+nodes.Call._infer = infer_call  # type: ignore[assignment]
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_import(
+    self: nodes.Import,
+    context: InferenceContext | None = None,
+    asname: bool = True,
+    **kwargs: Any,
+) -> Generator[nodes.Module, None, None]:
+    """Infer an Import node: return the imported module/object."""
+    context = context or InferenceContext()
+    name = context.lookupname
+    if name is None:
+        raise InferenceError(node=self, context=context)
+
+    try:
+        if asname:
+            yield self.do_import_module(self.real_name(name))
+        else:
+            yield self.do_import_module(name)
+    except AstroidBuildingError as exc:
+        raise InferenceError(node=self, context=context) from exc
+
+
+nodes.Import._infer = infer_import
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_import_from(
+    self: nodes.ImportFrom,
+    context: InferenceContext | None = None,
+    asname: bool = True,
+    **kwargs: Any,
+) -> Generator[InferenceResult, None, None]:
+    """Infer a ImportFrom node: return the imported module/object."""
+    context = context or InferenceContext()
+    name = context.lookupname
+    if name is None:
+        raise InferenceError(node=self, context=context)
+    if asname:
+        try:
+            name = self.real_name(name)
+        except AttributeInferenceError as exc:
+            # See https://github.com/PyCQA/pylint/issues/4692
+            raise InferenceError(node=self, context=context) from exc
+    try:
+        module = self.do_import_module()
+    except AstroidBuildingError as exc:
+        raise InferenceError(node=self, context=context) from exc
+
+    try:
+        context = copy_context(context)
+        context.lookupname = name
+        stmts = module.getattr(name, ignore_locals=module is self.root())
+        return bases._infer_stmts(stmts, context)
+    except AttributeInferenceError as error:
+        raise InferenceError(
+            str(error), target=self, attribute=name, context=context
+        ) from error
+
+
+nodes.ImportFrom._infer = infer_import_from  # type: ignore[assignment]
+
+
+def infer_attribute(
+    self: nodes.Attribute | nodes.AssignAttr,
+    context: InferenceContext | None = None,
+    **kwargs: Any,
+) -> Generator[InferenceResult, None, InferenceErrorInfo]:
+    """Infer an Attribute node by using getattr on the associated object."""
+    for owner in self.expr.infer(context):
+        if isinstance(owner, util.UninferableBase):
+            yield owner
+            continue
+
+        context = copy_context(context)
+        old_boundnode = context.boundnode
+        try:
+            context.boundnode = owner
+            if isinstance(owner, (nodes.ClassDef, bases.Instance)):
+                frame = owner if isinstance(owner, nodes.ClassDef) else owner._proxied
+                context.constraints[self.attrname] = constraint.get_constraints(
+                    self, frame=frame
+                )
+            if self.attrname == "argv" and owner.name == "sys":
+                # sys.argv will never be inferable during static analysis
+                # It's value would be the args passed to the linter itself
+                yield util.Uninferable
+            else:
+                yield from owner.igetattr(self.attrname, context)
+        except (
+            AttributeInferenceError,
+            InferenceError,
+            AttributeError,
+        ):
+            pass
+        finally:
+            context.boundnode = old_boundnode
+    return InferenceErrorInfo(node=self, context=context)
+
+
+# The order of the decorators here is important
+# See https://github.com/PyCQA/astroid/commit/0a8a75db30da060a24922e05048bc270230f5
+nodes.Attribute._infer = decorators.raise_if_nothing_inferred(
+    decorators.path_wrapper(infer_attribute)
+)
+# won't work with a path wrapper
+nodes.AssignAttr.infer_lhs = decorators.raise_if_nothing_inferred(infer_attribute)
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_global(
+    self: nodes.Global, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, None]:
+    if context is None or context.lookupname is None:
+        raise InferenceError(node=self, context=context)
+    try:
+        return bases._infer_stmts(self.root().getattr(context.lookupname), context)
+    except AttributeInferenceError as error:
+        raise InferenceError(
+            str(error), target=self, attribute=context.lookupname, context=context
+        ) from error
+
+
+nodes.Global._infer = infer_global  # type: ignore[assignment]
+
+
+_SUBSCRIPT_SENTINEL = object()
+
+
+def infer_subscript(
+    self: nodes.Subscript, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
+    """Inference for subscripts.
+
+    We're understanding if the index is a Const
+    or a slice, passing the result of inference
+    to the value's `getitem` method, which should
+    handle each supported index type accordingly.
+    """
+
+    found_one = False
+    for value in self.value.infer(context):
+        if isinstance(value, util.UninferableBase):
+            yield util.Uninferable
+            return None
+        for index in self.slice.infer(context):
+            if isinstance(index, util.UninferableBase):
+                yield util.Uninferable
+                return None
+
+            # Try to deduce the index value.
+            index_value = _SUBSCRIPT_SENTINEL
+            if value.__class__ == bases.Instance:
+                index_value = index
+            elif index.__class__ == bases.Instance:
+                instance_as_index = helpers.class_instance_as_index(index)
+                if instance_as_index:
+                    index_value = instance_as_index
+            else:
+                index_value = index
+
+            if index_value is _SUBSCRIPT_SENTINEL:
+                raise InferenceError(node=self, context=context)
+
+            try:
+                assigned = value.getitem(index_value, context)
+            except (
+                AstroidTypeError,
+                AstroidIndexError,
+                AstroidValueError,
+                AttributeInferenceError,
+                AttributeError,
+            ) as exc:
+                raise InferenceError(node=self, context=context) from exc
+
+            # Prevent inferring if the inferred subscript
+            # is the same as the original subscripted object.
+            if self is assigned or isinstance(assigned, util.UninferableBase):
+                yield util.Uninferable
+                return None
+            yield from assigned.infer(context)
+            found_one = True
+
+    if found_one:
+        return InferenceErrorInfo(node=self, context=context)
+    return None
+
+
+# The order of the decorators here is important
+# See https://github.com/PyCQA/astroid/commit/0a8a75db30da060a24922e05048bc270230f5
+nodes.Subscript._infer = decorators.raise_if_nothing_inferred(  # type: ignore[assignment]
+    decorators.path_wrapper(infer_subscript)
+)
+nodes.Subscript.infer_lhs = decorators.raise_if_nothing_inferred(infer_subscript)
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def _infer_boolop(
+    self: nodes.BoolOp, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
+    """Infer a boolean operation (and / or / not).
+
+    The function will calculate the boolean operation
+    for all pairs generated through inference for each component
+    node.
+    """
+    values = self.values
+    if self.op == "or":
+        predicate = operator.truth
+    else:
+        predicate = operator.not_
+
+    try:
+        inferred_values = [value.infer(context=context) for value in values]
+    except InferenceError:
+        yield util.Uninferable
+        return None
+
+    for pair in itertools.product(*inferred_values):
+        if any(isinstance(item, util.UninferableBase) for item in pair):
+            # Can't infer the final result, just yield Uninferable.
+            yield util.Uninferable
+            continue
+
+        bool_values = [item.bool_value() for item in pair]
+        if any(isinstance(item, util.UninferableBase) for item in bool_values):
+            # Can't infer the final result, just yield Uninferable.
+            yield util.Uninferable
+            continue
+
+        # Since the boolean operations are short circuited operations,
+        # this code yields the first value for which the predicate is True
+        # and if no value respected the predicate, then the last value will
+        # be returned (or Uninferable if there was no last value).
+        # This is conforming to the semantics of `and` and `or`:
+        #   1 and 0 -> 1
+        #   0 and 1 -> 0
+        #   1 or 0 -> 1
+        #   0 or 1 -> 1
+        value = util.Uninferable
+        for value, bool_value in zip(pair, bool_values):
+            if predicate(bool_value):
+                yield value
+                break
+        else:
+            yield value
+
+    return InferenceErrorInfo(node=self, context=context)
+
+
+nodes.BoolOp._infer = _infer_boolop
+
+
+# UnaryOp, BinOp and AugAssign inferences
+
+
+def _filter_operation_errors(
+    self: _T,
+    infer_callable: Callable[
+        [_T, InferenceContext | None],
+        Generator[InferenceResult | util.BadOperationMessage, None, None],
+    ],
+    context: InferenceContext | None,
+    error: type[util.BadOperationMessage],
+) -> Generator[InferenceResult, None, None]:
+    for result in infer_callable(self, context):
+        if isinstance(result, error):
+            # For the sake of .infer(), we don't care about operation
+            # errors, which is the job of pylint. So return something
+            # which shows that we can't infer the result.
+            yield util.Uninferable
+        else:
+            yield result
+
+
+def _infer_unaryop(
+    self: nodes.UnaryOp, context: InferenceContext | None = None
+) -> Generator[InferenceResult | util.BadUnaryOperationMessage, None, None]:
+    """Infer what an UnaryOp should return when evaluated."""
+    for operand in self.operand.infer(context):
+        try:
+            yield operand.infer_unary_op(self.op)
+        except TypeError as exc:
+            # The operand doesn't support this operation.
+            yield util.BadUnaryOperationMessage(operand, self.op, exc)
+        except AttributeError as exc:
+            meth = protocols.UNARY_OP_METHOD[self.op]
+            if meth is None:
+                # `not node`. Determine node's boolean
+                # value and negate its result, unless it is
+                # Uninferable, which will be returned as is.
+                bool_value = operand.bool_value()
+                if not isinstance(bool_value, util.UninferableBase):
+                    yield nodes.const_factory(not bool_value)
+                else:
+                    yield util.Uninferable
+            else:
+                if not isinstance(operand, (bases.Instance, nodes.ClassDef)):
+                    # The operation was used on something which
+                    # doesn't support it.
+                    yield util.BadUnaryOperationMessage(operand, self.op, exc)
+                    continue
+
+                try:
+                    try:
+                        methods = dunder_lookup.lookup(operand, meth)
+                    except AttributeInferenceError:
+                        yield util.BadUnaryOperationMessage(operand, self.op, exc)
+                        continue
+
+                    meth = methods[0]
+                    inferred = next(meth.infer(context=context), None)
+                    if (
+                        isinstance(inferred, util.UninferableBase)
+                        or not inferred.callable()
+                    ):
+                        continue
+
+                    context = copy_context(context)
+                    context.boundnode = operand
+                    context.callcontext = CallContext(args=[], callee=inferred)
+
+                    call_results = inferred.infer_call_result(self, context=context)
+                    result = next(call_results, None)
+                    if result is None:
+                        # Failed to infer, return the same type.
+                        yield operand
+                    else:
+                        yield result
+                except AttributeInferenceError as inner_exc:
+                    # The unary operation special method was not found.
+                    yield util.BadUnaryOperationMessage(operand, self.op, inner_exc)
+                except InferenceError:
+                    yield util.Uninferable
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_unaryop(
+    self: nodes.UnaryOp, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, InferenceErrorInfo]:
+    """Infer what an UnaryOp should return when evaluated."""
+    yield from _filter_operation_errors(
+        self, _infer_unaryop, context, util.BadUnaryOperationMessage
+    )
+    return InferenceErrorInfo(node=self, context=context)
+
+
+nodes.UnaryOp._infer_unaryop = _infer_unaryop
+nodes.UnaryOp._infer = infer_unaryop
+
+
+def _is_not_implemented(const) -> bool:
+    """Check if the given const node is NotImplemented."""
+    return isinstance(const, nodes.Const) and const.value is NotImplemented
+
+
+def _infer_old_style_string_formatting(
+    instance: nodes.Const, other: nodes.NodeNG, context: InferenceContext
+) -> tuple[util.UninferableBase | nodes.Const]:
+    """Infer the result of '"string" % ...'.
+
+    TODO: Instead of returning Uninferable we should rely
+    on the call to '%' to see if the result is actually uninferable.
+    """
+    if isinstance(other, nodes.Tuple):
+        if util.Uninferable in other.elts:
+            return (util.Uninferable,)
+        inferred_positional = [helpers.safe_infer(i, context) for i in other.elts]
+        if all(isinstance(i, nodes.Const) for i in inferred_positional):
+            values = tuple(i.value for i in inferred_positional)
+        else:
+            values = None
+    elif isinstance(other, nodes.Dict):
+        values: dict[Any, Any] = {}
+        for pair in other.items:
+            key = helpers.safe_infer(pair[0], context)
+            if not isinstance(key, nodes.Const):
+                return (util.Uninferable,)
+            value = helpers.safe_infer(pair[1], context)
+            if not isinstance(value, nodes.Const):
+                return (util.Uninferable,)
+            values[key.value] = value.value
+    elif isinstance(other, nodes.Const):
+        values = other.value
+    else:
+        return (util.Uninferable,)
+
+    try:
+        return (nodes.const_factory(instance.value % values),)
+    except (TypeError, KeyError, ValueError):
+        return (util.Uninferable,)
+
+
+def _invoke_binop_inference(
+    instance: InferenceResult,
+    opnode: nodes.AugAssign | nodes.BinOp,
+    op: str,
+    other: InferenceResult,
+    context: InferenceContext,
+    method_name: str,
+) -> Generator[InferenceResult, None, None]:
+    """Invoke binary operation inference on the given instance."""
+    methods = dunder_lookup.lookup(instance, method_name)
+    context = bind_context_to_node(context, instance)
+    method = methods[0]
+    context.callcontext.callee = method
+
+    if (
+        isinstance(instance, nodes.Const)
+        and isinstance(instance.value, str)
+        and op == "%"
+    ):
+        return iter(_infer_old_style_string_formatting(instance, other, context))
+
+    try:
+        inferred = next(method.infer(context=context))
+    except StopIteration as e:
+        raise InferenceError(node=method, context=context) from e
+    if isinstance(inferred, util.UninferableBase):
+        raise InferenceError
+    if not isinstance(
+        instance, (nodes.Const, nodes.Tuple, nodes.List, nodes.ClassDef, bases.Instance)
+    ):
+        raise InferenceError  # pragma: no cover # Used as a failsafe
+    return instance.infer_binary_op(opnode, op, other, context, inferred)
+
+
+def _aug_op(
+    instance: InferenceResult,
+    opnode: nodes.AugAssign,
+    op: str,
+    other: InferenceResult,
+    context: InferenceContext,
+    reverse: bool = False,
+) -> functools.partial[Generator[InferenceResult, None, None]]:
+    """Get an inference callable for an augmented binary operation."""
+    method_name = protocols.AUGMENTED_OP_METHOD[op]
+    return functools.partial(
+        _invoke_binop_inference,
+        instance=instance,
+        op=op,
+        opnode=opnode,
+        other=other,
+        context=context,
+        method_name=method_name,
+    )
+
+
+def _bin_op(
+    instance: InferenceResult,
+    opnode: nodes.AugAssign | nodes.BinOp,
+    op: str,
+    other: InferenceResult,
+    context: InferenceContext,
+    reverse: bool = False,
+) -> functools.partial[Generator[InferenceResult, None, None]]:
+    """Get an inference callable for a normal binary operation.
+
+    If *reverse* is True, then the reflected method will be used instead.
+    """
+    if reverse:
+        method_name = protocols.REFLECTED_BIN_OP_METHOD[op]
+    else:
+        method_name = protocols.BIN_OP_METHOD[op]
+    return functools.partial(
+        _invoke_binop_inference,
+        instance=instance,
+        op=op,
+        opnode=opnode,
+        other=other,
+        context=context,
+        method_name=method_name,
+    )
+
+
+def _bin_op_or_union_type(
+    left: bases.UnionType | nodes.ClassDef | nodes.Const,
+    right: bases.UnionType | nodes.ClassDef | nodes.Const,
+) -> Generator[InferenceResult, None, None]:
+    """Create a new UnionType instance for binary or, e.g. int | str."""
+    yield bases.UnionType(left, right)
+
+
+def _get_binop_contexts(context, left, right):
+    """Get contexts for binary operations.
+
+    This will return two inference contexts, the first one
+    for x.__op__(y), the other one for y.__rop__(x), where
+    only the arguments are inversed.
+    """
+    # The order is important, since the first one should be
+    # left.__op__(right).
+    for arg in (right, left):
+        new_context = context.clone()
+        new_context.callcontext = CallContext(args=[arg])
+        new_context.boundnode = None
+        yield new_context
+
+
+def _same_type(type1, type2) -> bool:
+    """Check if type1 is the same as type2."""
+    return type1.qname() == type2.qname()
+
+
+def _get_binop_flow(
+    left: InferenceResult,
+    left_type: InferenceResult | None,
+    binary_opnode: nodes.AugAssign | nodes.BinOp,
+    right: InferenceResult,
+    right_type: InferenceResult | None,
+    context: InferenceContext,
+    reverse_context: InferenceContext,
+) -> list[functools.partial[Generator[InferenceResult, None, None]]]:
+    """Get the flow for binary operations.
+
+    The rules are a bit messy:
+
+        * if left and right have the same type, then only one
+          method will be called, left.__op__(right)
+        * if left and right are unrelated typewise, then first
+          left.__op__(right) is tried and if this does not exist
+          or returns NotImplemented, then right.__rop__(left) is tried.
+        * if left is a subtype of right, then only left.__op__(right)
+          is tried.
+        * if left is a supertype of right, then right.__rop__(left)
+          is first tried and then left.__op__(right)
+    """
+    op = binary_opnode.op
+    if _same_type(left_type, right_type):
+        methods = [_bin_op(left, binary_opnode, op, right, context)]
+    elif helpers.is_subtype(left_type, right_type):
+        methods = [_bin_op(left, binary_opnode, op, right, context)]
+    elif helpers.is_supertype(left_type, right_type):
+        methods = [
+            _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True),
+            _bin_op(left, binary_opnode, op, right, context),
+        ]
+    else:
+        methods = [
+            _bin_op(left, binary_opnode, op, right, context),
+            _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True),
+        ]
+
+    if (
+        PY310_PLUS
+        and op == "|"
+        and (
+            isinstance(left, (bases.UnionType, nodes.ClassDef))
+            or isinstance(left, nodes.Const)
+            and left.value is None
+        )
+        and (
+            isinstance(right, (bases.UnionType, nodes.ClassDef))
+            or isinstance(right, nodes.Const)
+            and right.value is None
+        )
+    ):
+        methods.extend([functools.partial(_bin_op_or_union_type, left, right)])
+    return methods
+
+
+def _get_aug_flow(
+    left: InferenceResult,
+    left_type: InferenceResult | None,
+    aug_opnode: nodes.AugAssign,
+    right: InferenceResult,
+    right_type: InferenceResult | None,
+    context: InferenceContext,
+    reverse_context: InferenceContext,
+) -> list[functools.partial[Generator[InferenceResult, None, None]]]:
+    """Get the flow for augmented binary operations.
+
+    The rules are a bit messy:
+
+        * if left and right have the same type, then left.__augop__(right)
+          is first tried and then left.__op__(right).
+        * if left and right are unrelated typewise, then
+          left.__augop__(right) is tried, then left.__op__(right)
+          is tried and then right.__rop__(left) is tried.
+        * if left is a subtype of right, then left.__augop__(right)
+          is tried and then left.__op__(right).
+        * if left is a supertype of right, then left.__augop__(right)
+          is tried, then right.__rop__(left) and then
+          left.__op__(right)
+    """
+    bin_op = aug_opnode.op.strip("=")
+    aug_op = aug_opnode.op
+    if _same_type(left_type, right_type):
+        methods = [
+            _aug_op(left, aug_opnode, aug_op, right, context),
+            _bin_op(left, aug_opnode, bin_op, right, context),
+        ]
+    elif helpers.is_subtype(left_type, right_type):
+        methods = [
+            _aug_op(left, aug_opnode, aug_op, right, context),
+            _bin_op(left, aug_opnode, bin_op, right, context),
+        ]
+    elif helpers.is_supertype(left_type, right_type):
+        methods = [
+            _aug_op(left, aug_opnode, aug_op, right, context),
+            _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True),
+            _bin_op(left, aug_opnode, bin_op, right, context),
+        ]
+    else:
+        methods = [
+            _aug_op(left, aug_opnode, aug_op, right, context),
+            _bin_op(left, aug_opnode, bin_op, right, context),
+            _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True),
+        ]
+    return methods
+
+
+def _infer_binary_operation(
+    left: InferenceResult,
+    right: InferenceResult,
+    binary_opnode: nodes.AugAssign | nodes.BinOp,
+    context: InferenceContext,
+    flow_factory: GetFlowFactory,
+) -> Generator[InferenceResult | util.BadBinaryOperationMessage, None, None]:
+    """Infer a binary operation between a left operand and a right operand.
+
+    This is used by both normal binary operations and augmented binary
+    operations, the only difference is the flow factory used.
+    """
+
+    context, reverse_context = _get_binop_contexts(context, left, right)
+    left_type = helpers.object_type(left)
+    right_type = helpers.object_type(right)
+    methods = flow_factory(
+        left, left_type, binary_opnode, right, right_type, context, reverse_context
+    )
+    for method in methods:
+        try:
+            results = list(method())
+        except AttributeError:
+            continue
+        except AttributeInferenceError:
+            continue
+        except InferenceError:
+            yield util.Uninferable
+            return
+        else:
+            if any(isinstance(result, util.UninferableBase) for result in results):
+                yield util.Uninferable
+                return
+
+            if all(map(_is_not_implemented, results)):
+                continue
+            not_implemented = sum(
+                1 for result in results if _is_not_implemented(result)
+            )
+            if not_implemented and not_implemented != len(results):
+                # Can't infer yet what this is.
+                yield util.Uninferable
+                return
+
+            yield from results
+            return
+    # The operation doesn't seem to be supported so let the caller know about it
+    yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type)
+
+
+def _infer_binop(
+    self: nodes.BinOp, context: InferenceContext | None = None
+) -> Generator[InferenceResult | util.BadBinaryOperationMessage, None, None]:
+    """Binary operation inference logic."""
+    left = self.left
+    right = self.right
+
+    # we use two separate contexts for evaluating lhs and rhs because
+    # 1. evaluating lhs may leave some undesired entries in context.path
+    #    which may not let us infer right value of rhs
+    context = context or InferenceContext()
+    lhs_context = copy_context(context)
+    rhs_context = copy_context(context)
+    lhs_iter = left.infer(context=lhs_context)
+    rhs_iter = right.infer(context=rhs_context)
+    for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
+        if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
+            # Don't know how to process this.
+            yield util.Uninferable
+            return
+
+        try:
+            yield from _infer_binary_operation(lhs, rhs, self, context, _get_binop_flow)
+        except _NonDeducibleTypeHierarchy:
+            yield util.Uninferable
+
+
+@decorators.yes_if_nothing_inferred
+@decorators.path_wrapper
+def infer_binop(
+    self: nodes.BinOp, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, None]:
+    return _filter_operation_errors(
+        self, _infer_binop, context, util.BadBinaryOperationMessage
+    )
+
+
+nodes.BinOp._infer_binop = _infer_binop
+nodes.BinOp._infer = infer_binop
+
+COMPARE_OPS: dict[str, Callable[[Any, Any], bool]] = {
+    "==": operator.eq,
+    "!=": operator.ne,
+    "<": operator.lt,
+    "<=": operator.le,
+    ">": operator.gt,
+    ">=": operator.ge,
+    "in": lambda a, b: a in b,
+    "not in": lambda a, b: a not in b,
+}
+UNINFERABLE_OPS = {
+    "is",
+    "is not",
+}
+
+
+def _to_literal(node: nodes.NodeNG) -> Any:
+    # Can raise SyntaxError or ValueError from ast.literal_eval
+    # Can raise AttributeError from node.as_string() as not all nodes have a visitor
+    # Is this the stupidest idea or the simplest idea?
+    return ast.literal_eval(node.as_string())
+
+
+def _do_compare(
+    left_iter: Iterable[nodes.NodeNG], op: str, right_iter: Iterable[nodes.NodeNG]
+) -> bool | util.UninferableBase:
+    """
+    If all possible combinations are either True or False, return that:
+    >>> _do_compare([1, 2], '<=', [3, 4])
+    True
+    >>> _do_compare([1, 2], '==', [3, 4])
+    False
+
+    If any item is uninferable, or if some combinations are True and some
+    are False, return Uninferable:
+    >>> _do_compare([1, 3], '<=', [2, 4])
+    util.Uninferable
+    """
+    retval: bool | None = None
+    if op in UNINFERABLE_OPS:
+        return util.Uninferable
+    op_func = COMPARE_OPS[op]
+
+    for left, right in itertools.product(left_iter, right_iter):
+        if isinstance(left, util.UninferableBase) or isinstance(
+            right, util.UninferableBase
+        ):
+            return util.Uninferable
+
+        try:
+            left, right = _to_literal(left), _to_literal(right)
+        except (SyntaxError, ValueError, AttributeError):
+            return util.Uninferable
+
+        try:
+            expr = op_func(left, right)
+        except TypeError as exc:
+            raise AstroidTypeError from exc
+
+        if retval is None:
+            retval = expr
+        elif retval != expr:
+            return util.Uninferable
+            # (or both, but "True | False" is basically the same)
+
+    assert retval is not None
+    return retval  # it was all the same value
+
+
+def _infer_compare(
+    self: nodes.Compare, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[nodes.Const | util.UninferableBase, None, None]:
+    """Chained comparison inference logic."""
+    retval: bool | util.UninferableBase = True
+
+    ops = self.ops
+    left_node = self.left
+    lhs = list(left_node.infer(context=context))
+    # should we break early if first element is uninferable?
+    for op, right_node in ops:
+        # eagerly evaluate rhs so that values can be re-used as lhs
+        rhs = list(right_node.infer(context=context))
+        try:
+            retval = _do_compare(lhs, op, rhs)
+        except AstroidTypeError:
+            retval = util.Uninferable
+            break
+        if retval is not True:
+            break  # short-circuit
+        lhs = rhs  # continue
+    if retval is util.Uninferable:
+        yield retval  # type: ignore[misc]
+    else:
+        yield nodes.Const(retval)
+
+
+nodes.Compare._infer = _infer_compare  # type: ignore[assignment]
+
+
+def _infer_augassign(
+    self: nodes.AugAssign, context: InferenceContext | None = None
+) -> Generator[InferenceResult | util.BadBinaryOperationMessage, None, None]:
+    """Inference logic for augmented binary operations."""
+    context = context or InferenceContext()
+
+    rhs_context = context.clone()
+
+    lhs_iter = self.target.infer_lhs(context=context)
+    rhs_iter = self.value.infer(context=rhs_context)
+    for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
+        if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
+            # Don't know how to process this.
+            yield util.Uninferable
+            return
+
+        try:
+            yield from _infer_binary_operation(
+                left=lhs,
+                right=rhs,
+                binary_opnode=self,
+                context=context,
+                flow_factory=_get_aug_flow,
+            )
+        except _NonDeducibleTypeHierarchy:
+            yield util.Uninferable
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_augassign(
+    self: nodes.AugAssign, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, None]:
+    return _filter_operation_errors(
+        self, _infer_augassign, context, util.BadBinaryOperationMessage
+    )
+
+
+nodes.AugAssign._infer_augassign = _infer_augassign
+nodes.AugAssign._infer = infer_augassign
+
+# End of binary operation inference.
+
+
+@decorators.raise_if_nothing_inferred
+def infer_arguments(
+    self: nodes.Arguments, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, None]:
+    if context is None or context.lookupname is None:
+        raise InferenceError(node=self, context=context)
+    return protocols._arguments_infer_argname(self, context.lookupname, context)
+
+
+nodes.Arguments._infer = infer_arguments  # type: ignore[assignment]
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_assign(
+    self: nodes.AssignName | nodes.AssignAttr,
+    context: InferenceContext | None = None,
+    **kwargs: Any,
+) -> Generator[InferenceResult, None, None]:
+    """Infer a AssignName/AssignAttr: need to inspect the RHS part of the
+    assign node.
+    """
+    if isinstance(self.parent, nodes.AugAssign):
+        return self.parent.infer(context)
+
+    stmts = list(self.assigned_stmts(context=context))
+    return bases._infer_stmts(stmts, context)
+
+
+nodes.AssignName._infer = infer_assign
+nodes.AssignAttr._infer = infer_assign
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_empty_node(
+    self: nodes.EmptyNode, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, None]:
+    if not self.has_underlying_object():
+        yield util.Uninferable
+    else:
+        try:
+            yield from AstroidManager().infer_ast_from_something(
+                self.object, context=context
+            )
+        except AstroidError:
+            yield util.Uninferable
+
+
+nodes.EmptyNode._infer = infer_empty_node  # type: ignore[assignment]
+
+
+def _populate_context_lookup(call: nodes.Call, context: InferenceContext | None):
+    # Allows context to be saved for later
+    # for inference inside a function
+    context_lookup: dict[InferenceResult, InferenceContext] = {}
+    if context is None:
+        return context_lookup
+    for arg in call.args:
+        if isinstance(arg, nodes.Starred):
+            context_lookup[arg.value] = context
+        else:
+            context_lookup[arg] = context
+    keywords = call.keywords if call.keywords is not None else []
+    for keyword in keywords:
+        context_lookup[keyword.value] = context
+    return context_lookup
+
+
+@decorators.raise_if_nothing_inferred
+def infer_ifexp(
+    self: nodes.IfExp, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[InferenceResult, None, None]:
+    """Support IfExp inference.
+
+    If we can't infer the truthiness of the condition, we default
+    to inferring both branches. Otherwise, we infer either branch
+    depending on the condition.
+    """
+    both_branches = False
+    # We use two separate contexts for evaluating lhs and rhs because
+    # evaluating lhs may leave some undesired entries in context.path
+    # which may not let us infer right value of rhs.
+
+    context = context or InferenceContext()
+    lhs_context = copy_context(context)
+    rhs_context = copy_context(context)
+    try:
+        test = next(self.test.infer(context=context.clone()))
+    except (InferenceError, StopIteration):
+        both_branches = True
+    else:
+        if not isinstance(test, util.UninferableBase):
+            if test.bool_value():
+                yield from self.body.infer(context=lhs_context)
+            else:
+                yield from self.orelse.infer(context=rhs_context)
+        else:
+            both_branches = True
+    if both_branches:
+        yield from self.body.infer(context=lhs_context)
+        yield from self.orelse.infer(context=rhs_context)
+
+
+nodes.IfExp._infer = infer_ifexp  # type: ignore[assignment]
+
+
+def infer_functiondef(
+    self: _FunctionDefT, context: InferenceContext | None = None, **kwargs: Any
+) -> Generator[Property | _FunctionDefT, None, InferenceErrorInfo]:
+    if not self.decorators or not bases._is_property(self):
+        yield self
+        return InferenceErrorInfo(node=self, context=context)
+
+    # When inferring a property, we instantiate a new `objects.Property` object,
+    # which in turn, because it inherits from `FunctionDef`, sets itself in the locals
+    # of the wrapping frame. This means that every time we infer a property, the locals
+    # are mutated with a new instance of the property. To avoid this, we detect this
+    # scenario and avoid passing the `parent` argument to the constructor.
+    parent_frame = self.parent.frame(future=True)
+    property_already_in_parent_locals = self.name in parent_frame.locals and any(
+        isinstance(val, objects.Property) for val in parent_frame.locals[self.name]
+    )
+    # We also don't want to pass parent if the definition is within a Try node
+    if isinstance(self.parent, (nodes.TryExcept, nodes.TryFinally, nodes.If)):
+        property_already_in_parent_locals = True
+
+    prop_func = objects.Property(
+        function=self,
+        name=self.name,
+        lineno=self.lineno,
+        parent=self.parent if not property_already_in_parent_locals else None,
+        col_offset=self.col_offset,
+    )
+    if property_already_in_parent_locals:
+        prop_func.parent = self.parent
+    prop_func.postinit(body=[], args=self.args, doc_node=self.doc_node)
+    yield prop_func
+    return InferenceErrorInfo(node=self, context=context)
+
+
+nodes.FunctionDef._infer = infer_functiondef

+ 85 - 0
venv/lib/python3.11/site-packages/astroid/inference_tip.py

@@ -0,0 +1,85 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Transform utilities (filters and decorator)."""
+
+from __future__ import annotations
+
+import typing
+from collections.abc import Iterator
+
+import wrapt
+
+from astroid.exceptions import InferenceOverwriteError, UseInferenceDefault
+from astroid.nodes import NodeNG
+from astroid.typing import InferenceResult, InferFn
+
+_cache: dict[tuple[InferFn, NodeNG], list[InferenceResult] | None] = {}
+
+
+def clear_inference_tip_cache() -> None:
+    """Clear the inference tips cache."""
+    _cache.clear()
+
+
+@wrapt.decorator
+def _inference_tip_cached(
+    func: InferFn, instance: None, args: typing.Any, kwargs: typing.Any
+) -> Iterator[InferenceResult]:
+    """Cache decorator used for inference tips."""
+    node = args[0]
+    try:
+        result = _cache[func, node]
+        # If through recursion we end up trying to infer the same
+        # func + node we raise here.
+        if result is None:
+            raise UseInferenceDefault()
+    except KeyError:
+        _cache[func, node] = None
+        result = _cache[func, node] = list(func(*args, **kwargs))
+        assert result
+    return iter(result)
+
+
+def inference_tip(infer_function: InferFn, raise_on_overwrite: bool = False) -> InferFn:
+    """Given an instance specific inference function, return a function to be
+    given to AstroidManager().register_transform to set this inference function.
+
+    :param bool raise_on_overwrite: Raise an `InferenceOverwriteError`
+        if the inference tip will overwrite another. Used for debugging
+
+    Typical usage
+
+    .. sourcecode:: python
+
+       AstroidManager().register_transform(Call, inference_tip(infer_named_tuple),
+                                  predicate)
+
+    .. Note::
+
+        Using an inference tip will override
+        any previously set inference tip for the given
+        node. Use a predicate in the transform to prevent
+        excess overwrites.
+    """
+
+    def transform(node: NodeNG, infer_function: InferFn = infer_function) -> NodeNG:
+        if (
+            raise_on_overwrite
+            and node._explicit_inference is not None
+            and node._explicit_inference is not infer_function
+        ):
+            raise InferenceOverwriteError(
+                "Inference already set to {existing_inference}. "
+                "Trying to overwrite with {new_inference} for {node}".format(
+                    existing_inference=infer_function,
+                    new_inference=node._explicit_inference,
+                    node=node,
+                )
+            )
+        # pylint: disable=no-value-for-parameter
+        node._explicit_inference = _inference_tip_cached(infer_function)
+        return node
+
+    return transform

+ 0 - 0
venv/lib/python3.11/site-packages/astroid/interpreter/__init__.py


+ 0 - 0
venv/lib/python3.11/site-packages/astroid/interpreter/_import/__init__.py


+ 475 - 0
venv/lib/python3.11/site-packages/astroid/interpreter/_import/spec.py

@@ -0,0 +1,475 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+import abc
+import enum
+import importlib
+import importlib.machinery
+import importlib.util
+import os
+import pathlib
+import sys
+import types
+import warnings
+import zipimport
+from collections.abc import Iterator, Sequence
+from pathlib import Path
+from typing import Any, NamedTuple
+
+from astroid.const import PY310_PLUS
+from astroid.modutils import EXT_LIB_DIRS
+
+from . import util
+
+if sys.version_info >= (3, 8):
+    from typing import Literal, Protocol
+else:
+    from typing_extensions import Literal, Protocol
+
+
+# The MetaPathFinder protocol comes from typeshed, which says:
+# Intentionally omits one deprecated and one optional method of `importlib.abc.MetaPathFinder`
+class _MetaPathFinder(Protocol):
+    def find_spec(
+        self,
+        fullname: str,
+        path: Sequence[str] | None,
+        target: types.ModuleType | None = ...,
+    ) -> importlib.machinery.ModuleSpec | None:
+        ...  # pragma: no cover
+
+
+class ModuleType(enum.Enum):
+    """Python module types used for ModuleSpec."""
+
+    C_BUILTIN = enum.auto()
+    C_EXTENSION = enum.auto()
+    PKG_DIRECTORY = enum.auto()
+    PY_CODERESOURCE = enum.auto()
+    PY_COMPILED = enum.auto()
+    PY_FROZEN = enum.auto()
+    PY_RESOURCE = enum.auto()
+    PY_SOURCE = enum.auto()
+    PY_ZIPMODULE = enum.auto()
+    PY_NAMESPACE = enum.auto()
+
+
+_MetaPathFinderModuleTypes: dict[str, ModuleType] = {
+    # Finders created by setuptools editable installs
+    "_EditableFinder": ModuleType.PY_SOURCE,
+    "_EditableNamespaceFinder": ModuleType.PY_NAMESPACE,
+    # Finders create by six
+    "_SixMetaPathImporter": ModuleType.PY_SOURCE,
+}
+
+_EditableFinderClasses: set[str] = {
+    "_EditableFinder",
+    "_EditableNamespaceFinder",
+}
+
+
+class ModuleSpec(NamedTuple):
+    """Defines a class similar to PEP 420's ModuleSpec.
+
+    A module spec defines a name of a module, its type, location
+    and where submodules can be found, if the module is a package.
+    """
+
+    name: str
+    type: ModuleType | None
+    location: str | None = None
+    origin: str | None = None
+    submodule_search_locations: Sequence[str] | None = None
+
+
+class Finder:
+    """A finder is a class which knows how to find a particular module."""
+
+    def __init__(self, path: Sequence[str] | None = None) -> None:
+        self._path = path or sys.path
+
+    @abc.abstractmethod
+    def find_module(
+        self,
+        modname: str,
+        module_parts: Sequence[str],
+        processed: list[str],
+        submodule_path: Sequence[str] | None,
+    ) -> ModuleSpec | None:
+        """Find the given module.
+
+        Each finder is responsible for each protocol of finding, as long as
+        they all return a ModuleSpec.
+
+        :param modname: The module which needs to be searched.
+        :param module_parts: It should be a list of strings,
+                                  where each part contributes to the module's
+                                  namespace.
+        :param processed: What parts from the module parts were processed
+                               so far.
+        :param submodule_path: A list of paths where the module
+                                    can be looked into.
+        :returns: A ModuleSpec, describing how and where the module was found,
+                  None, otherwise.
+        """
+
+    def contribute_to_path(
+        self, spec: ModuleSpec, processed: list[str]
+    ) -> Sequence[str] | None:
+        """Get a list of extra paths where this finder can search."""
+
+
+class ImportlibFinder(Finder):
+    """A finder based on the importlib module."""
+
+    _SUFFIXES: Sequence[tuple[str, ModuleType]] = (
+        [(s, ModuleType.C_EXTENSION) for s in importlib.machinery.EXTENSION_SUFFIXES]
+        + [(s, ModuleType.PY_SOURCE) for s in importlib.machinery.SOURCE_SUFFIXES]
+        + [(s, ModuleType.PY_COMPILED) for s in importlib.machinery.BYTECODE_SUFFIXES]
+    )
+
+    def find_module(
+        self,
+        modname: str,
+        module_parts: Sequence[str],
+        processed: list[str],
+        submodule_path: Sequence[str] | None,
+    ) -> ModuleSpec | None:
+        if submodule_path is not None:
+            submodule_path = list(submodule_path)
+        elif modname in sys.builtin_module_names:
+            return ModuleSpec(
+                name=modname,
+                location=None,
+                type=ModuleType.C_BUILTIN,
+            )
+        else:
+            try:
+                with warnings.catch_warnings():
+                    warnings.filterwarnings("ignore", category=UserWarning)
+                    spec = importlib.util.find_spec(modname)
+                if (
+                    spec
+                    and spec.loader  # type: ignore[comparison-overlap] # noqa: E501
+                    is importlib.machinery.FrozenImporter
+                ):
+                    # No need for BuiltinImporter; builtins handled above
+                    return ModuleSpec(
+                        name=modname,
+                        location=getattr(spec.loader_state, "filename", None),
+                        type=ModuleType.PY_FROZEN,
+                    )
+            except ValueError:
+                pass
+            submodule_path = sys.path
+
+        for entry in submodule_path:
+            package_directory = os.path.join(entry, modname)
+            for suffix in (".py", importlib.machinery.BYTECODE_SUFFIXES[0]):
+                package_file_name = "__init__" + suffix
+                file_path = os.path.join(package_directory, package_file_name)
+                if os.path.isfile(file_path):
+                    return ModuleSpec(
+                        name=modname,
+                        location=package_directory,
+                        type=ModuleType.PKG_DIRECTORY,
+                    )
+            for suffix, type_ in ImportlibFinder._SUFFIXES:
+                file_name = modname + suffix
+                file_path = os.path.join(entry, file_name)
+                if os.path.isfile(file_path):
+                    return ModuleSpec(name=modname, location=file_path, type=type_)
+        return None
+
+    def contribute_to_path(
+        self, spec: ModuleSpec, processed: list[str]
+    ) -> Sequence[str] | None:
+        if spec.location is None:
+            # Builtin.
+            return None
+
+        if _is_setuptools_namespace(Path(spec.location)):
+            # extend_path is called, search sys.path for module/packages
+            # of this name see pkgutil.extend_path documentation
+            path = [
+                os.path.join(p, *processed)
+                for p in sys.path
+                if os.path.isdir(os.path.join(p, *processed))
+            ]
+        elif spec.name == "distutils" and not any(
+            spec.location.lower().startswith(ext_lib_dir.lower())
+            for ext_lib_dir in EXT_LIB_DIRS
+        ):
+            # virtualenv below 20.0 patches distutils in an unexpected way
+            # so we just find the location of distutils that will be
+            # imported to avoid spurious import-error messages
+            # https://github.com/PyCQA/pylint/issues/5645
+            # A regression test to create this scenario exists in release-tests.yml
+            # and can be triggered manually from GitHub Actions
+            distutils_spec = importlib.util.find_spec("distutils")
+            if distutils_spec and distutils_spec.origin:
+                origin_path = Path(
+                    distutils_spec.origin
+                )  # e.g. .../distutils/__init__.py
+                path = [str(origin_path.parent)]  # e.g. .../distutils
+            else:
+                path = [spec.location]
+        else:
+            path = [spec.location]
+        return path
+
+
+class ExplicitNamespacePackageFinder(ImportlibFinder):
+    """A finder for the explicit namespace packages."""
+
+    def find_module(
+        self,
+        modname: str,
+        module_parts: Sequence[str],
+        processed: list[str],
+        submodule_path: Sequence[str] | None,
+    ) -> ModuleSpec | None:
+        if processed:
+            modname = ".".join(processed + [modname])
+        if util.is_namespace(modname) and modname in sys.modules:
+            submodule_path = sys.modules[modname].__path__
+            return ModuleSpec(
+                name=modname,
+                location="",
+                origin="namespace",
+                type=ModuleType.PY_NAMESPACE,
+                submodule_search_locations=submodule_path,
+            )
+        return None
+
+    def contribute_to_path(
+        self, spec: ModuleSpec, processed: list[str]
+    ) -> Sequence[str] | None:
+        return spec.submodule_search_locations
+
+
+class ZipFinder(Finder):
+    """Finder that knows how to find a module inside zip files."""
+
+    def __init__(self, path: Sequence[str]) -> None:
+        super().__init__(path)
+        for entry_path in path:
+            if entry_path not in sys.path_importer_cache:
+                try:
+                    sys.path_importer_cache[entry_path] = zipimport.zipimporter(  # type: ignore[assignment]
+                        entry_path
+                    )
+                except zipimport.ZipImportError:
+                    continue
+
+    def find_module(
+        self,
+        modname: str,
+        module_parts: Sequence[str],
+        processed: list[str],
+        submodule_path: Sequence[str] | None,
+    ) -> ModuleSpec | None:
+        try:
+            file_type, filename, path = _search_zip(module_parts)
+        except ImportError:
+            return None
+
+        return ModuleSpec(
+            name=modname,
+            location=filename,
+            origin="egg",
+            type=file_type,
+            submodule_search_locations=path,
+        )
+
+
+class PathSpecFinder(Finder):
+    """Finder based on importlib.machinery.PathFinder."""
+
+    def find_module(
+        self,
+        modname: str,
+        module_parts: Sequence[str],
+        processed: list[str],
+        submodule_path: Sequence[str] | None,
+    ) -> ModuleSpec | None:
+        spec = importlib.machinery.PathFinder.find_spec(modname, path=submodule_path)
+        if spec is not None:
+            is_namespace_pkg = spec.origin is None
+            location = spec.origin if not is_namespace_pkg else None
+            module_type = ModuleType.PY_NAMESPACE if is_namespace_pkg else None
+            return ModuleSpec(
+                name=spec.name,
+                location=location,
+                origin=spec.origin,
+                type=module_type,
+                submodule_search_locations=list(spec.submodule_search_locations or []),
+            )
+        return spec
+
+    def contribute_to_path(
+        self, spec: ModuleSpec, processed: list[str]
+    ) -> Sequence[str] | None:
+        if spec.type == ModuleType.PY_NAMESPACE:
+            return spec.submodule_search_locations
+        return None
+
+
+_SPEC_FINDERS = (
+    ImportlibFinder,
+    ZipFinder,
+    PathSpecFinder,
+    ExplicitNamespacePackageFinder,
+)
+
+
+def _is_setuptools_namespace(location: pathlib.Path) -> bool:
+    try:
+        with open(location / "__init__.py", "rb") as stream:
+            data = stream.read(4096)
+    except OSError:
+        return False
+    extend_path = b"pkgutil" in data and b"extend_path" in data
+    declare_namespace = (
+        b"pkg_resources" in data and b"declare_namespace(__name__)" in data
+    )
+    return extend_path or declare_namespace
+
+
+def _get_zipimporters() -> Iterator[tuple[str, zipimport.zipimporter]]:
+    for filepath, importer in sys.path_importer_cache.items():
+        if isinstance(importer, zipimport.zipimporter):
+            yield filepath, importer
+
+
+def _search_zip(
+    modpath: Sequence[str],
+) -> tuple[Literal[ModuleType.PY_ZIPMODULE], str, str]:
+    for filepath, importer in _get_zipimporters():
+        if PY310_PLUS:
+            found: Any = importer.find_spec(modpath[0])
+        else:
+            found = importer.find_module(modpath[0])
+        if found:
+            if PY310_PLUS:
+                if not importer.find_spec(os.path.sep.join(modpath)):
+                    raise ImportError(
+                        "No module named %s in %s/%s"
+                        % (".".join(modpath[1:]), filepath, modpath)
+                    )
+            elif not importer.find_module(os.path.sep.join(modpath)):
+                raise ImportError(
+                    "No module named %s in %s/%s"
+                    % (".".join(modpath[1:]), filepath, modpath)
+                )
+            return (
+                ModuleType.PY_ZIPMODULE,
+                os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath),
+                filepath,
+            )
+    raise ImportError(f"No module named {'.'.join(modpath)}")
+
+
+def _find_spec_with_path(
+    search_path: Sequence[str],
+    modname: str,
+    module_parts: list[str],
+    processed: list[str],
+    submodule_path: Sequence[str] | None,
+) -> tuple[Finder | _MetaPathFinder, ModuleSpec]:
+    for finder in _SPEC_FINDERS:
+        finder_instance = finder(search_path)
+        spec = finder_instance.find_module(
+            modname, module_parts, processed, submodule_path
+        )
+        if spec is None:
+            continue
+        return finder_instance, spec
+
+    # Support for custom finders
+    for meta_finder in sys.meta_path:
+        # See if we support the customer import hook of the meta_finder
+        meta_finder_name = meta_finder.__class__.__name__
+        if meta_finder_name not in _MetaPathFinderModuleTypes:
+            # Setuptools>62 creates its EditableFinders dynamically and have
+            # "type" as their __class__.__name__. We check __name__ as well
+            # to see if we can support the finder.
+            try:
+                meta_finder_name = meta_finder.__name__
+            except AttributeError:
+                continue
+            if meta_finder_name not in _MetaPathFinderModuleTypes:
+                continue
+
+        module_type = _MetaPathFinderModuleTypes[meta_finder_name]
+
+        # Meta path finders are supposed to have a find_spec method since
+        # Python 3.4. However, some third-party finders do not implement it.
+        # PEP302 does not refer to find_spec as well.
+        # See: https://github.com/PyCQA/astroid/pull/1752/
+        if not hasattr(meta_finder, "find_spec"):
+            continue
+
+        spec = meta_finder.find_spec(modname, submodule_path)
+        if spec:
+            return (
+                meta_finder,
+                ModuleSpec(
+                    spec.name,
+                    module_type,
+                    spec.origin,
+                    spec.origin,
+                    spec.submodule_search_locations,
+                ),
+            )
+
+    raise ImportError(f"No module named {'.'.join(module_parts)}")
+
+
+def find_spec(modpath: list[str], path: Sequence[str] | None = None) -> ModuleSpec:
+    """Find a spec for the given module.
+
+    :type modpath: list or tuple
+    :param modpath:
+      split module's name (i.e name of a module or package split
+      on '.'), with leading empty strings for explicit relative import
+
+    :type path: list or None
+    :param path:
+      optional list of path where the module or package should be
+      searched (use sys.path if nothing or None is given)
+
+    :rtype: ModuleSpec
+    :return: A module spec, which describes how the module was
+             found and where.
+    """
+    _path = path or sys.path
+
+    # Need a copy for not mutating the argument.
+    modpath = modpath[:]
+
+    submodule_path = None
+    module_parts = modpath[:]
+    processed: list[str] = []
+
+    while modpath:
+        modname = modpath.pop(0)
+        finder, spec = _find_spec_with_path(
+            _path, modname, module_parts, processed, submodule_path or path
+        )
+        processed.append(modname)
+        if modpath:
+            if isinstance(finder, Finder):
+                submodule_path = finder.contribute_to_path(spec, processed)
+            # If modname is a package from an editable install, update submodule_path
+            # so that the next module in the path will be found inside of it using importlib.
+            elif finder.__name__ in _EditableFinderClasses:
+                submodule_path = spec.submodule_search_locations
+
+        if spec.type == ModuleType.PKG_DIRECTORY:
+            spec = spec._replace(submodule_search_locations=submodule_path)
+
+    return spec

+ 108 - 0
venv/lib/python3.11/site-packages/astroid/interpreter/_import/util.py

@@ -0,0 +1,108 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+import pathlib
+import sys
+from functools import lru_cache
+from importlib._bootstrap_external import _NamespacePath
+from importlib.util import _find_spec_from_path  # type: ignore[attr-defined]
+
+from astroid.const import IS_PYPY
+
+
+@lru_cache(maxsize=4096)
+def is_namespace(modname: str) -> bool:
+    from astroid.modutils import (  # pylint: disable=import-outside-toplevel
+        EXT_LIB_DIRS,
+        STD_LIB_DIRS,
+    )
+
+    STD_AND_EXT_LIB_DIRS = STD_LIB_DIRS.union(EXT_LIB_DIRS)
+
+    if modname in sys.builtin_module_names:
+        return False
+
+    found_spec = None
+
+    # find_spec() attempts to import parent packages when given dotted paths.
+    # That's unacceptable here, so we fallback to _find_spec_from_path(), which does
+    # not, but requires instead that each single parent ('astroid', 'nodes', etc.)
+    # be specced from left to right.
+    processed_components = []
+    last_submodule_search_locations: _NamespacePath | None = None
+    for component in modname.split("."):
+        processed_components.append(component)
+        working_modname = ".".join(processed_components)
+        try:
+            # Both the modname and the path are built iteratively, with the
+            # path (e.g. ['a', 'a/b', 'a/b/c']) lagging the modname by one
+            found_spec = _find_spec_from_path(
+                working_modname, path=last_submodule_search_locations
+            )
+        except AttributeError:
+            return False
+        except ValueError:
+            if modname == "__main__":
+                return False
+            try:
+                # .pth files will be on sys.modules
+                # __spec__ is set inconsistently on PyPy so we can't really on the heuristic here
+                # See: https://foss.heptapod.net/pypy/pypy/-/issues/3736
+                # Check first fragment of modname, e.g. "astroid", not "astroid.interpreter"
+                # because of cffi's behavior
+                # See: https://github.com/PyCQA/astroid/issues/1776
+                mod = sys.modules[processed_components[0]]
+                return (
+                    mod.__spec__ is None
+                    and getattr(mod, "__file__", None) is None
+                    and hasattr(mod, "__path__")
+                    and not IS_PYPY
+                )
+            except KeyError:
+                return False
+            except AttributeError:
+                # Workaround for "py" module
+                # https://github.com/pytest-dev/apipkg/issues/13
+                return False
+        except KeyError:
+            # Intermediate steps might raise KeyErrors
+            # https://github.com/python/cpython/issues/93334
+            # TODO: update if fixed in importlib
+            # For tree a > b > c.py
+            # >>> from importlib.machinery import PathFinder
+            # >>> PathFinder.find_spec('a.b', ['a'])
+            # KeyError: 'a'
+
+            # Repair last_submodule_search_locations
+            if last_submodule_search_locations:
+                # TODO: py38: remove except
+                try:
+                    # pylint: disable=unsubscriptable-object
+                    last_item = last_submodule_search_locations[-1]
+                except TypeError:
+                    last_item = last_submodule_search_locations._recalculate()[-1]
+                # e.g. for failure example above, add 'a/b' and keep going
+                # so that find_spec('a.b.c', path=['a', 'a/b']) succeeds
+                assumed_location = pathlib.Path(last_item) / component
+                last_submodule_search_locations.append(str(assumed_location))
+            continue
+
+        # Update last_submodule_search_locations for next iteration
+        if found_spec and found_spec.submodule_search_locations:
+            # But immediately return False if we can detect we are in stdlib
+            # or external lib (e.g site-packages)
+            if any(
+                any(location.startswith(lib_dir) for lib_dir in STD_AND_EXT_LIB_DIRS)
+                for location in found_spec.submodule_search_locations
+            ):
+                return False
+            last_submodule_search_locations = found_spec.submodule_search_locations
+
+    return (
+        found_spec is not None
+        and found_spec.submodule_search_locations is not None
+        and found_spec.origin is None
+    )

+ 66 - 0
venv/lib/python3.11/site-packages/astroid/interpreter/dunder_lookup.py

@@ -0,0 +1,66 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
+
+"""Contains logic for retrieving special methods.
+
+This implementation does not rely on the dot attribute access
+logic, found in ``.getattr()``. The difference between these two
+is that the dunder methods are looked with the type slots
+(you can find more about these here
+http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/)
+As such, the lookup for the special methods is actually simpler than
+the dot attribute access.
+"""
+import itertools
+
+import astroid
+from astroid.exceptions import AttributeInferenceError
+
+
+def _lookup_in_mro(node, name) -> list:
+    attrs = node.locals.get(name, [])
+
+    nodes = itertools.chain.from_iterable(
+        ancestor.locals.get(name, []) for ancestor in node.ancestors(recurs=True)
+    )
+    values = list(itertools.chain(attrs, nodes))
+    if not values:
+        raise AttributeInferenceError(attribute=name, target=node)
+
+    return values
+
+
+def lookup(node, name) -> list:
+    """Lookup the given special method name in the given *node*.
+
+    If the special method was found, then a list of attributes
+    will be returned. Otherwise, `astroid.AttributeInferenceError`
+    is going to be raised.
+    """
+    if isinstance(
+        node, (astroid.List, astroid.Tuple, astroid.Const, astroid.Dict, astroid.Set)
+    ):
+        return _builtin_lookup(node, name)
+    if isinstance(node, astroid.Instance):
+        return _lookup_in_mro(node, name)
+    if isinstance(node, astroid.ClassDef):
+        return _class_lookup(node, name)
+
+    raise AttributeInferenceError(attribute=name, target=node)
+
+
+def _class_lookup(node, name) -> list:
+    metaclass = node.metaclass()
+    if metaclass is None:
+        raise AttributeInferenceError(attribute=name, target=node)
+
+    return _lookup_in_mro(metaclass, name)
+
+
+def _builtin_lookup(node, name) -> list:
+    values = node.locals.get(name, [])
+    if not values:
+        raise AttributeInferenceError(attribute=name, target=node)
+
+    return values

部分文件因文件數量過多而無法顯示