New Upstream Release - astroid

Ready changes

Summary

Merged new upstream version: 2.15.5 (was: 2.14.2).

Diff

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 7e60642..c2c025e 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -13,6 +13,10 @@ env:
   DEFAULT_PYTHON: "3.11"
   PRE_COMMIT_CACHE: ~/.cache/pre-commit
 
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
 jobs:
   base-checks:
     name: Checks
@@ -36,7 +40,7 @@ jobs:
           'requirements_test_pre_commit.txt') }}" >> $GITHUB_OUTPUT
       - name: Restore Python virtual environment
         id: cache-venv
-        uses: actions/cache@v3.2.4
+        uses: actions/cache@v3.2.6
         with:
           path: venv
           key: >-
@@ -56,7 +60,7 @@ jobs:
             hashFiles('.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
       - name: Restore pre-commit environment
         id: cache-precommit
-        uses: actions/cache@v3.2.4
+        uses: actions/cache@v3.2.6
         with:
           path: ${{ env.PRE_COMMIT_CACHE }}
           key: >-
@@ -104,7 +108,7 @@ jobs:
           'requirements_test_pre_commit.txt') }}" >> $GITHUB_OUTPUT
       - name: Restore Python virtual environment
         id: cache-venv
-        uses: actions/cache@v3.2.4
+        uses: actions/cache@v3.2.6
         with:
           path: venv
           key: >-
@@ -158,7 +162,7 @@ jobs:
           'requirements_test_brain.txt') }}" >> $env:GITHUB_OUTPUT
       - name: Restore Python virtual environment
         id: cache-venv
-        uses: actions/cache@v3.2.4
+        uses: actions/cache@v3.2.6
         with:
           path: venv
           key: >-
@@ -207,7 +211,7 @@ jobs:
           }}" >> $GITHUB_OUTPUT
       - name: Restore Python virtual environment
         id: cache-venv
-        uses: actions/cache@v3.2.4
+        uses: actions/cache@v3.2.6
         with:
           path: venv
           key: >-
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 550ec97..51c5665 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -23,6 +23,10 @@ on:
 permissions:
   contents: read
 
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
 jobs:
   analyze:
     name: Analyze
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f3be0c3..9b5cc55 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -10,7 +10,7 @@ repos:
       - id: end-of-file-fixer
         exclude: tests/testdata
   - repo: https://github.com/PyCQA/autoflake
-    rev: v2.0.0
+    rev: v2.0.1
     hooks:
       - id: autoflake
         exclude: tests/testdata|astroid/__init__.py|astroid/scoped_nodes.py|astroid/node_classes.py
@@ -42,9 +42,9 @@ repos:
     rev: v1.1.3
     hooks:
       - id: black-disable-checker
-        exclude: tests/unittest_nodes_lineno.py
+        exclude: tests/test_nodes_lineno.py
   - repo: https://github.com/psf/black
-    rev: 22.12.0
+    rev: 23.1.0
     hooks:
       - id: black
         args: [--safe, --quiet]
@@ -54,7 +54,7 @@ repos:
     hooks:
       - id: flake8
         additional_dependencies:
-          [flake8-bugbear==22.10.27, flake8-typing-imports==1.14.0]
+          [flake8-bugbear==23.2.13, flake8-typing-imports==1.14.0]
         exclude: tests/testdata|doc/conf.py
   - repo: local
     hooks:
@@ -71,7 +71,7 @@ repos:
           ]
         exclude: tests/testdata|conf.py
   - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: v0.991
+    rev: v1.0.1
     hooks:
       - id: mypy
         name: mypy
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 24a11da..685f8a0 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -41,9 +41,9 @@ Contributors
 - 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>
-- Hugo van Kemenade <hugovk@users.noreply.github.com>
 - Alex Hall <alex.mojaki@gmail.com>
 - Raphael Gaschignard <raphael@makeleaps.com>
 - Radosław Ganczarek <radoslaw@ganczarek.in>
@@ -100,6 +100,8 @@ Contributors
 - 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>
@@ -175,6 +177,7 @@ Contributors
 - 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>
diff --git a/ChangeLog b/ChangeLog
index e38393d..f29525c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,14 +2,114 @@
 astroid's ChangeLog
 ===================
 
-What's New in astroid 2.15.0?
+What's New in astroid 2.16.0?
 =============================
 Release date: TBA
 
 
 
+What's New in astroid 2.15.6?
+=============================
+Release date: TBA
+
+
+
+What's New in astroid 2.15.5?
+=============================
+Release date: 2023-05-14
+
+* Handle ``objects.Super`` in ``helpers.object_type()``.
+
+  Refs pylint-dev/pylint#8554
+
+
+What's New in astroid 2.15.4?
+=============================
+Release date: 2023-04-24
+
+* Add visitor function for ``TryStar`` to ``AsStringVisitor`` and
+  add ``TryStar`` to ``astroid.nodes.ALL_NODE_CLASSES``.
+
+  Refs #2142
+
+
+What's New in astroid 2.15.3?
+=============================
+Release date: 2023-04-16
+
+* Fix ``infer_call_result()`` crash on methods called ``with_metaclass()``.
+
+  Closes #1735
+
+* Suppress ``UserWarning`` when finding module specs.
+
+  Closes pylint-dev/pylint#7906
+
+
+What's New in astroid 2.15.2?
+=============================
+Release date: 2023-04-02
+
+* Support more possible usages of ``attrs`` decorators.
+
+  Closes pylint-dev/pylint#7884
+
+
+What's New in astroid 2.15.1?
+=============================
+Release date: 2023-03-26
+
+* Restore behavior of setting a Call as a base for classes created using ``six.with_metaclass()``,
+  and harden support for using enums as metaclasses in this case.
+
+  Reverts #1622
+  Refs PyCQA/pylint#5935
+  Refs PyCQA/pylint#7506
+
+
+What's New in astroid 2.15.0?
+=============================
+Release date: 2023-03-06
+
+* astroid now supports ``TryStar`` nodes from python 3.11 and should be fully compatible with python 3.11.
+
+  Closes #2028
+
+* ``Formattedvalue.postinit`` is now keyword only. This is to allow correct typing of the
+  ``Formattedvalue`` class.
+
+  Refs #1516
+
+* ``Astroid`` now supports custom import hooks.
+
+  Refs PyCQA/pylint#7306
+
+* ``astroid`` now infers return values from match cases.
+
+  Refs PyCQA/pylint#5288
+
+* ``AstroidManager.clear_cache`` now also clears the inference context cache.
+
+  Refs #1780
+
+* ``Astroid`` now retrieves the default values of keyword only arguments and sets them on
+  ``Arguments.kw_defaults``.
+
+* ``Uninferable`` now has the type ``UninferableBase``. This is to facilitate correctly type annotating
+  code that uses this singleton.
+
+  Closes #1680
+
+* Deprecate ``modutils.is_standard_module()``. It will be removed in the next minor release.
+  Functionality has been replaced by two new functions,
+  ``modutils.is_stdlib_module()`` and ``modutils.module_in_path()``.
+
+  Closes #2012
+
 
+* Fix ``are_exclusive`` function when a walrus operator is used inside ``IfExp.test`` field.
 
+  Closes #2022
 
 What's New in astroid 2.14.2?
 =============================
diff --git a/astroid/__init__.py b/astroid/__init__.py
index 605a8b4..bcb0c2c 100644
--- a/astroid/__init__.py
+++ b/astroid/__init__.py
@@ -165,6 +165,7 @@ from astroid.nodes import (  # pylint: disable=redefined-builtin (Ellipsis)
     Subscript,
     TryExcept,
     TryFinally,
+    TryStar,
     Tuple,
     UnaryOp,
     Unknown,
diff --git a/astroid/__pkginfo__.py b/astroid/__pkginfo__.py
index 9742a07..ab8f0f4 100644
--- a/astroid/__pkginfo__.py
+++ b/astroid/__pkginfo__.py
@@ -2,5 +2,5 @@
 # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
 # Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
 
-__version__ = "2.14.2"
+__version__ = "2.15.5"
 version = __version__
diff --git a/astroid/_backport_stdlib_names.py b/astroid/_backport_stdlib_names.py
new file mode 100644
index 0000000..51d6957
--- /dev/null
+++ b/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")
diff --git a/astroid/arguments.py b/astroid/arguments.py
index 8ac83dc..5936995 100644
--- a/astroid/arguments.py
+++ b/astroid/arguments.py
@@ -8,7 +8,7 @@ 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
+from astroid.util import Uninferable, UninferableBase
 
 
 class CallSite:
@@ -44,12 +44,12 @@ class CallSite:
         self._unpacked_kwargs = self._unpack_keywords(keywords, context=context)
 
         self.positional_arguments = [
-            arg for arg in self._unpacked_args if arg is not Uninferable
+            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 value is not Uninferable
+            if not isinstance(value, UninferableBase)
         }
 
     @classmethod
@@ -142,7 +142,7 @@ class CallSite:
                 except StopIteration:
                     continue
 
-                if inferred is Uninferable:
+                if isinstance(inferred, UninferableBase):
                     values.append(Uninferable)
                     continue
                 if not hasattr(inferred, "elts"):
diff --git a/astroid/bases.py b/astroid/bases.py
index e930328..d1972c1 100644
--- a/astroid/bases.py
+++ b/astroid/bases.py
@@ -28,7 +28,7 @@ from astroid.exceptions import (
     NameInferenceError,
 )
 from astroid.typing import InferBinaryOp, InferenceErrorInfo, InferenceResult
-from astroid.util import Uninferable, lazy_descriptor, lazy_import
+from astroid.util import Uninferable, UninferableBase, lazy_descriptor, lazy_import
 
 if sys.version_info >= (3, 8):
     from typing import Literal
@@ -79,7 +79,9 @@ def _is_property(meth, context: InferenceContext | None = None) -> bool:
     if PROPERTIES.intersection(decoratornames):
         return True
     stripped = {
-        name.split(".")[-1] for name in decoratornames if name is not Uninferable
+        name.split(".")[-1]
+        for name in decoratornames
+        if not isinstance(name, UninferableBase)
     }
     if any(name in stripped for name in POSSIBLE_PROPERTIES):
         return True
@@ -89,7 +91,7 @@ def _is_property(meth, context: InferenceContext | None = None) -> bool:
         return False
     for decorator in meth.decorators.nodes or ():
         inferred = helpers.safe_infer(decorator, context=context)
-        if inferred is None or inferred is Uninferable:
+        if inferred is None or isinstance(inferred, UninferableBase):
             continue
         if inferred.__class__.__name__ == "ClassDef":
             for base_class in inferred.bases:
@@ -144,7 +146,7 @@ class Proxy:
 
 
 def _infer_stmts(
-    stmts: Sequence[nodes.NodeNG | type[Uninferable] | Instance],
+    stmts: Sequence[nodes.NodeNG | UninferableBase | Instance],
     context: InferenceContext | None,
     frame: nodes.NodeNG | Instance | None = None,
 ) -> collections.abc.Generator[InferenceResult, None, None]:
@@ -161,7 +163,7 @@ def _infer_stmts(
         context = InferenceContext()
 
     for stmt in stmts:
-        if stmt is Uninferable:
+        if isinstance(stmt, UninferableBase):
             yield stmt
             inferred = True
             continue
@@ -172,8 +174,7 @@ def _infer_stmts(
             for constraint_stmt, potential_constraints in constraints.items():
                 if not constraint_stmt.parent_of(stmt):
                     stmt_constraints.update(potential_constraints)
-            # Mypy doesn't recognize that 'stmt' can't be Uninferable
-            for inf in stmt.infer(context=context):  # type: ignore[union-attr]
+            for inf in stmt.infer(context=context):
                 if all(constraint.satisfied_by(inf) for constraint in stmt_constraints):
                     yield inf
                     inferred = True
@@ -206,7 +207,7 @@ def _infer_method_result_truth(instance, method_name, context):
         try:
             context.callcontext = CallContext(args=[], callee=meth)
             for value in meth.infer_call_result(instance, context=context):
-                if value is Uninferable:
+                if isinstance(value, UninferableBase):
                     return value
                 try:
                     inferred = next(value.infer(context=context))
@@ -316,7 +317,7 @@ class BaseInstance(Proxy):
 
         # Otherwise we infer the call to the __call__ dunder normally
         for node in self._proxied.igetattr("__call__", context):
-            if node is Uninferable or not node.callable():
+            if isinstance(node, UninferableBase) or not node.callable():
                 continue
             for res in node.infer_call_result(caller, context):
                 inferred = True
@@ -458,7 +459,7 @@ class UnboundMethod(Proxy):
         caller: nodes.Call,
         context: InferenceContext,
     ) -> collections.abc.Generator[
-        nodes.Const | Instance | type[Uninferable], None, None
+        nodes.Const | Instance | UninferableBase, None, None
     ]:
         if not caller.args:
             return
@@ -477,7 +478,7 @@ class UnboundMethod(Proxy):
 
         node_context = context.extra_context.get(caller.args[0])
         for inferred in caller.args[0].infer(context=node_context):
-            if inferred is Uninferable:
+            if isinstance(inferred, UninferableBase):
                 yield inferred
             if isinstance(inferred, nodes.ClassDef):
                 yield Instance(inferred)
diff --git a/astroid/brain/brain_attrs.py b/astroid/brain/brain_attrs.py
index acb069e..7afcc8a 100644
--- a/astroid/brain/brain_attrs.py
+++ b/astroid/brain/brain_attrs.py
@@ -8,6 +8,7 @@ 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
@@ -40,6 +41,10 @@ def is_decorated_with_attrs(node, decorator_names=ATTRS_NAMES) -> bool:
             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
 
 
diff --git a/astroid/brain/brain_builtin_inference.py b/astroid/brain/brain_builtin_inference.py
index 02e0ecc..383621d 100644
--- a/astroid/brain/brain_builtin_inference.py
+++ b/astroid/brain/brain_builtin_inference.py
@@ -209,10 +209,10 @@ def _container_generic_inference(node, context, node_type, transform):
             inferred = next(arg.infer(context=context))
         except (InferenceError, StopIteration) as exc:
             raise UseInferenceDefault from exc
-        if inferred is util.Uninferable:
+        if isinstance(inferred, util.UninferableBase):
             raise UseInferenceDefault
         transformed = transform(inferred)
-    if not transformed or transformed is util.Uninferable:
+    if not transformed or isinstance(transformed, util.UninferableBase):
         raise UseInferenceDefault
     return transformed
 
@@ -423,7 +423,9 @@ def infer_super(node, context: InferenceContext | None = None):
         except (InferenceError, StopIteration) as exc:
             raise UseInferenceDefault from exc
 
-    if mro_pointer is util.Uninferable or mro_type is util.Uninferable:
+    if isinstance(mro_pointer, util.UninferableBase) or isinstance(
+        mro_type, util.UninferableBase
+    ):
         # No way we could understand this.
         raise UseInferenceDefault
 
@@ -445,7 +447,7 @@ def _infer_getattr_args(node, context):
     except (InferenceError, StopIteration) as exc:
         raise UseInferenceDefault from exc
 
-    if obj is util.Uninferable or attr is util.Uninferable:
+    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.
@@ -467,8 +469,8 @@ def infer_getattr(node, context: InferenceContext | None = None):
     """
     obj, attr = _infer_getattr_args(node, context)
     if (
-        obj is util.Uninferable
-        or attr is util.Uninferable
+        isinstance(obj, util.UninferableBase)
+        or isinstance(attr, util.UninferableBase)
         or not hasattr(obj, "igetattr")
     ):
         return util.Uninferable
@@ -498,8 +500,8 @@ def infer_hasattr(node, context: InferenceContext | None = None):
     try:
         obj, attr = _infer_getattr_args(node, context)
         if (
-            obj is util.Uninferable
-            or attr is util.Uninferable
+            isinstance(obj, util.UninferableBase)
+            or isinstance(attr, util.UninferableBase)
             or not hasattr(obj, "getattr")
         ):
             return util.Uninferable
@@ -530,7 +532,7 @@ def infer_callable(node, context: InferenceContext | None = None):
         inferred = next(argument.infer(context=context))
     except (InferenceError, StopIteration):
         return util.Uninferable
-    if inferred is util.Uninferable:
+    if isinstance(inferred, util.UninferableBase):
         return util.Uninferable
     return nodes.Const(inferred.callable())
 
@@ -585,11 +587,11 @@ def infer_bool(node, context: InferenceContext | None = None):
         inferred = next(argument.infer(context=context))
     except (InferenceError, StopIteration):
         return util.Uninferable
-    if inferred is util.Uninferable:
+    if isinstance(inferred, util.UninferableBase):
         return util.Uninferable
 
     bool_value = inferred.bool_value(context=context)
-    if bool_value is util.Uninferable:
+    if isinstance(bool_value, util.UninferableBase):
         return util.Uninferable
     return nodes.Const(bool_value)
 
@@ -611,7 +613,7 @@ def infer_slice(node, context: InferenceContext | None = None):
     infer_func = partial(helpers.safe_infer, context=context)
     args = [infer_func(arg) for arg in args]
     for arg in args:
-        if not arg or arg is util.Uninferable:
+        if not arg or isinstance(arg, util.UninferableBase):
             raise UseInferenceDefault
         if not isinstance(arg, nodes.Const):
             raise UseInferenceDefault
@@ -725,7 +727,7 @@ def infer_isinstance(callnode, context: InferenceContext | None = None):
         raise UseInferenceDefault("TypeError: " + str(exc)) from exc
     except MroError as exc:
         raise UseInferenceDefault from exc
-    if isinstance_bool is util.Uninferable:
+    if isinstance(isinstance_bool, util.UninferableBase):
         raise UseInferenceDefault
     return nodes.Const(isinstance_bool)
 
@@ -811,7 +813,7 @@ def infer_int(node, context: InferenceContext | None = None):
         except (InferenceError, StopIteration) as exc:
             raise UseInferenceDefault(str(exc)) from exc
 
-        if first_value is util.Uninferable:
+        if isinstance(first_value, util.UninferableBase):
             raise UseInferenceDefault
 
         if isinstance(first_value, nodes.Const) and isinstance(
@@ -924,7 +926,7 @@ def _is_str_format_call(node: nodes.Call) -> bool:
 
 def _infer_str_format_call(
     node: nodes.Call, context: InferenceContext | None = None
-) -> Iterator[nodes.Const | type[util.Uninferable]]:
+) -> 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):
diff --git a/astroid/brain/brain_dataclasses.py b/astroid/brain/brain_dataclasses.py
index a2e7adf..1397ed1 100644
--- a/astroid/brain/brain_dataclasses.py
+++ b/astroid/brain/brain_dataclasses.py
@@ -25,7 +25,7 @@ from astroid.exceptions import AstroidSyntaxError, InferenceError, UseInferenceD
 from astroid.inference_tip import inference_tip
 from astroid.manager import AstroidManager
 from astroid.typing import InferenceResult
-from astroid.util import Uninferable
+from astroid.util import Uninferable, UninferableBase
 
 if sys.version_info >= (3, 8):
     from typing import Literal
@@ -446,7 +446,7 @@ def _looks_like_dataclass_decorator(
     except (InferenceError, StopIteration):
         inferred = Uninferable
 
-    if inferred is Uninferable:
+    if isinstance(inferred, UninferableBase):
         if isinstance(node, nodes.Name):
             return node.name in decorator_names
         if isinstance(node, nodes.Attribute):
@@ -594,7 +594,7 @@ _INFERABLE_TYPING_TYPES = frozenset(
 
 def _infer_instance_from_annotation(
     node: nodes.NodeNG, ctx: context.InferenceContext | None = None
-) -> Iterator[type[Uninferable] | bases.Instance]:
+) -> Iterator[UninferableBase | bases.Instance]:
     """Infer an instance corresponding to the type annotation represented by node.
 
     Currently has limited support for the typing module.
diff --git a/astroid/brain/brain_fstrings.py b/astroid/brain/brain_fstrings.py
index db7dd95..c0df22e 100644
--- a/astroid/brain/brain_fstrings.py
+++ b/astroid/brain/brain_fstrings.py
@@ -2,13 +2,20 @@
 # 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
-from astroid.nodes.node_classes import FormattedValue
+
+_NodeT = TypeVar("_NodeT", bound=nodes.NodeNG)
 
 
-def _clone_node_with_lineno(node, parent, lineno):
+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
@@ -28,16 +35,22 @@ def _clone_node_with_lineno(node, parent, lineno):
     return new_node
 
 
-def _transform_formatted_value(node):  # pylint: disable=inconsistent-return-statements
+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 = FormattedValue(
+            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, format_spec=node.format_spec)
+            new_node.postinit(
+                value=new_value,
+                conversion=node.conversion,
+                format_spec=node.format_spec,
+            )
             return new_node
 
 
@@ -45,4 +58,4 @@ def _transform_formatted_value(node):  # pylint: disable=inconsistent-return-sta
 # 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(FormattedValue, _transform_formatted_value)
+AstroidManager().register_transform(nodes.FormattedValue, _transform_formatted_value)
diff --git a/astroid/brain/brain_functools.py b/astroid/brain/brain_functools.py
index ffdbc88..f6a9830 100644
--- a/astroid/brain/brain_functools.py
+++ b/astroid/brain/brain_functools.py
@@ -18,7 +18,7 @@ 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 Uninferable
+from astroid.util import UninferableBase
 
 LRU_CACHE = "functools.lru_cache"
 
@@ -84,7 +84,7 @@ def _functools_partial_inference(
         inferred_wrapped_function = next(partial_function.infer(context=context))
     except (InferenceError, StopIteration) as exc:
         raise UseInferenceDefault from exc
-    if inferred_wrapped_function is Uninferable:
+    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")
diff --git a/astroid/brain/brain_namedtuple_enum.py b/astroid/brain/brain_namedtuple_enum.py
index f914162..36b7036 100644
--- a/astroid/brain/brain_namedtuple_enum.py
+++ b/astroid/brain/brain_namedtuple_enum.py
@@ -52,13 +52,13 @@ TYPING_NAMEDTUPLE_BASENAMES: Final = {
 
 
 def _infer_first(node, context):
-    if node is util.Uninferable:
+    if isinstance(node, util.UninferableBase):
         raise UseInferenceDefault
     try:
         value = next(node.infer(context=context))
     except StopIteration as exc:
         raise InferenceError from exc
-    if value is util.Uninferable:
+    if isinstance(value, util.UninferableBase):
         raise UseInferenceDefault()
     return value
 
diff --git a/astroid/brain/brain_six.py b/astroid/brain/brain_six.py
index a35cfdd..0eb945d 100644
--- a/astroid/brain/brain_six.py
+++ b/astroid/brain/brain_six.py
@@ -219,7 +219,6 @@ def transform_six_with_metaclass(node):
     """
     call = node.bases[0]
     node._metaclass = call.args[0]
-    node.bases = call.args[1:]
     return node
 
 
diff --git a/astroid/brain/brain_typing.py b/astroid/brain/brain_typing.py
index b11bfa1..e0a9dfd 100644
--- a/astroid/brain/brain_typing.py
+++ b/astroid/brain/brain_typing.py
@@ -33,7 +33,6 @@ from astroid.nodes.node_classes import (
     Tuple,
 )
 from astroid.nodes.scoped_nodes import ClassDef, FunctionDef
-from astroid.util import Uninferable
 
 if sys.version_info >= (3, 8):
     from typing import Final
@@ -297,7 +296,7 @@ def infer_typing_alias(
         col_offset=assign_name.col_offset,
         parent=node.parent,
     )
-    if res != Uninferable and isinstance(res, ClassDef):
+    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)
diff --git a/astroid/builder.py b/astroid/builder.py
index a03bd98..d115feb 100644
--- a/astroid/builder.py
+++ b/astroid/builder.py
@@ -238,7 +238,7 @@ class AstroidBuilder(raw_building.InspectBuilder):
         try:
             frame = node.frame(future=True)
             for inferred in node.expr.infer():
-                if inferred is util.Uninferable:
+                if isinstance(inferred, util.UninferableBase):
                     continue
                 try:
                     # pylint: disable=unidiomatic-typecheck # We want a narrow check on the
@@ -255,10 +255,7 @@ class AstroidBuilder(raw_building.InspectBuilder):
                         # Const, Tuple or other containers that inherit from
                         # `Instance`
                         continue
-                    elif (
-                        isinstance(inferred, bases.Proxy)
-                        or inferred is util.Uninferable
-                    ):
+                    elif isinstance(inferred, (bases.Proxy, util.UninferableBase)):
                         continue
                     elif inferred.is_function:
                         iattrs = inferred.instance_attrs
diff --git a/astroid/constraint.py b/astroid/constraint.py
index deed9ac..b6dc35c 100644
--- a/astroid/constraint.py
+++ b/astroid/constraint.py
@@ -74,7 +74,7 @@ class NoneConstraint(Constraint):
     def satisfied_by(self, inferred: InferenceResult) -> bool:
         """Return True if this constraint is satisfied by the given inferred value."""
         # Assume true if uninferable
-        if inferred is util.Uninferable:
+        if isinstance(inferred, util.UninferableBase):
             return True
 
         # Return the XOR of self.negate and matches(inferred, self.CONST_NONE)
diff --git a/astroid/decorators.py b/astroid/decorators.py
index b99803a..6bba37b 100644
--- a/astroid/decorators.py
+++ b/astroid/decorators.py
@@ -67,6 +67,7 @@ class cachedproperty:
                 "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__
@@ -214,6 +215,7 @@ if util.check_warnings_filter():  # noqa: C901
                             f" in astroid {astroid_version} "
                             f"('{arg}' should be of type: '{type_annotation}')",
                             DeprecationWarning,
+                            stacklevel=2,
                         )
                 return func(*args, **kwargs)
 
@@ -251,6 +253,7 @@ if util.check_warnings_filter():  # noqa: C901
                             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)
 
diff --git a/astroid/helpers.py b/astroid/helpers.py
index 8ab01b8..bb19bbf 100644
--- a/astroid/helpers.py
+++ b/astroid/helpers.py
@@ -8,7 +8,7 @@ from __future__ import annotations
 
 from collections.abc import Generator
 
-from astroid import bases, manager, nodes, raw_building, util
+from astroid import bases, manager, nodes, objects, raw_building, util
 from astroid.context import CallContext, InferenceContext
 from astroid.exceptions import (
     AstroidTypeError,
@@ -63,9 +63,9 @@ def _object_type(
             yield _build_proxy_class("module", builtins)
         elif isinstance(inferred, nodes.Unknown):
             raise InferenceError
-        elif inferred is util.Uninferable:
+        elif isinstance(inferred, util.UninferableBase):
             yield inferred
-        elif isinstance(inferred, (bases.Proxy, nodes.Slice)):
+        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")
@@ -100,7 +100,7 @@ def _object_type_is_subclass(
     else:
         class_seq = class_or_seq
 
-    if obj_type is util.Uninferable:
+    if isinstance(obj_type, util.UninferableBase):
         return util.Uninferable
 
     # Instances are not types
@@ -112,7 +112,7 @@ def _object_type_is_subclass(
     # issubclass(type, (object, 1)) evaluates to true
     # issubclass(object, (1, type)) raises TypeError
     for klass in class_seq:
-        if klass is util.Uninferable:
+        if isinstance(klass, util.UninferableBase):
             raise AstroidTypeError("arg 2 must be a type or tuple of types")
 
         for obj_subclass in obj_type.mro():
@@ -131,7 +131,7 @@ def object_isinstance(node, class_or_seq, context: InferenceContext | None = Non
     :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
     """
     obj_type = object_type(node, context)
-    if obj_type is util.Uninferable:
+    if isinstance(obj_type, util.UninferableBase):
         return util.Uninferable
     return _object_type_is_subclass(obj_type, class_or_seq, context=context)
 
@@ -275,7 +275,7 @@ def object_len(node, context: InferenceContext | None = None):
         )
         raise InferenceError(message)
 
-    if inferred_node is None or inferred_node is util.Uninferable:
+    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)
@@ -300,7 +300,7 @@ def object_len(node, context: InferenceContext | None = None):
         ) from e
 
     inferred = len_call.infer_call_result(node, context)
-    if inferred is util.Uninferable:
+    if isinstance(inferred, util.UninferableBase):
         raise InferenceError(node=node, context=context)
     result_of_len = next(inferred, None)
     if (
diff --git a/astroid/inference.py b/astroid/inference.py
index dd0a487..65d03d3 100644
--- a/astroid/inference.py
+++ b/astroid/inference.py
@@ -268,7 +268,7 @@ def infer_call(
         callcontext.extra_context = _populate_context_lookup(self, context.clone())
 
     for callee in self.func.infer(context):
-        if callee is util.Uninferable:
+        if isinstance(callee, util.UninferableBase):
             yield callee
             continue
         try:
@@ -356,7 +356,7 @@ def infer_attribute(
 ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
     """Infer an Attribute node by using getattr on the associated object."""
     for owner in self.expr.infer(context):
-        if owner is util.Uninferable:
+        if isinstance(owner, util.UninferableBase):
             yield owner
             continue
 
@@ -424,11 +424,11 @@ def infer_subscript(
 
     found_one = False
     for value in self.value.infer(context):
-        if value is util.Uninferable:
+        if isinstance(value, util.UninferableBase):
             yield util.Uninferable
             return None
         for index in self.slice.infer(context):
-            if index is util.Uninferable:
+            if isinstance(index, util.UninferableBase):
                 yield util.Uninferable
                 return None
 
@@ -459,7 +459,7 @@ def infer_subscript(
 
             # Prevent inferring if the inferred subscript
             # is the same as the original subscripted object.
-            if self is assigned or assigned is util.Uninferable:
+            if self is assigned or isinstance(assigned, util.UninferableBase):
                 yield util.Uninferable
                 return None
             yield from assigned.infer(context)
@@ -502,13 +502,13 @@ def _infer_boolop(
         return None
 
     for pair in itertools.product(*inferred_values):
-        if any(item is util.Uninferable for item in pair):
+        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(item is util.Uninferable for item in bool_values):
+        if any(isinstance(item, util.UninferableBase) for item in bool_values):
             # Can't infer the final result, just yield Uninferable.
             yield util.Uninferable
             continue
@@ -575,7 +575,7 @@ def _infer_unaryop(
                 # value and negate its result, unless it is
                 # Uninferable, which will be returned as is.
                 bool_value = operand.bool_value()
-                if bool_value is not util.Uninferable:
+                if not isinstance(bool_value, util.UninferableBase):
                     yield nodes.const_factory(not bool_value)
                 else:
                     yield util.Uninferable
@@ -595,7 +595,10 @@ def _infer_unaryop(
 
                     meth = methods[0]
                     inferred = next(meth.infer(context=context), None)
-                    if inferred is util.Uninferable or not inferred.callable():
+                    if (
+                        isinstance(inferred, util.UninferableBase)
+                        or not inferred.callable()
+                    ):
                         continue
 
                     context = copy_context(context)
@@ -639,7 +642,7 @@ def _is_not_implemented(const) -> bool:
 
 def _infer_old_style_string_formatting(
     instance: nodes.Const, other: nodes.NodeNG, context: InferenceContext
-) -> tuple[type[util.Uninferable] | nodes.Const]:
+) -> tuple[util.UninferableBase | nodes.Const]:
     """Infer the result of '"string" % ...'.
 
     TODO: Instead of returning Uninferable we should rely
@@ -699,7 +702,7 @@ def _invoke_binop_inference(
         inferred = next(method.infer(context=context))
     except StopIteration as e:
         raise InferenceError(node=method, context=context) from e
-    if inferred is util.Uninferable:
+    if isinstance(inferred, util.UninferableBase):
         raise InferenceError
     if not isinstance(
         instance, (nodes.Const, nodes.Tuple, nodes.List, nodes.ClassDef, bases.Instance)
@@ -923,7 +926,7 @@ def _infer_binary_operation(
             yield util.Uninferable
             return
         else:
-            if any(result is util.Uninferable for result in results):
+            if any(isinstance(result, util.UninferableBase) for result in results):
                 yield util.Uninferable
                 return
 
@@ -959,7 +962,7 @@ def _infer_binop(
     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(value is util.Uninferable for value in (rhs, lhs)):
+        if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
             # Don't know how to process this.
             yield util.Uninferable
             return
@@ -1008,7 +1011,7 @@ def _to_literal(node: nodes.NodeNG) -> Any:
 
 def _do_compare(
     left_iter: Iterable[nodes.NodeNG], op: str, right_iter: Iterable[nodes.NodeNG]
-) -> bool | type[util.Uninferable]:
+) -> bool | util.UninferableBase:
     """
     If all possible combinations are either True or False, return that:
     >>> _do_compare([1, 2], '<=', [3, 4])
@@ -1027,7 +1030,9 @@ def _do_compare(
     op_func = COMPARE_OPS[op]
 
     for left, right in itertools.product(left_iter, right_iter):
-        if left is util.Uninferable or right is util.Uninferable:
+        if isinstance(left, util.UninferableBase) or isinstance(
+            right, util.UninferableBase
+        ):
             return util.Uninferable
 
         try:
@@ -1052,9 +1057,9 @@ def _do_compare(
 
 def _infer_compare(
     self: nodes.Compare, context: InferenceContext | None = None, **kwargs: Any
-) -> Generator[nodes.Const | type[util.Uninferable], None, None]:
+) -> Generator[nodes.Const | util.UninferableBase, None, None]:
     """Chained comparison inference logic."""
-    retval: bool | type[util.Uninferable] = True
+    retval: bool | util.UninferableBase = True
 
     ops = self.ops
     left_node = self.left
@@ -1091,7 +1096,7 @@ def _infer_augassign(
     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(value is util.Uninferable for value in (rhs, lhs)):
+        if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
             # Don't know how to process this.
             yield util.Uninferable
             return
@@ -1216,7 +1221,7 @@ def infer_ifexp(
     except (InferenceError, StopIteration):
         both_branches = True
     else:
-        if test is not util.Uninferable:
+        if not isinstance(test, util.UninferableBase):
             if test.bool_value():
                 yield from self.body.infer(context=lhs_context)
             else:
diff --git a/astroid/inference_tip.py b/astroid/inference_tip.py
index 957cd04..9f315a5 100644
--- a/astroid/inference_tip.py
+++ b/astroid/inference_tip.py
@@ -11,16 +11,11 @@ from collections.abc import Iterator
 
 import wrapt
 
-from astroid import bases, util
 from astroid.exceptions import InferenceOverwriteError, UseInferenceDefault
 from astroid.nodes import NodeNG
-from astroid.typing import InferFn
+from astroid.typing import InferenceResult, InferFn
 
-InferOptions = typing.Union[
-    NodeNG, bases.Instance, bases.UnboundMethod, typing.Type[util.Uninferable]
-]
-
-_cache: dict[tuple[InferFn, NodeNG], list[InferOptions] | None] = {}
+_cache: dict[tuple[InferFn, NodeNG], list[InferenceResult] | None] = {}
 
 
 def clear_inference_tip_cache() -> None:
@@ -31,7 +26,7 @@ def clear_inference_tip_cache() -> None:
 @wrapt.decorator
 def _inference_tip_cached(
     func: InferFn, instance: None, args: typing.Any, kwargs: typing.Any
-) -> Iterator[InferOptions]:
+) -> Iterator[InferenceResult]:
     """Cache decorator used for inference tips."""
     node = args[0]
     try:
diff --git a/astroid/interpreter/_import/spec.py b/astroid/interpreter/_import/spec.py
index ecf330b..5a7c4e8 100644
--- a/astroid/interpreter/_import/spec.py
+++ b/astroid/interpreter/_import/spec.py
@@ -12,6 +12,8 @@ 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
@@ -23,9 +25,21 @@ from astroid.modutils import EXT_LIB_DIRS
 from . import util
 
 if sys.version_info >= (3, 8):
-    from typing import Literal
+    from typing import Literal, Protocol
 else:
-    from typing_extensions import Literal
+    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):
@@ -43,6 +57,20 @@ class ModuleType(enum.Enum):
     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.
 
@@ -120,10 +148,14 @@ class ImportlibFinder(Finder):
             )
         else:
             try:
-                spec = importlib.util.find_spec(modname)
+                with warnings.catch_warnings():
+                    warnings.filterwarnings("ignore", category=UserWarning)
+                    spec = importlib.util.find_spec(modname)
                 if (
-                    spec and spec.loader is importlib.machinery.FrozenImporter
-                ):  # noqa: E501 # type: ignore[comparison-overlap]
+                    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,
@@ -226,7 +258,6 @@ class ZipFinder(Finder):
         super().__init__(path)
         for entry_path in path:
             if entry_path not in sys.path_importer_cache:
-                # pylint: disable=no-member
                 try:
                     sys.path_importer_cache[entry_path] = zipimport.zipimporter(  # type: ignore[assignment]
                         entry_path
@@ -310,7 +341,6 @@ def _is_setuptools_namespace(location: pathlib.Path) -> bool:
 
 def _get_zipimporters() -> Iterator[tuple[str, zipimport.zipimporter]]:
     for filepath, importer in sys.path_importer_cache.items():
-        # pylint: disable-next=no-member
         if isinstance(importer, zipimport.zipimporter):
             yield filepath, importer
 
@@ -349,7 +379,7 @@ def _find_spec_with_path(
     module_parts: list[str],
     processed: list[str],
     submodule_path: Sequence[str] | None,
-) -> tuple[Finder, ModuleSpec]:
+) -> tuple[Finder | _MetaPathFinder, ModuleSpec]:
     for finder in _SPEC_FINDERS:
         finder_instance = finder(search_path)
         spec = finder_instance.find_module(
@@ -359,6 +389,43 @@ def _find_spec_with_path(
             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)}")
 
 
@@ -395,7 +462,12 @@ def find_spec(modpath: list[str], path: Sequence[str] | None = None) -> ModuleSp
         )
         processed.append(modname)
         if modpath:
-            submodule_path = finder.contribute_to_path(spec, processed)
+            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)
diff --git a/astroid/interpreter/objectmodel.py b/astroid/interpreter/objectmodel.py
index 491358a..b7dac7f 100644
--- a/astroid/interpreter/objectmodel.py
+++ b/astroid/interpreter/objectmodel.py
@@ -358,7 +358,7 @@ class FunctionModel(ObjectModel):
                 except StopIteration as e:
                     raise InferenceError(context=context, node=caller.args[0]) from e
 
-                if cls is astroid.Uninferable:
+                if isinstance(cls, util.UninferableBase):
                     raise InferenceError(
                         "Invalid class inferred", target=self, context=context
                     )
diff --git a/astroid/manager.py b/astroid/manager.py
index 8a5b05c..965dd5a 100644
--- a/astroid/manager.py
+++ b/astroid/manager.py
@@ -20,7 +20,7 @@ from typing import Any, ClassVar
 from astroid import nodes
 from astroid._cache import CACHE_MANAGER
 from astroid.const import BRAIN_MODULES_DIRECTORY
-from astroid.context import InferenceContext
+from astroid.context import InferenceContext, _invalidate_cache
 from astroid.exceptions import AstroidBuildingError, AstroidImportError
 from astroid.interpreter._import import spec, util
 from astroid.modutils import (
@@ -30,7 +30,7 @@ from astroid.modutils import (
     get_source_file,
     is_module_name_part_of_extension_package_whitelist,
     is_python_source,
-    is_standard_module,
+    is_stdlib_module,
     load_module_from_name,
     modpath_from_file,
 )
@@ -154,7 +154,7 @@ class AstroidManager:
     def _can_load_extension(self, modname: str) -> bool:
         if self.always_load_extensions:
             return True
-        if is_standard_module(modname):
+        if is_stdlib_module(modname):
             return True
         return is_module_name_part_of_extension_package_whitelist(
             modname, self.extension_package_whitelist
@@ -407,7 +407,7 @@ class AstroidManager:
         raw_building._astroid_bootstrapping()
 
     def clear_cache(self) -> None:
-        """Clear the underlying cache, bootstrap the builtins module and
+        """Clear the underlying caches, bootstrap the builtins module and
         re-register transforms.
         """
         # import here because of cyclic imports
@@ -418,6 +418,7 @@ class AstroidManager:
         from astroid.nodes.scoped_nodes import ClassDef
 
         clear_inference_tip_cache()
+        _invalidate_cache()  # inference context cache
 
         self.astroid_cache.clear()
         # NB: not a new TransformVisitor()
diff --git a/astroid/mixins.py b/astroid/mixins.py
index d7fc1de..942e824 100644
--- a/astroid/mixins.py
+++ b/astroid/mixins.py
@@ -27,4 +27,5 @@ __all__ = (
 warnings.warn(
     "The 'astroid.mixins' module is deprecated and will become private in astroid 3.0.0",
     DeprecationWarning,
+    stacklevel=2,
 )
diff --git a/astroid/modutils.py b/astroid/modutils.py
index 1b5057f..f05b5f8 100644
--- a/astroid/modutils.py
+++ b/astroid/modutils.py
@@ -26,14 +26,20 @@ import os
 import sys
 import sysconfig
 import types
+import warnings
 from collections.abc import Callable, Iterable, Sequence
 from contextlib import redirect_stderr, redirect_stdout
 from functools import lru_cache
 from pathlib import Path
 
-from astroid.const import IS_JYTHON, IS_PYPY
+from astroid.const import IS_JYTHON, IS_PYPY, PY310_PLUS
 from astroid.interpreter._import import spec, util
 
+if PY310_PLUS:
+    from sys import stdlib_module_names
+else:
+    from astroid._backport_stdlib_names import stdlib_module_names
+
 logger = logging.getLogger(__name__)
 
 
@@ -510,6 +516,41 @@ def is_python_source(filename: str | None) -> bool:
     return os.path.splitext(filename)[1][1:] in PY_SOURCE_EXTS
 
 
+def is_stdlib_module(modname: str) -> bool:
+    """Return: True if the modname is in the standard library"""
+    return modname.split(".")[0] in stdlib_module_names
+
+
+def module_in_path(modname: str, path: str | Iterable[str]) -> bool:
+    """Try to determine if a module is imported from one of the specified paths
+
+    :param modname: name of the module
+
+    :param path: paths to consider
+
+    :return:
+      true if the module:
+      - is located on the path listed in one of the directory in `paths`
+    """
+
+    modname = modname.split(".")[0]
+    try:
+        filename = file_from_modpath([modname])
+    except ImportError:
+        # Import failed, we can't check path if we don't know it
+        return False
+
+    if filename is None:
+        # No filename likely means it's compiled in, or potentially a namespace
+        return False
+    filename = _normalize_path(filename)
+
+    if isinstance(path, str):
+        return filename.startswith(_cache_normalize_path(path))
+
+    return any(filename.startswith(_cache_normalize_path(entry)) for entry in path)
+
+
 def is_standard_module(modname: str, std_path: Iterable[str] | None = None) -> bool:
     """Try to guess if a module is a standard python module (by default,
     see `std_path` parameter's description).
@@ -523,6 +564,12 @@ def is_standard_module(modname: str, std_path: Iterable[str] | None = None) -> b
       - is located on the path listed in one of the directory in `std_path`
       - is a built-in module
     """
+    warnings.warn(
+        "is_standard_module() is deprecated. Use, is_stdlib_module() or module_in_path() instead",
+        DeprecationWarning,
+        stacklevel=2,
+    )
+
     modname = modname.split(".")[0]
     try:
         filename = file_from_modpath([modname])
diff --git a/astroid/node_classes.py b/astroid/node_classes.py
index 59bb010..6470963 100644
--- a/astroid/node_classes.py
+++ b/astroid/node_classes.py
@@ -76,6 +76,7 @@ from astroid.nodes.node_classes import (  # pylint: disable=redefined-builtin (E
     Subscript,
     TryExcept,
     TryFinally,
+    TryStar,
     Tuple,
     UnaryOp,
     Unknown,
@@ -95,4 +96,5 @@ warnings.warn(
     "The 'astroid.node_classes' module is deprecated and will be replaced by "
     "'astroid.nodes' in astroid 3.0.0",
     DeprecationWarning,
+    stacklevel=2,
 )
diff --git a/astroid/nodes/__init__.py b/astroid/nodes/__init__.py
index 68ddad7..157aa24 100644
--- a/astroid/nodes/__init__.py
+++ b/astroid/nodes/__init__.py
@@ -84,6 +84,7 @@ from astroid.nodes.node_classes import (  # pylint: disable=redefined-builtin (E
     Subscript,
     TryExcept,
     TryFinally,
+    TryStar,
     Tuple,
     UnaryOp,
     Unknown,
@@ -196,6 +197,7 @@ ALL_NODE_CLASSES = (
     Subscript,
     TryExcept,
     TryFinally,
+    TryStar,
     Tuple,
     UnaryOp,
     Unknown,
@@ -290,6 +292,7 @@ __all__ = (
     "Subscript",
     "TryExcept",
     "TryFinally",
+    "TryStar",
     "Tuple",
     "UnaryOp",
     "Unknown",
diff --git a/astroid/nodes/as_string.py b/astroid/nodes/as_string.py
index cbd5ee1..ae55ab8 100644
--- a/astroid/nodes/as_string.py
+++ b/astroid/nodes/as_string.py
@@ -9,6 +9,8 @@ from __future__ import annotations
 from collections.abc import Iterator
 from typing import TYPE_CHECKING
 
+from astroid import nodes
+
 if TYPE_CHECKING:
     from astroid.nodes import Const
     from astroid.nodes.node_classes import (
@@ -254,13 +256,16 @@ class AsStringVisitor:
         return ""
 
     def visit_excepthandler(self, node) -> str:
+        n = "except"
+        if isinstance(getattr(node, "parent", None), nodes.TryStar):
+            n = "except*"
         if node.type:
             if node.name:
-                excs = f"except {node.type.accept(self)} as {node.name.accept(self)}"
+                excs = f"{n} {node.type.accept(self)} as {node.name.accept(self)}"
             else:
-                excs = f"except {node.type.accept(self)}"
+                excs = f"{n} {node.type.accept(self)}"
         else:
-            excs = "except"
+            excs = f"{n}"
         return f"{excs}:\n{self._stmt_list(node.body)}"
 
     def visit_empty(self, node) -> str:
@@ -495,6 +500,17 @@ class AsStringVisitor:
             self._stmt_list(node.body), self._stmt_list(node.finalbody)
         )
 
+    def visit_trystar(self, node) -> str:
+        """return an astroid.TryStar node as string"""
+        trys = [f"try:\n{self._stmt_list(node.body)}"]
+        for handler in node.handlers:
+            trys.append(handler.accept(self))
+        if node.orelse:
+            trys.append(f"else:\n{self._stmt_list(node.orelse)}")
+        if node.finalbody:
+            trys.append(f"finally:\n{self._stmt_list(node.finalbody)}")
+        return "\n".join(trys)
+
     def visit_tuple(self, node) -> str:
         """return an astroid.Tuple node as string"""
         if len(node.elts) == 1:
diff --git a/astroid/nodes/node_classes.py b/astroid/nodes/node_classes.py
index c8834d0..b7772c3 100644
--- a/astroid/nodes/node_classes.py
+++ b/astroid/nodes/node_classes.py
@@ -101,7 +101,7 @@ def unpack_infer(stmt, context: InferenceContext | None = None):
         return {"node": stmt, "context": context}
     # else, infer recursively, except Uninferable object that should be returned as is
     for inferred in stmt.infer(context):
-        if inferred is util.Uninferable:
+        if isinstance(inferred, util.UninferableBase):
             yield inferred
         else:
             yield from unpack_infer(inferred, context)
@@ -137,10 +137,14 @@ def are_exclusive(stmt1, stmt2, exceptions: list[str] | None = None) -> bool:
             # if the common parent is a If or TryExcept statement, look if
             # nodes are in exclusive branches
             if isinstance(node, If) and exceptions is None:
-                if (
-                    node.locate_child(previous)[1]
-                    is not node.locate_child(children[node])[1]
-                ):
+                c2attr, c2node = node.locate_child(previous)
+                c1attr, c1node = node.locate_child(children[node])
+                if "test" in (c1attr, c2attr):
+                    # If any node is `If.test`, then it must be inclusive with
+                    # the other node (`If.body` and `If.orelse`)
+                    return False
+                if c1attr != c2attr:
+                    # different `If` branches (`If.body` and `If.orelse`)
                     return True
             elif isinstance(node, TryExcept):
                 c2attr, c2node = node.locate_child(previous)
@@ -650,8 +654,12 @@ class Arguments(_base_nodes.AssignTypeNode):
         self.posonlyargs: list[AssignName] = []
         """The arguments that can only be passed positionally."""
 
-        self.kw_defaults: list[NodeNG | None]
-        """The default values for keyword arguments that cannot be passed positionally."""
+        self.kw_defaults: list[NodeNG | None] | None
+        """
+        The default values for keyword arguments that cannot be passed positionally.
+
+        See .args for why this can be None.
+        """
 
         self.annotations: list[NodeNG | None]
         """The type annotations of arguments that can be passed positionally."""
@@ -695,7 +703,7 @@ class Arguments(_base_nodes.AssignTypeNode):
         args: list[AssignName] | None,
         defaults: list[NodeNG] | None,
         kwonlyargs: list[AssignName],
-        kw_defaults: list[NodeNG | None],
+        kw_defaults: list[NodeNG | None] | None,
         annotations: list[NodeNG | None],
         posonlyargs: list[AssignName] | None = None,
         kwonlyargs_annotations: list[NodeNG | None] | None = None,
@@ -979,7 +987,7 @@ class Arguments(_base_nodes.AssignTypeNode):
             yield from self.defaults
         yield from self.kwonlyargs
 
-        for elt in self.kw_defaults:
+        for elt in self.kw_defaults or ():
             if elt is not None:
                 yield elt
 
@@ -2457,7 +2465,7 @@ class Dict(NodeNG, Instance):
                     continue
 
             for inferredkey in key.infer(context):
-                if inferredkey is util.Uninferable:
+                if isinstance(inferredkey, util.UninferableBase):
                     continue
                 if isinstance(inferredkey, Const) and isinstance(index, Const):
                     if inferredkey.value == index.value:
@@ -3205,6 +3213,7 @@ class If(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
             "It has been moved to pylint and can be imported from 'pylint.checkers.utils' "
             "starting with pylint 2.12",
             DeprecationWarning,
+            stacklevel=2,
         )
         if isinstance(self.test, Compare):
             value = self.test.left
@@ -3232,6 +3241,7 @@ class If(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
             "It has been moved to pylint and can be imported from 'pylint.checkers.utils' "
             "starting with pylint 2.12",
             DeprecationWarning,
+            stacklevel=2,
         )
         return isinstance(
             self.test, (Name, Attribute)
@@ -4206,6 +4216,107 @@ class TryFinally(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
         yield from self.finalbody
 
 
+class TryStar(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
+    """Class representing an :class:`ast.TryStar` node."""
+
+    _astroid_fields = ("body", "handlers", "orelse", "finalbody")
+    _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")
+
+    def __init__(
+        self,
+        *,
+        lineno: int | None = None,
+        col_offset: int | None = None,
+        end_lineno: int | None = None,
+        end_col_offset: int | None = None,
+        parent: NodeNG | None = None,
+    ) -> None:
+        """
+        :param lineno: The line that this node appears on in the source code.
+        :param col_offset: The column that this node appears on in the
+            source code.
+        :param parent: The parent node in the syntax tree.
+        :param end_lineno: The last line this node appears on in the source code.
+        :param end_col_offset: The end column this node appears on in the
+            source code. Note: This is after the last symbol.
+        """
+        self.body: list[NodeNG] = []
+        """The contents of the block to catch exceptions from."""
+
+        self.handlers: list[ExceptHandler] = []
+        """The exception handlers."""
+
+        self.orelse: list[NodeNG] = []
+        """The contents of the ``else`` block."""
+
+        self.finalbody: list[NodeNG] = []
+        """The contents of the ``finally`` block."""
+
+        super().__init__(
+            lineno=lineno,
+            col_offset=col_offset,
+            end_lineno=end_lineno,
+            end_col_offset=end_col_offset,
+            parent=parent,
+        )
+
+    def postinit(
+        self,
+        *,
+        body: list[NodeNG] | None = None,
+        handlers: list[ExceptHandler] | None = None,
+        orelse: list[NodeNG] | None = None,
+        finalbody: list[NodeNG] | None = None,
+    ) -> None:
+        """Do some setup after initialisation.
+        :param body: The contents of the block to catch exceptions from.
+        :param handlers: The exception handlers.
+        :param orelse: The contents of the ``else`` block.
+        :param finalbody: The contents of the ``finally`` block.
+        """
+        if body:
+            self.body = body
+        if handlers:
+            self.handlers = handlers
+        if orelse:
+            self.orelse = orelse
+        if finalbody:
+            self.finalbody = finalbody
+
+    def _infer_name(self, frame, name):
+        return name
+
+    def block_range(self, lineno: int) -> tuple[int, int]:
+        """Get a range from a given line number to where this node ends."""
+        if lineno == self.fromlineno:
+            return lineno, lineno
+        if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno:
+            # Inside try body - return from lineno till end of try body
+            return lineno, self.body[-1].tolineno
+        for exhandler in self.handlers:
+            if exhandler.type and lineno == exhandler.type.fromlineno:
+                return lineno, lineno
+            if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
+                return lineno, exhandler.body[-1].tolineno
+        if self.orelse:
+            if self.orelse[0].fromlineno - 1 == lineno:
+                return lineno, lineno
+            if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno:
+                return lineno, self.orelse[-1].tolineno
+        if self.finalbody:
+            if self.finalbody[0].fromlineno - 1 == lineno:
+                return lineno, lineno
+            if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno:
+                return lineno, self.finalbody[-1].tolineno
+        return lineno, self.tolineno
+
+    def get_children(self):
+        yield from self.body
+        yield from self.handlers
+        yield from self.orelse
+        yield from self.finalbody
+
+
 class Tuple(BaseContainer):
     """Class representing an :class:`ast.Tuple` node.
 
@@ -4692,20 +4803,18 @@ class FormattedValue(NodeNG):
         self.value: NodeNG
         """The value to be formatted into the string."""
 
-        self.conversion: int | None = None  # can be None
+        self.conversion: int
         """The type of formatting to be applied to the value.
 
         .. seealso::
             :class:`ast.FormattedValue`
         """
 
-        self.format_spec: NodeNG | None = None  # can be None
+        self.format_spec: JoinedStr | None = None
         """The formatting to be applied to the value.
 
         .. seealso::
             :class:`ast.FormattedValue`
-
-        :type: JoinedStr or None
         """
 
         super().__init__(
@@ -4718,9 +4827,10 @@ class FormattedValue(NodeNG):
 
     def postinit(
         self,
+        *,
         value: NodeNG,
-        conversion: int | None = None,
-        format_spec: NodeNG | None = None,
+        conversion: int,
+        format_spec: JoinedStr | None = None,
     ) -> None:
         """Do some setup after initialisation.
 
@@ -4951,13 +5061,11 @@ class EvaluatedObject(NodeNG):
     _astroid_fields = ("original",)
     _other_fields = ("value",)
 
-    def __init__(
-        self, original: NodeNG, value: NodeNG | type[util.Uninferable]
-    ) -> None:
+    def __init__(self, original: NodeNG, value: NodeNG | util.UninferableBase) -> None:
         self.original: NodeNG = original
         """The original node that has already been evaluated"""
 
-        self.value: NodeNG | type[util.Uninferable] = value
+        self.value: NodeNG | util.UninferableBase = value
         """The inferred value"""
 
         super().__init__(
@@ -4968,14 +5076,14 @@ class EvaluatedObject(NodeNG):
 
     def _infer(
         self, context: InferenceContext | None = None, **kwargs: Any
-    ) -> Generator[NodeNG | type[util.Uninferable], None, None]:
+    ) -> Generator[NodeNG | util.UninferableBase, None, None]:
         yield self.value
 
 
 # Pattern matching #######################################################
 
 
-class Match(_base_nodes.Statement):
+class Match(_base_nodes.Statement, _base_nodes.MultiLineBlockNode):
     """Class representing a :class:`ast.Match` node.
 
     >>> import astroid
@@ -4991,6 +5099,7 @@ class Match(_base_nodes.Statement):
     """
 
     _astroid_fields = ("subject", "cases")
+    _multi_line_block_fields = ("cases",)
 
     def __init__(
         self,
diff --git a/astroid/nodes/node_ng.py b/astroid/nodes/node_ng.py
index 38172f5..3f8222e 100644
--- a/astroid/nodes/node_ng.py
+++ b/astroid/nodes/node_ng.py
@@ -321,6 +321,7 @@ class NodeNG:
                 "This behaviour can already be triggered "
                 "by passing 'future=True' to a statement() call.",
                 DeprecationWarning,
+                stacklevel=2,
             )
             raise AttributeError(f"{self} object has no attribute 'parent'")
         return self.parent.statement(future=future)
@@ -344,6 +345,7 @@ class NodeNG:
                 "This behaviour can already be triggered "
                 "by passing 'future=True' to a frame() call.",
                 DeprecationWarning,
+                stacklevel=2,
             )
             raise AttributeError(f"{self} object has no attribute 'parent'")
 
@@ -588,7 +590,7 @@ class NodeNG:
         yield from ()
 
     def _infer_name(self, frame, name):
-        # overridden for ImportFrom, Import, Global, TryExcept and Arguments
+        # overridden for ImportFrom, Import, Global, TryExcept, TryStar and Arguments
         pass
 
     def _infer(
diff --git a/astroid/nodes/scoped_nodes/scoped_nodes.py b/astroid/nodes/scoped_nodes/scoped_nodes.py
index 411fe8b..5945f05 100644
--- a/astroid/nodes/scoped_nodes/scoped_nodes.py
+++ b/astroid/nodes/scoped_nodes/scoped_nodes.py
@@ -315,6 +315,7 @@ class Module(LocalsDictNodeNG):
             "The 'Module.doc' attribute is deprecated, "
             "use 'Module.doc_node' instead.",
             DeprecationWarning,
+            stacklevel=2,
         )
         return self._doc
 
@@ -324,6 +325,7 @@ class Module(LocalsDictNodeNG):
             "Setting the 'Module.doc' attribute is deprecated, "
             "use 'Module.doc_node' instead.",
             DeprecationWarning,
+            stacklevel=2,
         )
         self._doc = value
 
@@ -474,6 +476,7 @@ class Module(LocalsDictNodeNG):
             "considered a statement. This behaviour can already be triggered "
             "by passing 'future=True' to a statement() call.",
             DeprecationWarning,
+            stacklevel=2,
         )
         return self
 
@@ -1403,6 +1406,7 @@ class FunctionDef(_base_nodes.MultiLineBlockNode, _base_nodes.Statement, Lambda)
             "The 'FunctionDef.doc' attribute is deprecated, "
             "use 'FunctionDef.doc_node' instead.",
             DeprecationWarning,
+            stacklevel=2,
         )
         return self._doc
 
@@ -1412,6 +1416,7 @@ class FunctionDef(_base_nodes.MultiLineBlockNode, _base_nodes.Statement, Lambda)
             "Setting the 'FunctionDef.doc' attribute is deprecated, "
             "use 'FunctionDef.doc_node' instead.",
             DeprecationWarning,
+            stacklevel=2,
         )
         self._doc = value
 
@@ -1692,20 +1697,40 @@ class FunctionDef(_base_nodes.MultiLineBlockNode, _base_nodes.Statement, Lambda)
         # generators, and filter it out later.
         if (
             self.name == "with_metaclass"
+            and caller is not None
             and len(self.args.args) == 1
             and self.args.vararg is not None
         ):
-            metaclass = next(caller.args[0].infer(context), None)
+            if isinstance(caller.args, Arguments):
+                metaclass = next(caller.args.args[0].infer(context), None)
+            elif isinstance(caller.args, list):
+                metaclass = next(caller.args[0].infer(context), None)
+            else:
+                raise TypeError(  # pragma: no cover
+                    f"caller.args was neither Arguments nor list; got {type(caller.args)}"
+                )
             if isinstance(metaclass, ClassDef):
                 try:
-                    class_bases = [next(arg.infer(context)) for arg in caller.args[1:]]
+                    class_bases = [
+                        # Find the first non-None inferred base value
+                        next(
+                            b
+                            for b in arg.infer(context=context.clone())
+                            if not (isinstance(b, Const) and b.value is None)
+                        )
+                        for arg in caller.args[1:]
+                    ]
                 except StopIteration as e:
                     raise InferenceError(node=caller.args[1:], context=context) from e
                 new_class = ClassDef(name="temporary_class")
                 new_class.hide = True
                 new_class.parent = self
                 new_class.postinit(
-                    bases=[base for base in class_bases if base != util.Uninferable],
+                    bases=[
+                        base
+                        for base in class_bases
+                        if not isinstance(base, util.UninferableBase)
+                    ],
                     body=[],
                     decorators=[],
                     metaclass=metaclass,
@@ -1826,8 +1851,6 @@ def _is_metaclass(klass, seen=None) -> bool:
                 if isinstance(baseobj, bases.Instance):
                     # not abstract
                     return False
-                if baseobj is util.Uninferable:
-                    continue
                 if baseobj is klass:
                     continue
                 if not isinstance(baseobj, ClassDef):
@@ -2036,6 +2059,7 @@ class ClassDef(
             "The 'ClassDef.doc' attribute is deprecated, "
             "use 'ClassDef.doc_node' instead.",
             DeprecationWarning,
+            stacklevel=2,
         )
         return self._doc
 
@@ -2045,6 +2069,7 @@ class ClassDef(
             "Setting the 'ClassDef.doc' attribute is deprecated, "
             "use 'ClassDef.doc_node.value' instead.",
             DeprecationWarning,
+            stacklevel=2,
         )
         self._doc = value
 
@@ -2817,7 +2842,7 @@ class ClassDef(
                 return next(
                     node
                     for node in self._metaclass.infer(context=context)
-                    if node is not util.Uninferable
+                    if not isinstance(node, util.UninferableBase)
                 )
             except (InferenceError, StopIteration):
                 return None
@@ -2883,7 +2908,7 @@ class ClassDef(
                 values = [item[0] for item in slots.items]
             else:
                 values = slots.itered()
-            if values is util.Uninferable:
+            if isinstance(values, util.UninferableBase):
                 continue
             if not values:
                 # Stop the iteration, because the class
@@ -2893,8 +2918,6 @@ class ClassDef(
             for elt in values:
                 try:
                     for inferred in elt.infer():
-                        if inferred is util.Uninferable:
-                            continue
                         if not isinstance(
                             inferred, node_classes.Const
                         ) or not isinstance(inferred.value, str):
diff --git a/astroid/protocols.py b/astroid/protocols.py
index 72549b7..dcc9e2b 100644
--- a/astroid/protocols.py
+++ b/astroid/protocols.py
@@ -132,7 +132,7 @@ def const_infer_binary_op(
     other: InferenceResult,
     context: InferenceContext,
     _: SuccessfulInferenceResult,
-) -> Generator[ConstFactoryResult | type[util.Uninferable], None, None]:
+) -> Generator[ConstFactoryResult | util.UninferableBase, None, None]:
     not_implemented = nodes.Const(NotImplemented)
     if isinstance(other, nodes.Const):
         if (
@@ -174,7 +174,7 @@ def _multiply_seq_by_int(
     filtered_elts = (
         helpers.safe_infer(elt, context) or util.Uninferable
         for elt in self.elts
-        if elt is not util.Uninferable
+        if not isinstance(elt, util.UninferableBase)
     )
     node.elts = list(filtered_elts) * other.value
     return node
@@ -184,11 +184,11 @@ def _filter_uninferable_nodes(
     elts: Sequence[InferenceResult], context: InferenceContext
 ) -> Iterator[SuccessfulInferenceResult]:
     for elt in elts:
-        if elt is util.Uninferable:
+        if isinstance(elt, util.UninferableBase):
             yield nodes.Unknown()
         else:
             for inferred in elt.infer(context):
-                if inferred is not util.Uninferable:
+                if not isinstance(inferred, util.UninferableBase):
                     yield inferred
                 else:
                     yield nodes.Unknown()
@@ -202,7 +202,7 @@ def tl_infer_binary_op(
     other: InferenceResult,
     context: InferenceContext,
     method: SuccessfulInferenceResult,
-) -> Generator[_TupleListNodeT | nodes.Const | type[util.Uninferable], None, None]:
+) -> Generator[_TupleListNodeT | nodes.Const | util.UninferableBase, None, None]:
     """Infer a binary operation on a tuple or list.
 
     The instance on which the binary operation is performed is a tuple
@@ -276,7 +276,7 @@ def _resolve_looppart(parts, assign_path, context):
     assign_path = assign_path[:]
     index = assign_path.pop(0)
     for part in parts:
-        if part is util.Uninferable:
+        if isinstance(part, util.UninferableBase):
             continue
         if not hasattr(part, "itered"):
             continue
@@ -299,7 +299,7 @@ def _resolve_looppart(parts, assign_path, context):
                 # we achieved to resolved the assignment path,
                 # don't infer the last part
                 yield assigned
-            elif assigned is util.Uninferable:
+            elif isinstance(assigned, util.UninferableBase):
                 break
             else:
                 # we are not yet on the last part of the path
@@ -546,7 +546,7 @@ def _resolve_assignment_parts(parts, assign_path, context):
             # we achieved to resolved the assignment path, don't infer the
             # last part
             yield assigned
-        elif assigned is util.Uninferable:
+        elif isinstance(assigned, util.UninferableBase):
             return
         else:
             # we are not yet on the last part of the path search on each
@@ -793,7 +793,7 @@ def starred_assigned_stmts(  # noqa: C901
         except (InferenceError, StopIteration):
             yield util.Uninferable
             return
-        if rhs is util.Uninferable or not hasattr(rhs, "itered"):
+        if isinstance(rhs, util.UninferableBase) or not hasattr(rhs, "itered"):
             yield util.Uninferable
             return
 
@@ -841,7 +841,7 @@ def starred_assigned_stmts(  # noqa: C901
         except (InferenceError, StopIteration):
             yield util.Uninferable
             return
-        if inferred_iterable is util.Uninferable or not hasattr(
+        if isinstance(inferred_iterable, util.UninferableBase) or not hasattr(
             inferred_iterable, "itered"
         ):
             yield util.Uninferable
diff --git a/astroid/raw_building.py b/astroid/raw_building.py
index 453ce8b..4409a63 100644
--- a/astroid/raw_building.py
+++ b/astroid/raw_building.py
@@ -117,6 +117,7 @@ def build_function(
     defaults: list[Any] | None = None,
     doc: str | None = None,
     kwonlyargs: list[str] | None = None,
+    kwonlydefaults: list[Any] | None = None,
 ) -> nodes.FunctionDef:
     """create and initialize an astroid FunctionDef node"""
     # first argument is now a list of decorators
@@ -140,13 +141,22 @@ def build_function(
     else:
         default_nodes = None
 
+    kwonlydefault_nodes: list[nodes.NodeNG | None] | None = []
+    if kwonlydefaults is not None:
+        for kwonlydefault in kwonlydefaults:
+            kwonlydefault_node = nodes.const_factory(kwonlydefault)
+            kwonlydefault_node.parent = argsnode
+            kwonlydefault_nodes.append(kwonlydefault_node)
+    else:
+        kwonlydefault_nodes = None
+
     argsnode.postinit(
         args=arguments,
         defaults=default_nodes,
         kwonlyargs=[
             nodes.AssignName(name=arg, parent=argsnode) for arg in kwonlyargs or ()
         ],
-        kw_defaults=[],
+        kw_defaults=kwonlydefault_nodes,
         annotations=[],
         posonlyargs=[
             nodes.AssignName(name=arg, parent=argsnode) for arg in posonlyargs or ()
@@ -200,7 +210,7 @@ def object_build_class(
 
 def _get_args_info_from_callable(
     member: _FunctionTypes,
-) -> tuple[list[str], list[str], list[Any], list[str]]:
+) -> tuple[list[str], list[str], list[Any], list[str], list[Any]]:
     """Returns args, posonlyargs, defaults, kwonlyargs.
 
     :note: currently ignores the return annotation.
@@ -210,6 +220,7 @@ def _get_args_info_from_callable(
     defaults: list[Any] = []
     posonlyargs: list[str] = []
     kwonlyargs: list[str] = []
+    kwonlydefaults: list[Any] = []
 
     for param_name, param in signature.parameters.items():
         if param.kind == inspect.Parameter.POSITIONAL_ONLY:
@@ -222,17 +233,26 @@ def _get_args_info_from_callable(
             args.append(param_name)
         elif param.kind == inspect.Parameter.KEYWORD_ONLY:
             kwonlyargs.append(param_name)
-        if param.default is not inspect._empty:
+            if param.default is not inspect.Parameter.empty:
+                kwonlydefaults.append(param.default)
+            continue
+        if param.default is not inspect.Parameter.empty:
             defaults.append(param.default)
 
-    return args, posonlyargs, defaults, kwonlyargs
+    return args, posonlyargs, defaults, kwonlyargs, kwonlydefaults
 
 
 def object_build_function(
     node: nodes.Module | nodes.ClassDef, member: _FunctionTypes, localname: str
 ) -> None:
     """create astroid for a living function object"""
-    args, posonlyargs, defaults, kwonlyargs = _get_args_info_from_callable(member)
+    (
+        args,
+        posonlyargs,
+        defaults,
+        kwonlyargs,
+        kwonly_defaults,
+    ) = _get_args_info_from_callable(member)
 
     func = build_function(
         getattr(member, "__name__", None) or localname,
@@ -241,6 +261,7 @@ def object_build_function(
         defaults,
         member.__doc__,
         kwonlyargs=kwonlyargs,
+        kwonlydefaults=kwonly_defaults,
     )
 
     node.add_local_node(func, localname)
diff --git a/astroid/rebuilder.py b/astroid/rebuilder.py
index f0acac3..0d409c4 100644
--- a/astroid/rebuilder.py
+++ b/astroid/rebuilder.py
@@ -507,6 +507,12 @@ class TreeRebuilder:
         ) -> nodes.TryExcept | nodes.TryFinally:
             ...
 
+        if sys.version_info >= (3, 11):
+
+            @overload
+            def visit(self, node: ast.TryStar, parent: NodeNG) -> nodes.TryStar:
+                ...
+
         @overload
         def visit(self, node: ast.Tuple, parent: NodeNG) -> nodes.Tuple:
             ...
@@ -1442,9 +1448,9 @@ class TreeRebuilder:
             parent=parent,
         )
         newnode.postinit(
-            self.visit(node.value, newnode),
-            node.conversion,
-            self.visit(node.format_spec, newnode),
+            value=self.visit(node.value, newnode),
+            conversion=node.conversion,
+            format_spec=self.visit(node.format_spec, newnode),
         )
         return newnode
 
@@ -1822,6 +1828,22 @@ class TreeRebuilder:
             return self.visit_tryexcept(node, parent)
         return None
 
+    def visit_trystar(self, node: ast.TryStar, parent: NodeNG) -> nodes.TryStar:
+        newnode = nodes.TryStar(
+            lineno=node.lineno,
+            col_offset=node.col_offset,
+            end_lineno=getattr(node, "end_lineno", None),
+            end_col_offset=getattr(node, "end_col_offset", None),
+            parent=parent,
+        )
+        newnode.postinit(
+            body=[self.visit(n, newnode) for n in node.body],
+            handlers=[self.visit(n, newnode) for n in node.handlers],
+            orelse=[self.visit(n, newnode) for n in node.orelse],
+            finalbody=[self.visit(n, newnode) for n in node.finalbody],
+        )
+        return newnode
+
     def visit_tuple(self, node: ast.Tuple, parent: NodeNG) -> nodes.Tuple:
         """Visit a Tuple node by returning a fresh instance of it."""
         context = self._get_context(node)
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 1e3fbf3..0e5ef13 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -31,4 +31,5 @@ warnings.warn(
     "The 'astroid.scoped_nodes' module is deprecated and will be replaced by "
     "'astroid.nodes' in astroid 3.0.0",
     DeprecationWarning,
+    stacklevel=2,
 )
diff --git a/astroid/typing.py b/astroid/typing.py
index 990c988..62d8995 100644
--- a/astroid/typing.py
+++ b/astroid/typing.py
@@ -46,7 +46,7 @@ class AstroidManagerBrain(TypedDict):
     _transform: transforms.TransformVisitor
 
 
-InferenceResult = Union["nodes.NodeNG", "type[util.Uninferable]", "bases.Proxy"]
+InferenceResult = Union["nodes.NodeNG", "util.UninferableBase", "bases.Proxy"]
 SuccessfulInferenceResult = Union["nodes.NodeNG", "bases.Proxy"]
 
 ConstFactoryResult = Union[
diff --git a/astroid/util.py b/astroid/util.py
index 39fba92..20ff810 100644
--- a/astroid/util.py
+++ b/astroid/util.py
@@ -2,6 +2,9 @@
 # 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 importlib
 import sys
 import warnings
@@ -10,9 +13,9 @@ from typing import Any
 import lazy_object_proxy
 
 if sys.version_info >= (3, 8):
-    from typing import Literal
+    from typing import Final, Literal
 else:
-    from typing_extensions import Literal
+    from typing_extensions import Final, Literal
 
 
 def lazy_descriptor(obj):
@@ -29,11 +32,13 @@ def lazy_import(module_name: str) -> lazy_object_proxy.Proxy:
     )
 
 
-@object.__new__
-class Uninferable:
-    """Special inference object, which is returned when inference fails."""
+class UninferableBase:
+    """Special inference object, which is returned when inference fails.
+
+    This is meant to be used as a singleton. Use astroid.util.Uninferable to access it.
+    """
 
-    def __repr__(self) -> str:
+    def __repr__(self) -> Literal["Uninferable"]:
         return "Uninferable"
 
     __str__ = __repr__
@@ -47,7 +52,7 @@ class Uninferable:
             return object.__getattribute__(self, name)
         return self
 
-    def __call__(self, *args, **kwargs):
+    def __call__(self, *args: Any, **kwargs: Any) -> UninferableBase:
         return self
 
     def __bool__(self) -> Literal[False]:
@@ -59,6 +64,9 @@ class Uninferable:
         return visitor.visit_uninferable(self)
 
 
+Uninferable: Final = UninferableBase()
+
+
 class BadOperationMessage:
     """Object which describes a TypeError occurred somewhere in the inference chain.
 
@@ -82,7 +90,7 @@ class BadUnaryOperationMessage(BadOperationMessage):
 
     def _object_type(self, obj):
         objtype = self._object_type_helper(obj)
-        if objtype is Uninferable:
+        if isinstance(objtype, UninferableBase):
             return None
 
         return objtype
diff --git a/debian/changelog b/debian/changelog
index a71eec5..f92bbe0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+astroid (2.15.5-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sat, 10 Jun 2023 02:13:12 -0000
+
 astroid (2.14.2-1) unstable; urgency=medium
 
   * New upstream release
diff --git a/doc/api/astroid.nodes.rst b/doc/api/astroid.nodes.rst
index 7372cdd..3e99e93 100644
--- a/doc/api/astroid.nodes.rst
+++ b/doc/api/astroid.nodes.rst
@@ -80,6 +80,7 @@ Nodes
    astroid.nodes.Subscript
    astroid.nodes.TryExcept
    astroid.nodes.TryFinally
+   astroid.nodes.TryStar
    astroid.nodes.Tuple
    astroid.nodes.UnaryOp
    astroid.nodes.Unknown
@@ -230,6 +231,8 @@ Nodes
 
 .. autoclass:: astroid.nodes.TryFinally
 
+.. autoclass:: astroid.nodes.TryStar
+
 .. autoclass:: astroid.nodes.Tuple
 
 .. autoclass:: astroid.nodes.UnaryOp
diff --git a/doc/api/base_nodes.rst b/doc/api/base_nodes.rst
index 068c7bb..6253ce5 100644
--- a/doc/api/base_nodes.rst
+++ b/doc/api/base_nodes.rst
@@ -5,34 +5,31 @@ These are abstract node classes that :ref:`other nodes <nodes>` inherit from.
 
 .. autosummary::
 
-   astroid._base_nodes.AssignTypeNode
+   astroid.nodes._base_nodes.AssignTypeNode
    astroid.nodes.BaseContainer
-   astroid._base_nodes.MultiLineWithElseBlockNode
+   astroid.nodes._base_nodes.MultiLineWithElseBlockNode
    astroid.nodes.ComprehensionScope
-   astroid._base_nodes.FilterStmtsBaseNode
-   astroid._base_nodes.ImportNode
-   astroid.nodes.ListComp
-   astroid.nodes.LocalsDictNodeNG
+   astroid.nodes._base_nodes.FilterStmtsBaseNode
+   astroid.nodes._base_nodes.ImportNode
+     astroid.nodes.LocalsDictNodeNG
    astroid.nodes.node_classes.LookupMixIn
    astroid.nodes.NodeNG
-   astroid._base_nodes.ParentAssignNode
+   astroid.nodes._base_nodes.ParentAssignNode
    astroid.nodes.Statement
    astroid.nodes.Pattern
 
 
-.. autoclass:: astroid._base_nodes.AssignTypeNode
+.. autoclass:: astroid.nodes._base_nodes.AssignTypeNode
 
 .. autoclass:: astroid.nodes.BaseContainer
 
-.. autoclass:: astroid._base_nodes.MultiLineWithElseBlockNode
+.. autoclass:: astroid.nodes._base_nodes.MultiLineWithElseBlockNode
 
 .. autoclass:: astroid.nodes.ComprehensionScope
 
-.. autoclass:: astroid._base_nodes.FilterStmtsBaseNode
+.. autoclass:: astroid.nodes._base_nodes.FilterStmtsBaseNode
 
-.. autoclass:: astroid._base_nodes.ImportNode
-
-.. autoclass:: astroid.nodes.ListComp
+.. autoclass:: astroid.nodes._base_nodes.ImportNode
 
 .. autoclass:: astroid.nodes.LocalsDictNodeNG
 
@@ -40,7 +37,7 @@ These are abstract node classes that :ref:`other nodes <nodes>` inherit from.
 
 .. autoclass:: astroid.nodes.NodeNG
 
-.. autoclass:: astroid._base_nodes.ParentAssignNode
+.. autoclass:: astroid.nodes._base_nodes.ParentAssignNode
 
 .. autoclass:: astroid.nodes.Statement
 
diff --git a/doc/conf.py b/doc/conf.py
index 8c5a03a..d9e4254 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -55,7 +55,8 @@ master_doc = "index"
 # General information about the project.
 project = "Astroid"
 current_year = datetime.utcnow().year
-copyright = f"2003-{current_year}, Logilab, PyCQA and contributors"
+contributors = "Logilab, and astroid contributors"
+copyright = f"2003-{current_year}, {contributors}"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -202,7 +203,7 @@ latex_documents = [
         "index",
         "Astroid.tex",
         "Astroid Documentation",
-        "Logilab, PyCQA and contributors",
+        contributors,
         "manual",
     ),
 ]
@@ -240,7 +241,7 @@ man_pages = [
         "index",
         "astroid",
         "Astroid Documentation",
-        ["Logilab, PyCQA and contributors"],
+        [contributors],
         1,
     )
 ]
diff --git a/doc/inference.rst b/doc/inference.rst
index 8d2c7e4..d66ea5e 100644
--- a/doc/inference.rst
+++ b/doc/inference.rst
@@ -22,10 +22,10 @@ In both cases the :meth:`infer` must return a *generator* which iterates
 through the various *values* the node could take.
 
 In some case the value yielded will not be a node found in the AST of the node
-but an instance of a special inference class such as :class:`Uninferable`,
+but an instance of a special inference class such as :obj:`Uninferable`,
 or :class:`Instance`.
 
-Namely, the special singleton :obj:`Uninferable()` is yielded when the inference reaches
+Namely, the special singleton :obj:`Uninferable` is yielded when the inference reaches
 a point where it can't follow the code and is so unable to guess a value; and
 instances of the :class:`Instance` class are yielded when the current node is
 inferred to be an instance of some known class.
diff --git a/pyproject.toml b/pyproject.toml
index 3fac032..b49ee2f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,5 @@
 [build-system]
-requires = ["setuptools~=62.6", "wheel~=0.37.1"]
+requires = ["setuptools>=64.0", "wheel>=0.37.1"]
 build-backend = "setuptools.build_meta"
 
 [project]
@@ -7,9 +7,6 @@ name        = "astroid"
 license     = {text = "LGPL-2.1-or-later"}
 description = "An abstract syntax tree for Python with inference support."
 readme      = "README.rst"
-authors     = [
-    {name = "Python Code Quality Authority", email = "code-quality@python.org"}
-]
 keywords    = ["static code analysis", "python", "abstract syntax tree"]
 classifiers = [
     "Development Status :: 6 - Mature",
@@ -24,6 +21,7 @@ classifiers = [
     "Programming Language :: Python :: 3.8",
     "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
     "Programming Language :: Python :: Implementation :: CPython",
     "Programming Language :: Python :: Implementation :: PyPy",
     "Topic :: Software Development :: Libraries :: Python Modules",
@@ -41,7 +39,7 @@ dependencies = [
 dynamic = ["version"]
 
 [project.urls]
-"Docs"           = "https://pylint.pycqa.org/projects/astroid/en/latest/"
+"Docs"           = "https://pylint.readthedocs.io/projects/astroid/en/latest/"
 "Source Code"    = "https://github.com/PyCQA/astroid"
 "Bug tracker"    = "https://github.com/PyCQA/astroid/issues"
 "Discord server" = "https://discord.gg/Egy6P8AMB5"
diff --git a/requirements_test.txt b/requirements_test.txt
index 48f5cc8..6e32914 100644
--- a/requirements_test.txt
+++ b/requirements_test.txt
@@ -1,7 +1,6 @@
 -r requirements_test_min.txt
 -r requirements_test_pre_commit.txt
 contributors-txt>=0.7.4
-pre-commit~=2.21
 tbump~=6.9.0
 types-typed-ast; implementation_name=="cpython" and python_version<"3.8"
 types-pkg_resources==0.1.3
diff --git a/requirements_test_brain.txt b/requirements_test_brain.txt
index b4778ba..2014e07 100644
--- a/requirements_test_brain.txt
+++ b/requirements_test_brain.txt
@@ -8,5 +8,5 @@ regex
 types-python-dateutil
 six
 types-six
-urllib3
+urllib3>1,<2
 typing_extensions>=4.4.0
diff --git a/requirements_test_min.txt b/requirements_test_min.txt
index 4a2be5e..6f4db81 100644
--- a/requirements_test_min.txt
+++ b/requirements_test_min.txt
@@ -1,3 +1,3 @@
-coverage~=7.1
+coverage~=7.2
 pytest
 pytest-cov~=4.0
diff --git a/requirements_test_pre_commit.txt b/requirements_test_pre_commit.txt
index 829b327..371936b 100644
--- a/requirements_test_pre_commit.txt
+++ b/requirements_test_pre_commit.txt
@@ -1,7 +1,9 @@
-black==23.1a1
-pylint==2.15.10
+-r requirements_test_min.txt
+black==23.1.0
+pylint==2.16.2
 isort==5.12.0;python_version>='3.8'
 flake8==6.0.0;python_version>='3.8'
 flake8-typing-imports==1.14.0;python_version>='3.8'
-flake8-bugbear==22.10.27;python_version>='3.8'
-mypy==0.991
+flake8-bugbear==23.2.13;python_version>='3.8'
+mypy==1.0.1
+pre-commit~=2.21
diff --git a/setup.cfg b/setup.cfg
index 19ab335..a0fa37c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -13,7 +13,10 @@ license_files =
 # E203; Incompatible with black see https://github.com/psf/black/issues/315
 # W503; Incompatible with black
 # B901; Combine yield and return statements in one function
-extend-ignore = F401, E203, W503, B901
+# B905; We still support python 3.9
+# B906; Flake8 plugin specific check that is always going to be a false positive in pylint
+# B907; Not worth a refactor
+extend-ignore = F401, E203, W503, B901, B905, B906, B907
 max-line-length = 110
 select = B,C,E,F,W,T4,B9
 # Required for flake8-typing-imports (v1.12.0)
diff --git a/tbump.toml b/tbump.toml
index 2fa235b..492ff3c 100644
--- a/tbump.toml
+++ b/tbump.toml
@@ -1,7 +1,7 @@
 github_url = "https://github.com/PyCQA/astroid"
 
 [version]
-current = "2.14.2"
+current = "2.15.5"
 regex = '''
 ^(?P<major>0|[1-9]\d*)
 \.
diff --git a/tests/brain/__init__.py b/tests/brain/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/brain/numpy/__init__.py b/tests/brain/numpy/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/unittest_brain_numpy_core_einsumfunc.py b/tests/brain/numpy/test_core_einsumfunc.py
similarity index 100%
rename from tests/unittest_brain_numpy_core_einsumfunc.py
rename to tests/brain/numpy/test_core_einsumfunc.py
diff --git a/tests/unittest_brain_numpy_core_fromnumeric.py b/tests/brain/numpy/test_core_fromnumeric.py
similarity index 97%
rename from tests/unittest_brain_numpy_core_fromnumeric.py
rename to tests/brain/numpy/test_core_fromnumeric.py
index 09589f5..4fa2099 100644
--- a/tests/unittest_brain_numpy_core_fromnumeric.py
+++ b/tests/brain/numpy/test_core_fromnumeric.py
@@ -44,7 +44,3 @@ class BrainNumpyCoreFromNumericTest(unittest.TestCase):
                     inferred_values[-1].pytype() in licit_array_types,
                     msg=f"Illicit type for {func_[0]:s} ({inferred_values[-1].pytype()})",
                 )
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_brain_numpy_core_function_base.py b/tests/brain/numpy/test_core_function_base.py
similarity index 97%
rename from tests/unittest_brain_numpy_core_function_base.py
rename to tests/brain/numpy/test_core_function_base.py
index f66fc94..1a59f4d 100644
--- a/tests/unittest_brain_numpy_core_function_base.py
+++ b/tests/brain/numpy/test_core_function_base.py
@@ -50,7 +50,3 @@ class BrainNumpyCoreFunctionBaseTest(unittest.TestCase):
                         func_[0], inferred_values[-1].pytype()
                     ),
                 )
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_brain_numpy_core_multiarray.py b/tests/brain/numpy/test_core_multiarray.py
similarity index 99%
rename from tests/unittest_brain_numpy_core_multiarray.py
rename to tests/brain/numpy/test_core_multiarray.py
index c4818a4..0e0f9f7 100644
--- a/tests/unittest_brain_numpy_core_multiarray.py
+++ b/tests/brain/numpy/test_core_multiarray.py
@@ -188,7 +188,3 @@ class BrainNumpyCoreMultiarrayTest(unittest.TestCase):
                             func_[0], inferred_values[-1].pytype()
                         ),
                     )
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_brain_numpy_core_numeric.py b/tests/brain/numpy/test_core_numeric.py
similarity index 98%
rename from tests/unittest_brain_numpy_core_numeric.py
rename to tests/brain/numpy/test_core_numeric.py
index e2b51fe..2481ece 100644
--- a/tests/unittest_brain_numpy_core_numeric.py
+++ b/tests/brain/numpy/test_core_numeric.py
@@ -76,7 +76,3 @@ def test_function_parameters(method: str, expected_args: list[str]) -> None:
     )
     actual_args = instance.inferred()[0].args.args
     assert [arg.name for arg in actual_args] == expected_args
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_brain_numpy_core_numerictypes.py b/tests/brain/numpy/test_core_numerictypes.py
similarity index 99%
rename from tests/unittest_brain_numpy_core_numerictypes.py
rename to tests/brain/numpy/test_core_numerictypes.py
index 40679fc..3cf053e 100644
--- a/tests/unittest_brain_numpy_core_numerictypes.py
+++ b/tests/brain/numpy/test_core_numerictypes.py
@@ -409,7 +409,3 @@ class NumpyBrainUtilsTest(unittest.TestCase):
         node = builder.extract_node(src)
         cls_node = node.inferred()[0]
         self.assertIs(cls_node, Uninferable)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_brain_numpy_core_umath.py b/tests/brain/numpy/test_core_umath.py
similarity index 99%
rename from tests/unittest_brain_numpy_core_umath.py
rename to tests/brain/numpy/test_core_umath.py
index 9a03119..d34c0f1 100644
--- a/tests/unittest_brain_numpy_core_umath.py
+++ b/tests/brain/numpy/test_core_umath.py
@@ -239,7 +239,3 @@ class NumpyBrainCoreUmathTest(unittest.TestCase):
                             f" as a ndarray and not as {effective_infer}"
                         ),
                     )
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_brain_numpy_ma.py b/tests/brain/numpy/test_ma.py
similarity index 100%
rename from tests/unittest_brain_numpy_ma.py
rename to tests/brain/numpy/test_ma.py
diff --git a/tests/unittest_brain_numpy_ndarray.py b/tests/brain/numpy/test_ndarray.py
similarity index 99%
rename from tests/unittest_brain_numpy_ndarray.py
rename to tests/brain/numpy/test_ndarray.py
index 1f778b0..1fe0f1e 100644
--- a/tests/unittest_brain_numpy_ndarray.py
+++ b/tests/brain/numpy/test_ndarray.py
@@ -168,7 +168,3 @@ class NumpyBrainNdarrayTest(unittest.TestCase):
         cls_node = node.inferred()[0]
         self.assertIsInstance(cls_node, nodes.ClassDef)
         self.assertEqual(cls_node.name, "ndarray")
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_brain_numpy_random_mtrand.py b/tests/brain/numpy/test_random_mtrand.py
similarity index 98%
rename from tests/unittest_brain_numpy_random_mtrand.py
rename to tests/brain/numpy/test_random_mtrand.py
index 665d1de..ff3270f 100644
--- a/tests/unittest_brain_numpy_random_mtrand.py
+++ b/tests/brain/numpy/test_random_mtrand.py
@@ -99,7 +99,3 @@ class NumpyBrainRandomMtrandTest(unittest.TestCase):
                     default.value for default in inferred.args.defaults
                 ]
                 self.assertEqual(default_args_values, exact_kwargs_default_values)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_brain_argparse.py b/tests/brain/test_argparse.py
similarity index 100%
rename from tests/unittest_brain_argparse.py
rename to tests/brain/test_argparse.py
diff --git a/tests/brain/test_attr.py b/tests/brain/test_attr.py
new file mode 100644
index 0000000..0648109
--- /dev/null
+++ b/tests/brain/test_attr.py
@@ -0,0 +1,221 @@
+# 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 unittest
+
+import astroid
+from astroid import nodes
+
+try:
+    import attr as attr_module  # pylint: disable=unused-import
+
+    HAS_ATTR = True
+except ImportError:
+    HAS_ATTR = False
+
+
+@unittest.skipUnless(HAS_ATTR, "These tests require the attr library")
+class AttrsTest(unittest.TestCase):
+    def test_attr_transform(self) -> None:
+        module = astroid.parse(
+            """
+        import attr
+        from attr import attrs, attrib, field
+
+        @attr.s
+        class Foo:
+
+            d = attr.ib(attr.Factory(dict))
+
+        f = Foo()
+        f.d['answer'] = 42
+
+        @attr.s(slots=True)
+        class Bar:
+            d = attr.ib(attr.Factory(dict))
+
+        g = Bar()
+        g.d['answer'] = 42
+
+        @attrs
+        class Bah:
+            d = attrib(attr.Factory(dict))
+
+        h = Bah()
+        h.d['answer'] = 42
+
+        @attr.attrs
+        class Bai:
+            d = attr.attrib(attr.Factory(dict))
+
+        i = Bai()
+        i.d['answer'] = 42
+
+        @attr.define
+        class Spam:
+            d = field(default=attr.Factory(dict))
+
+        j = Spam(d=1)
+        j.d['answer'] = 42
+
+        @attr.mutable
+        class Eggs:
+            d = attr.field(default=attr.Factory(dict))
+
+        k = Eggs(d=1)
+        k.d['answer'] = 42
+
+        @attr.frozen
+        class Eggs:
+            d = attr.field(default=attr.Factory(dict))
+
+        l = Eggs(d=1)
+        l.d['answer'] = 42
+        """
+        )
+
+        for name in ("f", "g", "h", "i", "j", "k", "l"):
+            should_be_unknown = next(module.getattr(name)[0].infer()).getattr("d")[0]
+            self.assertIsInstance(should_be_unknown, astroid.Unknown)
+
+    def test_attrs_transform(self) -> None:
+        """Test brain for decorators of the 'attrs' package.
+
+        Package added support for 'attrs' a long side 'attr' in v21.3.0.
+        See: https://github.com/python-attrs/attrs/releases/tag/21.3.0
+        """
+        module = astroid.parse(
+            """
+        import attrs
+        from attrs import field, mutable, frozen, define
+        from attrs import mutable as my_mutable
+
+        @attrs.define
+        class Foo:
+
+            d = attrs.field(attrs.Factory(dict))
+
+        f = Foo()
+        f.d['answer'] = 42
+
+        @attrs.define(slots=True)
+        class Bar:
+            d = field(attrs.Factory(dict))
+
+        g = Bar()
+        g.d['answer'] = 42
+
+        @attrs.mutable
+        class Bah:
+            d = field(attrs.Factory(dict))
+
+        h = Bah()
+        h.d['answer'] = 42
+
+        @attrs.frozen
+        class Bai:
+            d = attrs.field(attrs.Factory(dict))
+
+        i = Bai()
+        i.d['answer'] = 42
+
+        @attrs.define
+        class Spam:
+            d = field(default=attrs.Factory(dict))
+
+        j = Spam(d=1)
+        j.d['answer'] = 42
+
+        @attrs.mutable
+        class Eggs:
+            d = attrs.field(default=attrs.Factory(dict))
+
+        k = Eggs(d=1)
+        k.d['answer'] = 42
+
+        @attrs.frozen
+        class Eggs:
+            d = attrs.field(default=attrs.Factory(dict))
+
+        l = Eggs(d=1)
+        l.d['answer'] = 42
+
+
+        @frozen
+        class Legs:
+            d = attrs.field(default=attrs.Factory(dict))
+
+        m = Legs(d=1)
+        m.d['answer'] = 42
+
+        @define
+        class FooBar:
+            d = attrs.field(default=attrs.Factory(dict))
+
+        n = FooBar(d=1)
+        n.d['answer'] = 42
+
+        @mutable
+        class BarFoo:
+            d = attrs.field(default=attrs.Factory(dict))
+
+        o = BarFoo(d=1)
+        o.d['answer'] = 42
+
+        @my_mutable
+        class FooFoo:
+            d = attrs.field(default=attrs.Factory(dict))
+
+        p = FooFoo(d=1)
+        p.d['answer'] = 42
+        """
+        )
+
+        for name in ("f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"):
+            should_be_unknown = next(module.getattr(name)[0].infer()).getattr("d")[0]
+            self.assertIsInstance(should_be_unknown, astroid.Unknown)
+
+    def test_special_attributes(self) -> None:
+        """Make sure special attrs attributes exist"""
+
+        code = """
+        import attr
+
+        @attr.s
+        class Foo:
+            pass
+        Foo()
+        """
+        foo_inst = next(astroid.extract_node(code).infer())
+        [attr_node] = foo_inst.getattr("__attrs_attrs__")
+        # Prevents https://github.com/PyCQA/pylint/issues/1884
+        assert isinstance(attr_node, nodes.Unknown)
+
+    def test_dont_consider_assignments_but_without_attrs(self) -> None:
+        code = """
+        import attr
+
+        class Cls: pass
+        @attr.s
+        class Foo:
+            temp = Cls()
+            temp.prop = 5
+            bar_thing = attr.ib(default=temp)
+        Foo()
+        """
+        next(astroid.extract_node(code).infer())
+
+    def test_attrs_with_annotation(self) -> None:
+        code = """
+        import attr
+
+        @attr.s
+        class Foo:
+            bar: int = attr.ib(default=5)
+        Foo()
+        """
+        should_be_unknown = next(astroid.extract_node(code).infer()).getattr("bar")[0]
+        self.assertIsInstance(should_be_unknown, astroid.Unknown)
diff --git a/tests/unittest_brain.py b/tests/brain/test_brain.py
similarity index 57%
rename from tests/unittest_brain.py
rename to tests/brain/test_brain.py
index dd929bd..dc12ea2 100644
--- a/tests/unittest_brain.py
+++ b/tests/brain/test_brain.py
@@ -2,1332 +2,102 @@
 # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE
 # Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt
 
-"""Tests for basic functionality in astroid.brain."""
-
-from __future__ import annotations
-
-import io
-import queue
-import re
-import sys
-import unittest
-import warnings
-from typing import Any
-
-import pytest
-
-import astroid
-from astroid import MANAGER, bases, builder, nodes, objects, test_utils, util
-from astroid.bases import Instance
-from astroid.brain.brain_namedtuple_enum import _get_namedtuple_fields
-from astroid.const import PY39_PLUS
-from astroid.exceptions import (
-    AttributeInferenceError,
-    InferenceError,
-    UseInferenceDefault,
-)
-from astroid.nodes.node_classes import Const
-from astroid.nodes.scoped_nodes import ClassDef
-
-try:
-    import multiprocessing  # pylint: disable=unused-import
-
-    HAS_MULTIPROCESSING = True
-except ImportError:
-    HAS_MULTIPROCESSING = False
-
-
-try:
-    with warnings.catch_warnings():
-        warnings.simplefilter("ignore", DeprecationWarning)
-        import nose  # pylint: disable=unused-import
-    HAS_NOSE = True
-except ImportError:
-    HAS_NOSE = False
-
-try:
-    import dateutil  # pylint: disable=unused-import
-
-    HAS_DATEUTIL = True
-except ImportError:
-    HAS_DATEUTIL = False
-
-try:
-    import attr as attr_module  # pylint: disable=unused-import
-
-    HAS_ATTR = True
-except ImportError:
-    HAS_ATTR = False
-
-try:
-    import six  # pylint: disable=unused-import
-
-    HAS_SIX = True
-except ImportError:
-    HAS_SIX = False
-
-try:
-    import typing_extensions  # pylint: disable=unused-import
-
-    HAS_TYPING_EXTENSIONS = True
-    HAS_TYPING_EXTENSIONS_TYPEVAR = hasattr(typing_extensions, "TypeVar")
-except ImportError:
-    HAS_TYPING_EXTENSIONS = False
-    HAS_TYPING_EXTENSIONS_TYPEVAR = False
-
-
-def assertEqualMro(klass: ClassDef, expected_mro: list[str]) -> None:
-    """Check mro names."""
-    assert [member.qname() for member in klass.mro()] == expected_mro
-
-
-class HashlibTest(unittest.TestCase):
-    def _assert_hashlib_class(self, class_obj: ClassDef) -> None:
-        self.assertIn("update", class_obj)
-        self.assertIn("digest", class_obj)
-        self.assertIn("hexdigest", class_obj)
-        self.assertIn("block_size", class_obj)
-        self.assertIn("digest_size", class_obj)
-        # usedforsecurity was added in Python 3.9, see 8e7174a9
-        self.assertEqual(len(class_obj["__init__"].args.args), 3 if PY39_PLUS else 2)
-        self.assertEqual(
-            len(class_obj["__init__"].args.defaults), 2 if PY39_PLUS else 1
-        )
-        self.assertEqual(len(class_obj["update"].args.args), 2)
-
-    def test_hashlib(self) -> None:
-        """Tests that brain extensions for hashlib work."""
-        hashlib_module = MANAGER.ast_from_module_name("hashlib")
-        for class_name in (
-            "md5",
-            "sha1",
-            "sha224",
-            "sha256",
-            "sha384",
-            "sha512",
-            "sha3_224",
-            "sha3_256",
-            "sha3_384",
-            "sha3_512",
-        ):
-            class_obj = hashlib_module[class_name]
-            self._assert_hashlib_class(class_obj)
-            self.assertEqual(len(class_obj["digest"].args.args), 1)
-            self.assertEqual(len(class_obj["hexdigest"].args.args), 1)
-
-    def test_shake(self) -> None:
-        """Tests that the brain extensions for the hashlib shake algorithms work."""
-        hashlib_module = MANAGER.ast_from_module_name("hashlib")
-        for class_name in ("shake_128", "shake_256"):
-            class_obj = hashlib_module[class_name]
-            self._assert_hashlib_class(class_obj)
-            self.assertEqual(len(class_obj["digest"].args.args), 2)
-            self.assertEqual(len(class_obj["hexdigest"].args.args), 2)
-
-    def test_blake2(self) -> None:
-        """Tests that the brain extensions for the hashlib blake2 hash functions work."""
-        hashlib_module = MANAGER.ast_from_module_name("hashlib")
-        for class_name in ("blake2b", "blake2s"):
-            class_obj = hashlib_module[class_name]
-            self.assertEqual(len(class_obj["__init__"].args.args), 2)
-            self.assertEqual(len(class_obj["digest"].args.args), 1)
-            self.assertEqual(len(class_obj["hexdigest"].args.args), 1)
-
-
-class CollectionsDequeTests(unittest.TestCase):
-    def _inferred_queue_instance(self) -> Instance:
-        node = builder.extract_node(
-            """
-        import collections
-        q = collections.deque([])
-        q
-        """
-        )
-        return next(node.infer())
-
-    def test_deque(self) -> None:
-        inferred = self._inferred_queue_instance()
-        self.assertTrue(inferred.getattr("__len__"))
-
-    def test_deque_py35methods(self) -> None:
-        inferred = self._inferred_queue_instance()
-        self.assertIn("copy", inferred.locals)
-        self.assertIn("insert", inferred.locals)
-        self.assertIn("index", inferred.locals)
-
-    @test_utils.require_version(maxver="3.8")
-    def test_deque_not_py39methods(self):
-        inferred = self._inferred_queue_instance()
-        with self.assertRaises(AttributeInferenceError):
-            inferred.getattr("__class_getitem__")
-
-    @test_utils.require_version(minver="3.9")
-    def test_deque_py39methods(self):
-        inferred = self._inferred_queue_instance()
-        self.assertTrue(inferred.getattr("__class_getitem__"))
-
-
-class OrderedDictTest(unittest.TestCase):
-    def _inferred_ordered_dict_instance(self) -> Instance:
-        node = builder.extract_node(
-            """
-        import collections
-        d = collections.OrderedDict()
-        d
-        """
-        )
-        return next(node.infer())
-
-    def test_ordered_dict_py34method(self) -> None:
-        inferred = self._inferred_ordered_dict_instance()
-        self.assertIn("move_to_end", inferred.locals)
-
-
-class NamedTupleTest(unittest.TestCase):
-    def test_namedtuple_base(self) -> None:
-        klass = builder.extract_node(
-            """
-        from collections import namedtuple
-
-        class X(namedtuple("X", ["a", "b", "c"])):
-           pass
-        """
-        )
-        assert isinstance(klass, nodes.ClassDef)
-        self.assertEqual(
-            [anc.name for anc in klass.ancestors()], ["X", "tuple", "object"]
-        )
-        # See: https://github.com/PyCQA/pylint/issues/5982
-        self.assertNotIn("X", klass.locals)
-        for anc in klass.ancestors():
-            self.assertFalse(anc.parent is None)
-
-    def test_namedtuple_inference(self) -> None:
-        klass = builder.extract_node(
-            """
-        from collections import namedtuple
-
-        name = "X"
-        fields = ["a", "b", "c"]
-        class X(namedtuple(name, fields)):
-           pass
-        """
-        )
-        assert isinstance(klass, nodes.ClassDef)
-        base = next(base for base in klass.ancestors() if base.name == "X")
-        self.assertSetEqual({"a", "b", "c"}, set(base.instance_attrs))
-
-    def test_namedtuple_inference_failure(self) -> None:
-        klass = builder.extract_node(
-            """
-        from collections import namedtuple
-
-        def foo(fields):
-           return __(namedtuple("foo", fields))
-        """
-        )
-        self.assertIs(util.Uninferable, next(klass.infer()))
-
-    def test_namedtuple_advanced_inference(self) -> None:
-        # urlparse return an object of class ParseResult, which has a
-        # namedtuple call and a mixin as base classes
-        result = builder.extract_node(
-            """
-        from urllib.parse import urlparse
-
-        result = __(urlparse('gopher://'))
-        """
-        )
-        instance = next(result.infer())
-        self.assertGreaterEqual(len(instance.getattr("scheme")), 1)
-        self.assertGreaterEqual(len(instance.getattr("port")), 1)
-        with self.assertRaises(AttributeInferenceError):
-            instance.getattr("foo")
-        self.assertGreaterEqual(len(instance.getattr("geturl")), 1)
-        self.assertEqual(instance.name, "ParseResult")
-
-    def test_namedtuple_instance_attrs(self) -> None:
-        result = builder.extract_node(
-            """
-        from collections import namedtuple
-        namedtuple('a', 'a b c')(1, 2, 3) #@
-        """
-        )
-        inferred = next(result.infer())
-        for name, attr in inferred.instance_attrs.items():
-            self.assertEqual(attr[0].attrname, name)
-
-    def test_namedtuple_uninferable_fields(self) -> None:
-        node = builder.extract_node(
-            """
-        x = [A] * 2
-        from collections import namedtuple
-        l = namedtuple('a', x)
-        l(1)
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIs(util.Uninferable, inferred)
-
-    def test_namedtuple_access_class_fields(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", "field other")
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIn("field", inferred.locals)
-        self.assertIn("other", inferred.locals)
-
-    def test_namedtuple_rename_keywords(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", "abc def", rename=True)
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIn("abc", inferred.locals)
-        self.assertIn("_1", inferred.locals)
-
-    def test_namedtuple_rename_duplicates(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", "abc abc abc", rename=True)
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIn("abc", inferred.locals)
-        self.assertIn("_1", inferred.locals)
-        self.assertIn("_2", inferred.locals)
-
-    def test_namedtuple_rename_uninferable(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", "a b c", rename=UNINFERABLE)
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIn("a", inferred.locals)
-        self.assertIn("b", inferred.locals)
-        self.assertIn("c", inferred.locals)
-
-    def test_namedtuple_func_form(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple(typename="Tuple", field_names="a b c", rename=UNINFERABLE)
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertEqual(inferred.name, "Tuple")
-        self.assertIn("a", inferred.locals)
-        self.assertIn("b", inferred.locals)
-        self.assertIn("c", inferred.locals)
-
-    def test_namedtuple_func_form_args_and_kwargs(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", field_names="a b c", rename=UNINFERABLE)
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertEqual(inferred.name, "Tuple")
-        self.assertIn("a", inferred.locals)
-        self.assertIn("b", inferred.locals)
-        self.assertIn("c", inferred.locals)
-
-    def test_namedtuple_bases_are_actually_names_not_nodes(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", field_names="a b c", rename=UNINFERABLE)
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIsInstance(inferred, astroid.ClassDef)
-        self.assertIsInstance(inferred.bases[0], astroid.Name)
-        self.assertEqual(inferred.bases[0].name, "tuple")
-
-    def test_invalid_label_does_not_crash_inference(self) -> None:
-        code = """
-        import collections
-        a = collections.namedtuple( 'a', ['b c'] )
-        a
-        """
-        node = builder.extract_node(code)
-        inferred = next(node.infer())
-        assert isinstance(inferred, astroid.ClassDef)
-        assert "b" not in inferred.locals
-        assert "c" not in inferred.locals
-
-    def test_no_rename_duplicates_does_not_crash_inference(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", "abc abc")
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
-
-    def test_no_rename_keywords_does_not_crash_inference(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", "abc def")
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
-
-    def test_no_rename_nonident_does_not_crash_inference(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", "123 456")
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
-
-    def test_no_rename_underscore_does_not_crash_inference(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", "_1")
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
-
-    def test_invalid_typename_does_not_crash_inference(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("123", "abc")
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
-
-    def test_keyword_typename_does_not_crash_inference(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("while", "abc")
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
-
-    def test_typeerror_does_not_crash_inference(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        Tuple = namedtuple("Tuple", [123, 456])
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        # namedtuple converts all arguments to strings so these should be too
-        # and catch on the isidentifier() check
-        self.assertIs(util.Uninferable, inferred)
-
-    def test_pathological_str_does_not_crash_inference(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import namedtuple
-        class Invalid:
-            def __str__(self):
-                return 123  # will raise TypeError
-        Tuple = namedtuple("Tuple", [Invalid()])
-        Tuple #@
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIs(util.Uninferable, inferred)
-
-    def test_name_as_typename(self) -> None:
-        """Reported in https://github.com/PyCQA/pylint/issues/7429 as a crash."""
-        good_node, good_node_two, bad_node = builder.extract_node(
-            """
-            import collections
-            collections.namedtuple(typename="MyTuple", field_names=["birth_date", "city"])  #@
-            collections.namedtuple("MyTuple", field_names=["birth_date", "city"])  #@
-            collections.namedtuple(["birth_date", "city"], typename="MyTuple")  #@
-        """
-        )
-        good_inferred = next(good_node.infer())
-        assert isinstance(good_inferred, nodes.ClassDef)
-        good_node_two_inferred = next(good_node_two.infer())
-        assert isinstance(good_node_two_inferred, nodes.ClassDef)
-        bad_node_inferred = next(bad_node.infer())
-        assert bad_node_inferred == util.Uninferable
-
-
-class DefaultDictTest(unittest.TestCase):
-    def test_1(self) -> None:
-        node = builder.extract_node(
-            """
-        from collections import defaultdict
-
-        X = defaultdict(int)
-        X[0]
-        """
-        )
-        inferred = next(node.infer())
-        self.assertIs(util.Uninferable, inferred)
-
-
-class ModuleExtenderTest(unittest.TestCase):
-    def test_extension_modules(self) -> None:
-        transformer = MANAGER._transform
-        for extender, _ in transformer.transforms[nodes.Module]:
-            n = nodes.Module("__main__")
-            extender(n)
-
-
-@unittest.skipUnless(HAS_NOSE, "This test requires nose library.")
-class NoseBrainTest(unittest.TestCase):
-    def test_nose_tools(self):
-        methods = builder.extract_node(
-            """
-        from nose.tools import assert_equal
-        from nose.tools import assert_equals
-        from nose.tools import assert_true
-        assert_equal = assert_equal #@
-        assert_true = assert_true #@
-        assert_equals = assert_equals #@
-        """
-        )
-        assert isinstance(methods, list)
-        assert_equal = next(methods[0].value.infer())
-        assert_true = next(methods[1].value.infer())
-        assert_equals = next(methods[2].value.infer())
-
-        self.assertIsInstance(assert_equal, astroid.BoundMethod)
-        self.assertIsInstance(assert_true, astroid.BoundMethod)
-        self.assertIsInstance(assert_equals, astroid.BoundMethod)
-        self.assertEqual(assert_equal.qname(), "unittest.case.TestCase.assertEqual")
-        self.assertEqual(assert_true.qname(), "unittest.case.TestCase.assertTrue")
-        self.assertEqual(assert_equals.qname(), "unittest.case.TestCase.assertEqual")
-
-
-@unittest.skipUnless(HAS_SIX, "These tests require the six library")
-class SixBrainTest(unittest.TestCase):
-    def test_attribute_access(self) -> None:
-        ast_nodes = builder.extract_node(
-            """
-        import six
-        six.moves.http_client #@
-        six.moves.urllib_parse #@
-        six.moves.urllib_error #@
-        six.moves.urllib.request #@
-        """
-        )
-        assert isinstance(ast_nodes, list)
-        http_client = next(ast_nodes[0].infer())
-        self.assertIsInstance(http_client, nodes.Module)
-        self.assertEqual(http_client.name, "http.client")
-
-        urllib_parse = next(ast_nodes[1].infer())
-        self.assertIsInstance(urllib_parse, nodes.Module)
-        self.assertEqual(urllib_parse.name, "urllib.parse")
-        urljoin = next(urllib_parse.igetattr("urljoin"))
-        urlencode = next(urllib_parse.igetattr("urlencode"))
-        self.assertIsInstance(urljoin, nodes.FunctionDef)
-        self.assertEqual(urljoin.qname(), "urllib.parse.urljoin")
-        self.assertIsInstance(urlencode, nodes.FunctionDef)
-        self.assertEqual(urlencode.qname(), "urllib.parse.urlencode")
-
-        urllib_error = next(ast_nodes[2].infer())
-        self.assertIsInstance(urllib_error, nodes.Module)
-        self.assertEqual(urllib_error.name, "urllib.error")
-        urlerror = next(urllib_error.igetattr("URLError"))
-        self.assertIsInstance(urlerror, nodes.ClassDef)
-        content_too_short = next(urllib_error.igetattr("ContentTooShortError"))
-        self.assertIsInstance(content_too_short, nodes.ClassDef)
-
-        urllib_request = next(ast_nodes[3].infer())
-        self.assertIsInstance(urllib_request, nodes.Module)
-        self.assertEqual(urllib_request.name, "urllib.request")
-        urlopen = next(urllib_request.igetattr("urlopen"))
-        urlretrieve = next(urllib_request.igetattr("urlretrieve"))
-        self.assertIsInstance(urlopen, nodes.FunctionDef)
-        self.assertEqual(urlopen.qname(), "urllib.request.urlopen")
-        self.assertIsInstance(urlretrieve, nodes.FunctionDef)
-        self.assertEqual(urlretrieve.qname(), "urllib.request.urlretrieve")
-
-    def test_from_imports(self) -> None:
-        ast_node = builder.extract_node(
-            """
-        from six.moves import http_client
-        http_client.HTTPSConnection #@
-        """
-        )
-        inferred = next(ast_node.infer())
-        self.assertIsInstance(inferred, nodes.ClassDef)
-        qname = "http.client.HTTPSConnection"
-        self.assertEqual(inferred.qname(), qname)
-
-    def test_from_submodule_imports(self) -> None:
-        """Make sure ulrlib submodules can be imported from
-
-        See PyCQA/pylint#1640 for relevant issue
-        """
-        ast_node = builder.extract_node(
-            """
-        from six.moves.urllib.parse import urlparse
-        urlparse #@
-        """
-        )
-        inferred = next(ast_node.infer())
-        self.assertIsInstance(inferred, nodes.FunctionDef)
-
-    def test_with_metaclass_subclasses_inheritance(self) -> None:
-        ast_node = builder.extract_node(
-            """
-        class A(type):
-            def test(cls):
-                return cls
-
-        class C:
-            pass
-
-        import six
-        class B(six.with_metaclass(A, C)):
-            pass
-
-        B #@
-        """
-        )
-        inferred = next(ast_node.infer())
-        self.assertIsInstance(inferred, nodes.ClassDef)
-        self.assertEqual(inferred.name, "B")
-        self.assertIsInstance(inferred.bases[0], nodes.Name)
-        self.assertEqual(inferred.bases[0].name, "C")
-        ancestors = tuple(inferred.ancestors())
-        self.assertIsInstance(ancestors[0], nodes.ClassDef)
-        self.assertEqual(ancestors[0].name, "C")
-        self.assertIsInstance(ancestors[1], nodes.ClassDef)
-        self.assertEqual(ancestors[1].name, "object")
-
-    @staticmethod
-    def test_six_with_metaclass_enum_ancestor() -> None:
-        code = """
-        import six
-        from enum import Enum, EnumMeta
-
-        class FooMeta(EnumMeta):
-            pass
-
-        class Foo(six.with_metaclass(FooMeta, Enum)):  #@
-            bar = 1
-        """
-        klass = astroid.extract_node(code)
-        assert list(klass.ancestors())[-1].name == "Enum"
-
-    def test_six_with_metaclass_with_additional_transform(self) -> None:
-        def transform_class(cls: Any) -> ClassDef:
-            if cls.name == "A":
-                cls._test_transform = 314
-            return cls
-
-        MANAGER.register_transform(nodes.ClassDef, transform_class)
-        try:
-            ast_node = builder.extract_node(
-                """
-                import six
-                class A(six.with_metaclass(type, object)):
-                    pass
-
-                A #@
-            """
-            )
-            inferred = next(ast_node.infer())
-            assert getattr(inferred, "_test_transform", None) == 314
-        finally:
-            MANAGER.unregister_transform(nodes.ClassDef, transform_class)
-
-
-@unittest.skipUnless(
-    HAS_MULTIPROCESSING,
-    "multiprocesing is required for this test, but "
-    "on some platforms it is missing "
-    "(Jython for instance)",
-)
-class MultiprocessingBrainTest(unittest.TestCase):
-    def test_multiprocessing_module_attributes(self) -> None:
-        # Test that module attributes are working,
-        # especially on Python 3.4+, where they are obtained
-        # from a context.
-        module = builder.extract_node(
-            """
-        import multiprocessing
-        """
-        )
-        assert isinstance(module, nodes.Import)
-        module = module.do_import_module("multiprocessing")
-        cpu_count = next(module.igetattr("cpu_count"))
-        self.assertIsInstance(cpu_count, astroid.BoundMethod)
-
-    def test_module_name(self) -> None:
-        module = builder.extract_node(
-            """
-        import multiprocessing
-        multiprocessing.SyncManager()
-        """
-        )
-        inferred_sync_mgr = next(module.infer())
-        module = inferred_sync_mgr.root()
-        self.assertEqual(module.name, "multiprocessing.managers")
-
-    def test_multiprocessing_manager(self) -> None:
-        # Test that we have the proper attributes
-        # for a multiprocessing.managers.SyncManager
-        module = builder.parse(
-            """
-        import multiprocessing
-        manager = multiprocessing.Manager()
-        queue = manager.Queue()
-        joinable_queue = manager.JoinableQueue()
-        event = manager.Event()
-        rlock = manager.RLock()
-        lock = manager.Lock()
-        bounded_semaphore = manager.BoundedSemaphore()
-        condition = manager.Condition()
-        barrier = manager.Barrier()
-        pool = manager.Pool()
-        list = manager.list()
-        dict = manager.dict()
-        value = manager.Value()
-        array = manager.Array()
-        namespace = manager.Namespace()
-        """
-        )
-        ast_queue = next(module["queue"].infer())
-        self.assertEqual(ast_queue.qname(), f"{queue.__name__}.Queue")
-
-        joinable_queue = next(module["joinable_queue"].infer())
-        self.assertEqual(joinable_queue.qname(), f"{queue.__name__}.Queue")
-
-        event = next(module["event"].infer())
-        event_name = "threading.Event"
-        self.assertEqual(event.qname(), event_name)
-
-        rlock = next(module["rlock"].infer())
-        rlock_name = "threading._RLock"
-        self.assertEqual(rlock.qname(), rlock_name)
-
-        lock = next(module["lock"].infer())
-        lock_name = "threading.lock"
-        self.assertEqual(lock.qname(), lock_name)
-
-        bounded_semaphore = next(module["bounded_semaphore"].infer())
-        semaphore_name = "threading.BoundedSemaphore"
-        self.assertEqual(bounded_semaphore.qname(), semaphore_name)
-
-        pool = next(module["pool"].infer())
-        pool_name = "multiprocessing.pool.Pool"
-        self.assertEqual(pool.qname(), pool_name)
-
-        for attr in ("list", "dict"):
-            obj = next(module[attr].infer())
-            self.assertEqual(obj.qname(), f"builtins.{attr}")
-
-        # pypy's implementation of array.__spec__ return None. This causes problems for this inference.
-        if not hasattr(sys, "pypy_version_info"):
-            array = next(module["array"].infer())
-            self.assertEqual(array.qname(), "array.array")
-
-        manager = next(module["manager"].infer())
-        # Verify that we have these attributes
-        self.assertTrue(manager.getattr("start"))
-        self.assertTrue(manager.getattr("shutdown"))
-
-
-class ThreadingBrainTest(unittest.TestCase):
-    def test_lock(self) -> None:
-        lock_instance = builder.extract_node(
-            """
-        import threading
-        threading.Lock()
-        """
-        )
-        inferred = next(lock_instance.infer())
-        self.assert_is_valid_lock(inferred)
-
-        acquire_method = inferred.getattr("acquire")[0]
-        parameters = [param.name for param in acquire_method.args.args[1:]]
-        assert parameters == ["blocking", "timeout"]
-
-        assert inferred.getattr("locked")
-
-    def test_rlock(self) -> None:
-        self._test_lock_object("RLock")
-
-    def test_semaphore(self) -> None:
-        self._test_lock_object("Semaphore")
-
-    def test_boundedsemaphore(self) -> None:
-        self._test_lock_object("BoundedSemaphore")
-
-    def _test_lock_object(self, object_name: str) -> None:
-        lock_instance = builder.extract_node(
-            f"""
-        import threading
-        threading.{object_name}()
-        """
-        )
-        inferred = next(lock_instance.infer())
-        self.assert_is_valid_lock(inferred)
-
-    def assert_is_valid_lock(self, inferred: Instance) -> None:
-        self.assertIsInstance(inferred, astroid.Instance)
-        self.assertEqual(inferred.root().name, "threading")
-        for method in ("acquire", "release", "__enter__", "__exit__"):
-            self.assertIsInstance(next(inferred.igetattr(method)), astroid.BoundMethod)
-
-
-class EnumBrainTest(unittest.TestCase):
-    def test_simple_enum(self) -> None:
-        module = builder.parse(
-            """
-        import enum
-
-        class MyEnum(enum.Enum):
-            one = "one"
-            two = "two"
-
-            def mymethod(self, x):
-                return 5
-
-        """
-        )
-
-        enumeration = next(module["MyEnum"].infer())
-        one = enumeration["one"]
-        self.assertEqual(one.pytype(), ".MyEnum.one")
-
-        for propname in ("name", "value"):
-            prop = next(iter(one.getattr(propname)))
-            self.assertIn("builtins.property", prop.decoratornames())
-
-        meth = one.getattr("mymethod")[0]
-        self.assertIsInstance(meth, astroid.FunctionDef)
-
-    def test_looks_like_enum_false_positive(self) -> None:
-        # Test that a class named Enumeration is not considered a builtin enum.
-        module = builder.parse(
-            """
-        class Enumeration(object):
-            def __init__(self, name, enum_list):
-                pass
-            test = 42
-        """
-        )
-        enumeration = module["Enumeration"]
-        test = next(enumeration.igetattr("test"))
-        self.assertEqual(test.value, 42)
-
-    def test_user_enum_false_positive(self) -> None:
-        # Test that a user-defined class named Enum is not considered a builtin enum.
-        ast_node = astroid.extract_node(
-            """
-        class Enum:
-            pass
-
-        class Color(Enum):
-            red = 1
-
-        Color.red #@
-        """
-        )
-        assert isinstance(ast_node, nodes.NodeNG)
-        inferred = ast_node.inferred()
-        self.assertEqual(len(inferred), 1)
-        self.assertIsInstance(inferred[0], astroid.Const)
-        self.assertEqual(inferred[0].value, 1)
-
-    def test_ignores_with_nodes_from_body_of_enum(self) -> None:
-        code = """
-        import enum
-
-        class Error(enum.Enum):
-            Foo = "foo"
-            Bar = "bar"
-            with "error" as err:
-                pass
-        """
-        node = builder.extract_node(code)
-        inferred = next(node.infer())
-        assert "err" in inferred.locals
-        assert len(inferred.locals["err"]) == 1
-
-    def test_enum_multiple_base_classes(self) -> None:
-        module = builder.parse(
-            """
-        import enum
-
-        class Mixin:
-            pass
-
-        class MyEnum(Mixin, enum.Enum):
-            one = 1
-        """
-        )
-        enumeration = next(module["MyEnum"].infer())
-        one = enumeration["one"]
-
-        clazz = one.getattr("__class__")[0]
-        self.assertTrue(
-            clazz.is_subtype_of(".Mixin"),
-            "Enum instance should share base classes with generating class",
-        )
-
-    def test_int_enum(self) -> None:
-        module = builder.parse(
-            """
-        import enum
-
-        class MyEnum(enum.IntEnum):
-            one = 1
-        """
-        )
-
-        enumeration = next(module["MyEnum"].infer())
-        one = enumeration["one"]
-
-        clazz = one.getattr("__class__")[0]
-        self.assertTrue(
-            clazz.is_subtype_of("builtins.int"),
-            "IntEnum based enums should be a subtype of int",
-        )
-
-    def test_enum_func_form_is_class_not_instance(self) -> None:
-        cls, instance = builder.extract_node(
-            """
-        from enum import Enum
-        f = Enum('Audience', ['a', 'b', 'c'])
-        f #@
-        f(1) #@
-        """
-        )
-        inferred_cls = next(cls.infer())
-        self.assertIsInstance(inferred_cls, bases.Instance)
-        inferred_instance = next(instance.infer())
-        self.assertIsInstance(inferred_instance, bases.Instance)
-        self.assertIsInstance(next(inferred_instance.igetattr("name")), nodes.Const)
-        self.assertIsInstance(next(inferred_instance.igetattr("value")), nodes.Const)
-
-    def test_enum_func_form_iterable(self) -> None:
-        instance = builder.extract_node(
-            """
-        from enum import Enum
-        Animal = Enum('Animal', 'ant bee cat dog')
-        Animal
-        """
-        )
-        inferred = next(instance.infer())
-        self.assertIsInstance(inferred, astroid.Instance)
-        self.assertTrue(inferred.getattr("__iter__"))
-
-    def test_enum_func_form_subscriptable(self) -> None:
-        instance, name = builder.extract_node(
-            """
-        from enum import Enum
-        Animal = Enum('Animal', 'ant bee cat dog')
-        Animal['ant'] #@
-        Animal['ant'].name #@
-        """
-        )
-        instance = next(instance.infer())
-        self.assertIsInstance(instance, astroid.Instance)
+from __future__ import annotations
 
-        inferred = next(name.infer())
-        self.assertIsInstance(inferred, astroid.Const)
+import io
+import re
+import sys
+import unittest
 
-    def test_enum_func_form_has_dunder_members(self) -> None:
-        instance = builder.extract_node(
-            """
-        from enum import Enum
-        Animal = Enum('Animal', 'ant bee cat dog')
-        for i in Animal.__members__:
-            i #@
-        """
-        )
-        instance = next(instance.infer())
-        self.assertIsInstance(instance, astroid.Const)
-        self.assertIsInstance(instance.value, str)
+import pytest
 
-    def test_infer_enum_value_as_the_right_type(self) -> None:
-        string_value, int_value = builder.extract_node(
-            """
-        from enum import Enum
-        class A(Enum):
-            a = 'a'
-            b = 1
-        A.a.value #@
-        A.b.value #@
-        """
-        )
-        inferred_string = string_value.inferred()
-        assert any(
-            isinstance(elem, astroid.Const) and elem.value == "a"
-            for elem in inferred_string
-        )
+import astroid
+from astroid import MANAGER, builder, nodes, objects, test_utils, util
+from astroid.bases import Instance
+from astroid.brain.brain_namedtuple_enum import _get_namedtuple_fields
+from astroid.exceptions import (
+    AttributeInferenceError,
+    InferenceError,
+    UseInferenceDefault,
+)
+from astroid.nodes.node_classes import Const
+from astroid.nodes.scoped_nodes import ClassDef
 
-        inferred_int = int_value.inferred()
-        assert any(
-            isinstance(elem, astroid.Const) and elem.value == 1 for elem in inferred_int
-        )
 
-    def test_mingled_single_and_double_quotes_does_not_crash(self) -> None:
-        node = builder.extract_node(
-            """
-        from enum import Enum
-        class A(Enum):
-            a = 'x"y"'
-        A.a.value #@
-        """
-        )
-        inferred_string = next(node.infer())
-        assert inferred_string.value == 'x"y"'
+def assertEqualMro(klass: ClassDef, expected_mro: list[str]) -> None:
+    """Check mro names."""
+    assert [member.qname() for member in klass.mro()] == expected_mro
 
-    def test_special_characters_does_not_crash(self) -> None:
-        node = builder.extract_node(
-            """
-        import enum
-        class Example(enum.Enum):
-            NULL = '\\N{NULL}'
-        Example.NULL.value
-        """
-        )
-        inferred_string = next(node.infer())
-        assert inferred_string.value == "\N{NULL}"
 
-    def test_dont_crash_on_for_loops_in_body(self) -> None:
+class CollectionsDequeTests(unittest.TestCase):
+    def _inferred_queue_instance(self) -> Instance:
         node = builder.extract_node(
             """
-
-        class Commands(IntEnum):
-            _ignore_ = 'Commands index'
-            _init_ = 'value string'
-
-            BEL = 0x07, 'Bell'
-            Commands = vars()
-            for index in range(4):
-                Commands[f'DC{index + 1}'] = 0x11 + index, f'Device Control {index + 1}'
-
-        Commands
-        """
-        )
-        inferred = next(node.infer())
-        assert isinstance(inferred, astroid.ClassDef)
-
-    def test_enum_tuple_list_values(self) -> None:
-        tuple_node, list_node = builder.extract_node(
-            """
-        import enum
-
-        class MyEnum(enum.Enum):
-            a = (1, 2)
-            b = [2, 4]
-        MyEnum.a.value #@
-        MyEnum.b.value #@
-        """
-        )
-        inferred_tuple_node = next(tuple_node.infer())
-        inferred_list_node = next(list_node.infer())
-        assert isinstance(inferred_tuple_node, astroid.Tuple)
-        assert isinstance(inferred_list_node, astroid.List)
-        assert inferred_tuple_node.as_string() == "(1, 2)"
-        assert inferred_list_node.as_string() == "[2, 4]"
-
-    def test_enum_starred_is_skipped(self) -> None:
-        code = """
-        from enum import Enum
-        class ContentType(Enum):
-            TEXT, PHOTO, VIDEO, GIF, YOUTUBE, *_ = [1, 2, 3, 4, 5, 6]
-        ContentType.TEXT #@
-        """
-        node = astroid.extract_node(code)
-        next(node.infer())
-
-    def test_enum_name_is_str_on_self(self) -> None:
-        code = """
-        from enum import Enum
-        class TestEnum(Enum):
-            def func(self):
-                self.name #@
-                self.value #@
-        TestEnum.name #@
-        TestEnum.value #@
-        """
-        i_name, i_value, c_name, c_value = astroid.extract_node(code)
-
-        # <instance>.name should be a string, <class>.name should be a property (that
-        # forwards the lookup to __getattr__)
-        inferred = next(i_name.infer())
-        assert isinstance(inferred, nodes.Const)
-        assert inferred.pytype() == "builtins.str"
-        inferred = next(c_name.infer())
-        assert isinstance(inferred, objects.Property)
-
-        # Inferring .value should not raise InferenceError. It is probably Uninferable
-        # but we don't particularly care
-        next(i_value.infer())
-        next(c_value.infer())
-
-    def test_enum_name_and_value_members_override_dynamicclassattr(self) -> None:
-        code = """
-        from enum import Enum
-        class TrickyEnum(Enum):
-            name = 1
-            value = 2
-
-            def func(self):
-                self.name #@
-                self.value #@
-        TrickyEnum.name #@
-        TrickyEnum.value #@
-        """
-        i_name, i_value, c_name, c_value = astroid.extract_node(code)
-
-        # All of these cases should be inferred as enum members
-        inferred = next(i_name.infer())
-        assert isinstance(inferred, bases.Instance)
-        assert inferred.pytype() == ".TrickyEnum.name"
-        inferred = next(c_name.infer())
-        assert isinstance(inferred, bases.Instance)
-        assert inferred.pytype() == ".TrickyEnum.name"
-        inferred = next(i_value.infer())
-        assert isinstance(inferred, bases.Instance)
-        assert inferred.pytype() == ".TrickyEnum.value"
-        inferred = next(c_value.infer())
-        assert isinstance(inferred, bases.Instance)
-        assert inferred.pytype() == ".TrickyEnum.value"
-
-    def test_enum_subclass_member_name(self) -> None:
-        ast_node = astroid.extract_node(
-            """
-        from enum import Enum
-
-        class EnumSubclass(Enum):
-            pass
-
-        class Color(EnumSubclass):
-            red = 1
-
-        Color.red.name #@
-        """
-        )
-        assert isinstance(ast_node, nodes.NodeNG)
-        inferred = ast_node.inferred()
-        self.assertEqual(len(inferred), 1)
-        self.assertIsInstance(inferred[0], astroid.Const)
-        self.assertEqual(inferred[0].value, "red")
-
-    def test_enum_subclass_member_value(self) -> None:
-        ast_node = astroid.extract_node(
-            """
-        from enum import Enum
-
-        class EnumSubclass(Enum):
-            pass
-
-        class Color(EnumSubclass):
-            red = 1
-
-        Color.red.value #@
-        """
-        )
-        assert isinstance(ast_node, nodes.NodeNG)
-        inferred = ast_node.inferred()
-        self.assertEqual(len(inferred), 1)
-        self.assertIsInstance(inferred[0], astroid.Const)
-        self.assertEqual(inferred[0].value, 1)
-
-    def test_enum_subclass_member_method(self) -> None:
-        # See Pylint issue #2626
-        ast_node = astroid.extract_node(
-            """
-        from enum import Enum
-
-        class EnumSubclass(Enum):
-            def hello_pylint(self) -> str:
-                return self.name
-
-        class Color(EnumSubclass):
-            red = 1
-
-        Color.red.hello_pylint()  #@
+        import collections
+        q = collections.deque([])
+        q
         """
         )
-        assert isinstance(ast_node, nodes.NodeNG)
-        inferred = ast_node.inferred()
-        self.assertEqual(len(inferred), 1)
-        self.assertIsInstance(inferred[0], astroid.Const)
-        self.assertEqual(inferred[0].value, "red")
-
-    def test_enum_subclass_different_modules(self) -> None:
-        # See Pylint issue #2626
-        astroid.extract_node(
-            """
-        from enum import Enum
+        return next(node.infer())
 
-        class EnumSubclass(Enum):
-            pass
-        """,
-            "a",
-        )
-        ast_node = astroid.extract_node(
-            """
-        from a import EnumSubclass
+    def test_deque(self) -> None:
+        inferred = self._inferred_queue_instance()
+        self.assertTrue(inferred.getattr("__len__"))
 
-        class Color(EnumSubclass):
-            red = 1
+    def test_deque_py35methods(self) -> None:
+        inferred = self._inferred_queue_instance()
+        self.assertIn("copy", inferred.locals)
+        self.assertIn("insert", inferred.locals)
+        self.assertIn("index", inferred.locals)
 
-        Color.red.value #@
-        """
-        )
-        assert isinstance(ast_node, nodes.NodeNG)
-        inferred = ast_node.inferred()
-        self.assertEqual(len(inferred), 1)
-        self.assertIsInstance(inferred[0], astroid.Const)
-        self.assertEqual(inferred[0].value, 1)
+    @test_utils.require_version(maxver="3.8")
+    def test_deque_not_py39methods(self):
+        inferred = self._inferred_queue_instance()
+        with self.assertRaises(AttributeInferenceError):
+            inferred.getattr("__class_getitem__")
 
-    def test_members_member_ignored(self) -> None:
-        ast_node = builder.extract_node(
-            """
-        from enum import Enum
-        class Animal(Enum):
-            a = 1
-            __members__ = {}
-        Animal.__members__ #@
-        """
-        )
+    @test_utils.require_version(minver="3.9")
+    def test_deque_py39methods(self):
+        inferred = self._inferred_queue_instance()
+        self.assertTrue(inferred.getattr("__class_getitem__"))
 
-        inferred = next(ast_node.infer())
-        self.assertIsInstance(inferred, astroid.Dict)
-        self.assertTrue(inferred.locals)
 
-    def test_enum_as_renamed_import(self) -> None:
-        """Originally reported in https://github.com/PyCQA/pylint/issues/5776."""
-        ast_node: nodes.Attribute = builder.extract_node(
+class OrderedDictTest(unittest.TestCase):
+    def _inferred_ordered_dict_instance(self) -> Instance:
+        node = builder.extract_node(
             """
-        from enum import Enum as PyEnum
-        class MyEnum(PyEnum):
-            ENUM_KEY = "enum_value"
-        MyEnum.ENUM_KEY
+        import collections
+        d = collections.OrderedDict()
+        d
         """
         )
-        inferred = next(ast_node.infer())
-        assert isinstance(inferred, bases.Instance)
-        assert inferred._proxied.name == "ENUM_KEY"
+        return next(node.infer())
 
-    def test_class_named_enum(self) -> None:
-        """Test that the user-defined class named `Enum` is not inferred as `enum.Enum`"""
-        astroid.extract_node(
-            """
-        class Enum:
-            def __init__(self, one, two):
-                self.one = one
-                self.two = two
-            def pear(self):
-                ...
-        """,
-            "module_with_class_named_enum",
-        )
+    def test_ordered_dict_py34method(self) -> None:
+        inferred = self._inferred_ordered_dict_instance()
+        self.assertIn("move_to_end", inferred.locals)
 
-        attribute_nodes = astroid.extract_node(
-            """
-        import module_with_class_named_enum
-        module_with_class_named_enum.Enum("apple", "orange") #@
-        typo_module_with_class_named_enum.Enum("apple", "orange") #@
-        """
-        )
 
-        name_nodes = astroid.extract_node(
+class DefaultDictTest(unittest.TestCase):
+    def test_1(self) -> None:
+        node = builder.extract_node(
             """
-        from module_with_class_named_enum import Enum
-        Enum("apple", "orange") #@
-        TypoEnum("apple", "orange") #@
-        """
-        )
-
-        # Test that both of the successfully inferred `Name` & `Attribute`
-        # nodes refer to the user-defined Enum class.
-        for inferred in (attribute_nodes[0].inferred()[0], name_nodes[0].inferred()[0]):
-            assert isinstance(inferred, astroid.Instance)
-            assert inferred.name == "Enum"
-            assert inferred.qname() == "module_with_class_named_enum.Enum"
-            assert "pear" in inferred.locals
-
-        # Test that an `InferenceError` is raised when an attempt is made to
-        # infer a `Name` or `Attribute` node & they cannot be found.
-        for node in (attribute_nodes[1], name_nodes[1]):
-            with pytest.raises(InferenceError):
-                node.inferred()
-
+        from collections import defaultdict
 
-@unittest.skipUnless(HAS_DATEUTIL, "This test requires the dateutil library.")
-class DateutilBrainTest(unittest.TestCase):
-    def test_parser(self):
-        module = builder.parse(
-            """
-        from dateutil.parser import parse
-        d = parse('2000-01-01')
+        X = defaultdict(int)
+        X[0]
         """
         )
-        d_type = next(module["d"].infer())
-        self.assertEqual(d_type.qname(), "datetime.datetime")
+        inferred = next(node.infer())
+        self.assertIs(util.Uninferable, inferred)
 
 
-class PytestBrainTest(unittest.TestCase):
-    def test_pytest(self) -> None:
-        ast_node = builder.extract_node(
-            """
-        import pytest
-        pytest #@
-        """
-        )
-        module = next(ast_node.infer())
-        attrs = [
-            "deprecated_call",
-            "warns",
-            "exit",
-            "fail",
-            "skip",
-            "importorskip",
-            "xfail",
-            "mark",
-            "raises",
-            "freeze_includes",
-            "set_trace",
-            "fixture",
-            "yield_fixture",
-        ]
-        for attr in attrs:
-            self.assertIn(attr, module)
+class ModuleExtenderTest(unittest.TestCase):
+    def test_extension_modules(self) -> None:
+        transformer = MANAGER._transform
+        for extender, _ in transformer.transforms[nodes.Module]:
+            n = nodes.Module("__main__")
+            extender(n)
 
 
 def streams_are_fine():
@@ -2187,29 +957,6 @@ class TypingBrain(unittest.TestCase):
         assert i1.value == 2  # should be "Hello"!
 
 
-@pytest.mark.skipif(
-    not HAS_TYPING_EXTENSIONS,
-    reason="These tests require the typing_extensions library",
-)
-class TestTypingExtensions:
-    @staticmethod
-    @pytest.mark.skipif(
-        not HAS_TYPING_EXTENSIONS_TYPEVAR,
-        reason="Need typing_extensions>=4.4.0 to test TypeVar",
-    )
-    def test_typing_extensions_types() -> None:
-        ast_nodes = builder.extract_node(
-            """
-        from typing_extensions import TypeVar
-        TypeVar('MyTypeVar', int, float, complex) #@
-        TypeVar('AnyStr', str, bytes) #@
-        """
-        )
-        for node in ast_nodes:
-            inferred = next(node.infer())
-            assert isinstance(inferred, nodes.ClassDef)
-
-
 class ReBrainTest(unittest.TestCase):
     def test_regex_flags(self) -> None:
         names = [name for name in dir(re) if name.isupper()]
@@ -2336,180 +1083,6 @@ class BrainUUIDTest(unittest.TestCase):
         self.assertIsInstance(inferred, nodes.Const)
 
 
-@unittest.skipUnless(HAS_ATTR, "These tests require the attr library")
-class AttrsTest(unittest.TestCase):
-    def test_attr_transform(self) -> None:
-        module = astroid.parse(
-            """
-        import attr
-        from attr import attrs, attrib, field
-
-        @attr.s
-        class Foo:
-
-            d = attr.ib(attr.Factory(dict))
-
-        f = Foo()
-        f.d['answer'] = 42
-
-        @attr.s(slots=True)
-        class Bar:
-            d = attr.ib(attr.Factory(dict))
-
-        g = Bar()
-        g.d['answer'] = 42
-
-        @attrs
-        class Bah:
-            d = attrib(attr.Factory(dict))
-
-        h = Bah()
-        h.d['answer'] = 42
-
-        @attr.attrs
-        class Bai:
-            d = attr.attrib(attr.Factory(dict))
-
-        i = Bai()
-        i.d['answer'] = 42
-
-        @attr.define
-        class Spam:
-            d = field(default=attr.Factory(dict))
-
-        j = Spam(d=1)
-        j.d['answer'] = 42
-
-        @attr.mutable
-        class Eggs:
-            d = attr.field(default=attr.Factory(dict))
-
-        k = Eggs(d=1)
-        k.d['answer'] = 42
-
-        @attr.frozen
-        class Eggs:
-            d = attr.field(default=attr.Factory(dict))
-
-        l = Eggs(d=1)
-        l.d['answer'] = 42
-        """
-        )
-
-        for name in ("f", "g", "h", "i", "j", "k", "l"):
-            should_be_unknown = next(module.getattr(name)[0].infer()).getattr("d")[0]
-            self.assertIsInstance(should_be_unknown, astroid.Unknown)
-
-    def test_attrs_transform(self) -> None:
-        """Test brain for decorators of the 'attrs' package.
-
-        Package added support for 'attrs' a long side 'attr' in v21.3.0.
-        See: https://github.com/python-attrs/attrs/releases/tag/21.3.0
-        """
-        module = astroid.parse(
-            """
-        import attrs
-        from attrs import field, mutable, frozen
-
-        @attrs.define
-        class Foo:
-
-            d = attrs.field(attrs.Factory(dict))
-
-        f = Foo()
-        f.d['answer'] = 42
-
-        @attrs.define(slots=True)
-        class Bar:
-            d = field(attrs.Factory(dict))
-
-        g = Bar()
-        g.d['answer'] = 42
-
-        @attrs.mutable
-        class Bah:
-            d = field(attrs.Factory(dict))
-
-        h = Bah()
-        h.d['answer'] = 42
-
-        @attrs.frozen
-        class Bai:
-            d = attrs.field(attrs.Factory(dict))
-
-        i = Bai()
-        i.d['answer'] = 42
-
-        @attrs.define
-        class Spam:
-            d = field(default=attrs.Factory(dict))
-
-        j = Spam(d=1)
-        j.d['answer'] = 42
-
-        @attrs.mutable
-        class Eggs:
-            d = attrs.field(default=attrs.Factory(dict))
-
-        k = Eggs(d=1)
-        k.d['answer'] = 42
-
-        @attrs.frozen
-        class Eggs:
-            d = attrs.field(default=attrs.Factory(dict))
-
-        l = Eggs(d=1)
-        l.d['answer'] = 42
-        """
-        )
-
-        for name in ("f", "g", "h", "i", "j", "k", "l"):
-            should_be_unknown = next(module.getattr(name)[0].infer()).getattr("d")[0]
-            self.assertIsInstance(should_be_unknown, astroid.Unknown)
-
-    def test_special_attributes(self) -> None:
-        """Make sure special attrs attributes exist"""
-
-        code = """
-        import attr
-
-        @attr.s
-        class Foo:
-            pass
-        Foo()
-        """
-        foo_inst = next(astroid.extract_node(code).infer())
-        [attr_node] = foo_inst.getattr("__attrs_attrs__")
-        # Prevents https://github.com/PyCQA/pylint/issues/1884
-        assert isinstance(attr_node, nodes.Unknown)
-
-    def test_dont_consider_assignments_but_without_attrs(self) -> None:
-        code = """
-        import attr
-
-        class Cls: pass
-        @attr.s
-        class Foo:
-            temp = Cls()
-            temp.prop = 5
-            bar_thing = attr.ib(default=temp)
-        Foo()
-        """
-        next(astroid.extract_node(code).infer())
-
-    def test_attrs_with_annotation(self) -> None:
-        code = """
-        import attr
-
-        @attr.s
-        class Foo:
-            bar: int = attr.ib(default=5)
-        Foo()
-        """
-        should_be_unknown = next(astroid.extract_node(code).infer()).getattr("bar")[0]
-        self.assertIsInstance(should_be_unknown, astroid.Unknown)
-
-
 class RandomSampleTest(unittest.TestCase):
     def test_inferred_successfully(self) -> None:
         node = astroid.extract_node(
@@ -3489,7 +2062,3 @@ def test_no_attributeerror_on_self_referential_length_check() -> None:
         )
         assert isinstance(node, nodes.NodeNG)
         node.inferred()
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_brain_builtin.py b/tests/brain/test_builtin.py
similarity index 100%
rename from tests/unittest_brain_builtin.py
rename to tests/brain/test_builtin.py
diff --git a/tests/unittest_brain_ctypes.py b/tests/brain/test_ctypes.py
similarity index 100%
rename from tests/unittest_brain_ctypes.py
rename to tests/brain/test_ctypes.py
diff --git a/tests/unittest_brain_dataclasses.py b/tests/brain/test_dataclasses.py
similarity index 100%
rename from tests/unittest_brain_dataclasses.py
rename to tests/brain/test_dataclasses.py
diff --git a/tests/brain/test_dateutil.py b/tests/brain/test_dateutil.py
new file mode 100644
index 0000000..d542e8b
--- /dev/null
+++ b/tests/brain/test_dateutil.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
+
+from __future__ import annotations
+
+import unittest
+
+from astroid import builder
+
+try:
+    import dateutil  # pylint: disable=unused-import
+
+    HAS_DATEUTIL = True
+except ImportError:
+    HAS_DATEUTIL = False
+
+
+@unittest.skipUnless(HAS_DATEUTIL, "This test requires the dateutil library.")
+class DateutilBrainTest(unittest.TestCase):
+    def test_parser(self):
+        module = builder.parse(
+            """
+        from dateutil.parser import parse
+        d = parse('2000-01-01')
+        """
+        )
+        d_type = next(module["d"].infer())
+        self.assertEqual(d_type.qname(), "datetime.datetime")
diff --git a/tests/brain/test_enum.py b/tests/brain/test_enum.py
new file mode 100644
index 0000000..9d95d2f
--- /dev/null
+++ b/tests/brain/test_enum.py
@@ -0,0 +1,495 @@
+# 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 unittest
+
+import pytest
+
+import astroid
+from astroid import bases, builder, nodes, objects
+from astroid.exceptions import InferenceError
+
+
+class EnumBrainTest(unittest.TestCase):
+    def test_simple_enum(self) -> None:
+        module = builder.parse(
+            """
+        import enum
+
+        class MyEnum(enum.Enum):
+            one = "one"
+            two = "two"
+
+            def mymethod(self, x):
+                return 5
+
+        """
+        )
+
+        enumeration = next(module["MyEnum"].infer())
+        one = enumeration["one"]
+        self.assertEqual(one.pytype(), ".MyEnum.one")
+
+        for propname in ("name", "value"):
+            prop = next(iter(one.getattr(propname)))
+            self.assertIn("builtins.property", prop.decoratornames())
+
+        meth = one.getattr("mymethod")[0]
+        self.assertIsInstance(meth, astroid.FunctionDef)
+
+    def test_looks_like_enum_false_positive(self) -> None:
+        # Test that a class named Enumeration is not considered a builtin enum.
+        module = builder.parse(
+            """
+        class Enumeration(object):
+            def __init__(self, name, enum_list):
+                pass
+            test = 42
+        """
+        )
+        enumeration = module["Enumeration"]
+        test = next(enumeration.igetattr("test"))
+        self.assertEqual(test.value, 42)
+
+    def test_user_enum_false_positive(self) -> None:
+        # Test that a user-defined class named Enum is not considered a builtin enum.
+        ast_node = astroid.extract_node(
+            """
+        class Enum:
+            pass
+
+        class Color(Enum):
+            red = 1
+
+        Color.red #@
+        """
+        )
+        assert isinstance(ast_node, nodes.NodeNG)
+        inferred = ast_node.inferred()
+        self.assertEqual(len(inferred), 1)
+        self.assertIsInstance(inferred[0], astroid.Const)
+        self.assertEqual(inferred[0].value, 1)
+
+    def test_ignores_with_nodes_from_body_of_enum(self) -> None:
+        code = """
+        import enum
+
+        class Error(enum.Enum):
+            Foo = "foo"
+            Bar = "bar"
+            with "error" as err:
+                pass
+        """
+        node = builder.extract_node(code)
+        inferred = next(node.infer())
+        assert "err" in inferred.locals
+        assert len(inferred.locals["err"]) == 1
+
+    def test_enum_multiple_base_classes(self) -> None:
+        module = builder.parse(
+            """
+        import enum
+
+        class Mixin:
+            pass
+
+        class MyEnum(Mixin, enum.Enum):
+            one = 1
+        """
+        )
+        enumeration = next(module["MyEnum"].infer())
+        one = enumeration["one"]
+
+        clazz = one.getattr("__class__")[0]
+        self.assertTrue(
+            clazz.is_subtype_of(".Mixin"),
+            "Enum instance should share base classes with generating class",
+        )
+
+    def test_int_enum(self) -> None:
+        module = builder.parse(
+            """
+        import enum
+
+        class MyEnum(enum.IntEnum):
+            one = 1
+        """
+        )
+
+        enumeration = next(module["MyEnum"].infer())
+        one = enumeration["one"]
+
+        clazz = one.getattr("__class__")[0]
+        self.assertTrue(
+            clazz.is_subtype_of("builtins.int"),
+            "IntEnum based enums should be a subtype of int",
+        )
+
+    def test_enum_func_form_is_class_not_instance(self) -> None:
+        cls, instance = builder.extract_node(
+            """
+        from enum import Enum
+        f = Enum('Audience', ['a', 'b', 'c'])
+        f #@
+        f(1) #@
+        """
+        )
+        inferred_cls = next(cls.infer())
+        self.assertIsInstance(inferred_cls, bases.Instance)
+        inferred_instance = next(instance.infer())
+        self.assertIsInstance(inferred_instance, bases.Instance)
+        self.assertIsInstance(next(inferred_instance.igetattr("name")), nodes.Const)
+        self.assertIsInstance(next(inferred_instance.igetattr("value")), nodes.Const)
+
+    def test_enum_func_form_iterable(self) -> None:
+        instance = builder.extract_node(
+            """
+        from enum import Enum
+        Animal = Enum('Animal', 'ant bee cat dog')
+        Animal
+        """
+        )
+        inferred = next(instance.infer())
+        self.assertIsInstance(inferred, astroid.Instance)
+        self.assertTrue(inferred.getattr("__iter__"))
+
+    def test_enum_func_form_subscriptable(self) -> None:
+        instance, name = builder.extract_node(
+            """
+        from enum import Enum
+        Animal = Enum('Animal', 'ant bee cat dog')
+        Animal['ant'] #@
+        Animal['ant'].name #@
+        """
+        )
+        instance = next(instance.infer())
+        self.assertIsInstance(instance, astroid.Instance)
+
+        inferred = next(name.infer())
+        self.assertIsInstance(inferred, astroid.Const)
+
+    def test_enum_func_form_has_dunder_members(self) -> None:
+        instance = builder.extract_node(
+            """
+        from enum import Enum
+        Animal = Enum('Animal', 'ant bee cat dog')
+        for i in Animal.__members__:
+            i #@
+        """
+        )
+        instance = next(instance.infer())
+        self.assertIsInstance(instance, astroid.Const)
+        self.assertIsInstance(instance.value, str)
+
+    def test_infer_enum_value_as_the_right_type(self) -> None:
+        string_value, int_value = builder.extract_node(
+            """
+        from enum import Enum
+        class A(Enum):
+            a = 'a'
+            b = 1
+        A.a.value #@
+        A.b.value #@
+        """
+        )
+        inferred_string = string_value.inferred()
+        assert any(
+            isinstance(elem, astroid.Const) and elem.value == "a"
+            for elem in inferred_string
+        )
+
+        inferred_int = int_value.inferred()
+        assert any(
+            isinstance(elem, astroid.Const) and elem.value == 1 for elem in inferred_int
+        )
+
+    def test_mingled_single_and_double_quotes_does_not_crash(self) -> None:
+        node = builder.extract_node(
+            """
+        from enum import Enum
+        class A(Enum):
+            a = 'x"y"'
+        A.a.value #@
+        """
+        )
+        inferred_string = next(node.infer())
+        assert inferred_string.value == 'x"y"'
+
+    def test_special_characters_does_not_crash(self) -> None:
+        node = builder.extract_node(
+            """
+        import enum
+        class Example(enum.Enum):
+            NULL = '\\N{NULL}'
+        Example.NULL.value
+        """
+        )
+        inferred_string = next(node.infer())
+        assert inferred_string.value == "\N{NULL}"
+
+    def test_dont_crash_on_for_loops_in_body(self) -> None:
+        node = builder.extract_node(
+            """
+
+        class Commands(IntEnum):
+            _ignore_ = 'Commands index'
+            _init_ = 'value string'
+
+            BEL = 0x07, 'Bell'
+            Commands = vars()
+            for index in range(4):
+                Commands[f'DC{index + 1}'] = 0x11 + index, f'Device Control {index + 1}'
+
+        Commands
+        """
+        )
+        inferred = next(node.infer())
+        assert isinstance(inferred, astroid.ClassDef)
+
+    def test_enum_tuple_list_values(self) -> None:
+        tuple_node, list_node = builder.extract_node(
+            """
+        import enum
+
+        class MyEnum(enum.Enum):
+            a = (1, 2)
+            b = [2, 4]
+        MyEnum.a.value #@
+        MyEnum.b.value #@
+        """
+        )
+        inferred_tuple_node = next(tuple_node.infer())
+        inferred_list_node = next(list_node.infer())
+        assert isinstance(inferred_tuple_node, astroid.Tuple)
+        assert isinstance(inferred_list_node, astroid.List)
+        assert inferred_tuple_node.as_string() == "(1, 2)"
+        assert inferred_list_node.as_string() == "[2, 4]"
+
+    def test_enum_starred_is_skipped(self) -> None:
+        code = """
+        from enum import Enum
+        class ContentType(Enum):
+            TEXT, PHOTO, VIDEO, GIF, YOUTUBE, *_ = [1, 2, 3, 4, 5, 6]
+        ContentType.TEXT #@
+        """
+        node = astroid.extract_node(code)
+        next(node.infer())
+
+    def test_enum_name_is_str_on_self(self) -> None:
+        code = """
+        from enum import Enum
+        class TestEnum(Enum):
+            def func(self):
+                self.name #@
+                self.value #@
+        TestEnum.name #@
+        TestEnum.value #@
+        """
+        i_name, i_value, c_name, c_value = astroid.extract_node(code)
+
+        # <instance>.name should be a string, <class>.name should be a property (that
+        # forwards the lookup to __getattr__)
+        inferred = next(i_name.infer())
+        assert isinstance(inferred, nodes.Const)
+        assert inferred.pytype() == "builtins.str"
+        inferred = next(c_name.infer())
+        assert isinstance(inferred, objects.Property)
+
+        # Inferring .value should not raise InferenceError. It is probably Uninferable
+        # but we don't particularly care
+        next(i_value.infer())
+        next(c_value.infer())
+
+    def test_enum_name_and_value_members_override_dynamicclassattr(self) -> None:
+        code = """
+        from enum import Enum
+        class TrickyEnum(Enum):
+            name = 1
+            value = 2
+
+            def func(self):
+                self.name #@
+                self.value #@
+        TrickyEnum.name #@
+        TrickyEnum.value #@
+        """
+        i_name, i_value, c_name, c_value = astroid.extract_node(code)
+
+        # All of these cases should be inferred as enum members
+        inferred = next(i_name.infer())
+        assert isinstance(inferred, bases.Instance)
+        assert inferred.pytype() == ".TrickyEnum.name"
+        inferred = next(c_name.infer())
+        assert isinstance(inferred, bases.Instance)
+        assert inferred.pytype() == ".TrickyEnum.name"
+        inferred = next(i_value.infer())
+        assert isinstance(inferred, bases.Instance)
+        assert inferred.pytype() == ".TrickyEnum.value"
+        inferred = next(c_value.infer())
+        assert isinstance(inferred, bases.Instance)
+        assert inferred.pytype() == ".TrickyEnum.value"
+
+    def test_enum_subclass_member_name(self) -> None:
+        ast_node = astroid.extract_node(
+            """
+        from enum import Enum
+
+        class EnumSubclass(Enum):
+            pass
+
+        class Color(EnumSubclass):
+            red = 1
+
+        Color.red.name #@
+        """
+        )
+        assert isinstance(ast_node, nodes.NodeNG)
+        inferred = ast_node.inferred()
+        self.assertEqual(len(inferred), 1)
+        self.assertIsInstance(inferred[0], astroid.Const)
+        self.assertEqual(inferred[0].value, "red")
+
+    def test_enum_subclass_member_value(self) -> None:
+        ast_node = astroid.extract_node(
+            """
+        from enum import Enum
+
+        class EnumSubclass(Enum):
+            pass
+
+        class Color(EnumSubclass):
+            red = 1
+
+        Color.red.value #@
+        """
+        )
+        assert isinstance(ast_node, nodes.NodeNG)
+        inferred = ast_node.inferred()
+        self.assertEqual(len(inferred), 1)
+        self.assertIsInstance(inferred[0], astroid.Const)
+        self.assertEqual(inferred[0].value, 1)
+
+    def test_enum_subclass_member_method(self) -> None:
+        # See Pylint issue #2626
+        ast_node = astroid.extract_node(
+            """
+        from enum import Enum
+
+        class EnumSubclass(Enum):
+            def hello_pylint(self) -> str:
+                return self.name
+
+        class Color(EnumSubclass):
+            red = 1
+
+        Color.red.hello_pylint()  #@
+        """
+        )
+        assert isinstance(ast_node, nodes.NodeNG)
+        inferred = ast_node.inferred()
+        self.assertEqual(len(inferred), 1)
+        self.assertIsInstance(inferred[0], astroid.Const)
+        self.assertEqual(inferred[0].value, "red")
+
+    def test_enum_subclass_different_modules(self) -> None:
+        # See Pylint issue #2626
+        astroid.extract_node(
+            """
+        from enum import Enum
+
+        class EnumSubclass(Enum):
+            pass
+        """,
+            "a",
+        )
+        ast_node = astroid.extract_node(
+            """
+        from a import EnumSubclass
+
+        class Color(EnumSubclass):
+            red = 1
+
+        Color.red.value #@
+        """
+        )
+        assert isinstance(ast_node, nodes.NodeNG)
+        inferred = ast_node.inferred()
+        self.assertEqual(len(inferred), 1)
+        self.assertIsInstance(inferred[0], astroid.Const)
+        self.assertEqual(inferred[0].value, 1)
+
+    def test_members_member_ignored(self) -> None:
+        ast_node = builder.extract_node(
+            """
+        from enum import Enum
+        class Animal(Enum):
+            a = 1
+            __members__ = {}
+        Animal.__members__ #@
+        """
+        )
+
+        inferred = next(ast_node.infer())
+        self.assertIsInstance(inferred, astroid.Dict)
+        self.assertTrue(inferred.locals)
+
+    def test_enum_as_renamed_import(self) -> None:
+        """Originally reported in https://github.com/PyCQA/pylint/issues/5776."""
+        ast_node: nodes.Attribute = builder.extract_node(
+            """
+        from enum import Enum as PyEnum
+        class MyEnum(PyEnum):
+            ENUM_KEY = "enum_value"
+        MyEnum.ENUM_KEY
+        """
+        )
+        inferred = next(ast_node.infer())
+        assert isinstance(inferred, bases.Instance)
+        assert inferred._proxied.name == "ENUM_KEY"
+
+    def test_class_named_enum(self) -> None:
+        """Test that the user-defined class named `Enum` is not inferred as `enum.Enum`"""
+        astroid.extract_node(
+            """
+        class Enum:
+            def __init__(self, one, two):
+                self.one = one
+                self.two = two
+            def pear(self):
+                ...
+        """,
+            "module_with_class_named_enum",
+        )
+
+        attribute_nodes = astroid.extract_node(
+            """
+        import module_with_class_named_enum
+        module_with_class_named_enum.Enum("apple", "orange") #@
+        typo_module_with_class_named_enum.Enum("apple", "orange") #@
+        """
+        )
+
+        name_nodes = astroid.extract_node(
+            """
+        from module_with_class_named_enum import Enum
+        Enum("apple", "orange") #@
+        TypoEnum("apple", "orange") #@
+        """
+        )
+
+        # Test that both of the successfully inferred `Name` & `Attribute`
+        # nodes refer to the user-defined Enum class.
+        for inferred in (attribute_nodes[0].inferred()[0], name_nodes[0].inferred()[0]):
+            assert isinstance(inferred, astroid.Instance)
+            assert inferred.name == "Enum"
+            assert inferred.qname() == "module_with_class_named_enum.Enum"
+            assert "pear" in inferred.locals
+
+        # Test that an `InferenceError` is raised when an attempt is made to
+        # infer a `Name` or `Attribute` node & they cannot be found.
+        for node in (attribute_nodes[1], name_nodes[1]):
+            with pytest.raises(InferenceError):
+                node.inferred()
diff --git a/tests/brain/test_hashlib.py b/tests/brain/test_hashlib.py
new file mode 100644
index 0000000..84c8b17
--- /dev/null
+++ b/tests/brain/test_hashlib.py
@@ -0,0 +1,64 @@
+# 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 unittest
+
+from astroid import MANAGER
+from astroid.const import PY39_PLUS
+from astroid.nodes.scoped_nodes import ClassDef
+
+
+class HashlibTest(unittest.TestCase):
+    def _assert_hashlib_class(self, class_obj: ClassDef) -> None:
+        self.assertIn("update", class_obj)
+        self.assertIn("digest", class_obj)
+        self.assertIn("hexdigest", class_obj)
+        self.assertIn("block_size", class_obj)
+        self.assertIn("digest_size", class_obj)
+        # usedforsecurity was added in Python 3.9, see 8e7174a9
+        self.assertEqual(len(class_obj["__init__"].args.args), 3 if PY39_PLUS else 2)
+        self.assertEqual(
+            len(class_obj["__init__"].args.defaults), 2 if PY39_PLUS else 1
+        )
+        self.assertEqual(len(class_obj["update"].args.args), 2)
+
+    def test_hashlib(self) -> None:
+        """Tests that brain extensions for hashlib work."""
+        hashlib_module = MANAGER.ast_from_module_name("hashlib")
+        for class_name in (
+            "md5",
+            "sha1",
+            "sha224",
+            "sha256",
+            "sha384",
+            "sha512",
+            "sha3_224",
+            "sha3_256",
+            "sha3_384",
+            "sha3_512",
+        ):
+            class_obj = hashlib_module[class_name]
+            self._assert_hashlib_class(class_obj)
+            self.assertEqual(len(class_obj["digest"].args.args), 1)
+            self.assertEqual(len(class_obj["hexdigest"].args.args), 1)
+
+    def test_shake(self) -> None:
+        """Tests that the brain extensions for the hashlib shake algorithms work."""
+        hashlib_module = MANAGER.ast_from_module_name("hashlib")
+        for class_name in ("shake_128", "shake_256"):
+            class_obj = hashlib_module[class_name]
+            self._assert_hashlib_class(class_obj)
+            self.assertEqual(len(class_obj["digest"].args.args), 2)
+            self.assertEqual(len(class_obj["hexdigest"].args.args), 2)
+
+    def test_blake2(self) -> None:
+        """Tests that the brain extensions for the hashlib blake2 hash functions work."""
+        hashlib_module = MANAGER.ast_from_module_name("hashlib")
+        for class_name in ("blake2b", "blake2s"):
+            class_obj = hashlib_module[class_name]
+            self.assertEqual(len(class_obj["__init__"].args.args), 2)
+            self.assertEqual(len(class_obj["digest"].args.args), 1)
+            self.assertEqual(len(class_obj["hexdigest"].args.args), 1)
diff --git a/tests/brain/test_multiprocessing.py b/tests/brain/test_multiprocessing.py
new file mode 100644
index 0000000..ebcec7f
--- /dev/null
+++ b/tests/brain/test_multiprocessing.py
@@ -0,0 +1,115 @@
+# 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 queue
+import sys
+import unittest
+
+import astroid
+from astroid import builder, nodes
+
+try:
+    import multiprocessing  # pylint: disable=unused-import
+
+    HAS_MULTIPROCESSING = True
+except ImportError:
+    HAS_MULTIPROCESSING = False
+
+
+@unittest.skipUnless(
+    HAS_MULTIPROCESSING,
+    "multiprocesing is required for this test, but "
+    "on some platforms it is missing "
+    "(Jython for instance)",
+)
+class MultiprocessingBrainTest(unittest.TestCase):
+    def test_multiprocessing_module_attributes(self) -> None:
+        # Test that module attributes are working,
+        # especially on Python 3.4+, where they are obtained
+        # from a context.
+        module = builder.extract_node(
+            """
+        import multiprocessing
+        """
+        )
+        assert isinstance(module, nodes.Import)
+        module = module.do_import_module("multiprocessing")
+        cpu_count = next(module.igetattr("cpu_count"))
+        self.assertIsInstance(cpu_count, astroid.BoundMethod)
+
+    def test_module_name(self) -> None:
+        module = builder.extract_node(
+            """
+        import multiprocessing
+        multiprocessing.SyncManager()
+        """
+        )
+        inferred_sync_mgr = next(module.infer())
+        module = inferred_sync_mgr.root()
+        self.assertEqual(module.name, "multiprocessing.managers")
+
+    def test_multiprocessing_manager(self) -> None:
+        # Test that we have the proper attributes
+        # for a multiprocessing.managers.SyncManager
+        module = builder.parse(
+            """
+        import multiprocessing
+        manager = multiprocessing.Manager()
+        queue = manager.Queue()
+        joinable_queue = manager.JoinableQueue()
+        event = manager.Event()
+        rlock = manager.RLock()
+        lock = manager.Lock()
+        bounded_semaphore = manager.BoundedSemaphore()
+        condition = manager.Condition()
+        barrier = manager.Barrier()
+        pool = manager.Pool()
+        list = manager.list()
+        dict = manager.dict()
+        value = manager.Value()
+        array = manager.Array()
+        namespace = manager.Namespace()
+        """
+        )
+        ast_queue = next(module["queue"].infer())
+        self.assertEqual(ast_queue.qname(), f"{queue.__name__}.Queue")
+
+        joinable_queue = next(module["joinable_queue"].infer())
+        self.assertEqual(joinable_queue.qname(), f"{queue.__name__}.Queue")
+
+        event = next(module["event"].infer())
+        event_name = "threading.Event"
+        self.assertEqual(event.qname(), event_name)
+
+        rlock = next(module["rlock"].infer())
+        rlock_name = "threading._RLock"
+        self.assertEqual(rlock.qname(), rlock_name)
+
+        lock = next(module["lock"].infer())
+        lock_name = "threading.lock"
+        self.assertEqual(lock.qname(), lock_name)
+
+        bounded_semaphore = next(module["bounded_semaphore"].infer())
+        semaphore_name = "threading.BoundedSemaphore"
+        self.assertEqual(bounded_semaphore.qname(), semaphore_name)
+
+        pool = next(module["pool"].infer())
+        pool_name = "multiprocessing.pool.Pool"
+        self.assertEqual(pool.qname(), pool_name)
+
+        for attr in ("list", "dict"):
+            obj = next(module[attr].infer())
+            self.assertEqual(obj.qname(), f"builtins.{attr}")
+
+        # pypy's implementation of array.__spec__ return None. This causes problems for this inference.
+        if not hasattr(sys, "pypy_version_info"):
+            array = next(module["array"].infer())
+            self.assertEqual(array.qname(), "array.array")
+
+        manager = next(module["manager"].infer())
+        # Verify that we have these attributes
+        self.assertTrue(manager.getattr("start"))
+        self.assertTrue(manager.getattr("shutdown"))
diff --git a/tests/brain/test_named_tuple.py b/tests/brain/test_named_tuple.py
new file mode 100644
index 0000000..dff042e
--- /dev/null
+++ b/tests/brain/test_named_tuple.py
@@ -0,0 +1,311 @@
+# 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 unittest
+
+import astroid
+from astroid import builder, nodes, util
+from astroid.exceptions import AttributeInferenceError
+
+
+class NamedTupleTest(unittest.TestCase):
+    def test_namedtuple_base(self) -> None:
+        klass = builder.extract_node(
+            """
+        from collections import namedtuple
+
+        class X(namedtuple("X", ["a", "b", "c"])):
+           pass
+        """
+        )
+        assert isinstance(klass, nodes.ClassDef)
+        self.assertEqual(
+            [anc.name for anc in klass.ancestors()], ["X", "tuple", "object"]
+        )
+        # See: https://github.com/PyCQA/pylint/issues/5982
+        self.assertNotIn("X", klass.locals)
+        for anc in klass.ancestors():
+            self.assertFalse(anc.parent is None)
+
+    def test_namedtuple_inference(self) -> None:
+        klass = builder.extract_node(
+            """
+        from collections import namedtuple
+
+        name = "X"
+        fields = ["a", "b", "c"]
+        class X(namedtuple(name, fields)):
+           pass
+        """
+        )
+        assert isinstance(klass, nodes.ClassDef)
+        base = next(base for base in klass.ancestors() if base.name == "X")
+        self.assertSetEqual({"a", "b", "c"}, set(base.instance_attrs))
+
+    def test_namedtuple_inference_failure(self) -> None:
+        klass = builder.extract_node(
+            """
+        from collections import namedtuple
+
+        def foo(fields):
+           return __(namedtuple("foo", fields))
+        """
+        )
+        self.assertIs(util.Uninferable, next(klass.infer()))
+
+    def test_namedtuple_advanced_inference(self) -> None:
+        # urlparse return an object of class ParseResult, which has a
+        # namedtuple call and a mixin as base classes
+        result = builder.extract_node(
+            """
+        from urllib.parse import urlparse
+
+        result = __(urlparse('gopher://'))
+        """
+        )
+        instance = next(result.infer())
+        self.assertGreaterEqual(len(instance.getattr("scheme")), 1)
+        self.assertGreaterEqual(len(instance.getattr("port")), 1)
+        with self.assertRaises(AttributeInferenceError):
+            instance.getattr("foo")
+        self.assertGreaterEqual(len(instance.getattr("geturl")), 1)
+        self.assertEqual(instance.name, "ParseResult")
+
+    def test_namedtuple_instance_attrs(self) -> None:
+        result = builder.extract_node(
+            """
+        from collections import namedtuple
+        namedtuple('a', 'a b c')(1, 2, 3) #@
+        """
+        )
+        inferred = next(result.infer())
+        for name, attr in inferred.instance_attrs.items():
+            self.assertEqual(attr[0].attrname, name)
+
+    def test_namedtuple_uninferable_fields(self) -> None:
+        node = builder.extract_node(
+            """
+        x = [A] * 2
+        from collections import namedtuple
+        l = namedtuple('a', x)
+        l(1)
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIs(util.Uninferable, inferred)
+
+    def test_namedtuple_access_class_fields(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", "field other")
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIn("field", inferred.locals)
+        self.assertIn("other", inferred.locals)
+
+    def test_namedtuple_rename_keywords(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", "abc def", rename=True)
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIn("abc", inferred.locals)
+        self.assertIn("_1", inferred.locals)
+
+    def test_namedtuple_rename_duplicates(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", "abc abc abc", rename=True)
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIn("abc", inferred.locals)
+        self.assertIn("_1", inferred.locals)
+        self.assertIn("_2", inferred.locals)
+
+    def test_namedtuple_rename_uninferable(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", "a b c", rename=UNINFERABLE)
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIn("a", inferred.locals)
+        self.assertIn("b", inferred.locals)
+        self.assertIn("c", inferred.locals)
+
+    def test_namedtuple_func_form(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple(typename="Tuple", field_names="a b c", rename=UNINFERABLE)
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertEqual(inferred.name, "Tuple")
+        self.assertIn("a", inferred.locals)
+        self.assertIn("b", inferred.locals)
+        self.assertIn("c", inferred.locals)
+
+    def test_namedtuple_func_form_args_and_kwargs(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", field_names="a b c", rename=UNINFERABLE)
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertEqual(inferred.name, "Tuple")
+        self.assertIn("a", inferred.locals)
+        self.assertIn("b", inferred.locals)
+        self.assertIn("c", inferred.locals)
+
+    def test_namedtuple_bases_are_actually_names_not_nodes(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", field_names="a b c", rename=UNINFERABLE)
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIsInstance(inferred, astroid.ClassDef)
+        self.assertIsInstance(inferred.bases[0], astroid.Name)
+        self.assertEqual(inferred.bases[0].name, "tuple")
+
+    def test_invalid_label_does_not_crash_inference(self) -> None:
+        code = """
+        import collections
+        a = collections.namedtuple( 'a', ['b c'] )
+        a
+        """
+        node = builder.extract_node(code)
+        inferred = next(node.infer())
+        assert isinstance(inferred, astroid.ClassDef)
+        assert "b" not in inferred.locals
+        assert "c" not in inferred.locals
+
+    def test_no_rename_duplicates_does_not_crash_inference(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", "abc abc")
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
+
+    def test_no_rename_keywords_does_not_crash_inference(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", "abc def")
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
+
+    def test_no_rename_nonident_does_not_crash_inference(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", "123 456")
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
+
+    def test_no_rename_underscore_does_not_crash_inference(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", "_1")
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
+
+    def test_invalid_typename_does_not_crash_inference(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("123", "abc")
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
+
+    def test_keyword_typename_does_not_crash_inference(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("while", "abc")
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIs(util.Uninferable, inferred)  # would raise ValueError
+
+    def test_typeerror_does_not_crash_inference(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        Tuple = namedtuple("Tuple", [123, 456])
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        # namedtuple converts all arguments to strings so these should be too
+        # and catch on the isidentifier() check
+        self.assertIs(util.Uninferable, inferred)
+
+    def test_pathological_str_does_not_crash_inference(self) -> None:
+        node = builder.extract_node(
+            """
+        from collections import namedtuple
+        class Invalid:
+            def __str__(self):
+                return 123  # will raise TypeError
+        Tuple = namedtuple("Tuple", [Invalid()])
+        Tuple #@
+        """
+        )
+        inferred = next(node.infer())
+        self.assertIs(util.Uninferable, inferred)
+
+    def test_name_as_typename(self) -> None:
+        """Reported in https://github.com/PyCQA/pylint/issues/7429 as a crash."""
+        good_node, good_node_two, bad_node = builder.extract_node(
+            """
+            import collections
+            collections.namedtuple(typename="MyTuple", field_names=["birth_date", "city"])  #@
+            collections.namedtuple("MyTuple", field_names=["birth_date", "city"])  #@
+            collections.namedtuple(["birth_date", "city"], typename="MyTuple")  #@
+        """
+        )
+        good_inferred = next(good_node.infer())
+        assert isinstance(good_inferred, nodes.ClassDef)
+        good_node_two_inferred = next(good_node_two.infer())
+        assert isinstance(good_node_two_inferred, nodes.ClassDef)
+        bad_node_inferred = next(bad_node.infer())
+        assert bad_node_inferred == util.Uninferable
diff --git a/tests/brain/test_nose.py b/tests/brain/test_nose.py
new file mode 100644
index 0000000..7b72f28
--- /dev/null
+++ b/tests/brain/test_nose.py
@@ -0,0 +1,45 @@
+# 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 unittest
+import warnings
+
+import astroid
+from astroid import builder
+
+try:
+    with warnings.catch_warnings():
+        warnings.simplefilter("ignore", DeprecationWarning)
+        import nose  # pylint: disable=unused-import
+    HAS_NOSE = True
+except ImportError:
+    HAS_NOSE = False
+
+
+@unittest.skipUnless(HAS_NOSE, "This test requires nose library.")
+class NoseBrainTest(unittest.TestCase):
+    def test_nose_tools(self):
+        methods = builder.extract_node(
+            """
+        from nose.tools import assert_equal
+        from nose.tools import assert_equals
+        from nose.tools import assert_true
+        assert_equal = assert_equal #@
+        assert_true = assert_true #@
+        assert_equals = assert_equals #@
+        """
+        )
+        assert isinstance(methods, list)
+        assert_equal = next(methods[0].value.infer())
+        assert_true = next(methods[1].value.infer())
+        assert_equals = next(methods[2].value.infer())
+
+        self.assertIsInstance(assert_equal, astroid.BoundMethod)
+        self.assertIsInstance(assert_true, astroid.BoundMethod)
+        self.assertIsInstance(assert_equals, astroid.BoundMethod)
+        self.assertEqual(assert_equal.qname(), "unittest.case.TestCase.assertEqual")
+        self.assertEqual(assert_true.qname(), "unittest.case.TestCase.assertTrue")
+        self.assertEqual(assert_equals.qname(), "unittest.case.TestCase.assertEqual")
diff --git a/tests/unittest_brain_pathlib.py b/tests/brain/test_pathlib.py
similarity index 100%
rename from tests/unittest_brain_pathlib.py
rename to tests/brain/test_pathlib.py
diff --git a/tests/brain/test_pytest.py b/tests/brain/test_pytest.py
new file mode 100644
index 0000000..55ecfb2
--- /dev/null
+++ b/tests/brain/test_pytest.py
@@ -0,0 +1,34 @@
+# 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 builder
+
+
+def test_pytest() -> None:
+    ast_node = builder.extract_node(
+        """
+    import pytest
+    pytest #@
+    """
+    )
+    module = next(ast_node.infer())
+    attrs = [
+        "deprecated_call",
+        "warns",
+        "exit",
+        "fail",
+        "skip",
+        "importorskip",
+        "xfail",
+        "mark",
+        "raises",
+        "freeze_includes",
+        "set_trace",
+        "fixture",
+        "yield_fixture",
+    ]
+    for attr in attrs:
+        assert attr in module
diff --git a/tests/unittest_brain_qt.py b/tests/brain/test_qt.py
similarity index 100%
rename from tests/unittest_brain_qt.py
rename to tests/brain/test_qt.py
diff --git a/tests/test_brain_regex.py b/tests/brain/test_regex.py
similarity index 93%
rename from tests/test_brain_regex.py
rename to tests/brain/test_regex.py
index 1fbd59b..0d44074 100644
--- a/tests/test_brain_regex.py
+++ b/tests/brain/test_regex.py
@@ -24,6 +24,9 @@ class TestRegexBrain:
             assert name in re_ast
             assert next(re_ast[name].infer()).value == getattr(regex, name)
 
+    @pytest.mark.xfail(
+        reason="Started failing on main, but no one reproduced locally yet"
+    )
     @test_utils.require_version(minver="3.9")
     def test_regex_pattern_and_match_subscriptable(self):
         """Test regex.Pattern and regex.Match are subscriptable in PY39+."""
diff --git a/tests/unittest_brain_signal.py b/tests/brain/test_signal.py
similarity index 100%
rename from tests/unittest_brain_signal.py
rename to tests/brain/test_signal.py
diff --git a/tests/brain/test_six.py b/tests/brain/test_six.py
new file mode 100644
index 0000000..c9dac56
--- /dev/null
+++ b/tests/brain/test_six.py
@@ -0,0 +1,155 @@
+# 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 unittest
+from typing import Any
+
+import astroid
+from astroid import MANAGER, builder, nodes
+from astroid.nodes.scoped_nodes import ClassDef
+
+try:
+    import six  # pylint: disable=unused-import
+
+    HAS_SIX = True
+except ImportError:
+    HAS_SIX = False
+
+
+@unittest.skipUnless(HAS_SIX, "These tests require the six library")
+class SixBrainTest(unittest.TestCase):
+    def test_attribute_access(self) -> None:
+        ast_nodes = builder.extract_node(
+            """
+        import six
+        six.moves.http_client #@
+        six.moves.urllib_parse #@
+        six.moves.urllib_error #@
+        six.moves.urllib.request #@
+        """
+        )
+        assert isinstance(ast_nodes, list)
+        http_client = next(ast_nodes[0].infer())
+        self.assertIsInstance(http_client, nodes.Module)
+        self.assertEqual(http_client.name, "http.client")
+
+        urllib_parse = next(ast_nodes[1].infer())
+        self.assertIsInstance(urllib_parse, nodes.Module)
+        self.assertEqual(urllib_parse.name, "urllib.parse")
+        urljoin = next(urllib_parse.igetattr("urljoin"))
+        urlencode = next(urllib_parse.igetattr("urlencode"))
+        self.assertIsInstance(urljoin, nodes.FunctionDef)
+        self.assertEqual(urljoin.qname(), "urllib.parse.urljoin")
+        self.assertIsInstance(urlencode, nodes.FunctionDef)
+        self.assertEqual(urlencode.qname(), "urllib.parse.urlencode")
+
+        urllib_error = next(ast_nodes[2].infer())
+        self.assertIsInstance(urllib_error, nodes.Module)
+        self.assertEqual(urllib_error.name, "urllib.error")
+        urlerror = next(urllib_error.igetattr("URLError"))
+        self.assertIsInstance(urlerror, nodes.ClassDef)
+        content_too_short = next(urllib_error.igetattr("ContentTooShortError"))
+        self.assertIsInstance(content_too_short, nodes.ClassDef)
+
+        urllib_request = next(ast_nodes[3].infer())
+        self.assertIsInstance(urllib_request, nodes.Module)
+        self.assertEqual(urllib_request.name, "urllib.request")
+        urlopen = next(urllib_request.igetattr("urlopen"))
+        urlretrieve = next(urllib_request.igetattr("urlretrieve"))
+        self.assertIsInstance(urlopen, nodes.FunctionDef)
+        self.assertEqual(urlopen.qname(), "urllib.request.urlopen")
+        self.assertIsInstance(urlretrieve, nodes.FunctionDef)
+        self.assertEqual(urlretrieve.qname(), "urllib.request.urlretrieve")
+
+    def test_from_imports(self) -> None:
+        ast_node = builder.extract_node(
+            """
+        from six.moves import http_client
+        http_client.HTTPSConnection #@
+        """
+        )
+        inferred = next(ast_node.infer())
+        self.assertIsInstance(inferred, nodes.ClassDef)
+        qname = "http.client.HTTPSConnection"
+        self.assertEqual(inferred.qname(), qname)
+
+    def test_from_submodule_imports(self) -> None:
+        """Make sure ulrlib submodules can be imported from
+
+        See PyCQA/pylint#1640 for relevant issue
+        """
+        ast_node = builder.extract_node(
+            """
+        from six.moves.urllib.parse import urlparse
+        urlparse #@
+        """
+        )
+        inferred = next(ast_node.infer())
+        self.assertIsInstance(inferred, nodes.FunctionDef)
+
+    def test_with_metaclass_subclasses_inheritance(self) -> None:
+        ast_node = builder.extract_node(
+            """
+        class A(type):
+            def test(cls):
+                return cls
+
+        class C:
+            pass
+
+        import six
+        class B(six.with_metaclass(A, C)):
+            pass
+
+        B #@
+        """
+        )
+        inferred = next(ast_node.infer())
+        self.assertIsInstance(inferred, nodes.ClassDef)
+        self.assertEqual(inferred.name, "B")
+        self.assertIsInstance(inferred.bases[0], nodes.Call)
+        ancestors = tuple(inferred.ancestors())
+        self.assertIsInstance(ancestors[0], nodes.ClassDef)
+        self.assertEqual(ancestors[0].name, "C")
+        self.assertIsInstance(ancestors[1], nodes.ClassDef)
+        self.assertEqual(ancestors[1].name, "object")
+
+    @staticmethod
+    def test_six_with_metaclass_enum_ancestor() -> None:
+        code = """
+        import six
+        from enum import Enum, EnumMeta
+
+        class FooMeta(EnumMeta):
+            pass
+
+        class Foo(six.with_metaclass(FooMeta, Enum)):  #@
+            bar = 1
+        """
+        klass = astroid.extract_node(code)
+        assert next(klass.ancestors()).name == "Enum"
+
+    def test_six_with_metaclass_with_additional_transform(self) -> None:
+        def transform_class(cls: Any) -> ClassDef:
+            if cls.name == "A":
+                cls._test_transform = 314
+            return cls
+
+        MANAGER.register_transform(nodes.ClassDef, transform_class)
+        try:
+            ast_node = builder.extract_node(
+                """
+                import six
+                class A(six.with_metaclass(type, object)):
+                    pass
+
+                A #@
+            """
+            )
+            inferred = next(ast_node.infer())
+            assert getattr(inferred, "_test_transform", None) == 314
+        finally:
+            MANAGER.unregister_transform(nodes.ClassDef, transform_class)
diff --git a/tests/test_brain_ssl.py b/tests/brain/test_ssl.py
similarity index 100%
rename from tests/test_brain_ssl.py
rename to tests/brain/test_ssl.py
diff --git a/tests/brain/test_threading.py b/tests/brain/test_threading.py
new file mode 100644
index 0000000..f7da03d
--- /dev/null
+++ b/tests/brain/test_threading.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
+
+from __future__ import annotations
+
+import unittest
+
+import astroid
+from astroid import builder
+from astroid.bases import Instance
+
+
+class ThreadingBrainTest(unittest.TestCase):
+    def test_lock(self) -> None:
+        lock_instance = builder.extract_node(
+            """
+        import threading
+        threading.Lock()
+        """
+        )
+        inferred = next(lock_instance.infer())
+        self.assert_is_valid_lock(inferred)
+
+        acquire_method = inferred.getattr("acquire")[0]
+        parameters = [param.name for param in acquire_method.args.args[1:]]
+        assert parameters == ["blocking", "timeout"]
+
+        assert inferred.getattr("locked")
+
+    def test_rlock(self) -> None:
+        self._test_lock_object("RLock")
+
+    def test_semaphore(self) -> None:
+        self._test_lock_object("Semaphore")
+
+    def test_boundedsemaphore(self) -> None:
+        self._test_lock_object("BoundedSemaphore")
+
+    def _test_lock_object(self, object_name: str) -> None:
+        lock_instance = builder.extract_node(
+            f"""
+        import threading
+        threading.{object_name}()
+        """
+        )
+        inferred = next(lock_instance.infer())
+        self.assert_is_valid_lock(inferred)
+
+    def assert_is_valid_lock(self, inferred: Instance) -> None:
+        self.assertIsInstance(inferred, astroid.Instance)
+        self.assertEqual(inferred.root().name, "threading")
+        for method in ("acquire", "release", "__enter__", "__exit__"):
+            self.assertIsInstance(next(inferred.igetattr(method)), astroid.BoundMethod)
diff --git a/tests/brain/test_typing_extensions.py b/tests/brain/test_typing_extensions.py
new file mode 100644
index 0000000..27ee6ee
--- /dev/null
+++ b/tests/brain/test_typing_extensions.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
+
+from __future__ import annotations
+
+import pytest
+
+from astroid import builder, nodes
+
+try:
+    import typing_extensions  # pylint: disable=unused-import
+
+    HAS_TYPING_EXTENSIONS = True
+    HAS_TYPING_EXTENSIONS_TYPEVAR = hasattr(typing_extensions, "TypeVar")
+except ImportError:
+    HAS_TYPING_EXTENSIONS = False
+    HAS_TYPING_EXTENSIONS_TYPEVAR = False
+
+
+@pytest.mark.skipif(
+    not HAS_TYPING_EXTENSIONS,
+    reason="These tests require the typing_extensions library",
+)
+class TestTypingExtensions:
+    @staticmethod
+    @pytest.mark.skipif(
+        not HAS_TYPING_EXTENSIONS_TYPEVAR,
+        reason="Need typing_extensions>=4.4.0 to test TypeVar",
+    )
+    def test_typing_extensions_types() -> None:
+        ast_nodes = builder.extract_node(
+            """
+        from typing_extensions import TypeVar
+        TypeVar('MyTypeVar', int, float, complex) #@
+        TypeVar('AnyStr', str, bytes) #@
+        """
+        )
+        for node in ast_nodes:
+            inferred = next(node.infer())
+            assert isinstance(inferred, nodes.ClassDef)
diff --git a/tests/unittest_brain_unittest.py b/tests/brain/test_unittest.py
similarity index 100%
rename from tests/unittest_brain_unittest.py
rename to tests/brain/test_unittest.py
diff --git a/tests/unittest_builder.py b/tests/test_builder.py
similarity index 99%
rename from tests/unittest_builder.py
rename to tests/test_builder.py
index 6c0c5fa..0da3f7f 100644
--- a/tests/unittest_builder.py
+++ b/tests/test_builder.py
@@ -1023,7 +1023,3 @@ class HermeticInterpreterTest(unittest.TestCase):
                 my_builder.module_build(
                     self.imported_module, modname=self.imported_module_path.stem
                 )
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_constraint.py b/tests/test_constraint.py
similarity index 100%
rename from tests/unittest_constraint.py
rename to tests/test_constraint.py
diff --git a/tests/unittest_decorators.py b/tests/test_decorators.py
similarity index 100%
rename from tests/unittest_decorators.py
rename to tests/test_decorators.py
diff --git a/tests/unittest_filter_statements.py b/tests/test_filter_statements.py
similarity index 100%
rename from tests/unittest_filter_statements.py
rename to tests/test_filter_statements.py
diff --git a/tests/test_group_exceptions.py b/tests/test_group_exceptions.py
new file mode 100644
index 0000000..11065aa
--- /dev/null
+++ b/tests/test_group_exceptions.py
@@ -0,0 +1,111 @@
+# 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
+
+import pytest
+
+from astroid import (
+    AssignName,
+    ExceptHandler,
+    For,
+    Name,
+    TryExcept,
+    Uninferable,
+    bases,
+    extract_node,
+)
+from astroid.const import PY311_PLUS
+from astroid.context import InferenceContext
+from astroid.nodes import Expr, Raise, TryStar
+
+
+@pytest.mark.skipif(not PY311_PLUS, reason="Requires Python 3.11 or higher")
+def test_group_exceptions() -> None:
+    node = extract_node(
+        textwrap.dedent(
+            """
+        try:
+            raise ExceptionGroup("group", [ValueError(654)])
+        except ExceptionGroup as eg:
+            for err in eg.exceptions:
+                if isinstance(err, ValueError):
+                    print("Handling ValueError")
+                elif isinstance(err, TypeError):
+                    print("Handling TypeError")"""
+        )
+    )
+    assert isinstance(node, TryExcept)
+    handler = node.handlers[0]
+    exception_group_block_range = (1, 4)
+    assert node.block_range(lineno=1) == exception_group_block_range
+    assert node.block_range(lineno=2) == (2, 2)
+    assert node.block_range(lineno=5) == (5, 9)
+    assert isinstance(handler, ExceptHandler)
+    assert handler.type.name == "ExceptionGroup"
+    children = list(handler.get_children())
+    assert len(children) == 3
+    exception_group, short_name, for_loop = children
+    assert isinstance(exception_group, Name)
+    assert exception_group.block_range(1) == exception_group_block_range
+    assert isinstance(short_name, AssignName)
+    assert isinstance(for_loop, For)
+
+
+@pytest.mark.skipif(not PY311_PLUS, reason="Requires Python 3.11 or higher")
+def test_star_exceptions() -> None:
+    code = textwrap.dedent(
+        """
+    try:
+        raise ExceptionGroup("group", [ValueError(654)])
+    except* ValueError:
+        print("Handling ValueError")
+    except* TypeError:
+        print("Handling TypeError")
+    else:
+        sys.exit(127)
+    finally:
+        sys.exit(0)"""
+    )
+    node = extract_node(code)
+    assert isinstance(node, TryStar)
+    assert node.as_string() == code.replace('"', "'").strip()
+    assert isinstance(node.body[0], Raise)
+    assert node.block_range(1) == (1, 11)
+    assert node.block_range(2) == (2, 2)
+    assert node.block_range(3) == (3, 3)
+    assert node.block_range(4) == (4, 4)
+    assert node.block_range(5) == (5, 5)
+    assert node.block_range(6) == (6, 6)
+    assert node.block_range(7) == (7, 7)
+    assert node.block_range(8) == (8, 8)
+    assert node.block_range(9) == (9, 9)
+    assert node.block_range(10) == (10, 10)
+    assert node.block_range(11) == (11, 11)
+    assert node.handlers
+    handler = node.handlers[0]
+    assert isinstance(handler, ExceptHandler)
+    assert handler.type.name == "ValueError"
+    orelse = node.orelse[0]
+    assert isinstance(orelse, Expr)
+    assert orelse.value.args[0].value == 127
+    final = node.finalbody[0]
+    assert isinstance(final, Expr)
+    assert final.value.args[0].value == 0
+
+
+@pytest.mark.skipif(not PY311_PLUS, reason="Requires Python 3.11 or higher")
+def test_star_exceptions_infer_name() -> None:
+    trystar = extract_node(
+        """
+try:
+    1/0
+except* ValueError:
+    pass"""
+    )
+    name = "arbitraryName"
+    context = InferenceContext()
+    context.lookupname = name
+    stmts = bases._infer_stmts([trystar], context)
+    assert list(stmts) == [Uninferable]
+    assert context.lookupname == name
diff --git a/tests/unittest_helpers.py b/tests/test_helpers.py
similarity index 99%
rename from tests/unittest_helpers.py
rename to tests/test_helpers.py
index e0da009..90182a2 100644
--- a/tests/unittest_helpers.py
+++ b/tests/test_helpers.py
@@ -42,6 +42,7 @@ class TestHelpers(unittest.TestCase):
             ("type", self._extract("type")),
             ("object", self._extract("type")),
             ("object()", self._extract("object")),
+            ("super()", self._extract("super")),
             ("lambda: None", self._build_custom_builtin("function")),
             ("len", self._build_custom_builtin("builtin_function_or_method")),
             ("None", self._build_custom_builtin("NoneType")),
@@ -258,7 +259,3 @@ class TestHelpers(unittest.TestCase):
         builtin_type = self._extract("type")
         self.assertTrue(helpers.is_supertype(builtin_type, cls_a))
         self.assertTrue(helpers.is_subtype(cls_a, builtin_type))
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_inference.py b/tests/test_inference.py
similarity index 99%
rename from tests/unittest_inference.py
rename to tests/test_inference.py
index 5aebd04..86fdbcf 100644
--- a/tests/unittest_inference.py
+++ b/tests/test_inference.py
@@ -4048,6 +4048,11 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
             inferred = next(node.infer())
             self.assertRaises(InferenceError, next, inferred.infer_call_result(node))
 
+    def test_infer_call_result_with_metaclass(self) -> None:
+        node = extract_node("def with_metaclass(meta, *bases): return 42")
+        inferred = next(node.infer_call_result(caller=node))
+        self.assertIsInstance(inferred, nodes.Const)
+
     def test_context_call_for_context_managers(self) -> None:
         ast_nodes = extract_node(
             """
@@ -7097,7 +7102,3 @@ class TestOldStyleStringFormatting:
         inferred = next(node.infer())
         assert isinstance(inferred, nodes.Const)
         assert inferred.value == "My name is Daniel, I'm 12.00"
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_inference_calls.py b/tests/test_inference_calls.py
similarity index 100%
rename from tests/unittest_inference_calls.py
rename to tests/test_inference_calls.py
diff --git a/tests/unittest_lookup.py b/tests/test_lookup.py
similarity index 99%
rename from tests/unittest_lookup.py
rename to tests/test_lookup.py
index 835b315..cc882e6 100644
--- a/tests/unittest_lookup.py
+++ b/tests/test_lookup.py
@@ -1008,7 +1008,3 @@ class LookupControlFlowTest(unittest.TestCase):
         _, stmts = x_name.lookup("x")
         self.assertEqual(len(stmts), 1)
         self.assertEqual(stmts[0].lineno, 8)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_manager.py b/tests/test_manager.py
similarity index 97%
rename from tests/unittest_manager.py
rename to tests/test_manager.py
index f35b94e..19efc73 100644
--- a/tests/unittest_manager.py
+++ b/tests/test_manager.py
@@ -7,6 +7,7 @@ import site
 import sys
 import time
 import unittest
+import warnings
 from collections.abc import Iterator
 from contextlib import contextmanager
 from unittest import mock
@@ -22,7 +23,7 @@ from astroid.exceptions import (
     AttributeInferenceError,
 )
 from astroid.interpreter._import import util
-from astroid.modutils import EXT_LIB_DIRS, is_standard_module
+from astroid.modutils import EXT_LIB_DIRS, module_in_path
 from astroid.nodes import Const
 from astroid.nodes.scoped_nodes import ClassDef
 
@@ -383,6 +384,15 @@ class AstroidManagerTest(
             self.manager.ast_from_module_name(None)
 
 
+class IsolatedAstroidManagerTest(resources.AstroidCacheSetupMixin, unittest.TestCase):
+    def test_no_user_warning(self):
+        mgr = manager.AstroidManager()
+        with warnings.catch_warnings():
+            warnings.filterwarnings("error", category=UserWarning)
+            mgr.ast_from_module_name("setuptools")
+            mgr.ast_from_module_name("pip")
+
+
 class BorgAstroidManagerTC(unittest.TestCase):
     def test_borg(self) -> None:
         """Test that the AstroidManager is really a borg, i.e. that two different
@@ -411,7 +421,7 @@ class ClearCacheTest(unittest.TestCase):
 
         # Generate some hits and misses
         ClassDef().lookup("garbage")
-        is_standard_module("unittest", std_path=["garbage_path"])
+        module_in_path("unittest", "garbage_path")
         util.is_namespace("unittest")
         astroid.interpreter.objectmodel.ObjectModel().attributes()
         with pytest.raises(AttributeInferenceError):
@@ -430,6 +440,8 @@ class ClearCacheTest(unittest.TestCase):
 
         astroid.MANAGER.clear_cache()  # also calls bootstrap()
 
+        self.assertEqual(astroid.context._INFERENCE_CACHE, {})
+
         # The cache sizes are now as low or lower than the original baseline
         cleared_cache_infos = [lru.cache_info() for lru in lrus]
         for cleared_cache, baseline_cache in zip(
@@ -457,7 +469,3 @@ class ClearCacheTest(unittest.TestCase):
         isinstance_call = astroid.extract_node("isinstance(1, int)")
         inferred = next(isinstance_call.infer())
         self.assertIs(inferred.value, True)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_modutils.py b/tests/test_modutils.py
similarity index 70%
rename from tests/unittest_modutils.py
rename to tests/test_modutils.py
index ab1acaa..04f5eee 100644
--- a/tests/unittest_modutils.py
+++ b/tests/test_modutils.py
@@ -20,6 +20,7 @@ from pytest import CaptureFixture, LogCaptureFixture
 
 import astroid
 from astroid import modutils
+from astroid.const import PY310_PLUS
 from astroid.interpreter._import import spec
 
 from . import resources
@@ -27,9 +28,9 @@ from . import resources
 try:
     import urllib3  # pylint: disable=unused-import
 
-    HAS_URLLIB3 = True
+    HAS_URLLIB3_V1 = urllib3.__version__.startswith("1")
 except ImportError:
-    HAS_URLLIB3 = False
+    HAS_URLLIB3_V1 = False
 
 
 def _get_file_from_object(obj) -> str:
@@ -287,7 +288,7 @@ class GetSourceFileTest(unittest.TestCase):
         self.assertRaises(modutils.NoSourceFile, modutils.get_source_file, "whatever")
 
 
-class StandardLibModuleTest(resources.SysPathSetup, unittest.TestCase):
+class IsStandardModuleTest(resources.SysPathSetup, unittest.TestCase):
     """
     Return true if the module may be considered as a module from the standard
     library.
@@ -296,50 +297,153 @@ class StandardLibModuleTest(resources.SysPathSetup, unittest.TestCase):
     def test_datetime(self) -> None:
         # This is an interesting example, since datetime, on pypy,
         # is under lib_pypy, rather than the usual Lib directory.
-        self.assertTrue(modutils.is_standard_module("datetime"))
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("datetime")
 
     def test_builtins(self) -> None:
-        self.assertFalse(modutils.is_standard_module("__builtin__"))
-        self.assertTrue(modutils.is_standard_module("builtins"))
+        with pytest.warns(DeprecationWarning):
+            assert not modutils.is_standard_module("__builtin__")
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("builtins")
 
     def test_builtin(self) -> None:
-        self.assertTrue(modutils.is_standard_module("sys"))
-        self.assertTrue(modutils.is_standard_module("marshal"))
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("sys")
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("marshal")
 
     def test_nonstandard(self) -> None:
-        self.assertFalse(modutils.is_standard_module("astroid"))
+        with pytest.warns(DeprecationWarning):
+            assert not modutils.is_standard_module("astroid")
 
     def test_unknown(self) -> None:
-        self.assertFalse(modutils.is_standard_module("unknown"))
+        with pytest.warns(DeprecationWarning):
+            assert not modutils.is_standard_module("unknown")
 
     def test_4(self) -> None:
-        self.assertTrue(modutils.is_standard_module("hashlib"))
-        self.assertTrue(modutils.is_standard_module("pickle"))
-        self.assertTrue(modutils.is_standard_module("email"))
-        self.assertTrue(modutils.is_standard_module("io"))
-        self.assertFalse(modutils.is_standard_module("StringIO"))
-        self.assertTrue(modutils.is_standard_module("unicodedata"))
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("hashlib")
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("pickle")
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("email")
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("io")
+        with pytest.warns(DeprecationWarning):
+            assert not modutils.is_standard_module("StringIO")
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("unicodedata")
 
     def test_custom_path(self) -> None:
         datadir = resources.find("")
         if any(datadir.startswith(p) for p in modutils.EXT_LIB_DIRS):
             self.skipTest("known breakage of is_standard_module on installed package")
 
-        self.assertTrue(modutils.is_standard_module("data.module", (datadir,)))
-        self.assertTrue(
-            modutils.is_standard_module("data.module", (os.path.abspath(datadir),))
-        )
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("data.module", (datadir,))
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module(
+                "data.module", (os.path.abspath(datadir),)
+            )
         # "" will evaluate to cwd
-        self.assertTrue(modutils.is_standard_module("data.module", ("",)))
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("data.module", ("",))
 
     def test_failing_edge_cases(self) -> None:
         # using a subpackage/submodule path as std_path argument
-        self.assertFalse(modutils.is_standard_module("xml.etree", etree.__path__))
+        with pytest.warns(DeprecationWarning):
+            assert not modutils.is_standard_module("xml.etree", etree.__path__)
+        # using a module + object name as modname argument
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("sys.path")
+        # this is because only the first package/module is considered
+        with pytest.warns(DeprecationWarning):
+            assert modutils.is_standard_module("sys.whatever")
+        with pytest.warns(DeprecationWarning):
+            assert not modutils.is_standard_module("xml.whatever", etree.__path__)
+
+
+class IsStdLibModuleTest(resources.SysPathSetup, unittest.TestCase):
+    """
+    Return true if the module is path of the standard library
+    """
+
+    def test_datetime(self) -> None:
+        # This is an interesting example, since datetime, on pypy,
+        # is under lib_pypy, rather than the usual Lib directory.
+        assert modutils.is_stdlib_module("datetime")
+
+    def test_builtins(self) -> None:
+        assert not modutils.is_stdlib_module("__builtin__")
+        assert modutils.is_stdlib_module("builtins")
+
+    def test_builtin(self) -> None:
+        assert modutils.is_stdlib_module("sys")
+        assert modutils.is_stdlib_module("marshal")
+
+    def test_nonstandard(self) -> None:
+        assert not modutils.is_stdlib_module("astroid")
+
+    def test_unknown(self) -> None:
+        assert not modutils.is_stdlib_module("unknown")
+
+    def test_4(self) -> None:
+        assert modutils.is_stdlib_module("hashlib")
+        assert modutils.is_stdlib_module("pickle")
+        assert modutils.is_stdlib_module("email")
+        assert modutils.is_stdlib_module("io")
+        assert not modutils.is_stdlib_module("StringIO")
+        assert modutils.is_stdlib_module("unicodedata")
+
+    def test_subpackages(self) -> None:
         # using a module + object name as modname argument
-        self.assertTrue(modutils.is_standard_module("sys.path"))
+        assert modutils.is_stdlib_module("sys.path")
         # this is because only the first package/module is considered
-        self.assertTrue(modutils.is_standard_module("sys.whatever"))
-        self.assertFalse(modutils.is_standard_module("xml.whatever", etree.__path__))
+        assert modutils.is_stdlib_module("sys.whatever")
+
+    def test_platform_specific(self) -> None:
+        assert modutils.is_stdlib_module("_curses")
+        assert modutils.is_stdlib_module("msvcrt")
+        assert modutils.is_stdlib_module("termios")
+
+
+class ModuleInPathTest(resources.SysPathSetup, unittest.TestCase):
+    """
+    Return true if the module is imported from the specified path
+    """
+
+    def test_success(self) -> None:
+        datadir = resources.find("")
+        assert modutils.module_in_path("data.module", datadir)
+        assert modutils.module_in_path("data.module", (datadir,))
+        assert modutils.module_in_path("data.module", os.path.abspath(datadir))
+        # "" will evaluate to cwd
+        assert modutils.module_in_path("data.module", "")
+
+    def test_bad_import(self) -> None:
+        datadir = resources.find("")
+        assert not modutils.module_in_path("this_module_is_no_more", datadir)
+
+    def test_no_filename(self) -> None:
+        datadir = resources.find("")
+        assert not modutils.module_in_path("sys", datadir)
+
+    def test_failure(self) -> None:
+        datadir = resources.find("")
+        assert not modutils.module_in_path("etree", datadir)
+        assert not modutils.module_in_path("astroid", datadir)
+
+
+class BackportStdlibNamesTest(resources.SysPathSetup, unittest.TestCase):
+    """
+    Verify backport raises exception on newer versions
+    """
+
+    @pytest.mark.skipif(not PY310_PLUS, reason="Backport valid on <=3.9")
+    def test_import_error(self) -> None:
+        with pytest.raises(AssertionError):
+            # pylint: disable-next=import-outside-toplevel, unused-import
+            from astroid import _backport_stdlib_names  # noqa
 
 
 class IsRelativeTest(unittest.TestCase):
@@ -443,14 +547,19 @@ class ExtensionPackageWhitelistTest(unittest.TestCase):
         )
 
 
-@pytest.mark.skipif(not HAS_URLLIB3, reason="This test requires urllib3.")
+@pytest.mark.skipif(not HAS_URLLIB3_V1, reason="This test requires urllib3 < 2.")
 def test_file_info_from_modpath__SixMetaPathImporter() -> None:
-    pytest.raises(
-        ImportError,
-        modutils.file_info_from_modpath,
-        ["urllib3.packages.six.moves.http_client"],
-    )
+    """Six is not backported anymore in urllib3 v2.0.0+"""
+    assert modutils.file_info_from_modpath(["urllib3.packages.six.moves.http_client"])
 
 
-if __name__ == "__main__":
-    unittest.main()
+def test_find_setuptools_pep660_editable_install():
+    """Find the spec for a package installed via setuptools PEP 660 import hooks."""
+    # pylint: disable-next=import-outside-toplevel
+    from tests.testdata.python3.data.import_setuptools_pep660.__editable___example_0_1_0_finder import (
+        _EditableFinder,
+    )
+
+    with unittest.mock.patch.object(sys, "meta_path", new=[_EditableFinder]):
+        assert spec.find_spec(["example"])
+        assert spec.find_spec(["example", "subpackage"])
diff --git a/tests/unittest_nodes.py b/tests/test_nodes.py
similarity index 98%
rename from tests/unittest_nodes.py
rename to tests/test_nodes.py
index e0ef916..0633434 100644
--- a/tests/unittest_nodes.py
+++ b/tests/test_nodes.py
@@ -869,7 +869,22 @@ class ArgumentsNodeTC(unittest.TestCase):
         """
         )
         args = ast["func"].args
-        self.assertTrue(args.is_argument("x"))
+        assert isinstance(args, nodes.Arguments)
+        assert args.is_argument("x")
+        assert args.kw_defaults == [None]
+
+        ast = builder.parse(
+            """
+            def func(*, x = "default"):
+                pass
+        """
+        )
+        args = ast["func"].args
+        assert isinstance(args, nodes.Arguments)
+        assert args.is_argument("x")
+        assert len(args.kw_defaults) == 1
+        assert isinstance(args.kw_defaults[0], nodes.Const)
+        assert args.kw_defaults[0].value == "default"
 
     @test_utils.require_version(minver="3.8")
     def test_positional_only(self):
@@ -1933,6 +1948,21 @@ class TestPatternMatching:
             *case1.pattern.kwd_patterns,
         ]
 
-
-if __name__ == "__main__":
-    unittest.main()
+    @staticmethod
+    def test_return_from_match():
+        code = textwrap.dedent(
+            """
+        def return_from_match(x):
+            match x:
+                case 10:
+                    return 10
+                case _:
+                    return -1
+
+        return_from_match(10)  #@
+        """
+        ).strip()
+        node = builder.extract_node(code)
+        inferred = node.inferred()
+        assert len(inferred) == 2
+        assert [inf.value for inf in inferred] == [10, -1]
diff --git a/tests/unittest_nodes_lineno.py b/tests/test_nodes_lineno.py
similarity index 100%
rename from tests/unittest_nodes_lineno.py
rename to tests/test_nodes_lineno.py
diff --git a/tests/unittest_nodes_position.py b/tests/test_nodes_position.py
similarity index 100%
rename from tests/unittest_nodes_position.py
rename to tests/test_nodes_position.py
diff --git a/tests/unittest_object_model.py b/tests/test_object_model.py
similarity index 99%
rename from tests/unittest_object_model.py
rename to tests/test_object_model.py
index 732593d..8f41eda 100644
--- a/tests/unittest_object_model.py
+++ b/tests/test_object_model.py
@@ -832,7 +832,3 @@ class LruCacheModelTest(unittest.TestCase):
         self.assertEqual(wrapped.name, "foo")
         cache_info = next(ast_nodes[2].infer())
         self.assertIsInstance(cache_info, astroid.Instance)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_objects.py b/tests/test_objects.py
similarity index 99%
rename from tests/unittest_objects.py
rename to tests/test_objects.py
index 6e62d7d..d5994cc 100644
--- a/tests/unittest_objects.py
+++ b/tests/test_objects.py
@@ -587,7 +587,3 @@ class SuperTests(unittest.TestCase):
         assert isinstance(next(init_node[1].infer()), bases.BoundMethod)
         assert isinstance(next(init_node[2].infer()), bases.BoundMethod)
         assert isinstance(next(init_node[3].infer()), bases.BoundMethod)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_protocols.py b/tests/test_protocols.py
similarity index 99%
rename from tests/unittest_protocols.py
rename to tests/test_protocols.py
index cde7465..48351bc 100644
--- a/tests/unittest_protocols.py
+++ b/tests/test_protocols.py
@@ -16,7 +16,7 @@ from astroid import extract_node, nodes
 from astroid.const import PY38_PLUS, PY310_PLUS
 from astroid.exceptions import InferenceError
 from astroid.manager import AstroidManager
-from astroid.util import Uninferable
+from astroid.util import Uninferable, UninferableBase
 
 
 @contextlib.contextmanager
@@ -125,7 +125,7 @@ class ProtocolTests(unittest.TestCase):
         assert isinstance(assigned, astroid.List)
         assert assigned.as_string() == "[1, 2]"
 
-    def _get_starred_stmts(self, code: str) -> list | Uninferable:
+    def _get_starred_stmts(self, code: str) -> list | UninferableBase:
         assign_stmt = extract_node(f"{code} #@")
         starred = next(assign_stmt.nodes_of_class(nodes.Starred))
         return next(starred.assigned_stmts())
@@ -136,7 +136,7 @@ class ProtocolTests(unittest.TestCase):
         stmts = stmts.elts
         self.assertConstNodesEqual(expected, stmts)
 
-    def _helper_starred_expected(self, code: str, expected: Uninferable) -> None:
+    def _helper_starred_expected(self, code: str, expected: UninferableBase) -> None:
         stmts = self._get_starred_stmts(code)
         self.assertEqual(expected, stmts)
 
@@ -409,7 +409,3 @@ class TestPatternMatching:
         assert match_as.name
         assigned_match_as = next(match_as.name.assigned_stmts())
         assert assigned_match_as == subject
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_python3.py b/tests/test_python3.py
similarity index 99%
rename from tests/unittest_python3.py
rename to tests/test_python3.py
index bf0a0b3..07bccc5 100644
--- a/tests/unittest_python3.py
+++ b/tests/test_python3.py
@@ -399,7 +399,3 @@ class Python3TC(unittest.TestCase):
             )
             func = extract_node(code)
             self.assertEqual(func.as_string().strip(), code.strip())
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_raw_building.py b/tests/test_raw_building.py
similarity index 99%
rename from tests/unittest_raw_building.py
rename to tests/test_raw_building.py
index ad0f0af..cfb5d1b 100644
--- a/tests/unittest_raw_building.py
+++ b/tests/test_raw_building.py
@@ -162,7 +162,3 @@ def test_build_module_getattr_catch_output(
     assert expected_err in caplog.text
     assert not out
     assert not err
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_regrtest.py b/tests/test_regrtest.py
similarity index 99%
rename from tests/unittest_regrtest.py
rename to tests/test_regrtest.py
index 587255f..783f1cc 100644
--- a/tests/unittest_regrtest.py
+++ b/tests/test_regrtest.py
@@ -439,7 +439,3 @@ def test_recursion_during_inference(mocked) -> None:
     with pytest.raises(InferenceError) as error:
         next(node.infer())
     assert error.value.message.startswith("RecursionError raised")
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_scoped_nodes.py b/tests/test_scoped_nodes.py
similarity index 99%
rename from tests/unittest_scoped_nodes.py
rename to tests/test_scoped_nodes.py
index c59bdc3..2722c56 100644
--- a/tests/unittest_scoped_nodes.py
+++ b/tests/test_scoped_nodes.py
@@ -1403,6 +1403,20 @@ class ClassNodeTest(ModuleLoader, unittest.TestCase):
         self.assertEqual(["object"], [base.name for base in klass.ancestors()])
         self.assertEqual("type", klass.metaclass().name)
 
+    @unittest.skipUnless(HAS_SIX, "These tests require the six library")
+    def test_metaclass_generator_hack_enum_base(self):
+        """Regression test for https://github.com/PyCQA/pylint/issues/5935"""
+        klass = builder.extract_node(
+            """
+            import six
+            from enum import Enum, EnumMeta
+
+            class PetEnumPy2Metaclass(six.with_metaclass(EnumMeta, Enum)): #@
+                DOG = "dog"
+        """
+        )
+        self.assertEqual(list(klass.local_attr_ancestors("DOG")), [])
+
     def test_add_metaclass(self) -> None:
         klass = builder.extract_node(
             """
@@ -2891,7 +2905,3 @@ def test_deprecation_of_doc_attribute() -> None:
         node_class = nodes.ClassDef(name="MyClass", doc="Docstring")
         node_func = nodes.FunctionDef(name="MyFunction", doc="Docstring")
         assert len(records) == 3
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_transforms.py b/tests/test_transforms.py
similarity index 99%
rename from tests/unittest_transforms.py
rename to tests/test_transforms.py
index ce44d23..0f5b9c6 100644
--- a/tests/unittest_transforms.py
+++ b/tests/test_transforms.py
@@ -238,7 +238,3 @@ class TestTransforms(unittest.TestCase):
                 import UserDict
         """
         )
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/unittest_utils.py b/tests/test_utils.py
similarity index 54%
rename from tests/unittest_utils.py
rename to tests/test_utils.py
index 1b8d712..417b0dc 100644
--- a/tests/unittest_utils.py
+++ b/tests/test_utils.py
@@ -4,7 +4,10 @@
 
 import unittest
 
-from astroid import Uninferable, builder, nodes
+import pytest
+
+from astroid import Uninferable, builder, extract_node, nodes
+from astroid.const import PY38_PLUS
 from astroid.exceptions import InferenceError
 
 
@@ -30,6 +33,78 @@ class InferenceUtil(unittest.TestCase):
         self.assertEqual(nodes.are_exclusive(xass1, xnames[1]), False)
         self.assertEqual(nodes.are_exclusive(xass1, xnames[2]), False)
 
+    @pytest.mark.skipif(not PY38_PLUS, reason="needs assignment expressions")
+    def test_not_exclusive_walrus_operator(self) -> None:
+        node_if, node_body, node_or_else = extract_node(
+            """
+        if val := True:  #@
+            print(val)  #@
+        else:
+            print(val)  #@
+        """
+        )
+        node_if: nodes.If
+        node_walrus = next(node_if.nodes_of_class(nodes.NamedExpr))
+
+        assert nodes.are_exclusive(node_walrus, node_if) is False
+        assert nodes.are_exclusive(node_walrus, node_body) is False
+        assert nodes.are_exclusive(node_walrus, node_or_else) is False
+
+        assert nodes.are_exclusive(node_if, node_body) is False
+        assert nodes.are_exclusive(node_if, node_or_else) is False
+        assert nodes.are_exclusive(node_body, node_or_else) is True
+
+    @pytest.mark.skipif(not PY38_PLUS, reason="needs assignment expressions")
+    def test_not_exclusive_walrus_multiple(self) -> None:
+        node_if, body_1, body_2, or_else_1, or_else_2 = extract_node(
+            """
+        if (val := True) or (val_2 := True):  #@
+            print(val)  #@
+            print(val_2)  #@
+        else:
+            print(val)  #@
+            print(val_2)  #@
+        """
+        )
+        node_if: nodes.If
+        walruses = list(node_if.nodes_of_class(nodes.NamedExpr))
+
+        assert nodes.are_exclusive(node_if, walruses[0]) is False
+        assert nodes.are_exclusive(node_if, walruses[1]) is False
+
+        assert nodes.are_exclusive(walruses[0], walruses[1]) is False
+
+        assert nodes.are_exclusive(walruses[0], body_1) is False
+        assert nodes.are_exclusive(walruses[0], body_2) is False
+        assert nodes.are_exclusive(walruses[1], body_1) is False
+        assert nodes.are_exclusive(walruses[1], body_2) is False
+
+        assert nodes.are_exclusive(walruses[0], or_else_1) is False
+        assert nodes.are_exclusive(walruses[0], or_else_2) is False
+        assert nodes.are_exclusive(walruses[1], or_else_1) is False
+        assert nodes.are_exclusive(walruses[1], or_else_2) is False
+
+    @pytest.mark.skipif(not PY38_PLUS, reason="needs assignment expressions")
+    def test_not_exclusive_walrus_operator_nested(self) -> None:
+        node_if, node_body, node_or_else = extract_node(
+            """
+        if all((last_val := i) % 2 == 0 for i in range(10)): #@
+            print(last_val)  #@
+        else:
+            print(last_val)  #@
+        """
+        )
+        node_if: nodes.If
+        node_walrus = next(node_if.nodes_of_class(nodes.NamedExpr))
+
+        assert nodes.are_exclusive(node_walrus, node_if) is False
+        assert nodes.are_exclusive(node_walrus, node_body) is False
+        assert nodes.are_exclusive(node_walrus, node_or_else) is False
+
+        assert nodes.are_exclusive(node_if, node_body) is False
+        assert nodes.are_exclusive(node_if, node_or_else) is False
+        assert nodes.are_exclusive(node_body, node_or_else) is True
+
     def test_if(self) -> None:
         module = builder.parse(
             """
@@ -111,7 +186,3 @@ class InferenceUtil(unittest.TestCase):
         inferred = next(node.infer())
         with self.assertRaises(InferenceError):
             list(nodes.unpack_infer(inferred))
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/testdata/python3/data/import_setuptools_pep660/__editable___example_0_1_0_finder.py b/tests/testdata/python3/data/import_setuptools_pep660/__editable___example_0_1_0_finder.py
new file mode 100644
index 0000000..7e324f4
--- /dev/null
+++ b/tests/testdata/python3/data/import_setuptools_pep660/__editable___example_0_1_0_finder.py
@@ -0,0 +1,72 @@
+"""This file contains Finders automatically generated by setuptools for a package installed
+in editable mode via custom import hooks. It's generated here:
+https://github.com/pypa/setuptools/blob/c34b82735c1a9c8707bea00705ae2f621bf4c24d/setuptools/command/editable_wheel.py#L732-L801
+"""
+import sys
+from importlib.machinery import ModuleSpec
+from importlib.machinery import all_suffixes as module_suffixes
+from importlib.util import spec_from_file_location
+from itertools import chain
+from pathlib import Path
+
+MAPPING = {"example": Path(__file__).parent.resolve() / "example"}
+NAMESPACES = {}
+PATH_PLACEHOLDER = "__editable__.example-0.1.0.finder" + ".__path_hook__"
+
+
+class _EditableFinder:  # MetaPathFinder
+    @classmethod
+    def find_spec(cls, fullname, path=None, target=None):
+        for pkg, pkg_path in reversed(list(MAPPING.items())):
+            if fullname == pkg or fullname.startswith(f"{pkg}."):
+                rest = fullname.replace(pkg, "", 1).strip(".").split(".")
+                return cls._find_spec(fullname, Path(pkg_path, *rest))
+
+        return None
+
+    @classmethod
+    def _find_spec(cls, fullname, candidate_path):
+        init = candidate_path / "__init__.py"
+        candidates = (candidate_path.with_suffix(x) for x in module_suffixes())
+        for candidate in chain([init], candidates):
+            if candidate.exists():
+                return spec_from_file_location(fullname, candidate)
+
+
+class _EditableNamespaceFinder:  # PathEntryFinder
+    @classmethod
+    def _path_hook(cls, path):
+        if path == PATH_PLACEHOLDER:
+            return cls
+        raise ImportError
+
+    @classmethod
+    def _paths(cls, fullname):
+        # Ensure __path__ is not empty for the spec to be considered a namespace.
+        return NAMESPACES[fullname] or MAPPING.get(fullname) or [PATH_PLACEHOLDER]
+
+    @classmethod
+    def find_spec(cls, fullname, target=None):
+        if fullname in NAMESPACES:
+            spec = ModuleSpec(fullname, None, is_package=True)
+            spec.submodule_search_locations = cls._paths(fullname)
+            return spec
+        return None
+
+    @classmethod
+    def find_module(cls, fullname):
+        return None
+
+
+def install():
+    if not any(finder == _EditableFinder for finder in sys.meta_path):
+        sys.meta_path.append(_EditableFinder)
+
+    if not NAMESPACES:
+        return
+
+    if not any(hook == _EditableNamespaceFinder._path_hook for hook in sys.path_hooks):
+        # PathEntryFinder is needed to create NamespaceSpec without private APIS
+        sys.path_hooks.append(_EditableNamespaceFinder._path_hook)
+    if PATH_PLACEHOLDER not in sys.path:
+        sys.path.append(PATH_PLACEHOLDER)  # Used just to trigger the path hook
diff --git a/tests/testdata/python3/data/import_setuptools_pep660/example/__init__.py b/tests/testdata/python3/data/import_setuptools_pep660/example/__init__.py
new file mode 100644
index 0000000..643085b
--- /dev/null
+++ b/tests/testdata/python3/data/import_setuptools_pep660/example/__init__.py
@@ -0,0 +1 @@
+from subpackage import hello
diff --git a/tests/testdata/python3/data/import_setuptools_pep660/example/subpackage/__init__.py b/tests/testdata/python3/data/import_setuptools_pep660/example/subpackage/__init__.py
new file mode 100644
index 0000000..d750169
--- /dev/null
+++ b/tests/testdata/python3/data/import_setuptools_pep660/example/subpackage/__init__.py
@@ -0,0 +1 @@
+hello = 1
diff --git a/tests/testdata/python3/data/path_pkg_resources_1/package/__init__.py b/tests/testdata/python3/data/path_pkg_resources_1/package/__init__.py
index b0d6433..f7bd573 100644
--- a/tests/testdata/python3/data/path_pkg_resources_1/package/__init__.py
+++ b/tests/testdata/python3/data/path_pkg_resources_1/package/__init__.py
@@ -1 +1,5 @@
-__import__('pkg_resources').declare_namespace(__name__)
\ No newline at end of file
+import warnings
+
+with warnings.catch_warnings():
+    warnings.simplefilter("ignore", DeprecationWarning)
+    __import__("pkg_resources").declare_namespace(__name__)
diff --git a/tox.ini b/tox.ini
index 6f77def..4729e48 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,67 +1,21 @@
 [tox]
-envlist = py{37,38,39,310}
+envlist = py{37,38,39,310,311}
 skip_missing_interpreters = true
 isolated_build = true
 
-[testenv:pylint]
-deps =
-   # We do not use the latest pylint version in CI tests as we want to choose when
-   # we fix the warnings
-   git+https://github.com/pycqa/pylint@main
-   pre-commit~=2.13
-  -r requirements_test_min.txt
-commands = pre-commit run pylint --all-files
-
 [testenv]
 deps =
-  -r requirements_test_min.txt
+  -r requirements_test.txt
   -r requirements_test_brain.txt
-  coverage<5
-
-setenv =
-    COVERAGE_FILE = {toxinidir}/.coverage.{envname}
-
 commands =
-    ; --pyargs is needed so the directory astroid doesn't shadow the tox
-    ; installed astroid package
-    ; This is important for tests' test data which create files
-    ; inside the package
-    python -Wi {envsitepackagesdir}/coverage run -m pytest --pyargs {posargs:tests}
+  pytest --cov {posargs}
 
 [testenv:formatting]
-basepython = python3
 deps =
-    pytest
-    git+https://github.com/pycqa/pylint@main
-    pre-commit~=2.13
+    -r requirements_test_pre_commit.txt
 commands =
     pre-commit run --all-files
 
-[testenv:coveralls]
-setenv =
-    COVERAGE_FILE = {toxinidir}/.coverage
-passenv =
-    *
-deps =
-    coverage<5
-    coveralls
-skip_install = true
-commands =
-    python {envsitepackagesdir}/coverage combine --append
-    python {envsitepackagesdir}/coverage report --rcfile={toxinidir}/.coveragerc -m
-    - coveralls --rcfile={toxinidir}/.coveragerc
-changedir = {toxinidir}
-
-[testenv:coverage-erase]
-setenv =
-    COVERAGE_FILE = {toxinidir}/.coverage
-deps =
-    coverage<5
-skip_install = true
-commands =
-    python {envsitepackagesdir}/coverage erase
-changedir = {toxinidir}
-
 [testenv:docs]
 skipsdist = True
 usedevelop = True

More details

Full run details

Historical runs