2023-2 OSSProj 8팀 MSG_UP 입니다.
from django.apps import AppConfig


class EclassConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'eclass'

back/msgproject/eclass/apps.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'msgproject.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()

back/msgproject/manage.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""
ASGI config for msgproject project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
"""

import os

from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'msgproject.settings')

application = get_asgi_application()

back/msgproject/msgproject/asgi.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""
Django settings for msgproject project.

Generated by 'django-admin startproject' using Django 4.2.5.

For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-qg(fx&xlys34o!mdd5(=0!3j628)&c&q@0m#zl$rkc^c__f-t!'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['127.0.0.1']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'knox',
    'eclass',

]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'msgproject.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'msgproject.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/

LANGUAGE_CODE = 'ko-kr'

TIME_ZONE = 'Asia/Seoul'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

back/msgproject/msgproject/settings.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""
URL configuration for msgproject project.

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api-auth/', include('rest_framework.urls')),
]

back/msgproject/msgproject/urls.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Python Abstract Syntax Tree New Generation.

The aim of this module is to provide a common base representation of
python source code for projects such as pychecker, pyreverse,
pylint... Well, actually the development of this library is essentially
governed by pylint's needs.

It mimics the class defined in the python's _ast module with some
additional methods and attributes. New nodes instances are not fully
compatible with python's _ast.

Instance attributes are added by a
builder object, which can either generate extended ast (let's call
them astroid ;) by visiting an existent ast tree or by inspecting living
object.

Main modules are:

* nodes and scoped_nodes for more information about methods and
  attributes added to different node classes

* the manager contains a high level object to get astroid trees from
  source files and living objects. It maintains a cache of previously
  constructed tree for quick access

* builder contains the class responsible to build astroid trees
"""

import functools
import tokenize

# isort: off
# We have an isort: off on 'astroid.nodes' because of a circular import.
from astroid.nodes import node_classes, scoped_nodes

# isort: on

from astroid import raw_building
from astroid.__pkginfo__ import __version__, version
from astroid.bases import BaseInstance, BoundMethod, Instance, UnboundMethod
from astroid.brain.helpers import register_module_extender
from astroid.builder import extract_node, parse
from astroid.const import PY310_PLUS, Context
from astroid.exceptions import (
    AstroidBuildingError,
    AstroidBuildingException,
    AstroidError,
    AstroidImportError,
    AstroidIndexError,
    AstroidSyntaxError,
    AstroidTypeError,
    AstroidValueError,
    AttributeInferenceError,
    DuplicateBasesError,
    InconsistentMroError,
    InferenceError,
    InferenceOverwriteError,
    MroError,
    NameInferenceError,
    NoDefault,
    NotFoundError,
    ParentMissingError,
    ResolveError,
    StatementMissing,
    SuperArgumentTypeError,
    SuperError,
    TooManyLevelsError,
    UnresolvableName,
    UseInferenceDefault,
)
from astroid.inference_tip import _inference_tip_cached, inference_tip
from astroid.objects import ExceptionInstance

# isort: off
# It's impossible to import from astroid.nodes with a wildcard, because
# there is a cyclic import that prevent creating an __all__ in astroid/nodes
# and we need astroid/scoped_nodes and astroid/node_classes to work. So
# importing with a wildcard would clash with astroid/nodes/scoped_nodes
# and astroid/nodes/node_classes.
from astroid.astroid_manager import MANAGER
from astroid.nodes import (
    CONST_CLS,
    AnnAssign,
    Arguments,
    Assert,
    Assign,
    AssignAttr,
    AssignName,
    AsyncFor,
    AsyncFunctionDef,
    AsyncWith,
    Attribute,
    AugAssign,
    Await,
    BinOp,
    BoolOp,
    Break,
    Call,
    ClassDef,
    Compare,
    Comprehension,
    ComprehensionScope,
    Const,
    Continue,
    Decorators,
    DelAttr,
    Delete,
    DelName,
    Dict,
    DictComp,
    DictUnpack,
    EmptyNode,
    EvaluatedObject,
    ExceptHandler,
    Expr,
    For,
    FormattedValue,
    FunctionDef,
    GeneratorExp,
    Global,
    If,
    IfExp,
    Import,
    ImportFrom,
    JoinedStr,
    Keyword,
    Lambda,
    List,
    ListComp,
    Match,
    MatchAs,
    MatchCase,
    MatchClass,
    MatchMapping,
    MatchOr,
    MatchSequence,
    MatchSingleton,
    MatchStar,
    MatchValue,
    Module,
    Name,
    NamedExpr,
    NodeNG,
    Nonlocal,
    ParamSpec,
    Pass,
    Raise,
    Return,
    Set,
    SetComp,
    Slice,
    Starred,
    Subscript,
    Try,
    TryStar,
    Tuple,
    TypeAlias,
    TypeVar,
    TypeVarTuple,
    UnaryOp,
    Unknown,
    While,
    With,
    Yield,
    YieldFrom,
    are_exclusive,
    builtin_lookup,
    unpack_infer,
    function_to_method,
)

# isort: on

from astroid.util import Uninferable

# Performance hack for tokenize. See https://bugs.python.org/issue43014
# Adapted from https://github.com/PyCQA/pycodestyle/pull/993
if (
    not PY310_PLUS
    and callable(getattr(tokenize, "_compile", None))
    and getattr(tokenize._compile, "__wrapped__", None) is None  # type: ignore[attr-defined]
):
    tokenize._compile = functools.lru_cache(tokenize._compile)  # type: ignore[attr-defined]

back/venvmsg/Lib/site-packages/astroid/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

__version__ = "3.0.0"
version = __version__

back/venvmsg/Lib/site-packages/astroid/__pkginfo__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import ast
from typing import NamedTuple

from astroid.const import Context


class FunctionType(NamedTuple):
    argtypes: list[ast.expr]
    returns: ast.expr


class ParserModule(NamedTuple):
    unary_op_classes: dict[type[ast.unaryop], str]
    cmp_op_classes: dict[type[ast.cmpop], str]
    bool_op_classes: dict[type[ast.boolop], str]
    bin_op_classes: dict[type[ast.operator], str]
    context_classes: dict[type[ast.expr_context], Context]

    def parse(self, string: str, type_comments: bool = True) -> ast.Module:
        return ast.parse(string, type_comments=type_comments)


def parse_function_type_comment(type_comment: str) -> FunctionType | None:
    """Given a correct type comment, obtain a FunctionType object."""
    func_type = ast.parse(type_comment, "<type_comment>", "func_type")  # type: ignore[attr-defined]
    return FunctionType(argtypes=func_type.argtypes, returns=func_type.returns)


def get_parser_module(type_comments: bool = True) -> ParserModule:
    unary_op_classes = _unary_operators_from_module()
    cmp_op_classes = _compare_operators_from_module()
    bool_op_classes = _bool_operators_from_module()
    bin_op_classes = _binary_operators_from_module()
    context_classes = _contexts_from_module()

    return ParserModule(
        unary_op_classes,
        cmp_op_classes,
        bool_op_classes,
        bin_op_classes,
        context_classes,
    )


def _unary_operators_from_module() -> dict[type[ast.unaryop], str]:
    return {ast.UAdd: "+", ast.USub: "-", ast.Not: "not", ast.Invert: "~"}


def _binary_operators_from_module() -> dict[type[ast.operator], str]:
    return {
        ast.Add: "+",
        ast.BitAnd: "&",
        ast.BitOr: "|",
        ast.BitXor: "^",
        ast.Div: "/",
        ast.FloorDiv: "//",
        ast.MatMult: "@",
        ast.Mod: "%",
        ast.Mult: "*",
        ast.Pow: "**",
        ast.Sub: "-",
        ast.LShift: "<<",
        ast.RShift: ">>",
    }


def _bool_operators_from_module() -> dict[type[ast.boolop], str]:
    return {ast.And: "and", ast.Or: "or"}


def _compare_operators_from_module() -> dict[type[ast.cmpop], str]:
    return {
        ast.Eq: "==",
        ast.Gt: ">",
        ast.GtE: ">=",
        ast.In: "in",
        ast.Is: "is",
        ast.IsNot: "is not",
        ast.Lt: "<",
        ast.LtE: "<=",
        ast.NotEq: "!=",
        ast.NotIn: "not in",
    }


def _contexts_from_module() -> dict[type[ast.expr_context], Context]:
    return {
        ast.Load: Context.Load,
        ast.Store: Context.Store,
        ast.Del: Context.Del,
        ast.Param: Context.Store,
    }

back/venvmsg/Lib/site-packages/astroid/_ast.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/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")

back/venvmsg/Lib/site-packages/astroid/_backport_stdlib_names.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from astroid import nodes
from astroid.bases import Instance
from astroid.context import CallContext, InferenceContext
from astroid.exceptions import InferenceError, NoDefault
from astroid.typing import InferenceResult
from astroid.util import Uninferable, UninferableBase, safe_infer


class CallSite:
    """Class for understanding arguments passed into a call site.

    It needs a call context, which contains the arguments and the
    keyword arguments that were passed into a given call site.
    In order to infer what an argument represents, call :meth:`infer_argument`
    with the corresponding function node and the argument name.

    :param callcontext:
        An instance of :class:`astroid.context.CallContext`, that holds
        the arguments for the call site.
    :param argument_context_map:
        Additional contexts per node, passed in from :attr:`astroid.context.Context.extra_context`
    :param context:
        An instance of :class:`astroid.context.Context`.
    """

    def __init__(
        self,
        callcontext: CallContext,
        argument_context_map=None,
        context: InferenceContext | None = None,
    ):
        if argument_context_map is None:
            argument_context_map = {}
        self.argument_context_map = argument_context_map
        args = callcontext.args
        keywords = callcontext.keywords
        self.duplicated_keywords: set[str] = set()
        self._unpacked_args = self._unpack_args(args, context=context)
        self._unpacked_kwargs = self._unpack_keywords(keywords, context=context)

        self.positional_arguments = [
            arg for arg in self._unpacked_args if not isinstance(arg, UninferableBase)
        ]
        self.keyword_arguments = {
            key: value
            for key, value in self._unpacked_kwargs.items()
            if not isinstance(value, UninferableBase)
        }

    @classmethod
    def from_call(cls, call_node, context: InferenceContext | None = None):
        """Get a CallSite object from the given Call node.

        context will be used to force a single inference path.
        """

        # Determine the callcontext from the given `context` object if any.
        context = context or InferenceContext()
        callcontext = CallContext(call_node.args, call_node.keywords)
        return cls(callcontext, context=context)

    def has_invalid_arguments(self):
        """Check if in the current CallSite were passed *invalid* arguments.

        This can mean multiple things. For instance, if an unpacking
        of an invalid object was passed, then this method will return True.
        Other cases can be when the arguments can't be inferred by astroid,
        for example, by passing objects which aren't known statically.
        """
        return len(self.positional_arguments) != len(self._unpacked_args)

    def has_invalid_keywords(self) -> bool:
        """Check if in the current CallSite were passed *invalid* keyword arguments.

        For instance, unpacking a dictionary with integer keys is invalid
        (**{1:2}), because the keys must be strings, which will make this
        method to return True. Other cases where this might return True if
        objects which can't be inferred were passed.
        """
        return len(self.keyword_arguments) != len(self._unpacked_kwargs)

    def _unpack_keywords(
        self,
        keywords: list[tuple[str | None, nodes.NodeNG]],
        context: InferenceContext | None = None,
    ):
        values: dict[str | None, InferenceResult] = {}
        context = context or InferenceContext()
        context.extra_context = self.argument_context_map
        for name, value in keywords:
            if name is None:
                # Then it's an unpacking operation (**)
                inferred = safe_infer(value, context=context)
                if not isinstance(inferred, nodes.Dict):
                    # Not something we can work with.
                    values[name] = Uninferable
                    continue

                for dict_key, dict_value in inferred.items:
                    dict_key = safe_infer(dict_key, context=context)
                    if not isinstance(dict_key, nodes.Const):
                        values[name] = Uninferable
                        continue
                    if not isinstance(dict_key.value, str):
                        values[name] = Uninferable
                        continue
                    if dict_key.value in values:
                        # The name is already in the dictionary
                        values[dict_key.value] = Uninferable
                        self.duplicated_keywords.add(dict_key.value)
                        continue
                    values[dict_key.value] = dict_value
            else:
                values[name] = value
        return values

    def _unpack_args(self, args, context: InferenceContext | None = None):
        values = []
        context = context or InferenceContext()
        context.extra_context = self.argument_context_map
        for arg in args:
            if isinstance(arg, nodes.Starred):
                inferred = safe_infer(arg.value, context=context)
                if isinstance(inferred, UninferableBase):
                    values.append(Uninferable)
                    continue
                if not hasattr(inferred, "elts"):
                    values.append(Uninferable)
                    continue
                values.extend(inferred.elts)
            else:
                values.append(arg)
        return values

    def infer_argument(
        self, funcnode: InferenceResult, name: str, context: InferenceContext
    ):  # noqa: C901
        """Infer a function argument value according to the call context."""
        if not isinstance(funcnode, (nodes.FunctionDef, nodes.Lambda)):
            raise InferenceError(
                f"Can not infer function argument value for non-function node {funcnode!r}.",
                call_site=self,
                func=funcnode,
                arg=name,
                context=context,
            )

        if name in self.duplicated_keywords:
            raise InferenceError(
                "The arguments passed to {func!r} have duplicate keywords.",
                call_site=self,
                func=funcnode,
                arg=name,
                context=context,
            )

        # Look into the keywords first, maybe it's already there.
        try:
            return self.keyword_arguments[name].infer(context)
        except KeyError:
            pass

        # Too many arguments given and no variable arguments.
        if len(self.positional_arguments) > len(funcnode.args.args):
            if not funcnode.args.vararg and not funcnode.args.posonlyargs:
                raise InferenceError(
                    "Too many positional arguments "
                    "passed to {func!r} that does "
                    "not have *args.",
                    call_site=self,
                    func=funcnode,
                    arg=name,
                    context=context,
                )

        positional = self.positional_arguments[: len(funcnode.args.args)]
        vararg = self.positional_arguments[len(funcnode.args.args) :]

        # preserving previous behavior, when vararg and kwarg were not included in find_argname results
        if name in [funcnode.args.vararg, funcnode.args.kwarg]:
            argindex = None
        else:
            argindex = funcnode.args.find_argname(name)[0]

        kwonlyargs = {arg.name for arg in funcnode.args.kwonlyargs}
        kwargs = {
            key: value
            for key, value in self.keyword_arguments.items()
            if key not in kwonlyargs
        }
        # If there are too few positionals compared to
        # what the function expects to receive, check to see
        # if the missing positional arguments were passed
        # as keyword arguments and if so, place them into the
        # positional args list.
        if len(positional) < len(funcnode.args.args):
            for func_arg in funcnode.args.args:
                if func_arg.name in kwargs:
                    arg = kwargs.pop(func_arg.name)
                    positional.append(arg)

        if argindex is not None:
            boundnode = context.boundnode
            # 2. first argument of instance/class method
            if argindex == 0 and funcnode.type in {"method", "classmethod"}:
                # context.boundnode is None when an instance method is called with
                # the class, e.g. MyClass.method(obj, ...). In this case, self
                # is the first argument.
                if boundnode is None and funcnode.type == "method" and positional:
                    return positional[0].infer(context=context)
                if boundnode is None:
                    # XXX can do better ?
                    boundnode = funcnode.parent.frame()

                if isinstance(boundnode, nodes.ClassDef):
                    # Verify that we're accessing a method
                    # of the metaclass through a class, as in
                    # `cls.metaclass_method`. In this case, the
                    # first argument is always the class.
                    method_scope = funcnode.parent.scope()
                    if method_scope is boundnode.metaclass(context=context):
                        return iter((boundnode,))

                if funcnode.type == "method":
                    if not isinstance(boundnode, Instance):
                        boundnode = boundnode.instantiate_class()
                    return iter((boundnode,))
                if funcnode.type == "classmethod":
                    return iter((boundnode,))
            # if we have a method, extract one position
            # from the index, so we'll take in account
            # the extra parameter represented by `self` or `cls`
            if funcnode.type in {"method", "classmethod"} and boundnode:
                argindex -= 1
            # 2. search arg index
            try:
                return self.positional_arguments[argindex].infer(context)
            except IndexError:
                pass

        if funcnode.args.kwarg == name:
            # It wants all the keywords that were passed into
            # the call site.
            if self.has_invalid_keywords():
                raise InferenceError(
                    "Inference failed to find values for all keyword arguments "
                    "to {func!r}: {unpacked_kwargs!r} doesn't correspond to "
                    "{keyword_arguments!r}.",
                    keyword_arguments=self.keyword_arguments,
                    unpacked_kwargs=self._unpacked_kwargs,
                    call_site=self,
                    func=funcnode,
                    arg=name,
                    context=context,
                )
            kwarg = nodes.Dict(
                lineno=funcnode.args.lineno,
                col_offset=funcnode.args.col_offset,
                parent=funcnode.args,
                end_lineno=funcnode.args.end_lineno,
                end_col_offset=funcnode.args.end_col_offset,
            )
            kwarg.postinit(
                [(nodes.const_factory(key), value) for key, value in kwargs.items()]
            )
            return iter((kwarg,))
        if funcnode.args.vararg == name:
            # It wants all the args that were passed into
            # the call site.
            if self.has_invalid_arguments():
                raise InferenceError(
                    "Inference failed to find values for all positional "
                    "arguments to {func!r}: {unpacked_args!r} doesn't "
                    "correspond to {positional_arguments!r}.",
                    positional_arguments=self.positional_arguments,
                    unpacked_args=self._unpacked_args,
                    call_site=self,
                    func=funcnode,
                    arg=name,
                    context=context,
                )
            args = nodes.Tuple(
                lineno=funcnode.args.lineno,
                col_offset=funcnode.args.col_offset,
                parent=funcnode.args,
            )
            args.postinit(vararg)
            return iter((args,))

        # Check if it's a default parameter.
        try:
            return funcnode.args.default_value(name).infer(context)
        except NoDefault:
            pass
        raise InferenceError(
            "No value found for argument {arg} to {func!r}",
            call_site=self,
            func=funcnode,
            arg=name,
            context=context,
        )

back/venvmsg/Lib/site-packages/astroid/arguments.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""
This file contain the global astroid MANAGER.

It prevents a circular import that happened
when the only possibility to import it was from astroid.__init__.py.

This AstroidManager is a singleton/borg so it's possible to instantiate an
AstroidManager() directly.
"""

# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid.brain.helpers import register_all_brains
from astroid.manager import AstroidManager

MANAGER = AstroidManager()
# Register all brains after instantiating the singleton Manager
register_all_brains(MANAGER)

back/venvmsg/Lib/site-packages/astroid/astroid_manager.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""This module contains base classes and functions for the nodes and some
inference utils.
"""
from __future__ import annotations

import collections
import collections.abc
from collections.abc import Iterable, Iterator
from typing import TYPE_CHECKING, Any, Literal

from astroid import decorators, nodes
from astroid.const import PY310_PLUS
from astroid.context import (
    CallContext,
    InferenceContext,
    bind_context_to_node,
    copy_context,
)
from astroid.exceptions import (
    AstroidTypeError,
    AttributeInferenceError,
    InferenceError,
    NameInferenceError,
)
from astroid.interpreter import objectmodel
from astroid.typing import (
    InferenceErrorInfo,
    InferenceResult,
    SuccessfulInferenceResult,
)
from astroid.util import Uninferable, UninferableBase, safe_infer

if TYPE_CHECKING:
    from astroid.constraint import Constraint


PROPERTIES = {"builtins.property", "abc.abstractproperty"}
if PY310_PLUS:
    PROPERTIES.add("enum.property")

# List of possible property names. We use this list in order
# to see if a method is a property or not. This should be
# pretty reliable and fast, the alternative being to check each
# decorator to see if its a real property-like descriptor, which
# can be too complicated.
# Also, these aren't qualified, because each project can
# define them, we shouldn't expect to know every possible
# property-like decorator!
POSSIBLE_PROPERTIES = {
    "cached_property",
    "cachedproperty",
    "lazyproperty",
    "lazy_property",
    "reify",
    "lazyattribute",
    "lazy_attribute",
    "LazyProperty",
    "lazy",
    "cache_readonly",
    "DynamicClassAttribute",
}


def _is_property(
    meth: nodes.FunctionDef | UnboundMethod, context: InferenceContext | None = None
) -> bool:
    decoratornames = meth.decoratornames(context=context)
    if PROPERTIES.intersection(decoratornames):
        return True
    stripped = {
        name.split(".")[-1]
        for name in decoratornames
        if not isinstance(name, UninferableBase)
    }
    if any(name in stripped for name in POSSIBLE_PROPERTIES):
        return True

    # Lookup for subclasses of *property*
    if not meth.decorators:
        return False
    for decorator in meth.decorators.nodes or ():
        inferred = safe_infer(decorator, context=context)
        if inferred is None or isinstance(inferred, UninferableBase):
            continue
        if isinstance(inferred, nodes.ClassDef):
            for base_class in inferred.bases:
                if not isinstance(base_class, nodes.Name):
                    continue
                module, _ = base_class.lookup(base_class.name)
                if (
                    isinstance(module, nodes.Module)
                    and module.name == "builtins"
                    and base_class.name == "property"
                ):
                    return True

    return False


class Proxy:
    """A simple proxy object.

    Note:

    Subclasses of this object will need a custom __getattr__
    if new instance attributes are created. See the Const class
    """

    _proxied: nodes.ClassDef | nodes.FunctionDef | nodes.Lambda | UnboundMethod

    def __init__(
        self,
        proxied: nodes.ClassDef
        | nodes.FunctionDef
        | nodes.Lambda
        | UnboundMethod
        | None = None,
    ) -> None:
        if proxied is None:
            # This is a hack to allow calling this __init__ during bootstrapping of
            # builtin classes and their docstrings.
            # For Const, Generator, and UnionType nodes the _proxied attribute
            # is set during bootstrapping
            # as we first need to build the ClassDef that they can proxy.
            # Thus, if proxied is None self should be a Const or Generator
            # as that is the only way _proxied will be correctly set as a ClassDef.
            assert isinstance(self, (nodes.Const, Generator, UnionType))
        else:
            self._proxied = proxied

    def __getattr__(self, name: str) -> Any:
        if name == "_proxied":
            return self.__class__._proxied
        if name in self.__dict__:
            return self.__dict__[name]
        return getattr(self._proxied, name)

    def infer(  # type: ignore[return]
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> collections.abc.Generator[InferenceResult, None, InferenceErrorInfo | None]:
        yield self


def _infer_stmts(
    stmts: Iterable[InferenceResult],
    context: InferenceContext | None,
    frame: nodes.NodeNG | BaseInstance | None = None,
) -> collections.abc.Generator[InferenceResult, None, None]:
    """Return an iterator on statements inferred by each statement in *stmts*."""
    inferred = False
    constraint_failed = False
    if context is not None:
        name = context.lookupname
        context = context.clone()
        if name is not None:
            constraints = context.constraints.get(name, {})
        else:
            constraints = {}
    else:
        name = None
        constraints = {}
        context = InferenceContext()

    for stmt in stmts:
        if isinstance(stmt, UninferableBase):
            yield stmt
            inferred = True
            continue
        context.lookupname = stmt._infer_name(frame, name)
        try:
            stmt_constraints: set[Constraint] = set()
            for constraint_stmt, potential_constraints in constraints.items():
                if not constraint_stmt.parent_of(stmt):
                    stmt_constraints.update(potential_constraints)
            for inf in stmt.infer(context=context):
                if all(constraint.satisfied_by(inf) for constraint in stmt_constraints):
                    yield inf
                    inferred = True
                else:
                    constraint_failed = True
        except NameInferenceError:
            continue
        except InferenceError:
            yield Uninferable
            inferred = True

    if not inferred and constraint_failed:
        yield Uninferable
    elif not inferred:
        raise InferenceError(
            "Inference failed for all members of {stmts!r}.",
            stmts=stmts,
            frame=frame,
            context=context,
        )


def _infer_method_result_truth(
    instance: Instance, method_name: str, context: InferenceContext
) -> bool | UninferableBase:
    # Get the method from the instance and try to infer
    # its return's truth value.
    meth = next(instance.igetattr(method_name, context=context), None)
    if meth and hasattr(meth, "infer_call_result"):
        if not meth.callable():
            return Uninferable
        try:
            context.callcontext = CallContext(args=[], callee=meth)
            for value in meth.infer_call_result(instance, context=context):
                if isinstance(value, UninferableBase):
                    return value
                try:
                    inferred = next(value.infer(context=context))
                except StopIteration as e:
                    raise InferenceError(context=context) from e
                return inferred.bool_value()
        except InferenceError:
            pass
    return Uninferable


class BaseInstance(Proxy):
    """An instance base class, which provides lookup methods for potential
    instances.
    """

    _proxied: nodes.ClassDef

    special_attributes: objectmodel.ObjectModel

    def display_type(self) -> str:
        return "Instance of"

    def getattr(
        self,
        name: str,
        context: InferenceContext | None = None,
        lookupclass: bool = True,
    ) -> list[InferenceResult]:
        try:
            values = self._proxied.instance_attr(name, context)
        except AttributeInferenceError as exc:
            if self.special_attributes and name in self.special_attributes:
                return [self.special_attributes.lookup(name)]

            if lookupclass:
                # Class attributes not available through the instance
                # unless they are explicitly defined.
                return self._proxied.getattr(name, context, class_context=False)

            raise AttributeInferenceError(
                target=self, attribute=name, context=context
            ) from exc
        # since we've no context information, return matching class members as
        # well
        if lookupclass:
            try:
                return values + self._proxied.getattr(
                    name, context, class_context=False
                )
            except AttributeInferenceError:
                pass
        return values

    def igetattr(
        self, name: str, context: InferenceContext | None = None
    ) -> Iterator[InferenceResult]:
        """Inferred getattr."""
        if not context:
            context = InferenceContext()
        try:
            context.lookupname = name
            # XXX frame should be self._proxied, or not ?
            get_attr = self.getattr(name, context, lookupclass=False)
            yield from _infer_stmts(
                self._wrap_attr(get_attr, context), context, frame=self
            )
        except AttributeInferenceError:
            try:
                # fallback to class.igetattr since it has some logic to handle
                # descriptors
                # But only if the _proxied is the Class.
                if self._proxied.__class__.__name__ != "ClassDef":
                    raise
                attrs = self._proxied.igetattr(name, context, class_context=False)
                yield from self._wrap_attr(attrs, context)
            except AttributeInferenceError as error:
                raise InferenceError(**vars(error)) from error

    def _wrap_attr(
        self, attrs: Iterable[InferenceResult], context: InferenceContext | None = None
    ) -> Iterator[InferenceResult]:
        """Wrap bound methods of attrs in a InstanceMethod proxies."""
        for attr in attrs:
            if isinstance(attr, UnboundMethod):
                if _is_property(attr):
                    yield from attr.infer_call_result(self, context)
                else:
                    yield BoundMethod(attr, self)
            elif isinstance(attr, nodes.Lambda):
                if attr.args.arguments and attr.args.arguments[0].name == "self":
                    yield BoundMethod(attr, self)
                    continue
                yield attr
            else:
                yield attr

    def infer_call_result(
        self,
        caller: SuccessfulInferenceResult | None,
        context: InferenceContext | None = None,
    ) -> Iterator[InferenceResult]:
        """Infer what a class instance is returning when called."""
        context = bind_context_to_node(context, self)
        inferred = False

        # If the call is an attribute on the instance, we infer the attribute itself
        if isinstance(caller, nodes.Call) and isinstance(caller.func, nodes.Attribute):
            for res in self.igetattr(caller.func.attrname, context):
                inferred = True
                yield res

        # Otherwise we infer the call to the __call__ dunder normally
        for node in self._proxied.igetattr("__call__", context):
            if isinstance(node, UninferableBase) or not node.callable():
                continue
            for res in node.infer_call_result(caller, context):
                inferred = True
                yield res
        if not inferred:
            raise InferenceError(node=self, caller=caller, context=context)


class Instance(BaseInstance):
    """A special node representing a class instance."""

    special_attributes = objectmodel.InstanceModel()

    def __init__(self, proxied: nodes.ClassDef | None) -> None:
        super().__init__(proxied)

    @decorators.yes_if_nothing_inferred
    def infer_binary_op(
        self,
        opnode: nodes.AugAssign | nodes.BinOp,
        operator: str,
        other: InferenceResult,
        context: InferenceContext,
        method: SuccessfulInferenceResult,
    ) -> Generator[InferenceResult, None, None]:
        return method.infer_call_result(self, context)

    def __repr__(self) -> str:
        return "<Instance of {}.{} at 0x{}>".format(
            self._proxied.root().name, self._proxied.name, id(self)
        )

    def __str__(self) -> str:
        return f"Instance of {self._proxied.root().name}.{self._proxied.name}"

    def callable(self) -> bool:
        try:
            self._proxied.getattr("__call__", class_context=False)
            return True
        except AttributeInferenceError:
            return False

    def pytype(self) -> str:
        return self._proxied.qname()

    def display_type(self) -> str:
        return "Instance of"

    def bool_value(
        self, context: InferenceContext | None = None
    ) -> bool | UninferableBase:
        """Infer the truth value for an Instance.

        The truth value of an instance is determined by these conditions:

           * if it implements __bool__ on Python 3 or __nonzero__
             on Python 2, then its bool value will be determined by
             calling this special method and checking its result.
           * when this method is not defined, __len__() is called, if it
             is defined, and the object is considered true if its result is
             nonzero. If a class defines neither __len__() nor __bool__(),
             all its instances are considered true.
        """
        context = context or InferenceContext()
        context.boundnode = self

        try:
            result = _infer_method_result_truth(self, "__bool__", context)
        except (InferenceError, AttributeInferenceError):
            # Fallback to __len__.
            try:
                result = _infer_method_result_truth(self, "__len__", context)
            except (AttributeInferenceError, InferenceError):
                return True
        return result

    def getitem(
        self, index: nodes.Const, context: InferenceContext | None = None
    ) -> InferenceResult | None:
        new_context = bind_context_to_node(context, self)
        if not context:
            context = new_context
        method = next(self.igetattr("__getitem__", context=context), None)
        # Create a new CallContext for providing index as an argument.
        new_context.callcontext = CallContext(args=[index], callee=method)
        if not isinstance(method, BoundMethod):
            raise InferenceError(
                "Could not find __getitem__ for {node!r}.", node=self, context=context
            )
        if len(method.args.arguments) != 2:  # (self, index)
            raise AstroidTypeError(
                "__getitem__ for {node!r} does not have correct signature",
                node=self,
                context=context,
            )
        return next(method.infer_call_result(self, new_context), None)


class UnboundMethod(Proxy):
    """A special node representing a method not bound to an instance."""

    _proxied: nodes.FunctionDef | UnboundMethod

    special_attributes: objectmodel.BoundMethodModel | objectmodel.UnboundMethodModel = (
        objectmodel.UnboundMethodModel()
    )

    def __repr__(self) -> str:
        assert self._proxied.parent, "Expected a parent node"
        frame = self._proxied.parent.frame()
        return "<{} {} of {} at 0x{}".format(
            self.__class__.__name__, self._proxied.name, frame.qname(), id(self)
        )

    def implicit_parameters(self) -> Literal[0, 1]:
        return 0

    def is_bound(self) -> bool:
        return False

    def getattr(self, name: str, context: InferenceContext | None = None):
        if name in self.special_attributes:
            return [self.special_attributes.lookup(name)]
        return self._proxied.getattr(name, context)

    def igetattr(
        self, name: str, context: InferenceContext | None = None
    ) -> Iterator[InferenceResult]:
        if name in self.special_attributes:
            return iter((self.special_attributes.lookup(name),))
        return self._proxied.igetattr(name, context)

    def infer_call_result(
        self,
        caller: SuccessfulInferenceResult | None,
        context: InferenceContext | None = None,
    ) -> Iterator[InferenceResult]:
        """
        The boundnode of the regular context with a function called
        on ``object.__new__`` will be of type ``object``,
        which is incorrect for the argument in general.
        If no context is given the ``object.__new__`` call argument will
        be correctly inferred except when inside a call that requires
        the additional context (such as a classmethod) of the boundnode
        to determine which class the method was called from
        """

        # If we're unbound method __new__ of a builtin, the result is an
        # instance of the class given as first argument.
        if self._proxied.name == "__new__":
            assert self._proxied.parent, "Expected a parent node"
            qname = self._proxied.parent.frame().qname()
            # Avoid checking builtins.type: _infer_type_new_call() does more validation
            if qname.startswith("builtins.") and qname != "builtins.type":
                return self._infer_builtin_new(caller, context or InferenceContext())
        return self._proxied.infer_call_result(caller, context)

    def _infer_builtin_new(
        self,
        caller: SuccessfulInferenceResult | None,
        context: InferenceContext,
    ) -> collections.abc.Generator[
        nodes.Const | Instance | UninferableBase, None, None
    ]:
        if not isinstance(caller, nodes.Call):
            return
        if not caller.args:
            return
        # Attempt to create a constant
        if len(caller.args) > 1:
            value = None
            if isinstance(caller.args[1], nodes.Const):
                value = caller.args[1].value
            else:
                inferred_arg = next(caller.args[1].infer(), None)
                if isinstance(inferred_arg, nodes.Const):
                    value = inferred_arg.value
            if value is not None:
                const = nodes.const_factory(value)
                assert not isinstance(const, nodes.EmptyNode)
                yield const
                return

        node_context = context.extra_context.get(caller.args[0])
        for inferred in caller.args[0].infer(context=node_context):
            if isinstance(inferred, UninferableBase):
                yield inferred
            if isinstance(inferred, nodes.ClassDef):
                yield Instance(inferred)
            raise InferenceError

    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
        return True


class BoundMethod(UnboundMethod):
    """A special node representing a method bound to an instance."""

    special_attributes = objectmodel.BoundMethodModel()

    def __init__(
        self,
        proxy: nodes.FunctionDef | nodes.Lambda | UnboundMethod,
        bound: SuccessfulInferenceResult,
    ) -> None:
        super().__init__(proxy)
        self.bound = bound

    def implicit_parameters(self) -> Literal[0, 1]:
        if self.name == "__new__":
            # __new__ acts as a classmethod but the class argument is not implicit.
            return 0
        return 1

    def is_bound(self) -> Literal[True]:
        return True

    def _infer_type_new_call(
        self, caller: nodes.Call, context: InferenceContext
    ) -> nodes.ClassDef | None:  # noqa: C901
        """Try to infer what type.__new__(mcs, name, bases, attrs) returns.

        In order for such call to be valid, the metaclass needs to be
        a subtype of ``type``, the name needs to be a string, the bases
        needs to be a tuple of classes
        """
        # pylint: disable=import-outside-toplevel; circular import
        from astroid.nodes import Pass

        # Verify the metaclass
        try:
            mcs = next(caller.args[0].infer(context=context))
        except StopIteration as e:
            raise InferenceError(context=context) from e
        if not isinstance(mcs, nodes.ClassDef):
            # Not a valid first argument.
            return None
        if not mcs.is_subtype_of("builtins.type"):
            # Not a valid metaclass.
            return None

        # Verify the name
        try:
            name = next(caller.args[1].infer(context=context))
        except StopIteration as e:
            raise InferenceError(context=context) from e
        if not isinstance(name, nodes.Const):
            # Not a valid name, needs to be a const.
            return None
        if not isinstance(name.value, str):
            # Needs to be a string.
            return None

        # Verify the bases
        try:
            bases = next(caller.args[2].infer(context=context))
        except StopIteration as e:
            raise InferenceError(context=context) from e
        if not isinstance(bases, nodes.Tuple):
            # Needs to be a tuple.
            return None
        try:
            inferred_bases = [next(elt.infer(context=context)) for elt in bases.elts]
        except StopIteration as e:
            raise InferenceError(context=context) from e
        if any(not isinstance(base, nodes.ClassDef) for base in inferred_bases):
            # All the bases needs to be Classes
            return None

        # Verify the attributes.
        try:
            attrs = next(caller.args[3].infer(context=context))
        except StopIteration as e:
            raise InferenceError(context=context) from e
        if not isinstance(attrs, nodes.Dict):
            # Needs to be a dictionary.
            return None
        cls_locals: dict[str, list[InferenceResult]] = collections.defaultdict(list)
        for key, value in attrs.items:
            try:
                key = next(key.infer(context=context))
            except StopIteration as e:
                raise InferenceError(context=context) from e
            try:
                value = next(value.infer(context=context))
            except StopIteration as e:
                raise InferenceError(context=context) from e
            # Ignore non string keys
            if isinstance(key, nodes.Const) and isinstance(key.value, str):
                cls_locals[key.value].append(value)

        # Build the class from now.
        cls = mcs.__class__(
            name=name.value,
            lineno=caller.lineno or 0,
            col_offset=caller.col_offset or 0,
            parent=caller,
            end_lineno=caller.end_lineno,
            end_col_offset=caller.end_col_offset,
        )
        empty = Pass(
            parent=cls,
            lineno=caller.lineno,
            col_offset=caller.col_offset,
            end_lineno=caller.end_lineno,
            end_col_offset=caller.end_col_offset,
        )
        cls.postinit(
            bases=bases.elts,
            body=[empty],
            decorators=None,
            newstyle=True,
            metaclass=mcs,
            keywords=[],
        )
        cls.locals = cls_locals
        return cls

    def infer_call_result(
        self,
        caller: SuccessfulInferenceResult | None,
        context: InferenceContext | None = None,
    ) -> Iterator[InferenceResult]:
        context = bind_context_to_node(context, self.bound)
        if (
            isinstance(self.bound, nodes.ClassDef)
            and self.bound.name == "type"
            and self.name == "__new__"
            and isinstance(caller, nodes.Call)
            and len(caller.args) == 4
        ):
            # Check if we have a ``type.__new__(mcs, name, bases, attrs)`` call.
            new_cls = self._infer_type_new_call(caller, context)
            if new_cls:
                return iter((new_cls,))

        return super().infer_call_result(caller, context)

    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
        return True


class Generator(BaseInstance):
    """A special node representing a generator.

    Proxied class is set once for all in raw_building.
    """

    # We defer initialization of special_attributes to the __init__ method since the constructor
    # of GeneratorModel requires the raw_building to be complete
    # TODO: This should probably be refactored.
    special_attributes: objectmodel.GeneratorModel

    def __init__(
        self,
        parent: nodes.FunctionDef,
        generator_initial_context: InferenceContext | None = None,
    ) -> None:
        super().__init__()
        self.parent = parent
        self._call_context = copy_context(generator_initial_context)

        # See comment above: this is a deferred initialization.
        Generator.special_attributes = objectmodel.GeneratorModel()

    def infer_yield_types(self) -> Iterator[InferenceResult]:
        yield from self.parent.infer_yield_result(self._call_context)

    def callable(self) -> Literal[False]:
        return False

    def pytype(self) -> str:
        return "builtins.generator"

    def display_type(self) -> str:
        return "Generator"

    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
        return True

    def __repr__(self) -> str:
        return f"<Generator({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"

    def __str__(self) -> str:
        return f"Generator({self._proxied.name})"


class AsyncGenerator(Generator):
    """Special node representing an async generator."""

    def pytype(self) -> Literal["builtins.async_generator"]:
        return "builtins.async_generator"

    def display_type(self) -> str:
        return "AsyncGenerator"

    def __repr__(self) -> str:
        return f"<AsyncGenerator({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"

    def __str__(self) -> str:
        return f"AsyncGenerator({self._proxied.name})"


class UnionType(BaseInstance):
    """Special node representing new style typing unions.

    Proxied class is set once for all in raw_building.
    """

    def __init__(
        self,
        left: UnionType | nodes.ClassDef | nodes.Const,
        right: UnionType | nodes.ClassDef | nodes.Const,
        parent: nodes.NodeNG | None = None,
    ) -> None:
        super().__init__()
        self.parent = parent
        self.left = left
        self.right = right

    def callable(self) -> Literal[False]:
        return False

    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
        return True

    def pytype(self) -> Literal["types.UnionType"]:
        return "types.UnionType"

    def display_type(self) -> str:
        return "UnionType"

    def __repr__(self) -> str:
        return f"<UnionType({self._proxied.name}) l.{self.lineno} at 0x{id(self)}>"

    def __str__(self) -> str:
        return f"UnionType({self._proxied.name})"

back/venvmsg/Lib/site-packages/astroid/bases.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from astroid import arguments, inference_tip, nodes
from astroid.context import InferenceContext
from astroid.exceptions import UseInferenceDefault
from astroid.manager import AstroidManager


def infer_namespace(node, context: InferenceContext | None = None):
    callsite = arguments.CallSite.from_call(node, context=context)
    if not callsite.keyword_arguments:
        # Cannot make sense of it.
        raise UseInferenceDefault()

    class_node = nodes.ClassDef(
        "Namespace",
        lineno=node.lineno,
        col_offset=node.col_offset,
        parent=nodes.Unknown(),
        end_lineno=node.end_lineno,
        end_col_offset=node.end_col_offset,
    )
    # Set parent manually until ClassDef constructor fixed:
    # https://github.com/pylint-dev/astroid/issues/1490
    class_node.parent = node.parent
    for attr in set(callsite.keyword_arguments):
        fake_node = nodes.EmptyNode()
        fake_node.parent = class_node
        fake_node.attrname = attr
        class_node.instance_attrs[attr] = [fake_node]
    return iter((class_node.instantiate_class(),))


def _looks_like_namespace(node) -> bool:
    func = node.func
    if isinstance(func, nodes.Attribute):
        return (
            func.attrname == "Namespace"
            and isinstance(func.expr, nodes.Name)
            and func.expr.name == "argparse"
        )
    return False


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        nodes.Call, inference_tip(infer_namespace), _looks_like_namespace
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_argparse.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Astroid hook for the attrs library

Without this hook pylint reports unsupported-assignment-operation
for attrs classes
"""
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import AnnAssign, Assign, AssignName, Call, Unknown
from astroid.nodes.scoped_nodes import ClassDef
from astroid.util import safe_infer

ATTRIB_NAMES = frozenset(
    (
        "attr.Factory",
        "attr.ib",
        "attrib",
        "attr.attrib",
        "attr.field",
        "attrs.field",
        "field",
    )
)
ATTRS_NAMES = frozenset(
    (
        "attr.s",
        "attrs",
        "attr.attrs",
        "attr.attributes",
        "attr.define",
        "attr.mutable",
        "attr.frozen",
        "attrs.define",
        "attrs.mutable",
        "attrs.frozen",
    )
)


def is_decorated_with_attrs(node, decorator_names=ATTRS_NAMES) -> bool:
    """Return whether a decorated node has an attr decorator applied."""
    if not node.decorators:
        return False
    for decorator_attribute in node.decorators.nodes:
        if isinstance(decorator_attribute, Call):  # decorator with arguments
            decorator_attribute = decorator_attribute.func
        if decorator_attribute.as_string() in decorator_names:
            return True

        inferred = safe_infer(decorator_attribute)
        if inferred and inferred.root().name == "attr._next_gen":
            return True
    return False


def attr_attributes_transform(node: ClassDef) -> None:
    """Given that the ClassNode has an attr decorator,
    rewrite class attributes as instance attributes
    """
    # Astroid can't infer this attribute properly
    # Prevents https://github.com/pylint-dev/pylint/issues/1884
    node.locals["__attrs_attrs__"] = [Unknown(parent=node)]

    for cdef_body_node in node.body:
        if not isinstance(cdef_body_node, (Assign, AnnAssign)):
            continue
        if isinstance(cdef_body_node.value, Call):
            if cdef_body_node.value.func.as_string() not in ATTRIB_NAMES:
                continue
        else:
            continue
        targets = (
            cdef_body_node.targets
            if hasattr(cdef_body_node, "targets")
            else [cdef_body_node.target]
        )
        for target in targets:
            rhs_node = Unknown(
                lineno=cdef_body_node.lineno,
                col_offset=cdef_body_node.col_offset,
                parent=cdef_body_node,
            )
            if isinstance(target, AssignName):
                # Could be a subscript if the code analysed is
                # i = Optional[str] = ""
                # See https://github.com/pylint-dev/pylint/issues/4439
                node.locals[target.name] = [rhs_node]
                node.instance_attrs[target.name] = [rhs_node]


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        ClassDef, attr_attributes_transform, is_decorated_with_attrs
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_attrs.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for understanding ``boto3.ServiceRequest()``."""

from astroid import extract_node
from astroid.manager import AstroidManager
from astroid.nodes.scoped_nodes import ClassDef

BOTO_SERVICE_FACTORY_QUALIFIED_NAME = "boto3.resources.base.ServiceResource"


def service_request_transform(node):
    """Transform ServiceResource to look like dynamic classes."""
    code = """
    def __getattr__(self, attr):
        return 0
    """
    func_getattr = extract_node(code)
    node.locals["__getattr__"] = [func_getattr]
    return node


def _looks_like_boto3_service_request(node) -> bool:
    return node.qname() == BOTO_SERVICE_FACTORY_QUALIFIED_NAME


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        ClassDef, service_request_transform, _looks_like_boto3_service_request
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_boto3.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for various builtins."""

from __future__ import annotations

import itertools
from collections.abc import Callable, Iterable
from functools import partial
from typing import TYPE_CHECKING, Any, Iterator, NoReturn, Type, Union, cast

from astroid import arguments, helpers, inference_tip, nodes, objects, util
from astroid.builder import AstroidBuilder
from astroid.context import InferenceContext
from astroid.exceptions import (
    AstroidTypeError,
    AttributeInferenceError,
    InferenceError,
    MroError,
    UseInferenceDefault,
)
from astroid.manager import AstroidManager
from astroid.nodes import scoped_nodes
from astroid.typing import (
    ConstFactoryResult,
    InferenceResult,
    SuccessfulInferenceResult,
)

if TYPE_CHECKING:
    from astroid.bases import Instance

ContainerObjects = Union[
    objects.FrozenSet,
    objects.DictItems,
    objects.DictKeys,
    objects.DictValues,
]

BuiltContainers = Union[
    Type[tuple],
    Type[list],
    Type[set],
    Type[frozenset],
]

CopyResult = Union[
    nodes.Dict,
    nodes.List,
    nodes.Set,
    objects.FrozenSet,
]

OBJECT_DUNDER_NEW = "object.__new__"

STR_CLASS = """
class whatever(object):
    def join(self, iterable):
        return {rvalue}
    def replace(self, old, new, count=None):
        return {rvalue}
    def format(self, *args, **kwargs):
        return {rvalue}
    def encode(self, encoding='ascii', errors=None):
        return b''
    def decode(self, encoding='ascii', errors=None):
        return u''
    def capitalize(self):
        return {rvalue}
    def title(self):
        return {rvalue}
    def lower(self):
        return {rvalue}
    def upper(self):
        return {rvalue}
    def swapcase(self):
        return {rvalue}
    def index(self, sub, start=None, end=None):
        return 0
    def find(self, sub, start=None, end=None):
        return 0
    def count(self, sub, start=None, end=None):
        return 0
    def strip(self, chars=None):
        return {rvalue}
    def lstrip(self, chars=None):
        return {rvalue}
    def rstrip(self, chars=None):
        return {rvalue}
    def rjust(self, width, fillchar=None):
        return {rvalue}
    def center(self, width, fillchar=None):
        return {rvalue}
    def ljust(self, width, fillchar=None):
        return {rvalue}
"""


BYTES_CLASS = """
class whatever(object):
    def join(self, iterable):
        return {rvalue}
    def replace(self, old, new, count=None):
        return {rvalue}
    def decode(self, encoding='ascii', errors=None):
        return u''
    def capitalize(self):
        return {rvalue}
    def title(self):
        return {rvalue}
    def lower(self):
        return {rvalue}
    def upper(self):
        return {rvalue}
    def swapcase(self):
        return {rvalue}
    def index(self, sub, start=None, end=None):
        return 0
    def find(self, sub, start=None, end=None):
        return 0
    def count(self, sub, start=None, end=None):
        return 0
    def strip(self, chars=None):
        return {rvalue}
    def lstrip(self, chars=None):
        return {rvalue}
    def rstrip(self, chars=None):
        return {rvalue}
    def rjust(self, width, fillchar=None):
        return {rvalue}
    def center(self, width, fillchar=None):
        return {rvalue}
    def ljust(self, width, fillchar=None):
        return {rvalue}
"""


def _use_default() -> NoReturn:  # pragma: no cover
    raise UseInferenceDefault()


def _extend_string_class(class_node, code, rvalue):
    """Function to extend builtin str/unicode class."""
    code = code.format(rvalue=rvalue)
    fake = AstroidBuilder(AstroidManager()).string_build(code)["whatever"]
    for method in fake.mymethods():
        method.parent = class_node
        method.lineno = None
        method.col_offset = None
        if "__class__" in method.locals:
            method.locals["__class__"] = [class_node]
        class_node.locals[method.name] = [method]
        method.parent = class_node


def _extend_builtins(class_transforms):
    builtin_ast = AstroidManager().builtins_module
    for class_name, transform in class_transforms.items():
        transform(builtin_ast[class_name])


def on_bootstrap():
    """Called by astroid_bootstrapping()."""
    _extend_builtins(
        {
            "bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"),
            "str": partial(_extend_string_class, code=STR_CLASS, rvalue="''"),
        }
    )


def _builtin_filter_predicate(node, builtin_name) -> bool:
    if (
        builtin_name == "type"
        and node.root().name == "re"
        and isinstance(node.func, nodes.Name)
        and node.func.name == "type"
        and isinstance(node.parent, nodes.Assign)
        and len(node.parent.targets) == 1
        and isinstance(node.parent.targets[0], nodes.AssignName)
        and node.parent.targets[0].name in {"Pattern", "Match"}
    ):
        # Handle re.Pattern and re.Match in brain_re
        # Match these patterns from stdlib/re.py
        # ```py
        # Pattern = type(...)
        # Match = type(...)
        # ```
        return False
    if isinstance(node.func, nodes.Name) and node.func.name == builtin_name:
        return True
    if isinstance(node.func, nodes.Attribute):
        return (
            node.func.attrname == "fromkeys"
            and isinstance(node.func.expr, nodes.Name)
            and node.func.expr.name == "dict"
        )
    return False


def register_builtin_transform(
    manager: AstroidManager, transform, builtin_name
) -> None:
    """Register a new transform function for the given *builtin_name*.

    The transform function must accept two parameters, a node and
    an optional context.
    """

    def _transform_wrapper(
        node: nodes.Call, context: InferenceContext | None = None, **kwargs: Any
    ) -> Iterator:
        result = transform(node, context=context)
        if result:
            if not result.parent:
                # Let the transformation function determine
                # the parent for its result. Otherwise,
                # we set it to be the node we transformed from.
                result.parent = node

            if result.lineno is None:
                result.lineno = node.lineno
            # Can be a 'Module' see https://github.com/pylint-dev/pylint/issues/4671
            # We don't have a regression test on this one: tread carefully
            if hasattr(result, "col_offset") and result.col_offset is None:
                result.col_offset = node.col_offset
        return iter([result])

    manager.register_transform(
        nodes.Call,
        inference_tip(_transform_wrapper),
        partial(_builtin_filter_predicate, builtin_name=builtin_name),
    )


def _container_generic_inference(
    node: nodes.Call,
    context: InferenceContext | None,
    node_type: type[nodes.BaseContainer],
    transform: Callable[[SuccessfulInferenceResult], nodes.BaseContainer | None],
) -> nodes.BaseContainer:
    args = node.args
    if not args:
        return node_type(
            lineno=node.lineno,
            col_offset=node.col_offset,
            parent=node.parent,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
        )
    if len(node.args) > 1:
        raise UseInferenceDefault()

    (arg,) = args
    transformed = transform(arg)
    if not transformed:
        try:
            inferred = next(arg.infer(context=context))
        except (InferenceError, StopIteration) as exc:
            raise UseInferenceDefault from exc
        if isinstance(inferred, util.UninferableBase):
            raise UseInferenceDefault
        transformed = transform(inferred)
    if not transformed or isinstance(transformed, util.UninferableBase):
        raise UseInferenceDefault
    return transformed


def _container_generic_transform(
    arg: SuccessfulInferenceResult,
    context: InferenceContext | None,
    klass: type[nodes.BaseContainer],
    iterables: tuple[type[nodes.BaseContainer] | type[ContainerObjects], ...],
    build_elts: BuiltContainers,
) -> nodes.BaseContainer | None:
    elts: Iterable | str | bytes

    if isinstance(arg, klass):
        return arg
    if isinstance(arg, iterables):
        arg = cast(Union[nodes.BaseContainer, ContainerObjects], arg)
        if all(isinstance(elt, nodes.Const) for elt in arg.elts):
            elts = [cast(nodes.Const, elt).value for elt in arg.elts]
        else:
            # TODO: Does not handle deduplication for sets.
            elts = []
            for element in arg.elts:
                if not element:
                    continue
                inferred = util.safe_infer(element, context=context)
                if inferred:
                    evaluated_object = nodes.EvaluatedObject(
                        original=element, value=inferred
                    )
                    elts.append(evaluated_object)
    elif isinstance(arg, nodes.Dict):
        # Dicts need to have consts as strings already.
        elts = [
            item[0].value if isinstance(item[0], nodes.Const) else _use_default()
            for item in arg.items
        ]
    elif isinstance(arg, nodes.Const) and isinstance(arg.value, (str, bytes)):
        elts = arg.value
    else:
        return None
    return klass.from_elements(elts=build_elts(elts))


def _infer_builtin_container(
    node: nodes.Call,
    context: InferenceContext | None,
    klass: type[nodes.BaseContainer],
    iterables: tuple[type[nodes.NodeNG] | type[ContainerObjects], ...],
    build_elts: BuiltContainers,
) -> nodes.BaseContainer:
    transform_func = partial(
        _container_generic_transform,
        context=context,
        klass=klass,
        iterables=iterables,
        build_elts=build_elts,
    )

    return _container_generic_inference(node, context, klass, transform_func)


# pylint: disable=invalid-name
infer_tuple = partial(
    _infer_builtin_container,
    klass=nodes.Tuple,
    iterables=(
        nodes.List,
        nodes.Set,
        objects.FrozenSet,
        objects.DictItems,
        objects.DictKeys,
        objects.DictValues,
    ),
    build_elts=tuple,
)

infer_list = partial(
    _infer_builtin_container,
    klass=nodes.List,
    iterables=(
        nodes.Tuple,
        nodes.Set,
        objects.FrozenSet,
        objects.DictItems,
        objects.DictKeys,
        objects.DictValues,
    ),
    build_elts=list,
)

infer_set = partial(
    _infer_builtin_container,
    klass=nodes.Set,
    iterables=(nodes.List, nodes.Tuple, objects.FrozenSet, objects.DictKeys),
    build_elts=set,
)

infer_frozenset = partial(
    _infer_builtin_container,
    klass=objects.FrozenSet,
    iterables=(nodes.List, nodes.Tuple, nodes.Set, objects.FrozenSet, objects.DictKeys),
    build_elts=frozenset,
)


def _get_elts(arg, context):
    def is_iterable(n):
        return isinstance(n, (nodes.List, nodes.Tuple, nodes.Set))

    try:
        inferred = next(arg.infer(context))
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc
    if isinstance(inferred, nodes.Dict):
        items = inferred.items
    elif is_iterable(inferred):
        items = []
        for elt in inferred.elts:
            # If an item is not a pair of two items,
            # then fallback to the default inference.
            # Also, take in consideration only hashable items,
            # tuples and consts. We are choosing Names as well.
            if not is_iterable(elt):
                raise UseInferenceDefault()
            if len(elt.elts) != 2:
                raise UseInferenceDefault()
            if not isinstance(elt.elts[0], (nodes.Tuple, nodes.Const, nodes.Name)):
                raise UseInferenceDefault()
            items.append(tuple(elt.elts))
    else:
        raise UseInferenceDefault()
    return items


def infer_dict(node: nodes.Call, context: InferenceContext | None = None) -> nodes.Dict:
    """Try to infer a dict call to a Dict node.

    The function treats the following cases:

        * dict()
        * dict(mapping)
        * dict(iterable)
        * dict(iterable, **kwargs)
        * dict(mapping, **kwargs)
        * dict(**kwargs)

    If a case can't be inferred, we'll fallback to default inference.
    """
    call = arguments.CallSite.from_call(node, context=context)
    if call.has_invalid_arguments() or call.has_invalid_keywords():
        raise UseInferenceDefault

    args = call.positional_arguments
    kwargs = list(call.keyword_arguments.items())

    items: list[tuple[InferenceResult, InferenceResult]]
    if not args and not kwargs:
        # dict()
        return nodes.Dict(
            lineno=node.lineno,
            col_offset=node.col_offset,
            parent=node.parent,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
        )
    if kwargs and not args:
        # dict(a=1, b=2, c=4)
        items = [(nodes.Const(key), value) for key, value in kwargs]
    elif len(args) == 1 and kwargs:
        # dict(some_iterable, b=2, c=4)
        elts = _get_elts(args[0], context)
        keys = [(nodes.Const(key), value) for key, value in kwargs]
        items = elts + keys
    elif len(args) == 1:
        items = _get_elts(args[0], context)
    else:
        raise UseInferenceDefault()
    value = nodes.Dict(
        col_offset=node.col_offset,
        lineno=node.lineno,
        parent=node.parent,
        end_lineno=node.end_lineno,
        end_col_offset=node.end_col_offset,
    )
    value.postinit(items)
    return value


def infer_super(
    node: nodes.Call, context: InferenceContext | None = None
) -> objects.Super:
    """Understand super calls.

    There are some restrictions for what can be understood:

        * unbounded super (one argument form) is not understood.

        * if the super call is not inside a function (classmethod or method),
          then the default inference will be used.

        * if the super arguments can't be inferred, the default inference
          will be used.
    """
    if len(node.args) == 1:
        # Ignore unbounded super.
        raise UseInferenceDefault

    scope = node.scope()
    if not isinstance(scope, nodes.FunctionDef):
        # Ignore non-method uses of super.
        raise UseInferenceDefault
    if scope.type not in ("classmethod", "method"):
        # Not interested in staticmethods.
        raise UseInferenceDefault

    cls = scoped_nodes.get_wrapping_class(scope)
    assert cls is not None
    if not node.args:
        mro_pointer = cls
        # In we are in a classmethod, the interpreter will fill
        # automatically the class as the second argument, not an instance.
        if scope.type == "classmethod":
            mro_type = cls
        else:
            mro_type = cls.instantiate_class()
    else:
        try:
            mro_pointer = next(node.args[0].infer(context=context))
        except (InferenceError, StopIteration) as exc:
            raise UseInferenceDefault from exc
        try:
            mro_type = next(node.args[1].infer(context=context))
        except (InferenceError, StopIteration) as exc:
            raise UseInferenceDefault from exc

    if isinstance(mro_pointer, util.UninferableBase) or isinstance(
        mro_type, util.UninferableBase
    ):
        # No way we could understand this.
        raise UseInferenceDefault

    super_obj = objects.Super(
        mro_pointer=mro_pointer,
        mro_type=mro_type,
        self_class=cls,
        scope=scope,
        call=node,
    )
    super_obj.parent = node
    return super_obj


def _infer_getattr_args(node, context):
    if len(node.args) not in (2, 3):
        # Not a valid getattr call.
        raise UseInferenceDefault

    try:
        obj = next(node.args[0].infer(context=context))
        attr = next(node.args[1].infer(context=context))
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc

    if isinstance(obj, util.UninferableBase) or isinstance(attr, util.UninferableBase):
        # If one of the arguments is something we can't infer,
        # then also make the result of the getattr call something
        # which is unknown.
        return util.Uninferable, util.Uninferable

    is_string = isinstance(attr, nodes.Const) and isinstance(attr.value, str)
    if not is_string:
        raise UseInferenceDefault

    return obj, attr.value


def infer_getattr(node, context: InferenceContext | None = None):
    """Understand getattr calls.

    If one of the arguments is an Uninferable object, then the
    result will be an Uninferable object. Otherwise, the normal attribute
    lookup will be done.
    """
    obj, attr = _infer_getattr_args(node, context)
    if (
        isinstance(obj, util.UninferableBase)
        or isinstance(attr, util.UninferableBase)
        or not hasattr(obj, "igetattr")
    ):
        return util.Uninferable

    try:
        return next(obj.igetattr(attr, context=context))
    except (StopIteration, InferenceError, AttributeInferenceError):
        if len(node.args) == 3:
            # Try to infer the default and return it instead.
            try:
                return next(node.args[2].infer(context=context))
            except (StopIteration, InferenceError) as exc:
                raise UseInferenceDefault from exc

    raise UseInferenceDefault


def infer_hasattr(node, context: InferenceContext | None = None):
    """Understand hasattr calls.

    This always guarantees three possible outcomes for calling
    hasattr: Const(False) when we are sure that the object
    doesn't have the intended attribute, Const(True) when
    we know that the object has the attribute and Uninferable
    when we are unsure of the outcome of the function call.
    """
    try:
        obj, attr = _infer_getattr_args(node, context)
        if (
            isinstance(obj, util.UninferableBase)
            or isinstance(attr, util.UninferableBase)
            or not hasattr(obj, "getattr")
        ):
            return util.Uninferable
        obj.getattr(attr, context=context)
    except UseInferenceDefault:
        # Can't infer something from this function call.
        return util.Uninferable
    except AttributeInferenceError:
        # Doesn't have it.
        return nodes.Const(False)
    return nodes.Const(True)


def infer_callable(node, context: InferenceContext | None = None):
    """Understand callable calls.

    This follows Python's semantics, where an object
    is callable if it provides an attribute __call__,
    even though that attribute is something which can't be
    called.
    """
    if len(node.args) != 1:
        # Invalid callable call.
        raise UseInferenceDefault

    argument = node.args[0]
    try:
        inferred = next(argument.infer(context=context))
    except (InferenceError, StopIteration):
        return util.Uninferable
    if isinstance(inferred, util.UninferableBase):
        return util.Uninferable
    return nodes.Const(inferred.callable())


def infer_property(
    node: nodes.Call, context: InferenceContext | None = None
) -> objects.Property:
    """Understand `property` class.

    This only infers the output of `property`
    call, not the arguments themselves.
    """
    if len(node.args) < 1:
        # Invalid property call.
        raise UseInferenceDefault

    getter = node.args[0]
    try:
        inferred = next(getter.infer(context=context))
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc

    if not isinstance(inferred, (nodes.FunctionDef, nodes.Lambda)):
        raise UseInferenceDefault

    prop_func = objects.Property(
        function=inferred,
        name=inferred.name,
        lineno=node.lineno,
        col_offset=node.col_offset,
    )
    # Set parent outside __init__: https://github.com/pylint-dev/astroid/issues/1490
    prop_func.parent = node
    prop_func.postinit(
        body=[],
        args=inferred.args,
        doc_node=getattr(inferred, "doc_node", None),
    )
    return prop_func


def infer_bool(node, context: InferenceContext | None = None):
    """Understand bool calls."""
    if len(node.args) > 1:
        # Invalid bool call.
        raise UseInferenceDefault

    if not node.args:
        return nodes.Const(False)

    argument = node.args[0]
    try:
        inferred = next(argument.infer(context=context))
    except (InferenceError, StopIteration):
        return util.Uninferable
    if isinstance(inferred, util.UninferableBase):
        return util.Uninferable

    bool_value = inferred.bool_value(context=context)
    if isinstance(bool_value, util.UninferableBase):
        return util.Uninferable
    return nodes.Const(bool_value)


def infer_type(node, context: InferenceContext | None = None):
    """Understand the one-argument form of *type*."""
    if len(node.args) != 1:
        raise UseInferenceDefault

    return helpers.object_type(node.args[0], context)


def infer_slice(node, context: InferenceContext | None = None):
    """Understand `slice` calls."""
    args = node.args
    if not 0 < len(args) <= 3:
        raise UseInferenceDefault

    infer_func = partial(util.safe_infer, context=context)
    args = [infer_func(arg) for arg in args]
    for arg in args:
        if not arg or isinstance(arg, util.UninferableBase):
            raise UseInferenceDefault
        if not isinstance(arg, nodes.Const):
            raise UseInferenceDefault
        if not isinstance(arg.value, (type(None), int)):
            raise UseInferenceDefault

    if len(args) < 3:
        # Make sure we have 3 arguments.
        args.extend([None] * (3 - len(args)))

    slice_node = nodes.Slice(
        lineno=node.lineno,
        col_offset=node.col_offset,
        parent=node.parent,
        end_lineno=node.end_lineno,
        end_col_offset=node.end_col_offset,
    )
    slice_node.postinit(*args)
    return slice_node


def _infer_object__new__decorator(
    node: nodes.ClassDef, context: InferenceContext | None = None, **kwargs: Any
) -> Iterator[Instance]:
    # Instantiate class immediately
    # since that's what @object.__new__ does
    return iter((node.instantiate_class(),))


def _infer_object__new__decorator_check(node) -> bool:
    """Predicate before inference_tip.

    Check if the given ClassDef has an @object.__new__ decorator
    """
    if not node.decorators:
        return False

    for decorator in node.decorators.nodes:
        if isinstance(decorator, nodes.Attribute):
            if decorator.as_string() == OBJECT_DUNDER_NEW:
                return True
    return False


def infer_issubclass(callnode, context: InferenceContext | None = None):
    """Infer issubclass() calls.

    :param nodes.Call callnode: an `issubclass` call
    :param InferenceContext context: the context for the inference
    :rtype nodes.Const: Boolean Const value of the `issubclass` call
    :raises UseInferenceDefault: If the node cannot be inferred
    """
    call = arguments.CallSite.from_call(callnode, context=context)
    if call.keyword_arguments:
        # issubclass doesn't support keyword arguments
        raise UseInferenceDefault("TypeError: issubclass() takes no keyword arguments")
    if len(call.positional_arguments) != 2:
        raise UseInferenceDefault(
            f"Expected two arguments, got {len(call.positional_arguments)}"
        )
    # The left hand argument is the obj to be checked
    obj_node, class_or_tuple_node = call.positional_arguments

    try:
        obj_type = next(obj_node.infer(context=context))
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc
    if not isinstance(obj_type, nodes.ClassDef):
        raise UseInferenceDefault("TypeError: arg 1 must be class")

    # The right hand argument is the class(es) that the given
    # object is to be checked against.
    try:
        class_container = _class_or_tuple_to_container(
            class_or_tuple_node, context=context
        )
    except InferenceError as exc:
        raise UseInferenceDefault from exc
    try:
        issubclass_bool = helpers.object_issubclass(obj_type, class_container, context)
    except AstroidTypeError as exc:
        raise UseInferenceDefault("TypeError: " + str(exc)) from exc
    except MroError as exc:
        raise UseInferenceDefault from exc
    return nodes.Const(issubclass_bool)


def infer_isinstance(
    callnode: nodes.Call, context: InferenceContext | None = None
) -> nodes.Const:
    """Infer isinstance calls.

    :param nodes.Call callnode: an isinstance call
    :raises UseInferenceDefault: If the node cannot be inferred
    """
    call = arguments.CallSite.from_call(callnode, context=context)
    if call.keyword_arguments:
        # isinstance doesn't support keyword arguments
        raise UseInferenceDefault("TypeError: isinstance() takes no keyword arguments")
    if len(call.positional_arguments) != 2:
        raise UseInferenceDefault(
            f"Expected two arguments, got {len(call.positional_arguments)}"
        )
    # The left hand argument is the obj to be checked
    obj_node, class_or_tuple_node = call.positional_arguments
    # The right hand argument is the class(es) that the given
    # obj is to be check is an instance of
    try:
        class_container = _class_or_tuple_to_container(
            class_or_tuple_node, context=context
        )
    except InferenceError as exc:
        raise UseInferenceDefault from exc
    try:
        isinstance_bool = helpers.object_isinstance(obj_node, class_container, context)
    except AstroidTypeError as exc:
        raise UseInferenceDefault("TypeError: " + str(exc)) from exc
    except MroError as exc:
        raise UseInferenceDefault from exc
    if isinstance(isinstance_bool, util.UninferableBase):
        raise UseInferenceDefault
    return nodes.Const(isinstance_bool)


def _class_or_tuple_to_container(
    node: InferenceResult, context: InferenceContext | None = None
) -> list[InferenceResult]:
    # Move inferences results into container
    # to simplify later logic
    # raises InferenceError if any of the inferences fall through
    try:
        node_infer = next(node.infer(context=context))
    except StopIteration as e:
        raise InferenceError(node=node, context=context) from e
    # arg2 MUST be a type or a TUPLE of types
    # for isinstance
    if isinstance(node_infer, nodes.Tuple):
        try:
            class_container = [
                next(node.infer(context=context)) for node in node_infer.elts
            ]
        except StopIteration as e:
            raise InferenceError(node=node, context=context) from e
    else:
        class_container = [node_infer]
    return class_container


def infer_len(node, context: InferenceContext | None = None):
    """Infer length calls.

    :param nodes.Call node: len call to infer
    :param context.InferenceContext: node context
    :rtype nodes.Const: a Const node with the inferred length, if possible
    """
    call = arguments.CallSite.from_call(node, context=context)
    if call.keyword_arguments:
        raise UseInferenceDefault("TypeError: len() must take no keyword arguments")
    if len(call.positional_arguments) != 1:
        raise UseInferenceDefault(
            "TypeError: len() must take exactly one argument "
            "({len}) given".format(len=len(call.positional_arguments))
        )
    [argument_node] = call.positional_arguments

    try:
        return nodes.Const(helpers.object_len(argument_node, context=context))
    except (AstroidTypeError, InferenceError) as exc:
        raise UseInferenceDefault(str(exc)) from exc


def infer_str(node, context: InferenceContext | None = None):
    """Infer str() calls.

    :param nodes.Call node: str() call to infer
    :param context.InferenceContext: node context
    :rtype nodes.Const: a Const containing an empty string
    """
    call = arguments.CallSite.from_call(node, context=context)
    if call.keyword_arguments:
        raise UseInferenceDefault("TypeError: str() must take no keyword arguments")
    try:
        return nodes.Const("")
    except (AstroidTypeError, InferenceError) as exc:
        raise UseInferenceDefault(str(exc)) from exc


def infer_int(node, context: InferenceContext | None = None):
    """Infer int() calls.

    :param nodes.Call node: int() call to infer
    :param context.InferenceContext: node context
    :rtype nodes.Const: a Const containing the integer value of the int() call
    """
    call = arguments.CallSite.from_call(node, context=context)
    if call.keyword_arguments:
        raise UseInferenceDefault("TypeError: int() must take no keyword arguments")

    if call.positional_arguments:
        try:
            first_value = next(call.positional_arguments[0].infer(context=context))
        except (InferenceError, StopIteration) as exc:
            raise UseInferenceDefault(str(exc)) from exc

        if isinstance(first_value, util.UninferableBase):
            raise UseInferenceDefault

        if isinstance(first_value, nodes.Const) and isinstance(
            first_value.value, (int, str)
        ):
            try:
                actual_value = int(first_value.value)
            except ValueError:
                return nodes.Const(0)
            return nodes.Const(actual_value)

    return nodes.Const(0)


def infer_dict_fromkeys(node, context: InferenceContext | None = None):
    """Infer dict.fromkeys.

    :param nodes.Call node: dict.fromkeys() call to infer
    :param context.InferenceContext context: node context
    :rtype nodes.Dict:
        a Dictionary containing the values that astroid was able to infer.
        In case the inference failed for any reason, an empty dictionary
        will be inferred instead.
    """

    def _build_dict_with_elements(elements):
        new_node = nodes.Dict(
            col_offset=node.col_offset,
            lineno=node.lineno,
            parent=node.parent,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
        )
        new_node.postinit(elements)
        return new_node

    call = arguments.CallSite.from_call(node, context=context)
    if call.keyword_arguments:
        raise UseInferenceDefault("TypeError: int() must take no keyword arguments")
    if len(call.positional_arguments) not in {1, 2}:
        raise UseInferenceDefault(
            "TypeError: Needs between 1 and 2 positional arguments"
        )

    default = nodes.Const(None)
    values = call.positional_arguments[0]
    try:
        inferred_values = next(values.infer(context=context))
    except (InferenceError, StopIteration):
        return _build_dict_with_elements([])
    if inferred_values is util.Uninferable:
        return _build_dict_with_elements([])

    # Limit to a couple of potential values, as this can become pretty complicated
    accepted_iterable_elements = (nodes.Const,)
    if isinstance(inferred_values, (nodes.List, nodes.Set, nodes.Tuple)):
        elements = inferred_values.elts
        for element in elements:
            if not isinstance(element, accepted_iterable_elements):
                # Fallback to an empty dict
                return _build_dict_with_elements([])

        elements_with_value = [(element, default) for element in elements]
        return _build_dict_with_elements(elements_with_value)
    if isinstance(inferred_values, nodes.Const) and isinstance(
        inferred_values.value, (str, bytes)
    ):
        elements_with_value = [
            (nodes.Const(element), default) for element in inferred_values.value
        ]
        return _build_dict_with_elements(elements_with_value)
    if isinstance(inferred_values, nodes.Dict):
        keys = inferred_values.itered()
        for key in keys:
            if not isinstance(key, accepted_iterable_elements):
                # Fallback to an empty dict
                return _build_dict_with_elements([])

        elements_with_value = [(element, default) for element in keys]
        return _build_dict_with_elements(elements_with_value)

    # Fallback to an empty dictionary
    return _build_dict_with_elements([])


def _infer_copy_method(
    node: nodes.Call, context: InferenceContext | None = None, **kwargs: Any
) -> Iterator[CopyResult]:
    assert isinstance(node.func, nodes.Attribute)
    inferred_orig, inferred_copy = itertools.tee(node.func.expr.infer(context=context))
    if all(
        isinstance(
            inferred_node, (nodes.Dict, nodes.List, nodes.Set, objects.FrozenSet)
        )
        for inferred_node in inferred_orig
    ):
        return cast(Iterator[CopyResult], inferred_copy)

    raise UseInferenceDefault


def _is_str_format_call(node: nodes.Call) -> bool:
    """Catch calls to str.format()."""
    if not isinstance(node.func, nodes.Attribute) or not node.func.attrname == "format":
        return False

    if isinstance(node.func.expr, nodes.Name):
        value = util.safe_infer(node.func.expr)
    else:
        value = node.func.expr

    return isinstance(value, nodes.Const) and isinstance(value.value, str)


def _infer_str_format_call(
    node: nodes.Call, context: InferenceContext | None = None, **kwargs: Any
) -> Iterator[ConstFactoryResult | util.UninferableBase]:
    """Return a Const node based on the template and passed arguments."""
    call = arguments.CallSite.from_call(node, context=context)
    assert isinstance(node.func, (nodes.Attribute, nodes.AssignAttr, nodes.DelAttr))

    value: nodes.Const
    if isinstance(node.func.expr, nodes.Name):
        if not (inferred := util.safe_infer(node.func.expr)) or not isinstance(
            inferred, nodes.Const
        ):
            return iter([util.Uninferable])
        value = inferred
    elif isinstance(node.func.expr, nodes.Const):
        value = node.func.expr
    else:  # pragma: no cover
        return iter([util.Uninferable])

    format_template = value.value

    # Get the positional arguments passed
    inferred_positional: list[nodes.Const] = []
    for i in call.positional_arguments:
        one_inferred = util.safe_infer(i, context)
        if not isinstance(one_inferred, nodes.Const):
            return iter([util.Uninferable])
        inferred_positional.append(one_inferred)

    pos_values: list[str] = [i.value for i in inferred_positional]

    # Get the keyword arguments passed
    inferred_keyword: dict[str, nodes.Const] = {}
    for k, v in call.keyword_arguments.items():
        one_inferred = util.safe_infer(v, context)
        if not isinstance(one_inferred, nodes.Const):
            return iter([util.Uninferable])
        inferred_keyword[k] = one_inferred

    keyword_values: dict[str, str] = {k: v.value for k, v in inferred_keyword.items()}

    try:
        formatted_string = format_template.format(*pos_values, **keyword_values)
    except (AttributeError, IndexError, KeyError, TypeError, ValueError):
        # AttributeError: named field in format string was not found in the arguments
        # IndexError: there are too few arguments to interpolate
        # TypeError: Unsupported format string
        # ValueError: Unknown format code
        return iter([util.Uninferable])

    return iter([nodes.const_factory(formatted_string)])


def register(manager: AstroidManager) -> None:
    # Builtins inference
    register_builtin_transform(manager, infer_bool, "bool")
    register_builtin_transform(manager, infer_super, "super")
    register_builtin_transform(manager, infer_callable, "callable")
    register_builtin_transform(manager, infer_property, "property")
    register_builtin_transform(manager, infer_getattr, "getattr")
    register_builtin_transform(manager, infer_hasattr, "hasattr")
    register_builtin_transform(manager, infer_tuple, "tuple")
    register_builtin_transform(manager, infer_set, "set")
    register_builtin_transform(manager, infer_list, "list")
    register_builtin_transform(manager, infer_dict, "dict")
    register_builtin_transform(manager, infer_frozenset, "frozenset")
    register_builtin_transform(manager, infer_type, "type")
    register_builtin_transform(manager, infer_slice, "slice")
    register_builtin_transform(manager, infer_isinstance, "isinstance")
    register_builtin_transform(manager, infer_issubclass, "issubclass")
    register_builtin_transform(manager, infer_len, "len")
    register_builtin_transform(manager, infer_str, "str")
    register_builtin_transform(manager, infer_int, "int")
    register_builtin_transform(manager, infer_dict_fromkeys, "dict.fromkeys")

    # Infer object.__new__ calls
    manager.register_transform(
        nodes.ClassDef,
        inference_tip(_infer_object__new__decorator),
        _infer_object__new__decorator_check,
    )

    manager.register_transform(
        nodes.Call,
        inference_tip(_infer_copy_method),
        lambda node: isinstance(node.func, nodes.Attribute)
        and node.func.attrname == "copy",
    )

    manager.register_transform(
        nodes.Call,
        inference_tip(_infer_str_format_call),
        _is_str_format_call,
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_builtin_inference.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from astroid.brain.helpers import register_module_extender
from astroid.builder import extract_node, parse
from astroid.const import PY39_PLUS
from astroid.context import InferenceContext
from astroid.exceptions import AttributeInferenceError
from astroid.manager import AstroidManager
from astroid.nodes.scoped_nodes import ClassDef


def _collections_transform():
    return parse(
        """
    class defaultdict(dict):
        default_factory = None
        def __missing__(self, key): pass
        def __getitem__(self, key): return default_factory

    """
        + _deque_mock()
        + _ordered_dict_mock()
    )


def _deque_mock():
    base_deque_class = """
    class deque(object):
        maxlen = 0
        def __init__(self, iterable=None, maxlen=None):
            self.iterable = iterable or []
        def append(self, x): pass
        def appendleft(self, x): pass
        def clear(self): pass
        def count(self, x): return 0
        def extend(self, iterable): pass
        def extendleft(self, iterable): pass
        def pop(self): return self.iterable[0]
        def popleft(self): return self.iterable[0]
        def remove(self, value): pass
        def reverse(self): return reversed(self.iterable)
        def rotate(self, n=1): return self
        def __iter__(self): return self
        def __reversed__(self): return self.iterable[::-1]
        def __getitem__(self, index): return self.iterable[index]
        def __setitem__(self, index, value): pass
        def __delitem__(self, index): pass
        def __bool__(self): return bool(self.iterable)
        def __nonzero__(self): return bool(self.iterable)
        def __contains__(self, o): return o in self.iterable
        def __len__(self): return len(self.iterable)
        def __copy__(self): return deque(self.iterable)
        def copy(self): return deque(self.iterable)
        def index(self, x, start=0, end=0): return 0
        def insert(self, i, x): pass
        def __add__(self, other): pass
        def __iadd__(self, other): pass
        def __mul__(self, other): pass
        def __imul__(self, other): pass
        def __rmul__(self, other): pass"""
    if PY39_PLUS:
        base_deque_class += """
        @classmethod
        def __class_getitem__(self, item): return cls"""
    return base_deque_class


def _ordered_dict_mock():
    base_ordered_dict_class = """
    class OrderedDict(dict):
        def __reversed__(self): return self[::-1]
        def move_to_end(self, key, last=False): pass"""
    if PY39_PLUS:
        base_ordered_dict_class += """
        @classmethod
        def __class_getitem__(cls, item): return cls"""
    return base_ordered_dict_class


def _looks_like_subscriptable(node: ClassDef) -> bool:
    """
    Returns True if the node corresponds to a ClassDef of the Collections.abc module
    that supports subscripting.

    :param node: ClassDef node
    """
    if node.qname().startswith("_collections") or node.qname().startswith(
        "collections"
    ):
        try:
            node.getattr("__class_getitem__")
            return True
        except AttributeInferenceError:
            pass
    return False


CLASS_GET_ITEM_TEMPLATE = """
@classmethod
def __class_getitem__(cls, item):
    return cls
"""


def easy_class_getitem_inference(node, context: InferenceContext | None = None):
    # Here __class_getitem__ exists but is quite a mess to infer thus
    # put an easy inference tip
    func_to_add = extract_node(CLASS_GET_ITEM_TEMPLATE)
    node.locals["__class_getitem__"] = [func_to_add]


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "collections", _collections_transform)

    if PY39_PLUS:
        # Starting with Python39 some objects of the collection module are subscriptable
        # thanks to the __class_getitem__ method but the way it is implemented in
        # _collection_abc makes it difficult to infer. (We would have to handle AssignName inference in the
        # getitem method of the ClassDef class) Instead we put here a mock of the __class_getitem__ method
        manager.register_transform(
            ClassDef, easy_class_getitem_inference, _looks_like_subscriptable
        )

back/venvmsg/Lib/site-packages/astroid/brain/brain_collections.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def _re_transform():
    return parse(
        """
    from collections import namedtuple
    _Method = namedtuple('_Method', 'name ident salt_chars total_size')

    METHOD_SHA512 = _Method('SHA512', '6', 16, 106)
    METHOD_SHA256 = _Method('SHA256', '5', 16, 63)
    METHOD_BLOWFISH = _Method('BLOWFISH', 2, 'b', 22)
    METHOD_MD5 = _Method('MD5', '1', 8, 34)
    METHOD_CRYPT = _Method('CRYPT', None, 2, 13)
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "crypt", _re_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_crypt.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Astroid hooks for ctypes module.

Inside the ctypes module, the value class is defined inside
the C coded module _ctypes.
Thus astroid doesn't know that the value member is a builtin type
among float, int, bytes or str.
"""
import sys

from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def enrich_ctypes_redefined_types():
    """
    For each ctypes redefined types, overload 'value' and '_type_' members
    definition.

    Overloading 'value' is mandatory otherwise astroid cannot infer the correct type for it.
    Overloading '_type_' is necessary because the class definition made here replaces the original
    one, in which '_type_' member is defined. Luckily those original class definitions are very short
    and contain only the '_type_' member definition.
    """
    c_class_to_type = (
        ("c_byte", "int", "b"),
        ("c_char", "bytes", "c"),
        ("c_double", "float", "d"),
        ("c_float", "float", "f"),
        ("c_int", "int", "i"),
        ("c_int16", "int", "h"),
        ("c_int32", "int", "i"),
        ("c_int64", "int", "l"),
        ("c_int8", "int", "b"),
        ("c_long", "int", "l"),
        ("c_longdouble", "float", "g"),
        ("c_longlong", "int", "l"),
        ("c_short", "int", "h"),
        ("c_size_t", "int", "L"),
        ("c_ssize_t", "int", "l"),
        ("c_ubyte", "int", "B"),
        ("c_uint", "int", "I"),
        ("c_uint16", "int", "H"),
        ("c_uint32", "int", "I"),
        ("c_uint64", "int", "L"),
        ("c_uint8", "int", "B"),
        ("c_ulong", "int", "L"),
        ("c_ulonglong", "int", "L"),
        ("c_ushort", "int", "H"),
        ("c_wchar", "str", "u"),
    )

    src = [
        """
from _ctypes import _SimpleCData

class c_bool(_SimpleCData):
    def __init__(self, value):
        self.value = True
        self._type_ = '?'
    """
    ]

    for c_type, builtin_type, type_code in c_class_to_type:
        src.append(
            f"""
class {c_type}(_SimpleCData):
    def __init__(self, value):
        self.value = {builtin_type}(value)
        self._type_ = '{type_code}'
        """
        )

    return parse("\n".join(src))


def register(manager: AstroidManager) -> None:
    if not hasattr(sys, "pypy_version_info"):
        # No need of this module in pypy where everything is written in python
        register_module_extender(manager, "ctypes", enrich_ctypes_redefined_types)

back/venvmsg/Lib/site-packages/astroid/brain/brain_ctypes.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def _curses_transform():
    return parse(
        """
    A_ALTCHARSET = 1
    A_BLINK = 1
    A_BOLD = 1
    A_DIM = 1
    A_INVIS = 1
    A_ITALIC = 1
    A_NORMAL = 1
    A_PROTECT = 1
    A_REVERSE = 1
    A_STANDOUT = 1
    A_UNDERLINE = 1
    A_HORIZONTAL = 1
    A_LEFT = 1
    A_LOW = 1
    A_RIGHT = 1
    A_TOP = 1
    A_VERTICAL = 1
    A_CHARTEXT = 1
    A_ATTRIBUTES = 1
    A_CHARTEXT = 1
    A_COLOR = 1
    KEY_MIN = 1
    KEY_BREAK = 1
    KEY_DOWN = 1
    KEY_UP = 1
    KEY_LEFT = 1
    KEY_RIGHT = 1
    KEY_HOME = 1
    KEY_BACKSPACE = 1
    KEY_F0 = 1
    KEY_Fn = 1
    KEY_DL = 1
    KEY_IL = 1
    KEY_DC = 1
    KEY_IC = 1
    KEY_EIC = 1
    KEY_CLEAR = 1
    KEY_EOS = 1
    KEY_EOL = 1
    KEY_SF = 1
    KEY_SR = 1
    KEY_NPAGE = 1
    KEY_PPAGE = 1
    KEY_STAB = 1
    KEY_CTAB = 1
    KEY_CATAB = 1
    KEY_ENTER = 1
    KEY_SRESET = 1
    KEY_RESET = 1
    KEY_PRINT = 1
    KEY_LL = 1
    KEY_A1 = 1
    KEY_A3 = 1
    KEY_B2 = 1
    KEY_C1 = 1
    KEY_C3 = 1
    KEY_BTAB = 1
    KEY_BEG = 1
    KEY_CANCEL = 1
    KEY_CLOSE = 1
    KEY_COMMAND = 1
    KEY_COPY = 1
    KEY_CREATE = 1
    KEY_END = 1
    KEY_EXIT = 1
    KEY_FIND = 1
    KEY_HELP = 1
    KEY_MARK = 1
    KEY_MESSAGE = 1
    KEY_MOVE = 1
    KEY_NEXT = 1
    KEY_OPEN = 1
    KEY_OPTIONS = 1
    KEY_PREVIOUS = 1
    KEY_REDO = 1
    KEY_REFERENCE = 1
    KEY_REFRESH = 1
    KEY_REPLACE = 1
    KEY_RESTART = 1
    KEY_RESUME = 1
    KEY_SAVE = 1
    KEY_SBEG = 1
    KEY_SCANCEL = 1
    KEY_SCOMMAND = 1
    KEY_SCOPY = 1
    KEY_SCREATE = 1
    KEY_SDC = 1
    KEY_SDL = 1
    KEY_SELECT = 1
    KEY_SEND = 1
    KEY_SEOL = 1
    KEY_SEXIT = 1
    KEY_SFIND = 1
    KEY_SHELP = 1
    KEY_SHOME = 1
    KEY_SIC = 1
    KEY_SLEFT = 1
    KEY_SMESSAGE = 1
    KEY_SMOVE = 1
    KEY_SNEXT = 1
    KEY_SOPTIONS = 1
    KEY_SPREVIOUS = 1
    KEY_SPRINT = 1
    KEY_SREDO = 1
    KEY_SREPLACE = 1
    KEY_SRIGHT = 1
    KEY_SRSUME = 1
    KEY_SSAVE = 1
    KEY_SSUSPEND = 1
    KEY_SUNDO = 1
    KEY_SUSPEND = 1
    KEY_UNDO = 1
    KEY_MOUSE = 1
    KEY_RESIZE = 1
    KEY_MAX = 1
    ACS_BBSS = 1
    ACS_BLOCK = 1
    ACS_BOARD = 1
    ACS_BSBS = 1
    ACS_BSSB = 1
    ACS_BSSS = 1
    ACS_BTEE = 1
    ACS_BULLET = 1
    ACS_CKBOARD = 1
    ACS_DARROW = 1
    ACS_DEGREE = 1
    ACS_DIAMOND = 1
    ACS_GEQUAL = 1
    ACS_HLINE = 1
    ACS_LANTERN = 1
    ACS_LARROW = 1
    ACS_LEQUAL = 1
    ACS_LLCORNER = 1
    ACS_LRCORNER = 1
    ACS_LTEE = 1
    ACS_NEQUAL = 1
    ACS_PI = 1
    ACS_PLMINUS = 1
    ACS_PLUS = 1
    ACS_RARROW = 1
    ACS_RTEE = 1
    ACS_S1 = 1
    ACS_S3 = 1
    ACS_S7 = 1
    ACS_S9 = 1
    ACS_SBBS = 1
    ACS_SBSB = 1
    ACS_SBSS = 1
    ACS_SSBB = 1
    ACS_SSBS = 1
    ACS_SSSB = 1
    ACS_SSSS = 1
    ACS_STERLING = 1
    ACS_TTEE = 1
    ACS_UARROW = 1
    ACS_ULCORNER = 1
    ACS_URCORNER = 1
    ACS_VLINE = 1
    COLOR_BLACK = 1
    COLOR_BLUE = 1
    COLOR_CYAN = 1
    COLOR_GREEN = 1
    COLOR_MAGENTA = 1
    COLOR_RED = 1
    COLOR_WHITE = 1
    COLOR_YELLOW = 1
        """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "curses", _curses_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_curses.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Astroid hook for the dataclasses library.

Support built-in dataclasses, pydantic.dataclasses, and marshmallow_dataclass-annotated
dataclasses. References:
- https://docs.python.org/3/library/dataclasses.html
- https://pydantic-docs.helpmanual.io/usage/dataclasses/
- https://lovasoa.github.io/marshmallow_dataclass/
"""

from __future__ import annotations

from collections.abc import Iterator
from typing import Literal, Tuple, Union

from astroid import bases, context, nodes
from astroid.builder import parse
from astroid.const import PY39_PLUS, PY310_PLUS
from astroid.exceptions import AstroidSyntaxError, InferenceError, UseInferenceDefault
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.typing import InferenceResult
from astroid.util import Uninferable, UninferableBase, safe_infer

_FieldDefaultReturn = Union[
    None,
    Tuple[Literal["default"], nodes.NodeNG],
    Tuple[Literal["default_factory"], nodes.Call],
]

DATACLASSES_DECORATORS = frozenset(("dataclass",))
FIELD_NAME = "field"
DATACLASS_MODULES = frozenset(
    ("dataclasses", "marshmallow_dataclass", "pydantic.dataclasses")
)
DEFAULT_FACTORY = "_HAS_DEFAULT_FACTORY"  # based on typing.py


def is_decorated_with_dataclass(
    node: nodes.ClassDef, decorator_names: frozenset[str] = DATACLASSES_DECORATORS
) -> bool:
    """Return True if a decorated node has a `dataclass` decorator applied."""
    if not isinstance(node, nodes.ClassDef) or not node.decorators:
        return False

    return any(
        _looks_like_dataclass_decorator(decorator_attribute, decorator_names)
        for decorator_attribute in node.decorators.nodes
    )


def dataclass_transform(node: nodes.ClassDef) -> None:
    """Rewrite a dataclass to be easily understood by pylint."""
    node.is_dataclass = True

    for assign_node in _get_dataclass_attributes(node):
        name = assign_node.target.name

        rhs_node = nodes.Unknown(
            lineno=assign_node.lineno,
            col_offset=assign_node.col_offset,
            parent=assign_node,
        )
        rhs_node = AstroidManager().visit_transforms(rhs_node)
        node.instance_attrs[name] = [rhs_node]

    if not _check_generate_dataclass_init(node):
        return

    kw_only_decorated = False
    if PY310_PLUS and node.decorators.nodes:
        for decorator in node.decorators.nodes:
            if not isinstance(decorator, nodes.Call):
                kw_only_decorated = False
                break
            for keyword in decorator.keywords:
                if keyword.arg == "kw_only":
                    kw_only_decorated = keyword.value.bool_value()

    init_str = _generate_dataclass_init(
        node,
        list(_get_dataclass_attributes(node, init=True)),
        kw_only_decorated,
    )

    try:
        init_node = parse(init_str)["__init__"]
    except AstroidSyntaxError:
        pass
    else:
        init_node.parent = node
        init_node.lineno, init_node.col_offset = None, None
        node.locals["__init__"] = [init_node]

        root = node.root()
        if DEFAULT_FACTORY not in root.locals:
            new_assign = parse(f"{DEFAULT_FACTORY} = object()").body[0]
            new_assign.parent = root
            root.locals[DEFAULT_FACTORY] = [new_assign.targets[0]]


def _get_dataclass_attributes(
    node: nodes.ClassDef, init: bool = False
) -> Iterator[nodes.AnnAssign]:
    """Yield the AnnAssign nodes of dataclass attributes for the node.

    If init is True, also include InitVars.
    """
    for assign_node in node.body:
        if not isinstance(assign_node, nodes.AnnAssign) or not isinstance(
            assign_node.target, nodes.AssignName
        ):
            continue

        # Annotation is never None
        if _is_class_var(assign_node.annotation):  # type: ignore[arg-type]
            continue

        if _is_keyword_only_sentinel(assign_node.annotation):
            continue

        # Annotation is never None
        if not init and _is_init_var(assign_node.annotation):  # type: ignore[arg-type]
            continue

        yield assign_node


def _check_generate_dataclass_init(node: nodes.ClassDef) -> bool:
    """Return True if we should generate an __init__ method for node.

    This is True when:
        - node doesn't define its own __init__ method
        - the dataclass decorator was called *without* the keyword argument init=False
    """
    if "__init__" in node.locals:
        return False

    found = None

    for decorator_attribute in node.decorators.nodes:
        if not isinstance(decorator_attribute, nodes.Call):
            continue

        if _looks_like_dataclass_decorator(decorator_attribute):
            found = decorator_attribute

    if found is None:
        return True

    # Check for keyword arguments of the form init=False
    return not any(
        keyword.arg == "init"
        and not keyword.value.bool_value()  # type: ignore[union-attr] # value is never None
        for keyword in found.keywords
    )


def _find_arguments_from_base_classes(
    node: nodes.ClassDef,
) -> tuple[
    dict[str, tuple[str | None, str | None]], dict[str, tuple[str | None, str | None]]
]:
    """Iterate through all bases and get their typing and defaults."""
    pos_only_store: dict[str, tuple[str | None, str | None]] = {}
    kw_only_store: dict[str, tuple[str | None, str | None]] = {}
    # See TODO down below
    # all_have_defaults = True

    for base in reversed(node.mro()):
        if not base.is_dataclass:
            continue
        try:
            base_init: nodes.FunctionDef = base.locals["__init__"][0]
        except KeyError:
            continue

        pos_only, kw_only = base_init.args._get_arguments_data()
        for posarg, data in pos_only.items():
            # if data[1] is None:
            #     if all_have_defaults and pos_only_store:
            #         # TODO: This should return an Uninferable as this would raise
            #         # a TypeError at runtime. However, transforms can't return
            #         # Uninferables currently.
            #         pass
            #     all_have_defaults = False
            pos_only_store[posarg] = data

        for kwarg, data in kw_only.items():
            kw_only_store[kwarg] = data
    return pos_only_store, kw_only_store


def _parse_arguments_into_strings(
    pos_only_store: dict[str, tuple[str | None, str | None]],
    kw_only_store: dict[str, tuple[str | None, str | None]],
) -> tuple[str, str]:
    """Parse positional and keyword arguments into strings for an __init__ method."""
    pos_only, kw_only = "", ""
    for pos_arg, data in pos_only_store.items():
        pos_only += pos_arg
        if data[0]:
            pos_only += ": " + data[0]
        if data[1]:
            pos_only += " = " + data[1]
        pos_only += ", "
    for kw_arg, data in kw_only_store.items():
        kw_only += kw_arg
        if data[0]:
            kw_only += ": " + data[0]
        if data[1]:
            kw_only += " = " + data[1]
        kw_only += ", "

    return pos_only, kw_only


def _get_previous_field_default(node: nodes.ClassDef, name: str) -> nodes.NodeNG | None:
    """Get the default value of a previously defined field."""
    for base in reversed(node.mro()):
        if not base.is_dataclass:
            continue
        if name in base.locals:
            for assign in base.locals[name]:
                if (
                    isinstance(assign.parent, nodes.AnnAssign)
                    and assign.parent.value
                    and isinstance(assign.parent.value, nodes.Call)
                    and _looks_like_dataclass_field_call(assign.parent.value)
                ):
                    default = _get_field_default(assign.parent.value)
                    if default:
                        return default[1]
    return None


def _generate_dataclass_init(  # pylint: disable=too-many-locals
    node: nodes.ClassDef, assigns: list[nodes.AnnAssign], kw_only_decorated: bool
) -> str:
    """Return an init method for a dataclass given the targets."""
    params: list[str] = []
    kw_only_params: list[str] = []
    assignments: list[str] = []

    prev_pos_only_store, prev_kw_only_store = _find_arguments_from_base_classes(node)

    for assign in assigns:
        name, annotation, value = assign.target.name, assign.annotation, assign.value

        # Check whether this assign is overriden by a property assignment
        property_node: nodes.FunctionDef | None = None
        for additional_assign in node.locals[name]:
            if not isinstance(additional_assign, nodes.FunctionDef):
                continue
            if not additional_assign.decorators:
                continue
            if "builtins.property" in additional_assign.decoratornames():
                property_node = additional_assign
                break

        is_field = isinstance(value, nodes.Call) and _looks_like_dataclass_field_call(
            value, check_scope=False
        )

        if is_field:
            # Skip any fields that have `init=False`
            if any(
                keyword.arg == "init" and not keyword.value.bool_value()
                for keyword in value.keywords  # type: ignore[union-attr] # value is never None
            ):
                # Also remove the name from the previous arguments to be inserted later
                prev_pos_only_store.pop(name, None)
                prev_kw_only_store.pop(name, None)
                continue

        if _is_init_var(annotation):  # type: ignore[arg-type] # annotation is never None
            init_var = True
            if isinstance(annotation, nodes.Subscript):
                annotation = annotation.slice
            else:
                # Cannot determine type annotation for parameter from InitVar
                annotation = None
            assignment_str = ""
        else:
            init_var = False
            assignment_str = f"self.{name} = {name}"

        ann_str, default_str = None, None
        if annotation is not None:
            ann_str = annotation.as_string()

        if value:
            if is_field:
                result = _get_field_default(value)  # type: ignore[arg-type]
                if result:
                    default_type, default_node = result
                    if default_type == "default":
                        default_str = default_node.as_string()
                    elif default_type == "default_factory":
                        default_str = DEFAULT_FACTORY
                        assignment_str = (
                            f"self.{name} = {default_node.as_string()} "
                            f"if {name} is {DEFAULT_FACTORY} else {name}"
                        )
            else:
                default_str = value.as_string()
        elif property_node:
            # We set the result of the property call as default
            # This hides the fact that this would normally be a 'property object'
            # But we can't represent those as string
            try:
                # Call str to make sure also Uninferable gets stringified
                default_str = str(
                    next(property_node.infer_call_result(None)).as_string()
                )
            except (InferenceError, StopIteration):
                pass
        else:
            # Even with `init=False` the default value still can be propogated to
            # later assignments. Creating weird signatures like:
            # (self, a: str = 1) -> None
            previous_default = _get_previous_field_default(node, name)
            if previous_default:
                default_str = previous_default.as_string()

        # Construct the param string to add to the init if necessary
        param_str = name
        if ann_str is not None:
            param_str += f": {ann_str}"
        if default_str is not None:
            param_str += f" = {default_str}"

        # If the field is a kw_only field, we need to add it to the kw_only_params
        # This overwrites whether or not the class is kw_only decorated
        if is_field:
            kw_only = [k for k in value.keywords if k.arg == "kw_only"]  # type: ignore[union-attr]
            if kw_only:
                if kw_only[0].value.bool_value():
                    kw_only_params.append(param_str)
                else:
                    params.append(param_str)
                continue
        # If kw_only decorated, we need to add all parameters to the kw_only_params
        if kw_only_decorated:
            if name in prev_kw_only_store:
                prev_kw_only_store[name] = (ann_str, default_str)
            else:
                kw_only_params.append(param_str)
        else:
            # If the name was previously seen, overwrite that data
            # pylint: disable-next=else-if-used
            if name in prev_pos_only_store:
                prev_pos_only_store[name] = (ann_str, default_str)
            elif name in prev_kw_only_store:
                params = [name, *params]
                prev_kw_only_store.pop(name)
            else:
                params.append(param_str)

        if not init_var:
            assignments.append(assignment_str)

    prev_pos_only, prev_kw_only = _parse_arguments_into_strings(
        prev_pos_only_store, prev_kw_only_store
    )

    # Construct the new init method paramter string
    # First we do the positional only parameters, making sure to add the
    # the self parameter and the comma to allow adding keyword only parameters
    params_string = "" if "self" in prev_pos_only else "self, "
    params_string += prev_pos_only + ", ".join(params)
    if not params_string.endswith(", "):
        params_string += ", "

    # Then we add the keyword only parameters
    if prev_kw_only or kw_only_params:
        params_string += "*, "
    params_string += f"{prev_kw_only}{', '.join(kw_only_params)}"

    assignments_string = "\n    ".join(assignments) if assignments else "pass"
    return f"def __init__({params_string}) -> None:\n    {assignments_string}"


def infer_dataclass_attribute(
    node: nodes.Unknown, ctx: context.InferenceContext | None = None
) -> Iterator[InferenceResult]:
    """Inference tip for an Unknown node that was dynamically generated to
    represent a dataclass attribute.

    In the case that a default value is provided, that is inferred first.
    Then, an Instance of the annotated class is yielded.
    """
    assign = node.parent
    if not isinstance(assign, nodes.AnnAssign):
        yield Uninferable
        return

    annotation, value = assign.annotation, assign.value
    if value is not None:
        yield from value.infer(context=ctx)
    if annotation is not None:
        yield from _infer_instance_from_annotation(annotation, ctx=ctx)
    else:
        yield Uninferable


def infer_dataclass_field_call(
    node: nodes.Call, ctx: context.InferenceContext | None = None
) -> Iterator[InferenceResult]:
    """Inference tip for dataclass field calls."""
    if not isinstance(node.parent, (nodes.AnnAssign, nodes.Assign)):
        raise UseInferenceDefault
    result = _get_field_default(node)
    if not result:
        yield Uninferable
    else:
        default_type, default = result
        if default_type == "default":
            yield from default.infer(context=ctx)
        else:
            new_call = parse(default.as_string()).body[0].value
            new_call.parent = node.parent
            yield from new_call.infer(context=ctx)


def _looks_like_dataclass_decorator(
    node: nodes.NodeNG, decorator_names: frozenset[str] = DATACLASSES_DECORATORS
) -> bool:
    """Return True if node looks like a dataclass decorator.

    Uses inference to lookup the value of the node, and if that fails,
    matches against specific names.
    """
    if isinstance(node, nodes.Call):  # decorator with arguments
        node = node.func
    try:
        inferred = next(node.infer())
    except (InferenceError, StopIteration):
        inferred = Uninferable

    if isinstance(inferred, UninferableBase):
        if isinstance(node, nodes.Name):
            return node.name in decorator_names
        if isinstance(node, nodes.Attribute):
            return node.attrname in decorator_names

        return False

    return (
        isinstance(inferred, nodes.FunctionDef)
        and inferred.name in decorator_names
        and inferred.root().name in DATACLASS_MODULES
    )


def _looks_like_dataclass_attribute(node: nodes.Unknown) -> bool:
    """Return True if node was dynamically generated as the child of an AnnAssign
    statement.
    """
    parent = node.parent
    if not parent:
        return False

    scope = parent.scope()
    return (
        isinstance(parent, nodes.AnnAssign)
        and isinstance(scope, nodes.ClassDef)
        and is_decorated_with_dataclass(scope)
    )


def _looks_like_dataclass_field_call(
    node: nodes.Call, check_scope: bool = True
) -> bool:
    """Return True if node is calling dataclasses field or Field
    from an AnnAssign statement directly in the body of a ClassDef.

    If check_scope is False, skips checking the statement and body.
    """
    if check_scope:
        stmt = node.statement()
        scope = stmt.scope()
        if not (
            isinstance(stmt, nodes.AnnAssign)
            and stmt.value is not None
            and isinstance(scope, nodes.ClassDef)
            and is_decorated_with_dataclass(scope)
        ):
            return False

    try:
        inferred = next(node.func.infer())
    except (InferenceError, StopIteration):
        return False

    if not isinstance(inferred, nodes.FunctionDef):
        return False

    return inferred.name == FIELD_NAME and inferred.root().name in DATACLASS_MODULES


def _get_field_default(field_call: nodes.Call) -> _FieldDefaultReturn:
    """Return a the default value of a field call, and the corresponding keyword
    argument name.

    field(default=...) results in the ... node
    field(default_factory=...) results in a Call node with func ... and no arguments

    If neither or both arguments are present, return ("", None) instead,
    indicating that there is not a valid default value.
    """
    default, default_factory = None, None
    for keyword in field_call.keywords:
        if keyword.arg == "default":
            default = keyword.value
        elif keyword.arg == "default_factory":
            default_factory = keyword.value

    if default is not None and default_factory is None:
        return "default", default

    if default is None and default_factory is not None:
        new_call = nodes.Call(
            lineno=field_call.lineno,
            col_offset=field_call.col_offset,
            parent=field_call.parent,
            end_lineno=field_call.end_lineno,
            end_col_offset=field_call.end_col_offset,
        )
        new_call.postinit(func=default_factory, args=[], keywords=[])
        return "default_factory", new_call

    return None


def _is_class_var(node: nodes.NodeNG) -> bool:
    """Return True if node is a ClassVar, with or without subscripting."""
    if PY39_PLUS:
        try:
            inferred = next(node.infer())
        except (InferenceError, StopIteration):
            return False

        return getattr(inferred, "name", "") == "ClassVar"

    # Before Python 3.9, inference returns typing._SpecialForm instead of ClassVar.
    # Our backup is to inspect the node's structure.
    return isinstance(node, nodes.Subscript) and (
        isinstance(node.value, nodes.Name)
        and node.value.name == "ClassVar"
        or isinstance(node.value, nodes.Attribute)
        and node.value.attrname == "ClassVar"
    )


def _is_keyword_only_sentinel(node: nodes.NodeNG) -> bool:
    """Return True if node is the KW_ONLY sentinel."""
    if not PY310_PLUS:
        return False
    inferred = safe_infer(node)
    return (
        isinstance(inferred, bases.Instance)
        and inferred.qname() == "dataclasses._KW_ONLY_TYPE"
    )


def _is_init_var(node: nodes.NodeNG) -> bool:
    """Return True if node is an InitVar, with or without subscripting."""
    try:
        inferred = next(node.infer())
    except (InferenceError, StopIteration):
        return False

    return getattr(inferred, "name", "") == "InitVar"


# Allowed typing classes for which we support inferring instances
_INFERABLE_TYPING_TYPES = frozenset(
    (
        "Dict",
        "FrozenSet",
        "List",
        "Set",
        "Tuple",
    )
)


def _infer_instance_from_annotation(
    node: nodes.NodeNG, ctx: context.InferenceContext | None = None
) -> Iterator[UninferableBase | bases.Instance]:
    """Infer an instance corresponding to the type annotation represented by node.

    Currently has limited support for the typing module.
    """
    klass = None
    try:
        klass = next(node.infer(context=ctx))
    except (InferenceError, StopIteration):
        yield Uninferable
    if not isinstance(klass, nodes.ClassDef):
        yield Uninferable
    elif klass.root().name in {
        "typing",
        "_collections_abc",
        "",
    }:  # "" because of synthetic nodes in brain_typing.py
        if klass.name in _INFERABLE_TYPING_TYPES:
            yield klass.instantiate_class()
        else:
            yield Uninferable
    else:
        yield klass.instantiate_class()


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        nodes.ClassDef, dataclass_transform, is_decorated_with_dataclass
    )

    manager.register_transform(
        nodes.Call,
        inference_tip(infer_dataclass_field_call, raise_on_overwrite=True),
        _looks_like_dataclass_field_call,
    )

    manager.register_transform(
        nodes.Unknown,
        inference_tip(infer_dataclass_attribute, raise_on_overwrite=True),
        _looks_like_dataclass_attribute,
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_dataclasses.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder
from astroid.const import PY312_PLUS
from astroid.manager import AstroidManager


def datetime_transform():
    """The datetime module was C-accelerated in Python 3.12, so use the
    Python source."""
    return AstroidBuilder(AstroidManager()).string_build("from _pydatetime import *")


def register(manager: AstroidManager) -> None:
    if PY312_PLUS:
        register_module_extender(manager, "datetime", datetime_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_datetime.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for dateutil."""

import textwrap

from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder
from astroid.manager import AstroidManager


def dateutil_transform():
    return AstroidBuilder(AstroidManager()).string_build(
        textwrap.dedent(
            """
    import datetime
    def parse(timestr, parserinfo=None, **kwargs):
        return datetime.datetime()
    """
        )
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "dateutil.parser", dateutil_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_dateutil.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import collections.abc
from typing import TypeVar

from astroid import nodes
from astroid.manager import AstroidManager

_NodeT = TypeVar("_NodeT", bound=nodes.NodeNG)


def _clone_node_with_lineno(
    node: _NodeT, parent: nodes.NodeNG, lineno: int | None
) -> _NodeT:
    cls = node.__class__
    other_fields = node._other_fields
    _astroid_fields = node._astroid_fields
    init_params = {
        "lineno": lineno,
        "col_offset": node.col_offset,
        "parent": parent,
        "end_lineno": node.end_lineno,
        "end_col_offset": node.end_col_offset,
    }
    postinit_params = {param: getattr(node, param) for param in _astroid_fields}
    if other_fields:
        init_params.update({param: getattr(node, param) for param in other_fields})
    new_node = cls(**init_params)
    if hasattr(node, "postinit") and _astroid_fields:
        for param, child in postinit_params.items():
            if child and not isinstance(child, collections.abc.Sequence):
                cloned_child = _clone_node_with_lineno(
                    node=child, lineno=new_node.lineno, parent=new_node
                )
                postinit_params[param] = cloned_child
        new_node.postinit(**postinit_params)
    return new_node


def _transform_formatted_value(  # pylint: disable=inconsistent-return-statements
    node: nodes.FormattedValue,
) -> nodes.FormattedValue | None:
    if node.value and node.value.lineno == 1:
        if node.lineno != node.value.lineno:
            new_node = nodes.FormattedValue(
                lineno=node.lineno,
                col_offset=node.col_offset,
                parent=node.parent,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
            )
            new_value = _clone_node_with_lineno(
                node=node.value, lineno=node.lineno, parent=new_node
            )
            new_node.postinit(
                value=new_value,
                conversion=node.conversion,
                format_spec=node.format_spec,
            )
            return new_node


# TODO: this fix tries to *patch* http://bugs.python.org/issue29051
# The problem is that FormattedValue.value, which is a Name node,
# has wrong line numbers, usually 1. This creates problems for pylint,
# which expects correct line numbers for things such as message control.
def register(manager: AstroidManager) -> None:
    manager.register_transform(nodes.FormattedValue, _transform_formatted_value)

back/venvmsg/Lib/site-packages/astroid/brain/brain_fstrings.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for understanding functools library module."""

from __future__ import annotations

from collections.abc import Iterator
from functools import partial
from itertools import chain

from astroid import BoundMethod, arguments, extract_node, nodes, objects
from astroid.context import InferenceContext
from astroid.exceptions import InferenceError, UseInferenceDefault
from astroid.inference_tip import inference_tip
from astroid.interpreter import objectmodel
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import AssignName, Attribute, Call, Name
from astroid.nodes.scoped_nodes import FunctionDef
from astroid.typing import InferenceResult, SuccessfulInferenceResult
from astroid.util import UninferableBase, safe_infer

LRU_CACHE = "functools.lru_cache"


class LruWrappedModel(objectmodel.FunctionModel):
    """Special attribute model for functions decorated with functools.lru_cache.

    The said decorators patches at decoration time some functions onto
    the decorated function.
    """

    @property
    def attr___wrapped__(self):
        return self._instance

    @property
    def attr_cache_info(self):
        cache_info = extract_node(
            """
        from functools import _CacheInfo
        _CacheInfo(0, 0, 0, 0)
        """
        )

        class CacheInfoBoundMethod(BoundMethod):
            def infer_call_result(
                self,
                caller: SuccessfulInferenceResult | None,
                context: InferenceContext | None = None,
            ) -> Iterator[InferenceResult]:
                res = safe_infer(cache_info)
                assert res is not None
                yield res

        return CacheInfoBoundMethod(proxy=self._instance, bound=self._instance)

    @property
    def attr_cache_clear(self):
        node = extract_node("""def cache_clear(self): pass""")
        return BoundMethod(proxy=node, bound=self._instance.parent.scope())


def _transform_lru_cache(node, context: InferenceContext | None = None) -> None:
    # TODO: this is not ideal, since the node should be immutable,
    # but due to https://github.com/pylint-dev/astroid/issues/354,
    # there's not much we can do now.
    # Replacing the node would work partially, because,
    # in pylint, the old node would still be available, leading
    # to spurious false positives.
    node.special_attributes = LruWrappedModel()(node)


def _functools_partial_inference(
    node: nodes.Call, context: InferenceContext | None = None
) -> Iterator[objects.PartialFunction]:
    call = arguments.CallSite.from_call(node, context=context)
    number_of_positional = len(call.positional_arguments)
    if number_of_positional < 1:
        raise UseInferenceDefault("functools.partial takes at least one argument")
    if number_of_positional == 1 and not call.keyword_arguments:
        raise UseInferenceDefault(
            "functools.partial needs at least to have some filled arguments"
        )

    partial_function = call.positional_arguments[0]
    try:
        inferred_wrapped_function = next(partial_function.infer(context=context))
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc
    if isinstance(inferred_wrapped_function, UninferableBase):
        raise UseInferenceDefault("Cannot infer the wrapped function")
    if not isinstance(inferred_wrapped_function, FunctionDef):
        raise UseInferenceDefault("The wrapped function is not a function")

    # Determine if the passed keywords into the callsite are supported
    # by the wrapped function.
    if not inferred_wrapped_function.args:
        function_parameters = []
    else:
        function_parameters = chain(
            inferred_wrapped_function.args.args or (),
            inferred_wrapped_function.args.posonlyargs or (),
            inferred_wrapped_function.args.kwonlyargs or (),
        )
    parameter_names = {
        param.name for param in function_parameters if isinstance(param, AssignName)
    }
    if set(call.keyword_arguments) - parameter_names:
        raise UseInferenceDefault("wrapped function received unknown parameters")

    partial_function = objects.PartialFunction(
        call,
        name=inferred_wrapped_function.name,
        lineno=inferred_wrapped_function.lineno,
        col_offset=inferred_wrapped_function.col_offset,
        parent=node.parent,
    )
    partial_function.postinit(
        args=inferred_wrapped_function.args,
        body=inferred_wrapped_function.body,
        decorators=inferred_wrapped_function.decorators,
        returns=inferred_wrapped_function.returns,
        type_comment_returns=inferred_wrapped_function.type_comment_returns,
        type_comment_args=inferred_wrapped_function.type_comment_args,
        doc_node=inferred_wrapped_function.doc_node,
    )
    return iter((partial_function,))


def _looks_like_lru_cache(node) -> bool:
    """Check if the given function node is decorated with lru_cache."""
    if not node.decorators:
        return False
    for decorator in node.decorators.nodes:
        if not isinstance(decorator, (Attribute, Call)):
            continue
        if _looks_like_functools_member(decorator, "lru_cache"):
            return True
    return False


def _looks_like_functools_member(node: Attribute | Call, member: str) -> bool:
    """Check if the given Call node is the wanted member of functools."""
    if isinstance(node, Attribute):
        return node.attrname == member
    if isinstance(node.func, Name):
        return node.func.name == member
    if isinstance(node.func, Attribute):
        return (
            node.func.attrname == member
            and isinstance(node.func.expr, Name)
            and node.func.expr.name == "functools"
        )
    return False


_looks_like_partial = partial(_looks_like_functools_member, member="partial")


def register(manager: AstroidManager) -> None:
    manager.register_transform(FunctionDef, _transform_lru_cache, _looks_like_lru_cache)

    manager.register_transform(
        Call,
        inference_tip(_functools_partial_inference),
        _looks_like_partial,
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_functools.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for the Python 2 GObject introspection bindings.

Helps with understanding everything imported from 'gi.repository'
"""

# pylint:disable=import-error,import-outside-toplevel

import inspect
import itertools
import re
import sys
import warnings

from astroid import nodes
from astroid.builder import AstroidBuilder
from astroid.exceptions import AstroidBuildingError
from astroid.manager import AstroidManager

_inspected_modules = {}

_identifier_re = r"^[A-Za-z_]\w*$"

_special_methods = frozenset(
    {
        "__lt__",
        "__le__",
        "__eq__",
        "__ne__",
        "__ge__",
        "__gt__",
        "__iter__",
        "__getitem__",
        "__setitem__",
        "__delitem__",
        "__len__",
        "__bool__",
        "__nonzero__",
        "__next__",
        "__str__",
        "__contains__",
        "__enter__",
        "__exit__",
        "__repr__",
        "__getattr__",
        "__setattr__",
        "__delattr__",
        "__del__",
        "__hash__",
    }
)


def _gi_build_stub(parent):  # noqa: C901
    """
    Inspect the passed module recursively and build stubs for functions,
    classes, etc.
    """
    classes = {}
    functions = {}
    constants = {}
    methods = {}
    for name in dir(parent):
        if name.startswith("__") and name not in _special_methods:
            continue

        # Check if this is a valid name in python
        if not re.match(_identifier_re, name):
            continue

        try:
            obj = getattr(parent, name)
        except Exception:  # pylint: disable=broad-except
            # gi.module.IntrospectionModule.__getattr__() can raise all kinds of things
            # like ValueError, TypeError, NotImplementedError, RepositoryError, etc
            continue

        if inspect.isclass(obj):
            classes[name] = obj
        elif inspect.isfunction(obj) or inspect.isbuiltin(obj):
            functions[name] = obj
        elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj):
            methods[name] = obj
        elif (
            str(obj).startswith("<flags")
            or str(obj).startswith("<enum ")
            or str(obj).startswith("<GType ")
            or inspect.isdatadescriptor(obj)
        ):
            constants[name] = 0
        elif isinstance(obj, (int, str)):
            constants[name] = obj
        elif callable(obj):
            # Fall back to a function for anything callable
            functions[name] = obj
        else:
            # Assume everything else is some manner of constant
            constants[name] = 0

    ret = ""

    if constants:
        ret += f"# {parent.__name__} constants\n\n"
    for name in sorted(constants):
        if name[0].isdigit():
            # GDK has some busted constant names like
            # Gdk.EventType.2BUTTON_PRESS
            continue

        val = constants[name]

        strval = str(val)
        if isinstance(val, str):
            strval = '"%s"' % str(val).replace("\\", "\\\\")
        ret += f"{name} = {strval}\n"

    if ret:
        ret += "\n\n"
    if functions:
        ret += f"# {parent.__name__} functions\n\n"
    for name in sorted(functions):
        ret += f"def {name}(*args, **kwargs):\n"
        ret += "    pass\n"

    if ret:
        ret += "\n\n"
    if methods:
        ret += f"# {parent.__name__} methods\n\n"
    for name in sorted(methods):
        ret += f"def {name}(self, *args, **kwargs):\n"
        ret += "    pass\n"

    if ret:
        ret += "\n\n"
    if classes:
        ret += f"# {parent.__name__} classes\n\n"
    for name, obj in sorted(classes.items()):
        base = "object"
        if issubclass(obj, Exception):
            base = "Exception"
        ret += f"class {name}({base}):\n"

        classret = _gi_build_stub(obj)
        if not classret:
            classret = "pass\n"

        for line in classret.splitlines():
            ret += "    " + line + "\n"
        ret += "\n"

    return ret


def _import_gi_module(modname):
    # we only consider gi.repository submodules
    if not modname.startswith("gi.repository."):
        raise AstroidBuildingError(modname=modname)
    # build astroid representation unless we already tried so
    if modname not in _inspected_modules:
        modnames = [modname]
        optional_modnames = []

        # GLib and GObject may have some special case handling
        # in pygobject that we need to cope with. However at
        # least as of pygobject3-3.13.91 the _glib module doesn't
        # exist anymore, so if treat these modules as optional.
        if modname == "gi.repository.GLib":
            optional_modnames.append("gi._glib")
        elif modname == "gi.repository.GObject":
            optional_modnames.append("gi._gobject")

        try:
            modcode = ""
            for m in itertools.chain(modnames, optional_modnames):
                try:
                    with warnings.catch_warnings():
                        # Just inspecting the code can raise gi deprecation
                        # warnings, so ignore them.
                        try:
                            from gi import (  # pylint:disable=import-error
                                PyGIDeprecationWarning,
                                PyGIWarning,
                            )

                            warnings.simplefilter("ignore", PyGIDeprecationWarning)
                            warnings.simplefilter("ignore", PyGIWarning)
                        except Exception:  # pylint:disable=broad-except
                            pass

                        __import__(m)
                        modcode += _gi_build_stub(sys.modules[m])
                except ImportError:
                    if m not in optional_modnames:
                        raise
        except ImportError:
            astng = _inspected_modules[modname] = None
        else:
            astng = AstroidBuilder(AstroidManager()).string_build(modcode, modname)
            _inspected_modules[modname] = astng
    else:
        astng = _inspected_modules[modname]
    if astng is None:
        raise AstroidBuildingError(modname=modname)
    return astng


def _looks_like_require_version(node) -> bool:
    # Return whether this looks like a call to gi.require_version(<name>, <version>)
    # Only accept function calls with two constant arguments
    if len(node.args) != 2:
        return False

    if not all(isinstance(arg, nodes.Const) for arg in node.args):
        return False

    func = node.func
    if isinstance(func, nodes.Attribute):
        if func.attrname != "require_version":
            return False
        if isinstance(func.expr, nodes.Name) and func.expr.name == "gi":
            return True

        return False

    if isinstance(func, nodes.Name):
        return func.name == "require_version"

    return False


def _register_require_version(node):
    # Load the gi.require_version locally
    try:
        import gi

        gi.require_version(node.args[0].value, node.args[1].value)
    except Exception:  # pylint:disable=broad-except
        pass

    return node


def register(manager: AstroidManager) -> None:
    manager.register_failed_import_hook(_import_gi_module)
    manager.register_transform(
        nodes.Call, _register_require_version, _looks_like_require_version
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_gi.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.const import PY39_PLUS
from astroid.manager import AstroidManager


def _hashlib_transform():
    maybe_usedforsecurity = ", usedforsecurity=True" if PY39_PLUS else ""
    init_signature = f"value=''{maybe_usedforsecurity}"
    digest_signature = "self"
    shake_digest_signature = "self, length"

    template = """
    class %(name)s:
        def __init__(self, %(init_signature)s): pass
        def digest(%(digest_signature)s):
            return %(digest)s
        def copy(self):
            return self
        def update(self, value): pass
        def hexdigest(%(digest_signature)s):
            return ''
        @property
        def name(self):
            return %(name)r
        @property
        def block_size(self):
            return 1
        @property
        def digest_size(self):
            return 1
    """

    algorithms_with_signature = dict.fromkeys(
        [
            "md5",
            "sha1",
            "sha224",
            "sha256",
            "sha384",
            "sha512",
            "sha3_224",
            "sha3_256",
            "sha3_384",
            "sha3_512",
        ],
        (init_signature, digest_signature),
    )

    blake2b_signature = (
        "data=b'', *, digest_size=64, key=b'', salt=b'', "
        "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, "
        f"node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}"
    )

    blake2s_signature = (
        "data=b'', *, digest_size=32, key=b'', salt=b'', "
        "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, "
        f"node_depth=0, inner_size=0, last_node=False{maybe_usedforsecurity}"
    )

    shake_algorithms = dict.fromkeys(
        ["shake_128", "shake_256"],
        (init_signature, shake_digest_signature),
    )
    algorithms_with_signature.update(shake_algorithms)

    algorithms_with_signature.update(
        {
            "blake2b": (blake2b_signature, digest_signature),
            "blake2s": (blake2s_signature, digest_signature),
        }
    )

    classes = "".join(
        template
        % {
            "name": hashfunc,
            "digest": 'b""',
            "init_signature": init_signature,
            "digest_signature": digest_signature,
        }
        for hashfunc, (
            init_signature,
            digest_signature,
        ) in algorithms_with_signature.items()
    )

    return parse(classes)


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "hashlib", _hashlib_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_hashlib.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid brain hints for some of the `http` module."""
import textwrap

from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder
from astroid.manager import AstroidManager


def _http_transform():
    code = textwrap.dedent(
        """
    from enum import IntEnum
    from collections import namedtuple
    _HTTPStatus = namedtuple('_HTTPStatus', 'value phrase description')

    class HTTPStatus(IntEnum):

        @property
        def phrase(self):
            return ""
        @property
        def value(self):
            return 0
        @property
        def description(self):
            return ""

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


def _http_client_transform():
    return AstroidBuilder(AstroidManager()).string_build(
        textwrap.dedent(
            """
    from http import HTTPStatus

    CONTINUE = HTTPStatus.CONTINUE
    SWITCHING_PROTOCOLS = HTTPStatus.SWITCHING_PROTOCOLS
    PROCESSING = HTTPStatus.PROCESSING
    OK = HTTPStatus.OK
    CREATED = HTTPStatus.CREATED
    ACCEPTED = HTTPStatus.ACCEPTED
    NON_AUTHORITATIVE_INFORMATION = HTTPStatus.NON_AUTHORITATIVE_INFORMATION
    NO_CONTENT = HTTPStatus.NO_CONTENT
    RESET_CONTENT = HTTPStatus.RESET_CONTENT
    PARTIAL_CONTENT = HTTPStatus.PARTIAL_CONTENT
    MULTI_STATUS = HTTPStatus.MULTI_STATUS
    ALREADY_REPORTED = HTTPStatus.ALREADY_REPORTED
    IM_USED = HTTPStatus.IM_USED
    MULTIPLE_CHOICES = HTTPStatus.MULTIPLE_CHOICES
    MOVED_PERMANENTLY = HTTPStatus.MOVED_PERMANENTLY
    FOUND = HTTPStatus.FOUND
    SEE_OTHER = HTTPStatus.SEE_OTHER
    NOT_MODIFIED = HTTPStatus.NOT_MODIFIED
    USE_PROXY = HTTPStatus.USE_PROXY
    TEMPORARY_REDIRECT = HTTPStatus.TEMPORARY_REDIRECT
    PERMANENT_REDIRECT = HTTPStatus.PERMANENT_REDIRECT
    BAD_REQUEST = HTTPStatus.BAD_REQUEST
    UNAUTHORIZED = HTTPStatus.UNAUTHORIZED
    PAYMENT_REQUIRED = HTTPStatus.PAYMENT_REQUIRED
    FORBIDDEN = HTTPStatus.FORBIDDEN
    NOT_FOUND = HTTPStatus.NOT_FOUND
    METHOD_NOT_ALLOWED = HTTPStatus.METHOD_NOT_ALLOWED
    NOT_ACCEPTABLE = HTTPStatus.NOT_ACCEPTABLE
    PROXY_AUTHENTICATION_REQUIRED = HTTPStatus.PROXY_AUTHENTICATION_REQUIRED
    REQUEST_TIMEOUT = HTTPStatus.REQUEST_TIMEOUT
    CONFLICT = HTTPStatus.CONFLICT
    GONE = HTTPStatus.GONE
    LENGTH_REQUIRED = HTTPStatus.LENGTH_REQUIRED
    PRECONDITION_FAILED = HTTPStatus.PRECONDITION_FAILED
    REQUEST_ENTITY_TOO_LARGE = HTTPStatus.REQUEST_ENTITY_TOO_LARGE
    REQUEST_URI_TOO_LONG = HTTPStatus.REQUEST_URI_TOO_LONG
    UNSUPPORTED_MEDIA_TYPE = HTTPStatus.UNSUPPORTED_MEDIA_TYPE
    REQUESTED_RANGE_NOT_SATISFIABLE = HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE
    EXPECTATION_FAILED = HTTPStatus.EXPECTATION_FAILED
    UNPROCESSABLE_ENTITY = HTTPStatus.UNPROCESSABLE_ENTITY
    LOCKED = HTTPStatus.LOCKED
    FAILED_DEPENDENCY = HTTPStatus.FAILED_DEPENDENCY
    UPGRADE_REQUIRED = HTTPStatus.UPGRADE_REQUIRED
    PRECONDITION_REQUIRED = HTTPStatus.PRECONDITION_REQUIRED
    TOO_MANY_REQUESTS = HTTPStatus.TOO_MANY_REQUESTS
    REQUEST_HEADER_FIELDS_TOO_LARGE = HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE
    INTERNAL_SERVER_ERROR = HTTPStatus.INTERNAL_SERVER_ERROR
    NOT_IMPLEMENTED = HTTPStatus.NOT_IMPLEMENTED
    BAD_GATEWAY = HTTPStatus.BAD_GATEWAY
    SERVICE_UNAVAILABLE = HTTPStatus.SERVICE_UNAVAILABLE
    GATEWAY_TIMEOUT = HTTPStatus.GATEWAY_TIMEOUT
    HTTP_VERSION_NOT_SUPPORTED = HTTPStatus.HTTP_VERSION_NOT_SUPPORTED
    VARIANT_ALSO_NEGOTIATES = HTTPStatus.VARIANT_ALSO_NEGOTIATES
    INSUFFICIENT_STORAGE = HTTPStatus.INSUFFICIENT_STORAGE
    LOOP_DETECTED = HTTPStatus.LOOP_DETECTED
    NOT_EXTENDED = HTTPStatus.NOT_EXTENDED
    NETWORK_AUTHENTICATION_REQUIRED = HTTPStatus.NETWORK_AUTHENTICATION_REQUIRED
    """
        )
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "http", _http_transform)
    register_module_extender(manager, "http.client", _http_client_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_http.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Astroid hook for the Hypothesis library.

Without this hook pylint reports no-value-for-parameter for use of strategies
defined using the `@hypothesis.strategies.composite` decorator.  For example:

    from hypothesis import strategies as st

    @st.composite
    def a_strategy(draw):
        return draw(st.integers())

    a_strategy()
"""
from astroid.manager import AstroidManager
from astroid.nodes.scoped_nodes import FunctionDef

COMPOSITE_NAMES = (
    "composite",
    "st.composite",
    "strategies.composite",
    "hypothesis.strategies.composite",
)


def is_decorated_with_st_composite(node) -> bool:
    """Return whether a decorated node has @st.composite applied."""
    if node.decorators and node.args.args and node.args.args[0].name == "draw":
        for decorator_attribute in node.decorators.nodes:
            if decorator_attribute.as_string() in COMPOSITE_NAMES:
                return True
    return False


def remove_draw_parameter_from_composite_strategy(node):
    """Given that the FunctionDef is decorated with @st.composite, remove the
    first argument (`draw`) - it's always supplied by Hypothesis so we don't
    need to emit the no-value-for-parameter lint.
    """
    del node.args.args[0]
    del node.args.annotations[0]
    del node.args.type_comment_args[0]
    return node


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        node_class=FunctionDef,
        transform=remove_draw_parameter_from_composite_strategy,
        predicate=is_decorated_with_st_composite,
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_hypothesis.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid brain hints for some of the _io C objects."""
from astroid.manager import AstroidManager
from astroid.nodes import ClassDef

BUFFERED = {"BufferedWriter", "BufferedReader"}
TextIOWrapper = "TextIOWrapper"
FileIO = "FileIO"
BufferedWriter = "BufferedWriter"


def _generic_io_transform(node, name, cls):
    """Transform the given name, by adding the given *class* as a member of the
    node.
    """

    io_module = AstroidManager().ast_from_module_name("_io")
    attribute_object = io_module[cls]
    instance = attribute_object.instantiate_class()
    node.locals[name] = [instance]


def _transform_text_io_wrapper(node):
    # This is not always correct, since it can vary with the type of the descriptor,
    # being stdout, stderr or stdin. But we cannot get access to the name of the
    # stream, which is why we are using the BufferedWriter class as a default
    # value
    return _generic_io_transform(node, name="buffer", cls=BufferedWriter)


def _transform_buffered(node):
    return _generic_io_transform(node, name="raw", cls=FileIO)


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        ClassDef, _transform_buffered, lambda node: node.name in BUFFERED
    )
    manager.register_transform(
        ClassDef, _transform_text_io_wrapper, lambda node: node.name == TextIOWrapper
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_io.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder
from astroid.manager import AstroidManager


def mechanize_transform():
    return AstroidBuilder(AstroidManager()).string_build(
        """class Browser(object):
    def __getattr__(self, name):
        return None

    def __getitem__(self, name):
        return None

    def __setitem__(self, name, val):
        return None

    def back(self, n=1):
        return None

    def clear_history(self):
        return None

    def click(self, *args, **kwds):
        return None

    def click_link(self, link=None, **kwds):
        return None

    def close(self):
        return None

    def encoding(self):
        return None

    def find_link(
        self,
        text=None,
        text_regex=None,
        name=None,
        name_regex=None,
        url=None,
        url_regex=None,
        tag=None,
        predicate=None,
        nr=0,
    ):
        return None

    def follow_link(self, link=None, **kwds):
        return None

    def forms(self):
        return None

    def geturl(self):
        return None

    def global_form(self):
        return None

    def links(self, **kwds):
        return None

    def open_local_file(self, filename):
        return None

    def open(self, url, data=None, timeout=None):
        return None

    def open_novisit(self, url, data=None, timeout=None):
        return None

    def open_local_file(self, filename):
        return None

    def reload(self):
        return None

    def response(self):
        return None

    def select_form(self, name=None, predicate=None, nr=None, **attrs):
        return None

    def set_cookie(self, cookie_string):
        return None

    def set_handle_referer(self, handle):
        return None

    def set_header(self, header, value=None):
        return None

    def set_html(self, html, url="http://example.com/"):
        return None

    def set_response(self, response):
        return None

    def set_simple_cookie(self, name, value, domain, path="/"):
        return None

    def submit(self, *args, **kwds):
        return None

    def title(self):
        return None

    def viewing_html(self):
        return None

    def visit_response(self, response, request=None):
        return None
"""
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "mechanize", mechanize_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_mechanize.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid.bases import BoundMethod
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.exceptions import InferenceError
from astroid.manager import AstroidManager
from astroid.nodes.scoped_nodes import FunctionDef


def _multiprocessing_transform():
    module = parse(
        """
    from multiprocessing.managers import SyncManager
    def Manager():
        return SyncManager()
    """
    )
    # Multiprocessing uses a getattr lookup inside contexts,
    # in order to get the attributes they need. Since it's extremely
    # dynamic, we use this approach to fake it.
    node = parse(
        """
    from multiprocessing.context import DefaultContext, BaseContext
    default = DefaultContext()
    base = BaseContext()
    """
    )
    try:
        context = next(node["default"].infer())
        base = next(node["base"].infer())
    except (InferenceError, StopIteration):
        return module

    for node in (context, base):
        for key, value in node.locals.items():
            if key.startswith("_"):
                continue

            value = value[0]
            if isinstance(value, FunctionDef):
                # We need to rebound this, since otherwise
                # it will have an extra argument (self).
                value = BoundMethod(value, node)
            module[key] = value
    return module


def _multiprocessing_managers_transform():
    return parse(
        """
    import array
    import threading
    import multiprocessing.pool as pool
    import queue

    class Namespace(object):
        pass

    class Value(object):
        def __init__(self, typecode, value, lock=True):
            self._typecode = typecode
            self._value = value
        def get(self):
            return self._value
        def set(self, value):
            self._value = value
        def __repr__(self):
            return '%s(%r, %r)'%(type(self).__name__, self._typecode, self._value)
        value = property(get, set)

    def Array(typecode, sequence, lock=True):
        return array.array(typecode, sequence)

    class SyncManager(object):
        Queue = JoinableQueue = queue.Queue
        Event = threading.Event
        RLock = threading.RLock
        Lock = threading.Lock
        BoundedSemaphore = threading.BoundedSemaphore
        Condition = threading.Condition
        Barrier = threading.Barrier
        Pool = pool.Pool
        list = list
        dict = dict
        Value = Value
        Array = Array
        Namespace = Namespace
        __enter__ = lambda self: self
        __exit__ = lambda *args: args

        def start(self, initializer=None, initargs=None):
            pass
        def shutdown(self):
            pass
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(
        manager, "multiprocessing.managers", _multiprocessing_managers_transform
    )
    register_module_extender(manager, "multiprocessing", _multiprocessing_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_multiprocessing.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for the Python standard library."""

from __future__ import annotations

import functools
import keyword
from collections.abc import Iterator
from textwrap import dedent
from typing import Final

import astroid
from astroid import arguments, bases, inference_tip, nodes, util
from astroid.builder import AstroidBuilder, _extract_single_node, extract_node
from astroid.context import InferenceContext
from astroid.exceptions import (
    AstroidTypeError,
    AstroidValueError,
    InferenceError,
    UseInferenceDefault,
)
from astroid.manager import AstroidManager

ENUM_QNAME: Final[str] = "enum.Enum"
TYPING_NAMEDTUPLE_QUALIFIED: Final = {
    "typing.NamedTuple",
    "typing_extensions.NamedTuple",
}
TYPING_NAMEDTUPLE_BASENAMES: Final = {
    "NamedTuple",
    "typing.NamedTuple",
    "typing_extensions.NamedTuple",
}


def _infer_first(node, context):
    if isinstance(node, util.UninferableBase):
        raise UseInferenceDefault
    try:
        value = next(node.infer(context=context))
    except StopIteration as exc:
        raise InferenceError from exc
    if isinstance(value, util.UninferableBase):
        raise UseInferenceDefault()
    return value


def _find_func_form_arguments(node, context):
    def _extract_namedtuple_arg_or_keyword(  # pylint: disable=inconsistent-return-statements
        position, key_name=None
    ):
        if len(args) > position:
            return _infer_first(args[position], context)
        if key_name and key_name in found_keywords:
            return _infer_first(found_keywords[key_name], context)

    args = node.args
    keywords = node.keywords
    found_keywords = (
        {keyword.arg: keyword.value for keyword in keywords} if keywords else {}
    )

    name = _extract_namedtuple_arg_or_keyword(position=0, key_name="typename")
    names = _extract_namedtuple_arg_or_keyword(position=1, key_name="field_names")
    if name and names:
        return name.value, names

    raise UseInferenceDefault()


def infer_func_form(
    node: nodes.Call,
    base_type: list[nodes.NodeNG],
    context: InferenceContext | None = None,
    enum: bool = False,
) -> tuple[nodes.ClassDef, str, list[str]]:
    """Specific inference function for namedtuple or Python 3 enum."""
    # node is a Call node, class name as first argument and generated class
    # attributes as second argument

    # namedtuple or enums list of attributes can be a list of strings or a
    # whitespace-separate string
    try:
        name, names = _find_func_form_arguments(node, context)
        try:
            attributes: list[str] = names.value.replace(",", " ").split()
        except AttributeError as exc:
            # Handle attributes of NamedTuples
            if not enum:
                attributes = []
                fields = _get_namedtuple_fields(node)
                if fields:
                    fields_node = extract_node(fields)
                    attributes = [
                        _infer_first(const, context).value for const in fields_node.elts
                    ]

            # Handle attributes of Enums
            else:
                # Enums supports either iterator of (name, value) pairs
                # or mappings.
                if hasattr(names, "items") and isinstance(names.items, list):
                    attributes = [
                        _infer_first(const[0], context).value
                        for const in names.items
                        if isinstance(const[0], nodes.Const)
                    ]
                elif hasattr(names, "elts"):
                    # Enums can support either ["a", "b", "c"]
                    # or [("a", 1), ("b", 2), ...], but they can't
                    # be mixed.
                    if all(isinstance(const, nodes.Tuple) for const in names.elts):
                        attributes = [
                            _infer_first(const.elts[0], context).value
                            for const in names.elts
                            if isinstance(const, nodes.Tuple)
                        ]
                    else:
                        attributes = [
                            _infer_first(const, context).value for const in names.elts
                        ]
                else:
                    raise AttributeError from exc
                if not attributes:
                    raise AttributeError from exc
    except (AttributeError, InferenceError) as exc:
        raise UseInferenceDefault from exc

    if not enum:
        # namedtuple maps sys.intern(str()) over over field_names
        attributes = [str(attr) for attr in attributes]
        # XXX this should succeed *unless* __str__/__repr__ is incorrect or throws
        # in which case we should not have inferred these values and raised earlier
    attributes = [attr for attr in attributes if " " not in attr]

    # If we can't infer the name of the class, don't crash, up to this point
    # we know it is a namedtuple anyway.
    name = name or "Uninferable"
    # we want to return a Class node instance with proper attributes set
    class_node = nodes.ClassDef(
        name,
        lineno=node.lineno,
        col_offset=node.col_offset,
        end_lineno=node.end_lineno,
        end_col_offset=node.end_col_offset,
        parent=nodes.Unknown(),
    )
    # A typical ClassDef automatically adds its name to the parent scope,
    # but doing so causes problems, so defer setting parent until after init
    # see: https://github.com/pylint-dev/pylint/issues/5982
    class_node.parent = node.parent
    class_node.postinit(
        # set base class=tuple
        bases=base_type,
        body=[],
        decorators=None,
    )
    # XXX add __init__(*attributes) method
    for attr in attributes:
        fake_node = nodes.EmptyNode()
        fake_node.parent = class_node
        fake_node.attrname = attr
        class_node.instance_attrs[attr] = [fake_node]
    return class_node, name, attributes


def _has_namedtuple_base(node):
    """Predicate for class inference tip.

    :type node: ClassDef
    :rtype: bool
    """
    return set(node.basenames) & TYPING_NAMEDTUPLE_BASENAMES


def _looks_like(node, name) -> bool:
    func = node.func
    if isinstance(func, nodes.Attribute):
        return func.attrname == name
    if isinstance(func, nodes.Name):
        return func.name == name
    return False


_looks_like_namedtuple = functools.partial(_looks_like, name="namedtuple")
_looks_like_enum = functools.partial(_looks_like, name="Enum")
_looks_like_typing_namedtuple = functools.partial(_looks_like, name="NamedTuple")


def infer_named_tuple(
    node: nodes.Call, context: InferenceContext | None = None
) -> Iterator[nodes.ClassDef]:
    """Specific inference function for namedtuple Call node."""
    tuple_base_name: list[nodes.NodeNG] = [
        nodes.Name(
            name="tuple",
            parent=node.root(),
            lineno=0,
            col_offset=0,
            end_lineno=None,
            end_col_offset=None,
        )
    ]
    class_node, name, attributes = infer_func_form(
        node, tuple_base_name, context=context
    )
    call_site = arguments.CallSite.from_call(node, context=context)
    node = extract_node("import collections; collections.namedtuple")
    try:
        func = next(node.infer())
    except StopIteration as e:
        raise InferenceError(node=node) from e
    try:
        rename = next(
            call_site.infer_argument(func, "rename", context or InferenceContext())
        ).bool_value()
    except (InferenceError, StopIteration):
        rename = False

    try:
        attributes = _check_namedtuple_attributes(name, attributes, rename)
    except AstroidTypeError as exc:
        raise UseInferenceDefault("TypeError: " + str(exc)) from exc
    except AstroidValueError as exc:
        raise UseInferenceDefault("ValueError: " + str(exc)) from exc

    replace_args = ", ".join(f"{arg}=None" for arg in attributes)
    field_def = (
        "    {name} = property(lambda self: self[{index:d}], "
        "doc='Alias for field number {index:d}')"
    )
    field_defs = "\n".join(
        field_def.format(name=name, index=index)
        for index, name in enumerate(attributes)
    )
    fake = AstroidBuilder(AstroidManager()).string_build(
        f"""
class {name}(tuple):
    __slots__ = ()
    _fields = {attributes!r}
    def _asdict(self):
        return self.__dict__
    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        return new(cls, iterable)
    def _replace(self, {replace_args}):
        return self
    def __getnewargs__(self):
        return tuple(self)
{field_defs}
    """
    )
    class_node.locals["_asdict"] = fake.body[0].locals["_asdict"]
    class_node.locals["_make"] = fake.body[0].locals["_make"]
    class_node.locals["_replace"] = fake.body[0].locals["_replace"]
    class_node.locals["_fields"] = fake.body[0].locals["_fields"]
    for attr in attributes:
        class_node.locals[attr] = fake.body[0].locals[attr]
    # we use UseInferenceDefault, we can't be a generator so return an iterator
    return iter([class_node])


def _get_renamed_namedtuple_attributes(field_names):
    names = list(field_names)
    seen = set()
    for i, name in enumerate(field_names):
        if (
            not all(c.isalnum() or c == "_" for c in name)
            or keyword.iskeyword(name)
            or not name
            or name[0].isdigit()
            or name.startswith("_")
            or name in seen
        ):
            names[i] = "_%d" % i
        seen.add(name)
    return tuple(names)


def _check_namedtuple_attributes(typename, attributes, rename=False):
    attributes = tuple(attributes)
    if rename:
        attributes = _get_renamed_namedtuple_attributes(attributes)

    # The following snippet is derived from the CPython Lib/collections/__init__.py sources
    # <snippet>
    for name in (typename, *attributes):
        if not isinstance(name, str):
            raise AstroidTypeError("Type names and field names must be strings")
        if not name.isidentifier():
            raise AstroidValueError(
                "Type names and field names must be valid" + f"identifiers: {name!r}"
            )
        if keyword.iskeyword(name):
            raise AstroidValueError(
                f"Type names and field names cannot be a keyword: {name!r}"
            )

    seen = set()
    for name in attributes:
        if name.startswith("_") and not rename:
            raise AstroidValueError(
                f"Field names cannot start with an underscore: {name!r}"
            )
        if name in seen:
            raise AstroidValueError(f"Encountered duplicate field name: {name!r}")
        seen.add(name)
    # </snippet>

    return attributes


def infer_enum(
    node: nodes.Call, context: InferenceContext | None = None
) -> Iterator[bases.Instance]:
    """Specific inference function for enum Call node."""
    # Raise `UseInferenceDefault` if `node` is a call to a a user-defined Enum.
    try:
        inferred = node.func.infer(context)
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc

    if not any(
        isinstance(item, nodes.ClassDef) and item.qname() == ENUM_QNAME
        for item in inferred
    ):
        raise UseInferenceDefault

    enum_meta = _extract_single_node(
        """
    class EnumMeta(object):
        'docstring'
        def __call__(self, node):
            class EnumAttribute(object):
                name = ''
                value = 0
            return EnumAttribute()
        def __iter__(self):
            class EnumAttribute(object):
                name = ''
                value = 0
            return [EnumAttribute()]
        def __reversed__(self):
            class EnumAttribute(object):
                name = ''
                value = 0
            return (EnumAttribute, )
        def __next__(self):
            return next(iter(self))
        def __getitem__(self, attr):
            class Value(object):
                @property
                def name(self):
                    return ''
                @property
                def value(self):
                    return attr

            return Value()
        __members__ = ['']
    """
    )
    class_node = infer_func_form(node, [enum_meta], context=context, enum=True)[0]
    return iter([class_node.instantiate_class()])


INT_FLAG_ADDITION_METHODS = """
    def __or__(self, other):
        return {name}(self.value | other.value)
    def __and__(self, other):
        return {name}(self.value & other.value)
    def __xor__(self, other):
        return {name}(self.value ^ other.value)
    def __add__(self, other):
        return {name}(self.value + other.value)
    def __div__(self, other):
        return {name}(self.value / other.value)
    def __invert__(self):
        return {name}(~self.value)
    def __mul__(self, other):
        return {name}(self.value * other.value)
"""


def infer_enum_class(node: nodes.ClassDef) -> nodes.ClassDef:
    """Specific inference for enums."""
    for basename in (b for cls in node.mro() for b in cls.basenames):
        if node.root().name == "enum":
            # Skip if the class is directly from enum module.
            break
        dunder_members = {}
        target_names = set()
        for local, values in node.locals.items():
            if (
                any(not isinstance(value, nodes.AssignName) for value in values)
                or local == "_ignore_"
            ):
                continue

            stmt = values[0].statement()
            if isinstance(stmt, nodes.Assign):
                if isinstance(stmt.targets[0], nodes.Tuple):
                    targets = stmt.targets[0].itered()
                else:
                    targets = stmt.targets
            elif isinstance(stmt, nodes.AnnAssign):
                targets = [stmt.target]
            else:
                continue

            inferred_return_value = None
            if stmt.value is not None:
                if isinstance(stmt.value, nodes.Const):
                    if isinstance(stmt.value.value, str):
                        inferred_return_value = repr(stmt.value.value)
                    else:
                        inferred_return_value = stmt.value.value
                else:
                    inferred_return_value = stmt.value.as_string()

            new_targets = []
            for target in targets:
                if isinstance(target, nodes.Starred):
                    continue
                target_names.add(target.name)
                # Replace all the assignments with our mocked class.
                classdef = dedent(
                    """
                class {name}({types}):
                    @property
                    def value(self):
                        return {return_value}
                    @property
                    def _value_(self):
                        return {return_value}
                    @property
                    def name(self):
                        return "{name}"
                    @property
                    def _name_(self):
                        return "{name}"
                """.format(
                        name=target.name,
                        types=", ".join(node.basenames),
                        return_value=inferred_return_value,
                    )
                )
                if "IntFlag" in basename:
                    # Alright, we need to add some additional methods.
                    # Unfortunately we still can't infer the resulting objects as
                    # Enum members, but once we'll be able to do that, the following
                    # should result in some nice symbolic execution
                    classdef += INT_FLAG_ADDITION_METHODS.format(name=target.name)

                fake = AstroidBuilder(
                    AstroidManager(), apply_transforms=False
                ).string_build(classdef)[target.name]
                fake.parent = target.parent
                for method in node.mymethods():
                    fake.locals[method.name] = [method]
                new_targets.append(fake.instantiate_class())
                if stmt.value is None:
                    continue
                dunder_members[local] = fake
            node.locals[local] = new_targets

        # The undocumented `_value2member_map_` member:
        node.locals["_value2member_map_"] = [
            nodes.Dict(
                parent=node,
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
            )
        ]

        members = nodes.Dict(
            parent=node,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
        )
        members.postinit(
            [
                (
                    nodes.Const(k, parent=members),
                    nodes.Name(
                        v.name,
                        parent=members,
                        lineno=v.lineno,
                        col_offset=v.col_offset,
                        end_lineno=v.end_lineno,
                        end_col_offset=v.end_col_offset,
                    ),
                )
                for k, v in dunder_members.items()
            ]
        )
        node.locals["__members__"] = [members]
        # The enum.Enum class itself defines two @DynamicClassAttribute data-descriptors
        # "name" and "value" (which we override in the mocked class for each enum member
        # above). When dealing with inference of an arbitrary instance of the enum
        # class, e.g. in a method defined in the class body like:
        #     class SomeEnum(enum.Enum):
        #         def method(self):
        #             self.name  # <- here
        # In the absence of an enum member called "name" or "value", these attributes
        # should resolve to the descriptor on that particular instance, i.e. enum member.
        # For "value", we have no idea what that should be, but for "name", we at least
        # know that it should be a string, so infer that as a guess.
        if "name" not in target_names:
            code = dedent(
                """
            @property
            def name(self):
                return ''
            """
            )
            name_dynamicclassattr = AstroidBuilder(AstroidManager()).string_build(code)[
                "name"
            ]
            node.locals["name"] = [name_dynamicclassattr]
        break
    return node


def infer_typing_namedtuple_class(class_node, context: InferenceContext | None = None):
    """Infer a subclass of typing.NamedTuple."""
    # Check if it has the corresponding bases
    annassigns_fields = [
        annassign.target.name
        for annassign in class_node.body
        if isinstance(annassign, nodes.AnnAssign)
    ]
    code = dedent(
        """
    from collections import namedtuple
    namedtuple({typename!r}, {fields!r})
    """
    ).format(typename=class_node.name, fields=",".join(annassigns_fields))
    node = extract_node(code)
    try:
        generated_class_node = next(infer_named_tuple(node, context))
    except StopIteration as e:
        raise InferenceError(node=node, context=context) from e
    for method in class_node.mymethods():
        generated_class_node.locals[method.name] = [method]

    for body_node in class_node.body:
        if isinstance(body_node, nodes.Assign):
            for target in body_node.targets:
                attr = target.name
                generated_class_node.locals[attr] = class_node.locals[attr]
        elif isinstance(body_node, nodes.ClassDef):
            generated_class_node.locals[body_node.name] = [body_node]

    return iter((generated_class_node,))


def infer_typing_namedtuple_function(node, context: InferenceContext | None = None):
    """
    Starting with python3.9, NamedTuple is a function of the typing module.
    The class NamedTuple is build dynamically through a call to `type` during
    initialization of the `_NamedTuple` variable.
    """
    klass = extract_node(
        """
        from typing import _NamedTuple
        _NamedTuple
        """
    )
    return klass.infer(context)


def infer_typing_namedtuple(
    node: nodes.Call, context: InferenceContext | None = None
) -> Iterator[nodes.ClassDef]:
    """Infer a typing.NamedTuple(...) call."""
    # This is essentially a namedtuple with different arguments
    # so we extract the args and infer a named tuple.
    try:
        func = next(node.func.infer())
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc

    if func.qname() not in TYPING_NAMEDTUPLE_QUALIFIED:
        raise UseInferenceDefault

    if len(node.args) != 2:
        raise UseInferenceDefault

    if not isinstance(node.args[1], (nodes.List, nodes.Tuple)):
        raise UseInferenceDefault

    return infer_named_tuple(node, context)


def _get_namedtuple_fields(node: nodes.Call) -> str:
    """Get and return fields of a NamedTuple in code-as-a-string.

    Because the fields are represented in their code form we can
    extract a node from them later on.
    """
    names = []
    container = None
    try:
        container = next(node.args[1].infer())
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc
    # We pass on IndexError as we'll try to infer 'field_names' from the keywords
    except IndexError:
        pass
    if not container:
        for keyword_node in node.keywords:
            if keyword_node.arg == "field_names":
                try:
                    container = next(keyword_node.value.infer())
                except (InferenceError, StopIteration) as exc:
                    raise UseInferenceDefault from exc
                break
    if not isinstance(container, nodes.BaseContainer):
        raise UseInferenceDefault
    for elt in container.elts:
        if isinstance(elt, nodes.Const):
            names.append(elt.as_string())
            continue
        if not isinstance(elt, (nodes.List, nodes.Tuple)):
            raise UseInferenceDefault
        if len(elt.elts) != 2:
            raise UseInferenceDefault
        names.append(elt.elts[0].as_string())

    if names:
        field_names = f"({','.join(names)},)"
    else:
        field_names = ""
    return field_names


def _is_enum_subclass(cls: astroid.ClassDef) -> bool:
    """Return whether cls is a subclass of an Enum."""
    return cls.is_subtype_of("enum.Enum")


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        nodes.Call, inference_tip(infer_named_tuple), _looks_like_namedtuple
    )
    manager.register_transform(nodes.Call, inference_tip(infer_enum), _looks_like_enum)
    manager.register_transform(
        nodes.ClassDef, infer_enum_class, predicate=_is_enum_subclass
    )
    manager.register_transform(
        nodes.ClassDef,
        inference_tip(infer_typing_namedtuple_class),
        _has_namedtuple_base,
    )
    manager.register_transform(
        nodes.FunctionDef,
        inference_tip(infer_typing_namedtuple_function),
        lambda node: node.name == "NamedTuple"
        and getattr(node.root(), "name", None) == "typing",
    )
    manager.register_transform(
        nodes.Call,
        inference_tip(infer_typing_namedtuple),
        _looks_like_typing_namedtuple,
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_namedtuple_enum.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Hooks for nose library."""

import re
import textwrap

from astroid.bases import BoundMethod
from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder
from astroid.exceptions import InferenceError
from astroid.manager import AstroidManager
from astroid.nodes import List, Module

CAPITALS = re.compile("([A-Z])")


def _pep8(name, caps=CAPITALS):
    return caps.sub(lambda m: "_" + m.groups()[0].lower(), name)


def _nose_tools_functions():
    """Get an iterator of names and bound methods."""
    module = AstroidBuilder().string_build(
        textwrap.dedent(
            """
    import unittest

    class Test(unittest.TestCase):
        pass
    a = Test()
    """
        )
    )
    try:
        case = next(module["a"].infer())
    except (InferenceError, StopIteration):
        return
    for method in case.methods():
        if method.name.startswith("assert") and "_" not in method.name:
            pep8_name = _pep8(method.name)
            yield pep8_name, BoundMethod(method, case)
        if method.name == "assertEqual":
            # nose also exports assert_equals.
            yield "assert_equals", BoundMethod(method, case)


def _nose_tools_transform(node):
    for method_name, method in _nose_tools_functions():
        node.locals[method_name] = [method]


def _nose_tools_trivial_transform():
    """Custom transform for the nose.tools module."""
    stub = AstroidBuilder().string_build("""__all__ = []""")
    all_entries = ["ok_", "eq_"]

    for pep8_name, method in _nose_tools_functions():
        all_entries.append(pep8_name)
        stub[pep8_name] = method

    # Update the __all__ variable, since nose.tools
    # does this manually with .append.
    all_assign = stub["__all__"].parent
    all_object = List(all_entries)
    all_object.parent = all_assign
    all_assign.value = all_object
    return stub


def register(manager: AstroidManager) -> None:
    register_module_extender(
        manager, "nose.tools.trivial", _nose_tools_trivial_transform
    )
    manager.register_transform(
        Module, _nose_tools_transform, lambda n: n.name == "nose.tools"
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_nose.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Astroid hooks for numpy.core.einsumfunc module:
https://github.com/numpy/numpy/blob/main/numpy/core/einsumfunc.py.
"""

from astroid import nodes
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def numpy_core_einsumfunc_transform() -> nodes.Module:
    return parse(
        """
    def einsum(*operands, out=None, optimize=False, **kwargs):
        return numpy.ndarray([0, 0])
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(
        manager, "numpy.core.einsumfunc", numpy_core_einsumfunc_transform
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for numpy.core.fromnumeric module."""
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def numpy_core_fromnumeric_transform():
    return parse(
        """
    def sum(a, axis=None, dtype=None, out=None, keepdims=None, initial=None):
        return numpy.ndarray([0, 0])
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(
        manager, "numpy.core.fromnumeric", numpy_core_fromnumeric_transform
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for numpy.core.function_base module."""

import functools

from astroid.brain.brain_numpy_utils import (
    attribute_looks_like_numpy_member,
    infer_numpy_member,
)
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import Attribute

METHODS_TO_BE_INFERRED = {
    "linspace": """def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0):
            return numpy.ndarray([0, 0])""",
    "logspace": """def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0):
            return numpy.ndarray([0, 0])""",
    "geomspace": """def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0):
            return numpy.ndarray([0, 0])""",
}


def register(manager: AstroidManager) -> None:
    for func_name, func_src in METHODS_TO_BE_INFERRED.items():
        inference_function = functools.partial(infer_numpy_member, func_src)
        manager.register_transform(
            Attribute,
            inference_tip(inference_function),
            functools.partial(attribute_looks_like_numpy_member, func_name),
        )

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_core_function_base.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for numpy.core.multiarray module."""

import functools

from astroid.brain.brain_numpy_utils import (
    attribute_looks_like_numpy_member,
    infer_numpy_member,
    name_looks_like_numpy_member,
)
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import Attribute, Name


def numpy_core_multiarray_transform():
    return parse(
        """
    # different functions defined in multiarray.py
    def inner(a, b):
        return numpy.ndarray([0, 0])

    def vdot(a, b):
        return numpy.ndarray([0, 0])
        """
    )


METHODS_TO_BE_INFERRED = {
    "array": """def array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0):
            return numpy.ndarray([0, 0])""",
    "dot": """def dot(a, b, out=None):
            return numpy.ndarray([0, 0])""",
    "empty_like": """def empty_like(a, dtype=None, order='K', subok=True):
            return numpy.ndarray((0, 0))""",
    "concatenate": """def concatenate(arrays, axis=None, out=None):
            return numpy.ndarray((0, 0))""",
    "where": """def where(condition, x=None, y=None):
            return numpy.ndarray([0, 0])""",
    "empty": """def empty(shape, dtype=float, order='C'):
            return numpy.ndarray([0, 0])""",
    "bincount": """def bincount(x, weights=None, minlength=0):
            return numpy.ndarray([0, 0])""",
    "busday_count": """def busday_count(
        begindates, enddates, weekmask='1111100', holidays=[], busdaycal=None, out=None
    ):
        return numpy.ndarray([0, 0])""",
    "busday_offset": """def busday_offset(
        dates, offsets, roll='raise', weekmask='1111100', holidays=None,
        busdaycal=None, out=None
    ):
        return numpy.ndarray([0, 0])""",
    "can_cast": """def can_cast(from_, to, casting='safe'):
            return True""",
    "copyto": """def copyto(dst, src, casting='same_kind', where=True):
            return None""",
    "datetime_as_string": """def datetime_as_string(arr, unit=None, timezone='naive', casting='same_kind'):
            return numpy.ndarray([0, 0])""",
    "is_busday": """def is_busday(dates, weekmask='1111100', holidays=None, busdaycal=None, out=None):
            return numpy.ndarray([0, 0])""",
    "lexsort": """def lexsort(keys, axis=-1):
            return numpy.ndarray([0, 0])""",
    "may_share_memory": """def may_share_memory(a, b, max_work=None):
            return True""",
    # Not yet available because dtype is not yet present in those brains
    #     "min_scalar_type": """def min_scalar_type(a):
    #             return numpy.dtype('int16')""",
    "packbits": """def packbits(a, axis=None, bitorder='big'):
            return numpy.ndarray([0, 0])""",
    # Not yet available because dtype is not yet present in those brains
    #     "result_type": """def result_type(*arrays_and_dtypes):
    #             return numpy.dtype('int16')""",
    "shares_memory": """def shares_memory(a, b, max_work=None):
            return True""",
    "unpackbits": """def unpackbits(a, axis=None, count=None, bitorder='big'):
            return numpy.ndarray([0, 0])""",
    "unravel_index": """def unravel_index(indices, shape, order='C'):
            return (numpy.ndarray([0, 0]),)""",
    "zeros": """def zeros(shape, dtype=float, order='C'):
            return numpy.ndarray([0, 0])""",
}


def register(manager: AstroidManager) -> None:
    register_module_extender(
        manager, "numpy.core.multiarray", numpy_core_multiarray_transform
    )

    for method_name, function_src in METHODS_TO_BE_INFERRED.items():
        inference_function = functools.partial(infer_numpy_member, function_src)
        manager.register_transform(
            Attribute,
            inference_tip(inference_function),
            functools.partial(attribute_looks_like_numpy_member, method_name),
        )
        manager.register_transform(
            Name,
            inference_tip(inference_function),
            functools.partial(name_looks_like_numpy_member, method_name),
        )

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_core_multiarray.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for numpy.core.numeric module."""

import functools

from astroid.brain.brain_numpy_utils import (
    attribute_looks_like_numpy_member,
    infer_numpy_member,
)
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import Attribute


def numpy_core_numeric_transform():
    return parse(
        """
    # different functions defined in numeric.py
    import numpy
    def zeros_like(a, dtype=None, order='K', subok=True, shape=None): return numpy.ndarray((0, 0))
    def ones_like(a, dtype=None, order='K', subok=True, shape=None): return numpy.ndarray((0, 0))
    def full_like(a, fill_value, dtype=None, order='K', subok=True, shape=None): return numpy.ndarray((0, 0))
        """
    )


METHODS_TO_BE_INFERRED = {
    "ones": """def ones(shape, dtype=None, order='C'):
            return numpy.ndarray([0, 0])"""
}


def register(manager: AstroidManager) -> None:
    register_module_extender(
        manager, "numpy.core.numeric", numpy_core_numeric_transform
    )

    for method_name, function_src in METHODS_TO_BE_INFERRED.items():
        inference_function = functools.partial(infer_numpy_member, function_src)
        manager.register_transform(
            Attribute,
            inference_tip(inference_function),
            functools.partial(attribute_looks_like_numpy_member, method_name),
        )

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_core_numeric.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

# TODO(hippo91) : correct the methods signature.

"""Astroid hooks for numpy.core.numerictypes module."""
from astroid.brain.brain_numpy_utils import numpy_supports_type_hints
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def numpy_core_numerictypes_transform():
    # TODO: Uniformize the generic API with the ndarray one.
    #       According to numpy doc the generic object should expose
    #       the same API than ndarray. This has been done here partially
    #       through the astype method.
    generic_src = """
    class generic(object):
        def __init__(self, value):
            self.T = np.ndarray([0, 0])
            self.base = None
            self.data = None
            self.dtype = None
            self.flags = None
            # Should be a numpy.flatiter instance but not available for now
            # Putting an array instead so that iteration and indexing are authorized
            self.flat = np.ndarray([0, 0])
            self.imag = None
            self.itemsize = None
            self.nbytes = None
            self.ndim = None
            self.real = None
            self.size = None
            self.strides = None

        def all(self): return uninferable
        def any(self): return uninferable
        def argmax(self): return uninferable
        def argmin(self): return uninferable
        def argsort(self): return uninferable
        def astype(self, dtype, order='K', casting='unsafe', subok=True, copy=True): return np.ndarray([0, 0])
        def base(self): return uninferable
        def byteswap(self): return uninferable
        def choose(self): return uninferable
        def clip(self): return uninferable
        def compress(self): return uninferable
        def conj(self): return uninferable
        def conjugate(self): return uninferable
        def copy(self): return uninferable
        def cumprod(self): return uninferable
        def cumsum(self): return uninferable
        def data(self): return uninferable
        def diagonal(self): return uninferable
        def dtype(self): return uninferable
        def dump(self): return uninferable
        def dumps(self): return uninferable
        def fill(self): return uninferable
        def flags(self): return uninferable
        def flat(self): return uninferable
        def flatten(self): return uninferable
        def getfield(self): return uninferable
        def imag(self): return uninferable
        def item(self): return uninferable
        def itemset(self): return uninferable
        def itemsize(self): return uninferable
        def max(self): return uninferable
        def mean(self): return uninferable
        def min(self): return uninferable
        def nbytes(self): return uninferable
        def ndim(self): return uninferable
        def newbyteorder(self): return uninferable
        def nonzero(self): return uninferable
        def prod(self): return uninferable
        def ptp(self): return uninferable
        def put(self): return uninferable
        def ravel(self): return uninferable
        def real(self): return uninferable
        def repeat(self): return uninferable
        def reshape(self): return uninferable
        def resize(self): return uninferable
        def round(self): return uninferable
        def searchsorted(self): return uninferable
        def setfield(self): return uninferable
        def setflags(self): return uninferable
        def shape(self): return uninferable
        def size(self): return uninferable
        def sort(self): return uninferable
        def squeeze(self): return uninferable
        def std(self): return uninferable
        def strides(self): return uninferable
        def sum(self): return uninferable
        def swapaxes(self): return uninferable
        def take(self): return uninferable
        def tobytes(self): return uninferable
        def tofile(self): return uninferable
        def tolist(self): return uninferable
        def tostring(self): return uninferable
        def trace(self): return uninferable
        def transpose(self): return uninferable
        def var(self): return uninferable
        def view(self): return uninferable
        """
    if numpy_supports_type_hints():
        generic_src += """
        @classmethod
        def __class_getitem__(cls, value):
            return cls
        """
    return parse(
        generic_src
        + """
    class dtype(object):
        def __init__(self, obj, align=False, copy=False):
            self.alignment = None
            self.base = None
            self.byteorder = None
            self.char = None
            self.descr = None
            self.fields = None
            self.flags = None
            self.hasobject = None
            self.isalignedstruct = None
            self.isbuiltin = None
            self.isnative = None
            self.itemsize = None
            self.kind = None
            self.metadata = None
            self.name = None
            self.names = None
            self.num = None
            self.shape = None
            self.str = None
            self.subdtype = None
            self.type = None

        def newbyteorder(self, new_order='S'): return uninferable
        def __neg__(self): return uninferable

    class busdaycalendar(object):
        def __init__(self, weekmask='1111100', holidays=None):
            self.holidays = None
            self.weekmask = None

    class flexible(generic): pass
    class bool_(generic): pass
    class number(generic):
        def __neg__(self): return uninferable
    class datetime64(generic):
        def __init__(self, nb, unit=None): pass


    class void(flexible):
        def __init__(self, *args, **kwargs):
            self.base = None
            self.dtype = None
            self.flags = None
        def getfield(self): return uninferable
        def setfield(self): return uninferable


    class character(flexible): pass


    class integer(number):
        def __init__(self, value):
           self.denominator = None
           self.numerator = None


    class inexact(number): pass


    class str_(str, character):
        def maketrans(self, x, y=None, z=None): return uninferable


    class bytes_(bytes, character):
        def fromhex(self, string): return uninferable
        def maketrans(self, frm, to): return uninferable


    class signedinteger(integer): pass


    class unsignedinteger(integer): pass


    class complexfloating(inexact): pass


    class floating(inexact): pass


    class float64(floating, float):
        def fromhex(self, string): return uninferable


    class uint64(unsignedinteger): pass
    class complex64(complexfloating): pass
    class int16(signedinteger): pass
    class float96(floating): pass
    class int8(signedinteger): pass
    class uint32(unsignedinteger): pass
    class uint8(unsignedinteger): pass
    class _typedict(dict): pass
    class complex192(complexfloating): pass
    class timedelta64(signedinteger):
        def __init__(self, nb, unit=None): pass
    class int32(signedinteger): pass
    class uint16(unsignedinteger): pass
    class float32(floating): pass
    class complex128(complexfloating, complex): pass
    class float16(floating): pass
    class int64(signedinteger): pass

    buffer_type = memoryview
    bool8 = bool_
    byte = int8
    bytes0 = bytes_
    cdouble = complex128
    cfloat = complex128
    clongdouble = complex192
    clongfloat = complex192
    complex_ = complex128
    csingle = complex64
    double = float64
    float_ = float64
    half = float16
    int0 = int32
    int_ = int32
    intc = int32
    intp = int32
    long = int32
    longcomplex = complex192
    longdouble = float96
    longfloat = float96
    longlong = int64
    object0 = object_
    object_ = object_
    short = int16
    single = float32
    singlecomplex = complex64
    str0 = str_
    string_ = bytes_
    ubyte = uint8
    uint = uint32
    uint0 = uint32
    uintc = uint32
    uintp = uint32
    ulonglong = uint64
    unicode = str_
    unicode_ = str_
    ushort = uint16
    void0 = void
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(
        manager, "numpy.core.numerictypes", numpy_core_numerictypes_transform
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_core_numerictypes.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

# Note: starting with version 1.18 numpy module has `__getattr__` method which prevent
# `pylint` to emit `no-member` message for all numpy's attributes. (see pylint's module
# typecheck in `_emit_no_member` function)

"""Astroid hooks for numpy.core.umath module."""
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def numpy_core_umath_transform():
    ufunc_optional_keyword_arguments = (
        """out=None, where=True, casting='same_kind', order='K', """
        """dtype=None, subok=True"""
    )
    return parse(
        """
    class FakeUfunc:
        def __init__(self):
            self.__doc__ = str()
            self.__name__ = str()
            self.nin = 0
            self.nout = 0
            self.nargs = 0
            self.ntypes = 0
            self.types = None
            self.identity = None
            self.signature = None

        @classmethod
        def reduce(cls, a, axis=None, dtype=None, out=None):
            return numpy.ndarray([0, 0])

        @classmethod
        def accumulate(cls, array, axis=None, dtype=None, out=None):
            return numpy.ndarray([0, 0])

        @classmethod
        def reduceat(cls, a, indices, axis=None, dtype=None, out=None):
            return numpy.ndarray([0, 0])

        @classmethod
        def outer(cls, A, B, **kwargs):
            return numpy.ndarray([0, 0])

        @classmethod
        def at(cls, a, indices, b=None):
            return numpy.ndarray([0, 0])

    class FakeUfuncOneArg(FakeUfunc):
        def __call__(self, x, {opt_args:s}):
            return numpy.ndarray([0, 0])

    class FakeUfuncOneArgBis(FakeUfunc):
        def __call__(self, x, {opt_args:s}):
            return numpy.ndarray([0, 0]), numpy.ndarray([0, 0])

    class FakeUfuncTwoArgs(FakeUfunc):
        def __call__(self, x1, x2, {opt_args:s}):
            return numpy.ndarray([0, 0])

    # Constants
    e = 2.718281828459045
    euler_gamma = 0.5772156649015329

    # One arg functions with optional kwargs
    arccos = FakeUfuncOneArg()
    arccosh = FakeUfuncOneArg()
    arcsin = FakeUfuncOneArg()
    arcsinh = FakeUfuncOneArg()
    arctan = FakeUfuncOneArg()
    arctanh = FakeUfuncOneArg()
    cbrt = FakeUfuncOneArg()
    conj = FakeUfuncOneArg()
    conjugate = FakeUfuncOneArg()
    cosh = FakeUfuncOneArg()
    deg2rad = FakeUfuncOneArg()
    degrees = FakeUfuncOneArg()
    exp2 = FakeUfuncOneArg()
    expm1 = FakeUfuncOneArg()
    fabs = FakeUfuncOneArg()
    frexp = FakeUfuncOneArgBis()
    isfinite = FakeUfuncOneArg()
    isinf = FakeUfuncOneArg()
    log = FakeUfuncOneArg()
    log1p = FakeUfuncOneArg()
    log2 = FakeUfuncOneArg()
    logical_not = FakeUfuncOneArg()
    modf = FakeUfuncOneArgBis()
    negative = FakeUfuncOneArg()
    positive = FakeUfuncOneArg()
    rad2deg = FakeUfuncOneArg()
    radians = FakeUfuncOneArg()
    reciprocal = FakeUfuncOneArg()
    rint = FakeUfuncOneArg()
    sign = FakeUfuncOneArg()
    signbit = FakeUfuncOneArg()
    sinh = FakeUfuncOneArg()
    spacing = FakeUfuncOneArg()
    square = FakeUfuncOneArg()
    tan = FakeUfuncOneArg()
    tanh = FakeUfuncOneArg()
    trunc = FakeUfuncOneArg()

    # Two args functions with optional kwargs
    add = FakeUfuncTwoArgs()
    bitwise_and = FakeUfuncTwoArgs()
    bitwise_or = FakeUfuncTwoArgs()
    bitwise_xor = FakeUfuncTwoArgs()
    copysign = FakeUfuncTwoArgs()
    divide = FakeUfuncTwoArgs()
    divmod = FakeUfuncTwoArgs()
    equal = FakeUfuncTwoArgs()
    float_power = FakeUfuncTwoArgs()
    floor_divide = FakeUfuncTwoArgs()
    fmax = FakeUfuncTwoArgs()
    fmin = FakeUfuncTwoArgs()
    fmod = FakeUfuncTwoArgs()
    greater = FakeUfuncTwoArgs()
    gcd = FakeUfuncTwoArgs()
    hypot = FakeUfuncTwoArgs()
    heaviside = FakeUfuncTwoArgs()
    lcm = FakeUfuncTwoArgs()
    ldexp = FakeUfuncTwoArgs()
    left_shift = FakeUfuncTwoArgs()
    less = FakeUfuncTwoArgs()
    logaddexp = FakeUfuncTwoArgs()
    logaddexp2 = FakeUfuncTwoArgs()
    logical_and = FakeUfuncTwoArgs()
    logical_or = FakeUfuncTwoArgs()
    logical_xor = FakeUfuncTwoArgs()
    maximum = FakeUfuncTwoArgs()
    minimum = FakeUfuncTwoArgs()
    multiply = FakeUfuncTwoArgs()
    nextafter = FakeUfuncTwoArgs()
    not_equal = FakeUfuncTwoArgs()
    power = FakeUfuncTwoArgs()
    remainder = FakeUfuncTwoArgs()
    right_shift = FakeUfuncTwoArgs()
    subtract = FakeUfuncTwoArgs()
    true_divide = FakeUfuncTwoArgs()
    """.format(
            opt_args=ufunc_optional_keyword_arguments
        )
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "numpy.core.umath", numpy_core_umath_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_core_umath.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for numpy ma module."""

from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def numpy_ma_transform():
    """
    Infer the call of various numpy.ma functions.

    :param node: node to infer
    :param context: inference context
    """
    return parse(
        """
    import numpy.ma
    def masked_where(condition, a, copy=True):
        return numpy.ma.masked_array(a, mask=[])

    def masked_invalid(a, copy=True):
        return numpy.ma.masked_array(a, mask=[])
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "numpy.ma", numpy_ma_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_ma.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for numpy ndarray class."""
from __future__ import annotations

from astroid.brain.brain_numpy_utils import numpy_supports_type_hints
from astroid.builder import extract_node
from astroid.context import InferenceContext
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import Attribute


def infer_numpy_ndarray(node, context: InferenceContext | None = None):
    ndarray = """
    class ndarray(object):
        def __init__(self, shape, dtype=float, buffer=None, offset=0,
                     strides=None, order=None):
            self.T = numpy.ndarray([0, 0])
            self.base = None
            self.ctypes = None
            self.data = None
            self.dtype = None
            self.flags = None
            # Should be a numpy.flatiter instance but not available for now
            # Putting an array instead so that iteration and indexing are authorized
            self.flat = np.ndarray([0, 0])
            self.imag = np.ndarray([0, 0])
            self.itemsize = None
            self.nbytes = None
            self.ndim = None
            self.real = np.ndarray([0, 0])
            self.shape = numpy.ndarray([0, 0])
            self.size = None
            self.strides = None

        def __abs__(self): return numpy.ndarray([0, 0])
        def __add__(self, value): return numpy.ndarray([0, 0])
        def __and__(self, value): return numpy.ndarray([0, 0])
        def __array__(self, dtype=None): return numpy.ndarray([0, 0])
        def __array_wrap__(self, obj): return numpy.ndarray([0, 0])
        def __contains__(self, key): return True
        def __copy__(self): return numpy.ndarray([0, 0])
        def __deepcopy__(self, memo): return numpy.ndarray([0, 0])
        def __divmod__(self, value): return (numpy.ndarray([0, 0]), numpy.ndarray([0, 0]))
        def __eq__(self, value): return numpy.ndarray([0, 0])
        def __float__(self): return 0.
        def __floordiv__(self): return numpy.ndarray([0, 0])
        def __ge__(self, value): return numpy.ndarray([0, 0])
        def __getitem__(self, key): return uninferable
        def __gt__(self, value): return numpy.ndarray([0, 0])
        def __iadd__(self, value): return numpy.ndarray([0, 0])
        def __iand__(self, value): return numpy.ndarray([0, 0])
        def __ifloordiv__(self, value): return numpy.ndarray([0, 0])
        def __ilshift__(self, value): return numpy.ndarray([0, 0])
        def __imod__(self, value): return numpy.ndarray([0, 0])
        def __imul__(self, value): return numpy.ndarray([0, 0])
        def __int__(self): return 0
        def __invert__(self): return numpy.ndarray([0, 0])
        def __ior__(self, value): return numpy.ndarray([0, 0])
        def __ipow__(self, value): return numpy.ndarray([0, 0])
        def __irshift__(self, value): return numpy.ndarray([0, 0])
        def __isub__(self, value): return numpy.ndarray([0, 0])
        def __itruediv__(self, value): return numpy.ndarray([0, 0])
        def __ixor__(self, value): return numpy.ndarray([0, 0])
        def __le__(self, value): return numpy.ndarray([0, 0])
        def __len__(self): return 1
        def __lshift__(self, value): return numpy.ndarray([0, 0])
        def __lt__(self, value): return numpy.ndarray([0, 0])
        def __matmul__(self, value): return numpy.ndarray([0, 0])
        def __mod__(self, value): return numpy.ndarray([0, 0])
        def __mul__(self, value): return numpy.ndarray([0, 0])
        def __ne__(self, value): return numpy.ndarray([0, 0])
        def __neg__(self): return numpy.ndarray([0, 0])
        def __or__(self, value): return numpy.ndarray([0, 0])
        def __pos__(self): return numpy.ndarray([0, 0])
        def __pow__(self): return numpy.ndarray([0, 0])
        def __repr__(self): return str()
        def __rshift__(self): return numpy.ndarray([0, 0])
        def __setitem__(self, key, value): return uninferable
        def __str__(self): return str()
        def __sub__(self, value): return numpy.ndarray([0, 0])
        def __truediv__(self, value): return numpy.ndarray([0, 0])
        def __xor__(self, value): return numpy.ndarray([0, 0])
        def all(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0])
        def any(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0])
        def argmax(self, axis=None, out=None): return np.ndarray([0, 0])
        def argmin(self, axis=None, out=None): return np.ndarray([0, 0])
        def argpartition(self, kth, axis=-1, kind='introselect', order=None): return np.ndarray([0, 0])
        def argsort(self, axis=-1, kind='quicksort', order=None): return np.ndarray([0, 0])
        def astype(self, dtype, order='K', casting='unsafe', subok=True, copy=True): return np.ndarray([0, 0])
        def byteswap(self, inplace=False): return np.ndarray([0, 0])
        def choose(self, choices, out=None, mode='raise'): return np.ndarray([0, 0])
        def clip(self, min=None, max=None, out=None): return np.ndarray([0, 0])
        def compress(self, condition, axis=None, out=None): return np.ndarray([0, 0])
        def conj(self): return np.ndarray([0, 0])
        def conjugate(self): return np.ndarray([0, 0])
        def copy(self, order='C'): return np.ndarray([0, 0])
        def cumprod(self, axis=None, dtype=None, out=None): return np.ndarray([0, 0])
        def cumsum(self, axis=None, dtype=None, out=None): return np.ndarray([0, 0])
        def diagonal(self, offset=0, axis1=0, axis2=1): return np.ndarray([0, 0])
        def dot(self, b, out=None): return np.ndarray([0, 0])
        def dump(self, file): return None
        def dumps(self): return str()
        def fill(self, value): return None
        def flatten(self, order='C'): return np.ndarray([0, 0])
        def getfield(self, dtype, offset=0): return np.ndarray([0, 0])
        def item(self, *args): return uninferable
        def itemset(self, *args): return None
        def max(self, axis=None, out=None): return np.ndarray([0, 0])
        def mean(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0])
        def min(self, axis=None, out=None, keepdims=False): return np.ndarray([0, 0])
        def newbyteorder(self, new_order='S'): return np.ndarray([0, 0])
        def nonzero(self): return (1,)
        def partition(self, kth, axis=-1, kind='introselect', order=None): return None
        def prod(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0])
        def ptp(self, axis=None, out=None): return np.ndarray([0, 0])
        def put(self, indices, values, mode='raise'): return None
        def ravel(self, order='C'): return np.ndarray([0, 0])
        def repeat(self, repeats, axis=None): return np.ndarray([0, 0])
        def reshape(self, shape, order='C'): return np.ndarray([0, 0])
        def resize(self, new_shape, refcheck=True): return None
        def round(self, decimals=0, out=None): return np.ndarray([0, 0])
        def searchsorted(self, v, side='left', sorter=None): return np.ndarray([0, 0])
        def setfield(self, val, dtype, offset=0): return None
        def setflags(self, write=None, align=None, uic=None): return None
        def sort(self, axis=-1, kind='quicksort', order=None): return None
        def squeeze(self, axis=None): return np.ndarray([0, 0])
        def std(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return np.ndarray([0, 0])
        def sum(self, axis=None, dtype=None, out=None, keepdims=False): return np.ndarray([0, 0])
        def swapaxes(self, axis1, axis2): return np.ndarray([0, 0])
        def take(self, indices, axis=None, out=None, mode='raise'): return np.ndarray([0, 0])
        def tobytes(self, order='C'): return b''
        def tofile(self, fid, sep="", format="%s"): return None
        def tolist(self, ): return []
        def tostring(self, order='C'): return b''
        def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): return np.ndarray([0, 0])
        def transpose(self, *axes): return np.ndarray([0, 0])
        def var(self, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return np.ndarray([0, 0])
        def view(self, dtype=None, type=None): return np.ndarray([0, 0])
    """
    if numpy_supports_type_hints():
        ndarray += """
        @classmethod
        def __class_getitem__(cls, value):
            return cls
        """
    node = extract_node(ndarray)
    return node.infer(context=context)


def _looks_like_numpy_ndarray(node) -> bool:
    return isinstance(node, Attribute) and node.attrname == "ndarray"


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        Attribute,
        inference_tip(infer_numpy_ndarray),
        _looks_like_numpy_ndarray,
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_ndarray.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

# TODO(hippo91) : correct the functions return types
"""Astroid hooks for numpy.random.mtrand module."""
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def numpy_random_mtrand_transform():
    return parse(
        """
    def beta(a, b, size=None): return uninferable
    def binomial(n, p, size=None): return uninferable
    def bytes(length): return uninferable
    def chisquare(df, size=None): return uninferable
    def choice(a, size=None, replace=True, p=None): return uninferable
    def dirichlet(alpha, size=None): return uninferable
    def exponential(scale=1.0, size=None): return uninferable
    def f(dfnum, dfden, size=None): return uninferable
    def gamma(shape, scale=1.0, size=None): return uninferable
    def geometric(p, size=None): return uninferable
    def get_state(): return uninferable
    def gumbel(loc=0.0, scale=1.0, size=None): return uninferable
    def hypergeometric(ngood, nbad, nsample, size=None): return uninferable
    def laplace(loc=0.0, scale=1.0, size=None): return uninferable
    def logistic(loc=0.0, scale=1.0, size=None): return uninferable
    def lognormal(mean=0.0, sigma=1.0, size=None): return uninferable
    def logseries(p, size=None): return uninferable
    def multinomial(n, pvals, size=None): return uninferable
    def multivariate_normal(mean, cov, size=None): return uninferable
    def negative_binomial(n, p, size=None): return uninferable
    def noncentral_chisquare(df, nonc, size=None): return uninferable
    def noncentral_f(dfnum, dfden, nonc, size=None): return uninferable
    def normal(loc=0.0, scale=1.0, size=None): return uninferable
    def pareto(a, size=None): return uninferable
    def permutation(x): return uninferable
    def poisson(lam=1.0, size=None): return uninferable
    def power(a, size=None): return uninferable
    def rand(*args): return uninferable
    def randint(low, high=None, size=None, dtype='l'):
        import numpy
        return numpy.ndarray((1,1))
    def randn(*args): return uninferable
    def random(size=None): return uninferable
    def random_integers(low, high=None, size=None): return uninferable
    def random_sample(size=None): return uninferable
    def rayleigh(scale=1.0, size=None): return uninferable
    def seed(seed=None): return uninferable
    def set_state(state): return uninferable
    def shuffle(x): return uninferable
    def standard_cauchy(size=None): return uninferable
    def standard_exponential(size=None): return uninferable
    def standard_gamma(shape, size=None): return uninferable
    def standard_normal(size=None): return uninferable
    def standard_t(df, size=None): return uninferable
    def triangular(left, mode, right, size=None): return uninferable
    def uniform(low=0.0, high=1.0, size=None): return uninferable
    def vonmises(mu, kappa, size=None): return uninferable
    def wald(mean, scale, size=None): return uninferable
    def weibull(a, size=None): return uninferable
    def zipf(a, size=None): return uninferable
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(
        manager, "numpy.random.mtrand", numpy_random_mtrand_transform
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_random_mtrand.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Different utilities for the numpy brains."""

from __future__ import annotations

from astroid.builder import extract_node
from astroid.context import InferenceContext
from astroid.nodes.node_classes import Attribute, Import, Name

# Class subscript is available in numpy starting with version 1.20.0
NUMPY_VERSION_TYPE_HINTS_SUPPORT = ("1", "20", "0")


def numpy_supports_type_hints() -> bool:
    """Returns True if numpy supports type hints."""
    np_ver = _get_numpy_version()
    return np_ver and np_ver > NUMPY_VERSION_TYPE_HINTS_SUPPORT


def _get_numpy_version() -> tuple[str, str, str]:
    """
    Return the numpy version number if numpy can be imported.

    Otherwise returns ('0', '0', '0')
    """
    try:
        import numpy  # pylint: disable=import-outside-toplevel

        return tuple(numpy.version.version.split("."))
    except (ImportError, AttributeError):
        return ("0", "0", "0")


def infer_numpy_member(src, node, context: InferenceContext | None = None):
    node = extract_node(src)
    return node.infer(context=context)


def _is_a_numpy_module(node: Name) -> bool:
    """
    Returns True if the node is a representation of a numpy module.

    For example in :
        import numpy as np
        x = np.linspace(1, 2)
    The node <Name.np> is a representation of the numpy module.

    :param node: node to test
    :return: True if the node is a representation of the numpy module.
    """
    module_nickname = node.name
    potential_import_target = [
        x for x in node.lookup(module_nickname)[1] if isinstance(x, Import)
    ]
    return any(
        ("numpy", module_nickname) in target.names or ("numpy", None) in target.names
        for target in potential_import_target
    )


def name_looks_like_numpy_member(member_name: str, node: Name) -> bool:
    """
    Returns True if the Name is a member of numpy whose
    name is member_name.
    """
    return node.name == member_name and node.root().name.startswith("numpy")


def attribute_looks_like_numpy_member(member_name: str, node: Attribute) -> bool:
    """
    Returns True if the Attribute is a member of numpy whose
    name is member_name.
    """
    return (
        node.attrname == member_name
        and isinstance(node.expr, Name)
        and _is_a_numpy_module(node.expr)
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_numpy_utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from collections.abc import Iterator

from astroid import bases, context, inference_tip, nodes
from astroid.builder import _extract_single_node
from astroid.exceptions import InferenceError, UseInferenceDefault
from astroid.manager import AstroidManager

PATH_TEMPLATE = """
from pathlib import Path
Path
"""


def _looks_like_parents_subscript(node: nodes.Subscript) -> bool:
    if not (
        isinstance(node.value, nodes.Attribute) and node.value.attrname == "parents"
    ):
        return False

    try:
        value = next(node.value.infer())
    except (InferenceError, StopIteration):
        return False
    return (
        isinstance(value, bases.Instance)
        and isinstance(value._proxied, nodes.ClassDef)
        and value.qname() == "pathlib._PathParents"
    )


def infer_parents_subscript(
    subscript_node: nodes.Subscript, ctx: context.InferenceContext | None = None
) -> Iterator[bases.Instance]:
    if isinstance(subscript_node.slice, nodes.Const):
        path_cls = next(_extract_single_node(PATH_TEMPLATE).infer())
        return iter([path_cls.instantiate_class()])

    raise UseInferenceDefault


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        nodes.Subscript,
        inference_tip(infer_parents_subscript),
        _looks_like_parents_subscript,
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_pathlib.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid import parse
from astroid.brain.helpers import register_module_extender
from astroid.manager import AstroidManager


def pkg_resources_transform():
    return parse(
        """
def require(*requirements):
    return pkg_resources.working_set.require(*requirements)

def run_script(requires, script_name):
    return pkg_resources.working_set.run_script(requires, script_name)

def iter_entry_points(group, name=None):
    return pkg_resources.working_set.iter_entry_points(group, name)

def resource_exists(package_or_requirement, resource_name):
    return get_provider(package_or_requirement).has_resource(resource_name)

def resource_isdir(package_or_requirement, resource_name):
    return get_provider(package_or_requirement).resource_isdir(
        resource_name)

def resource_filename(package_or_requirement, resource_name):
    return get_provider(package_or_requirement).get_resource_filename(
        self, resource_name)

def resource_stream(package_or_requirement, resource_name):
    return get_provider(package_or_requirement).get_resource_stream(
        self, resource_name)

def resource_string(package_or_requirement, resource_name):
    return get_provider(package_or_requirement).get_resource_string(
        self, resource_name)

def resource_listdir(package_or_requirement, resource_name):
    return get_provider(package_or_requirement).resource_listdir(
        resource_name)

def extraction_error():
    pass

def get_cache_path(archive_name, names=()):
    extract_path = self.extraction_path or get_default_cache()
    target_path = os.path.join(extract_path, archive_name+'-tmp', *names)
    return target_path

def postprocess(tempname, filename):
    pass

def set_extraction_path(path):
    pass

def cleanup_resources(force=False):
    pass

def get_distribution(dist):
    return Distribution(dist)

_namespace_packages = {}
"""
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "pkg_resources", pkg_resources_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_pkg_resources.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for pytest."""
from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder
from astroid.manager import AstroidManager


def pytest_transform():
    return AstroidBuilder(AstroidManager()).string_build(
        """

try:
    import _pytest.mark
    import _pytest.recwarn
    import _pytest.runner
    import _pytest.python
    import _pytest.skipping
    import _pytest.assertion
except ImportError:
    pass
else:
    deprecated_call = _pytest.recwarn.deprecated_call
    warns = _pytest.recwarn.warns

    exit = _pytest.runner.exit
    fail = _pytest.runner.fail
    skip = _pytest.runner.skip
    importorskip = _pytest.runner.importorskip

    xfail = _pytest.skipping.xfail
    mark = _pytest.mark.MarkGenerator()
    raises = _pytest.python.raises

    # New in pytest 3.0
    try:
        approx = _pytest.python.approx
        register_assert_rewrite = _pytest.assertion.register_assert_rewrite
    except AttributeError:
        pass


# Moved in pytest 3.0

try:
    import _pytest.freeze_support
    freeze_includes = _pytest.freeze_support.freeze_includes
except ImportError:
    try:
        import _pytest.genscript
        freeze_includes = _pytest.genscript.freeze_includes
    except ImportError:
        pass

try:
    import _pytest.debugging
    set_trace = _pytest.debugging.pytestPDB().set_trace
except ImportError:
    try:
        import _pytest.pdb
        set_trace = _pytest.pdb.pytestPDB().set_trace
    except ImportError:
        pass

try:
    import _pytest.fixtures
    fixture = _pytest.fixtures.fixture
    yield_fixture = _pytest.fixtures.yield_fixture
except ImportError:
    try:
        import _pytest.python
        fixture = _pytest.python.fixture
        yield_fixture = _pytest.python.yield_fixture
    except ImportError:
        pass
"""
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "pytest", pytest_transform)
    register_module_extender(manager, "py.test", pytest_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_pytest.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for the PyQT library."""

from astroid import nodes, parse
from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder
from astroid.manager import AstroidManager


def _looks_like_signal(
    node: nodes.FunctionDef, signal_name: str = "pyqtSignal"
) -> bool:
    """Detect a Signal node."""
    klasses = node.instance_attrs.get("__class__", [])
    # On PySide2 or PySide6 (since  Qt 5.15.2) the Signal class changed locations
    if node.qname().partition(".")[0] in {"PySide2", "PySide6"}:
        return any(cls.qname() == "Signal" for cls in klasses)  # pragma: no cover
    if klasses:
        try:
            return klasses[0].name == signal_name
        except AttributeError:  # pragma: no cover
            # return False if the cls does not have a name attribute
            pass
    return False


def transform_pyqt_signal(node: nodes.FunctionDef) -> None:
    module = parse(
        """
    _UNSET = object()

    class pyqtSignal(object):
        def connect(self, slot, type=None, no_receiver_check=False):
            pass
        def disconnect(self, slot=_UNSET):
            pass
        def emit(self, *args):
            pass
    """
    )
    signal_cls: nodes.ClassDef = module["pyqtSignal"]
    node.instance_attrs["emit"] = [signal_cls["emit"]]
    node.instance_attrs["disconnect"] = [signal_cls["disconnect"]]
    node.instance_attrs["connect"] = [signal_cls["connect"]]


def transform_pyside_signal(node: nodes.FunctionDef) -> None:
    module = parse(
        """
    class NotPySideSignal(object):
        def connect(self, receiver, type=None):
            pass
        def disconnect(self, receiver):
            pass
        def emit(self, *args):
            pass
    """
    )
    signal_cls: nodes.ClassDef = module["NotPySideSignal"]
    node.instance_attrs["connect"] = [signal_cls["connect"]]
    node.instance_attrs["disconnect"] = [signal_cls["disconnect"]]
    node.instance_attrs["emit"] = [signal_cls["emit"]]


def pyqt4_qtcore_transform():
    return AstroidBuilder(AstroidManager()).string_build(
        """

def SIGNAL(signal_name): pass

class QObject(object):
    def emit(self, signal): pass
"""
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "PyQt4.QtCore", pyqt4_qtcore_transform)
    manager.register_transform(
        nodes.FunctionDef, transform_pyqt_signal, _looks_like_signal
    )
    manager.register_transform(
        nodes.ClassDef,
        transform_pyside_signal,
        lambda node: node.qname() in {"PySide.QtCore.Signal", "PySide2.QtCore.Signal"},
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_qt.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import random

from astroid.context import InferenceContext
from astroid.exceptions import UseInferenceDefault
from astroid.inference_tip import inference_tip
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import (
    Attribute,
    Call,
    Const,
    EvaluatedObject,
    List,
    Name,
    Set,
    Tuple,
)
from astroid.util import safe_infer

ACCEPTED_ITERABLES_FOR_SAMPLE = (List, Set, Tuple)


def _clone_node_with_lineno(node, parent, lineno):
    if isinstance(node, EvaluatedObject):
        node = node.original
    cls = node.__class__
    other_fields = node._other_fields
    _astroid_fields = node._astroid_fields
    init_params = {
        "lineno": lineno,
        "col_offset": node.col_offset,
        "parent": parent,
        "end_lineno": node.end_lineno,
        "end_col_offset": node.end_col_offset,
    }
    postinit_params = {param: getattr(node, param) for param in _astroid_fields}
    if other_fields:
        init_params.update({param: getattr(node, param) for param in other_fields})
    new_node = cls(**init_params)
    if hasattr(node, "postinit") and _astroid_fields:
        new_node.postinit(**postinit_params)
    return new_node


def infer_random_sample(node, context: InferenceContext | None = None):
    if len(node.args) != 2:
        raise UseInferenceDefault

    inferred_length = safe_infer(node.args[1], context=context)
    if not isinstance(inferred_length, Const):
        raise UseInferenceDefault
    if not isinstance(inferred_length.value, int):
        raise UseInferenceDefault

    inferred_sequence = safe_infer(node.args[0], context=context)
    if not inferred_sequence:
        raise UseInferenceDefault

    if not isinstance(inferred_sequence, ACCEPTED_ITERABLES_FOR_SAMPLE):
        raise UseInferenceDefault

    if inferred_length.value > len(inferred_sequence.elts):
        # In this case, this will raise a ValueError
        raise UseInferenceDefault

    try:
        elts = random.sample(inferred_sequence.elts, inferred_length.value)
    except ValueError as exc:
        raise UseInferenceDefault from exc

    new_node = List(
        lineno=node.lineno,
        col_offset=node.col_offset,
        parent=node.scope(),
        end_lineno=node.end_lineno,
        end_col_offset=node.end_col_offset,
    )
    new_elts = [
        _clone_node_with_lineno(elt, parent=new_node, lineno=new_node.lineno)
        for elt in elts
    ]
    new_node.postinit(new_elts)
    return iter((new_node,))


def _looks_like_random_sample(node) -> bool:
    func = node.func
    if isinstance(func, Attribute):
        return func.attrname == "sample"
    if isinstance(func, Name):
        return func.name == "sample"
    return False


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        Call, inference_tip(infer_random_sample), _looks_like_random_sample
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_random.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from astroid import context, inference_tip, nodes
from astroid.brain.helpers import register_module_extender
from astroid.builder import _extract_single_node, parse
from astroid.const import PY39_PLUS, PY311_PLUS
from astroid.manager import AstroidManager


def _re_transform() -> nodes.Module:
    # The RegexFlag enum exposes all its entries by updating globals()
    # In 3.6-3.10 all flags come from sre_compile
    # On 3.11+ all flags come from re._compiler
    if PY311_PLUS:
        import_compiler = "import re._compiler as _compiler"
    else:
        import_compiler = "import sre_compile as _compiler"
    return parse(
        f"""
    {import_compiler}
    NOFLAG = 0
    ASCII = _compiler.SRE_FLAG_ASCII
    IGNORECASE = _compiler.SRE_FLAG_IGNORECASE
    LOCALE = _compiler.SRE_FLAG_LOCALE
    UNICODE = _compiler.SRE_FLAG_UNICODE
    MULTILINE = _compiler.SRE_FLAG_MULTILINE
    DOTALL = _compiler.SRE_FLAG_DOTALL
    VERBOSE = _compiler.SRE_FLAG_VERBOSE
    TEMPLATE = _compiler.SRE_FLAG_TEMPLATE
    DEBUG = _compiler.SRE_FLAG_DEBUG
    A = ASCII
    I = IGNORECASE
    L = LOCALE
    U = UNICODE
    M = MULTILINE
    S = DOTALL
    X = VERBOSE
    T = TEMPLATE
    """
    )


CLASS_GETITEM_TEMPLATE = """
@classmethod
def __class_getitem__(cls, item):
    return cls
"""


def _looks_like_pattern_or_match(node: nodes.Call) -> bool:
    """Check for re.Pattern or re.Match call in stdlib.

    Match these patterns from stdlib/re.py
    ```py
    Pattern = type(...)
    Match = type(...)
    ```
    """
    return (
        node.root().name == "re"
        and isinstance(node.func, nodes.Name)
        and node.func.name == "type"
        and isinstance(node.parent, nodes.Assign)
        and len(node.parent.targets) == 1
        and isinstance(node.parent.targets[0], nodes.AssignName)
        and node.parent.targets[0].name in {"Pattern", "Match"}
    )


def infer_pattern_match(node: nodes.Call, ctx: context.InferenceContext | None = None):
    """Infer re.Pattern and re.Match as classes.

    For PY39+ add `__class_getitem__`.
    """
    class_def = nodes.ClassDef(
        name=node.parent.targets[0].name,
        lineno=node.lineno,
        col_offset=node.col_offset,
        parent=node.parent,
        end_lineno=node.end_lineno,
        end_col_offset=node.end_col_offset,
    )
    if PY39_PLUS:
        func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
        class_def.locals["__class_getitem__"] = [func_to_add]
    return iter([class_def])


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "re", _re_transform)
    manager.register_transform(
        nodes.Call, inference_tip(infer_pattern_match), _looks_like_pattern_or_match
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_re.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from astroid import context, inference_tip, nodes
from astroid.brain.helpers import register_module_extender
from astroid.builder import _extract_single_node, parse
from astroid.const import PY39_PLUS
from astroid.manager import AstroidManager


def _regex_transform() -> nodes.Module:
    """The RegexFlag enum exposes all its entries by updating globals().

    We hard-code the flags for now.
    # pylint: disable-next=line-too-long
    See https://github.com/mrabarnett/mrab-regex/blob/2022.10.31/regex_3/regex.py#L200
    """
    return parse(
        """
    A = ASCII = 0x80          # Assume ASCII locale.
    B = BESTMATCH = 0x1000    # Best fuzzy match.
    D = DEBUG = 0x200         # Print parsed pattern.
    E = ENHANCEMATCH = 0x8000 # Attempt to improve the fit after finding the first
                              # fuzzy match.
    F = FULLCASE = 0x4000     # Unicode full case-folding.
    I = IGNORECASE = 0x2      # Ignore case.
    L = LOCALE = 0x4          # Assume current 8-bit locale.
    M = MULTILINE = 0x8       # Make anchors look for newline.
    P = POSIX = 0x10000       # POSIX-style matching (leftmost longest).
    R = REVERSE = 0x400       # Search backwards.
    S = DOTALL = 0x10         # Make dot match newline.
    U = UNICODE = 0x20        # Assume Unicode locale.
    V0 = VERSION0 = 0x2000    # Old legacy behaviour.
    DEFAULT_VERSION = V0
    V1 = VERSION1 = 0x100     # New enhanced behaviour.
    W = WORD = 0x800          # Default Unicode word breaks.
    X = VERBOSE = 0x40        # Ignore whitespace and comments.
    T = TEMPLATE = 0x1        # Template (present because re module has it).
    """
    )


CLASS_GETITEM_TEMPLATE = """
@classmethod
def __class_getitem__(cls, item):
    return cls
"""


def _looks_like_pattern_or_match(node: nodes.Call) -> bool:
    """Check for regex.Pattern or regex.Match call in stdlib.

    Match these patterns from stdlib/re.py
    ```py
    Pattern = type(...)
    Match = type(...)
    ```
    """
    return (
        node.root().name == "regex.regex"
        and isinstance(node.func, nodes.Name)
        and node.func.name == "type"
        and isinstance(node.parent, nodes.Assign)
        and len(node.parent.targets) == 1
        and isinstance(node.parent.targets[0], nodes.AssignName)
        and node.parent.targets[0].name in {"Pattern", "Match"}
    )


def infer_pattern_match(node: nodes.Call, ctx: context.InferenceContext | None = None):
    """Infer regex.Pattern and regex.Match as classes.

    For PY39+ add `__class_getitem__`.
    """
    class_def = nodes.ClassDef(
        name=node.parent.targets[0].name,
        lineno=node.lineno,
        col_offset=node.col_offset,
        parent=node.parent,
        end_lineno=node.end_lineno,
        end_col_offset=node.end_col_offset,
    )
    if PY39_PLUS:
        func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
        class_def.locals["__class_getitem__"] = [func_to_add]
    return iter([class_def])


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "regex", _regex_transform)
    manager.register_transform(
        nodes.Call, inference_tip(infer_pattern_match), _looks_like_pattern_or_match
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_regex.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Astroid hooks for responses.

It might need to be manually updated from the public methods of
:class:`responses.RequestsMock`.

See: https://github.com/getsentry/responses/blob/master/responses.py
"""
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def responses_funcs():
    return parse(
        """
        DELETE = "DELETE"
        GET = "GET"
        HEAD = "HEAD"
        OPTIONS = "OPTIONS"
        PATCH = "PATCH"
        POST = "POST"
        PUT = "PUT"
        response_callback = None

        def reset():
            return

        def add(
            method=None,  # method or ``Response``
            url=None,
            body="",
            adding_headers=None,
            *args,
            **kwargs
        ):
            return

        def add_passthru(prefix):
            return

        def remove(method_or_response=None, url=None):
            return

        def replace(method_or_response=None, url=None, body="", *args, **kwargs):
            return

        def add_callback(
            method, url, callback, match_querystring=False, content_type="text/plain"
        ):
            return

        calls = []

        def __enter__():
            return

        def __exit__(type, value, traceback):
            success = type is None
            return success

        def activate(func):
            return func

        def start():
            return

        def stop(allow_assert=True):
            return
        """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "responses", responses_funcs)

back/venvmsg/Lib/site-packages/astroid/brain/brain_responses.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for scipy.signal module."""
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def scipy_signal():
    return parse(
        """
    # different functions defined in scipy.signals

    def barthann(M, sym=True):
        return numpy.ndarray([0])

    def bartlett(M, sym=True):
        return numpy.ndarray([0])

    def blackman(M, sym=True):
        return numpy.ndarray([0])

    def blackmanharris(M, sym=True):
        return numpy.ndarray([0])

    def bohman(M, sym=True):
        return numpy.ndarray([0])

    def boxcar(M, sym=True):
        return numpy.ndarray([0])

    def chebwin(M, at, sym=True):
        return numpy.ndarray([0])

    def cosine(M, sym=True):
        return numpy.ndarray([0])

    def exponential(M, center=None, tau=1.0, sym=True):
        return numpy.ndarray([0])

    def flattop(M, sym=True):
        return numpy.ndarray([0])

    def gaussian(M, std, sym=True):
        return numpy.ndarray([0])

    def general_gaussian(M, p, sig, sym=True):
        return numpy.ndarray([0])

    def hamming(M, sym=True):
        return numpy.ndarray([0])

    def hann(M, sym=True):
        return numpy.ndarray([0])

    def hanning(M, sym=True):
        return numpy.ndarray([0])

    def impulse2(system, X0=None, T=None, N=None, **kwargs):
        return numpy.ndarray([0]), numpy.ndarray([0])

    def kaiser(M, beta, sym=True):
        return numpy.ndarray([0])

    def nuttall(M, sym=True):
        return numpy.ndarray([0])

    def parzen(M, sym=True):
        return numpy.ndarray([0])

    def slepian(M, width, sym=True):
        return numpy.ndarray([0])

    def step2(system, X0=None, T=None, N=None, **kwargs):
        return numpy.ndarray([0]), numpy.ndarray([0])

    def triang(M, sym=True):
        return numpy.ndarray([0])

    def tukey(M, alpha=0.5, sym=True):
        return numpy.ndarray([0])
        """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "scipy.signal", scipy_signal)

back/venvmsg/Lib/site-packages/astroid/brain/brain_scipy_signal.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for the signal library.

The signal module generates the 'Signals', 'Handlers' and 'Sigmasks' IntEnums
dynamically using the IntEnum._convert() classmethod, which modifies the module
globals. Astroid is unable to handle this type of code.

Without these hooks, the following are erroneously triggered by Pylint:
    * E1101: Module 'signal' has no 'Signals' member (no-member)
    * E1101: Module 'signal' has no 'Handlers' member (no-member)
    * E1101: Module 'signal' has no 'Sigmasks' member (no-member)

These enums are defined slightly differently depending on the user's operating
system and platform. These platform differences should follow the current
Python typeshed stdlib `signal.pyi` stub file, available at:

* https://github.com/python/typeshed/blob/master/stdlib/signal.pyi

Note that the enum.auto() values defined here for the Signals, Handlers and
Sigmasks IntEnums are just dummy integer values, and do not correspond to the
actual standard signal numbers - which may vary depending on the system.
"""


import sys

from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def _signals_enums_transform():
    """Generates the AST for 'Signals', 'Handlers' and 'Sigmasks' IntEnums."""
    return parse(_signals_enum() + _handlers_enum() + _sigmasks_enum())


def _signals_enum() -> str:
    """Generates the source code for the Signals int enum."""
    signals_enum = """
    import enum
    class Signals(enum.IntEnum):
        SIGABRT   = enum.auto()
        SIGEMT    = enum.auto()
        SIGFPE    = enum.auto()
        SIGILL    = enum.auto()
        SIGINFO   = enum.auto()
        SIGINT    = enum.auto()
        SIGSEGV   = enum.auto()
        SIGTERM   = enum.auto()
    """
    if sys.platform != "win32":
        signals_enum += """
        SIGALRM   = enum.auto()
        SIGBUS    = enum.auto()
        SIGCHLD   = enum.auto()
        SIGCONT   = enum.auto()
        SIGHUP    = enum.auto()
        SIGIO     = enum.auto()
        SIGIOT    = enum.auto()
        SIGKILL   = enum.auto()
        SIGPIPE   = enum.auto()
        SIGPROF   = enum.auto()
        SIGQUIT   = enum.auto()
        SIGSTOP   = enum.auto()
        SIGSYS    = enum.auto()
        SIGTRAP   = enum.auto()
        SIGTSTP   = enum.auto()
        SIGTTIN   = enum.auto()
        SIGTTOU   = enum.auto()
        SIGURG    = enum.auto()
        SIGUSR1   = enum.auto()
        SIGUSR2   = enum.auto()
        SIGVTALRM = enum.auto()
        SIGWINCH  = enum.auto()
        SIGXCPU   = enum.auto()
        SIGXFSZ   = enum.auto()
        """
    if sys.platform == "win32":
        signals_enum += """
        SIGBREAK  = enum.auto()
        """
    if sys.platform not in ("darwin", "win32"):
        signals_enum += """
        SIGCLD    = enum.auto()
        SIGPOLL   = enum.auto()
        SIGPWR    = enum.auto()
        SIGRTMAX  = enum.auto()
        SIGRTMIN  = enum.auto()
        """
    return signals_enum


def _handlers_enum() -> str:
    """Generates the source code for the Handlers int enum."""
    return """
    import enum
    class Handlers(enum.IntEnum):
        SIG_DFL = enum.auto()
        SIG_IGN = eunm.auto()
    """


def _sigmasks_enum() -> str:
    """Generates the source code for the Sigmasks int enum."""
    if sys.platform != "win32":
        return """
    import enum
    class Sigmasks(enum.IntEnum):
        SIG_BLOCK   = enum.auto()
        SIG_UNBLOCK = enum.auto()
        SIG_SETMASK = enum.auto()
        """
    return ""


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "signal", _signals_enums_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_signal.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for six module."""

from textwrap import dedent

from astroid import nodes
from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder
from astroid.exceptions import (
    AstroidBuildingError,
    AttributeInferenceError,
    InferenceError,
)
from astroid.manager import AstroidManager

SIX_ADD_METACLASS = "six.add_metaclass"
SIX_WITH_METACLASS = "six.with_metaclass"


def default_predicate(line):
    return line.strip()


def _indent(text, prefix, predicate=default_predicate) -> str:
    """Adds 'prefix' to the beginning of selected lines in 'text'.

    If 'predicate' is provided, 'prefix' will only be added to the lines
    where 'predicate(line)' is True. If 'predicate' is not provided,
    it will default to adding 'prefix' to all non-empty lines that do not
    consist solely of whitespace characters.
    """

    def prefixed_lines():
        for line in text.splitlines(True):
            yield prefix + line if predicate(line) else line

    return "".join(prefixed_lines())


_IMPORTS = """
import _io
cStringIO = _io.StringIO
filter = filter
from itertools import filterfalse
input = input
from sys import intern
map = map
range = range
from importlib import reload
reload_module = lambda module: reload(module)
from functools import reduce
from shlex import quote as shlex_quote
from io import StringIO
from collections import UserDict, UserList, UserString
xrange = range
zip = zip
from itertools import zip_longest
import builtins
import configparser
import copyreg
import _dummy_thread
import http.cookiejar as http_cookiejar
import http.cookies as http_cookies
import html.entities as html_entities
import html.parser as html_parser
import http.client as http_client
import http.server as http_server
BaseHTTPServer = CGIHTTPServer = SimpleHTTPServer = http.server
import pickle as cPickle
import queue
import reprlib
import socketserver
import _thread
import winreg
import xmlrpc.server as xmlrpc_server
import xmlrpc.client as xmlrpc_client
import urllib.robotparser as urllib_robotparser
import email.mime.multipart as email_mime_multipart
import email.mime.nonmultipart as email_mime_nonmultipart
import email.mime.text as email_mime_text
import email.mime.base as email_mime_base
import urllib.parse as urllib_parse
import urllib.error as urllib_error
import tkinter
import tkinter.dialog as tkinter_dialog
import tkinter.filedialog as tkinter_filedialog
import tkinter.scrolledtext as tkinter_scrolledtext
import tkinter.simpledialog as tkinder_simpledialog
import tkinter.tix as tkinter_tix
import tkinter.ttk as tkinter_ttk
import tkinter.constants as tkinter_constants
import tkinter.dnd as tkinter_dnd
import tkinter.colorchooser as tkinter_colorchooser
import tkinter.commondialog as tkinter_commondialog
import tkinter.filedialog as tkinter_tkfiledialog
import tkinter.font as tkinter_font
import tkinter.messagebox as tkinter_messagebox
import urllib
import urllib.request as urllib_request
import urllib.robotparser as urllib_robotparser
import urllib.parse as urllib_parse
import urllib.error as urllib_error
"""


def six_moves_transform():
    code = dedent(
        """
    class Moves(object):
    {}
    moves = Moves()
    """
    ).format(_indent(_IMPORTS, "    "))
    module = AstroidBuilder(AstroidManager()).string_build(code)
    module.name = "six.moves"
    return module


def _six_fail_hook(modname):
    """Fix six.moves imports due to the dynamic nature of this
    class.

    Construct a pseudo-module which contains all the necessary imports
    for six

    :param modname: Name of failed module
    :type modname: str

    :return: An astroid module
    :rtype: nodes.Module
    """

    attribute_of = modname != "six.moves" and modname.startswith("six.moves")
    if modname != "six.moves" and not attribute_of:
        raise AstroidBuildingError(modname=modname)
    module = AstroidBuilder(AstroidManager()).string_build(_IMPORTS)
    module.name = "six.moves"
    if attribute_of:
        # Facilitate import of submodules in Moves
        start_index = len(module.name)
        attribute = modname[start_index:].lstrip(".").replace(".", "_")
        try:
            import_attr = module.getattr(attribute)[0]
        except AttributeInferenceError as exc:
            raise AstroidBuildingError(modname=modname) from exc
        if isinstance(import_attr, nodes.Import):
            submodule = AstroidManager().ast_from_module_name(import_attr.names[0][0])
            return submodule
    # Let dummy submodule imports pass through
    # This will cause an Uninferable result, which is okay
    return module


def _looks_like_decorated_with_six_add_metaclass(node) -> bool:
    if not node.decorators:
        return False

    for decorator in node.decorators.nodes:
        if not isinstance(decorator, nodes.Call):
            continue
        if decorator.func.as_string() == SIX_ADD_METACLASS:
            return True
    return False


def transform_six_add_metaclass(node):  # pylint: disable=inconsistent-return-statements
    """Check if the given class node is decorated with *six.add_metaclass*.

    If so, inject its argument as the metaclass of the underlying class.
    """
    if not node.decorators:
        return

    for decorator in node.decorators.nodes:
        if not isinstance(decorator, nodes.Call):
            continue

        try:
            func = next(decorator.func.infer())
        except (InferenceError, StopIteration):
            continue
        if func.qname() == SIX_ADD_METACLASS and decorator.args:
            metaclass = decorator.args[0]
            node._metaclass = metaclass
            return node
    return


def _looks_like_nested_from_six_with_metaclass(node) -> bool:
    if len(node.bases) != 1:
        return False
    base = node.bases[0]
    if not isinstance(base, nodes.Call):
        return False
    try:
        if hasattr(base.func, "expr"):
            # format when explicit 'six.with_metaclass' is used
            mod = base.func.expr.name
            func = base.func.attrname
            func = f"{mod}.{func}"
        else:
            # format when 'with_metaclass' is used directly (local import from six)
            # check reference module to avoid 'with_metaclass' name clashes
            mod = base.parent.parent
            import_from = mod.locals["with_metaclass"][0]
            func = f"{import_from.modname}.{base.func.name}"
    except (AttributeError, KeyError, IndexError):
        return False
    return func == SIX_WITH_METACLASS


def transform_six_with_metaclass(node):
    """Check if the given class node is defined with *six.with_metaclass*.

    If so, inject its argument as the metaclass of the underlying class.
    """
    call = node.bases[0]
    node._metaclass = call.args[0]
    return node


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "six", six_moves_transform)
    register_module_extender(
        manager, "requests.packages.urllib3.packages.six", six_moves_transform
    )
    manager.register_failed_import_hook(_six_fail_hook)
    manager.register_transform(
        nodes.ClassDef,
        transform_six_add_metaclass,
        _looks_like_decorated_with_six_add_metaclass,
    )
    manager.register_transform(
        nodes.ClassDef,
        transform_six_with_metaclass,
        _looks_like_nested_from_six_with_metaclass,
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_six.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def _session_transform():
    return parse(
        """
    from sqlalchemy.orm.session import Session

    class sessionmaker:
        def __init__(
            self,
            bind=None,
            class_=Session,
            autoflush=True,
            autocommit=False,
            expire_on_commit=True,
            info=None,
            **kw
        ):
            return

        def __call__(self, **local_kw):
            return Session()

        def configure(self, **new_kw):
            return

        return Session()
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "sqlalchemy.orm.session", _session_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_sqlalchemy.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for the ssl library."""

from astroid import parse
from astroid.brain.helpers import register_module_extender
from astroid.const import PY310_PLUS
from astroid.manager import AstroidManager


def _verifyflags_enum() -> str:
    enum = """
    class VerifyFlags(_IntFlag):
        VERIFY_DEFAULT = 0
        VERIFY_CRL_CHECK_LEAF = 1
        VERIFY_CRL_CHECK_CHAIN = 2
        VERIFY_X509_STRICT = 3
        VERIFY_X509_TRUSTED_FIRST = 4"""
    if PY310_PLUS:
        enum += """
        VERIFY_ALLOW_PROXY_CERTS = 5
        VERIFY_X509_PARTIAL_CHAIN = 6
        """
    return enum


def _options_enum() -> str:
    enum = """
    class Options(_IntFlag):
        OP_ALL = 1
        OP_NO_SSLv2 = 2
        OP_NO_SSLv3 = 3
        OP_NO_TLSv1 = 4
        OP_NO_TLSv1_1 = 5
        OP_NO_TLSv1_2 = 6
        OP_NO_TLSv1_3 = 7
        OP_CIPHER_SERVER_PREFERENCE = 8
        OP_SINGLE_DH_USE = 9
        OP_SINGLE_ECDH_USE = 10
        OP_NO_COMPRESSION = 11
        OP_NO_TICKET = 12
        OP_NO_RENEGOTIATION = 13
        OP_ENABLE_MIDDLEBOX_COMPAT = 14"""
    return enum


def ssl_transform():
    return parse(
        """
    # Import necessary for conversion of objects defined in C into enums
    from enum import IntEnum as _IntEnum, IntFlag as _IntFlag

    from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
    from _ssl import _SSLContext, MemoryBIO
    from _ssl import (
        SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError,
        SSLSyscallError, SSLEOFError,
        )
    from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
    from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj
    from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes
    try:
        from _ssl import RAND_egd
    except ImportError:
        # LibreSSL does not provide RAND_egd
        pass
    from _ssl import (OP_ALL, OP_CIPHER_SERVER_PREFERENCE,
                      OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3,
                      OP_NO_TLSv1, OP_NO_TLSv1_1, OP_NO_TLSv1_2,
                      OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE)

    from _ssl import (ALERT_DESCRIPTION_ACCESS_DENIED, ALERT_DESCRIPTION_BAD_CERTIFICATE,
                      ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE,
                      ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE,
                      ALERT_DESCRIPTION_BAD_RECORD_MAC,
                      ALERT_DESCRIPTION_CERTIFICATE_EXPIRED,
                      ALERT_DESCRIPTION_CERTIFICATE_REVOKED,
                      ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN,
                      ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE,
                      ALERT_DESCRIPTION_CLOSE_NOTIFY, ALERT_DESCRIPTION_DECODE_ERROR,
                      ALERT_DESCRIPTION_DECOMPRESSION_FAILURE,
                      ALERT_DESCRIPTION_DECRYPT_ERROR,
                      ALERT_DESCRIPTION_HANDSHAKE_FAILURE,
                      ALERT_DESCRIPTION_ILLEGAL_PARAMETER,
                      ALERT_DESCRIPTION_INSUFFICIENT_SECURITY,
                      ALERT_DESCRIPTION_INTERNAL_ERROR,
                      ALERT_DESCRIPTION_NO_RENEGOTIATION,
                      ALERT_DESCRIPTION_PROTOCOL_VERSION,
                      ALERT_DESCRIPTION_RECORD_OVERFLOW,
                      ALERT_DESCRIPTION_UNEXPECTED_MESSAGE,
                      ALERT_DESCRIPTION_UNKNOWN_CA,
                      ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY,
                      ALERT_DESCRIPTION_UNRECOGNIZED_NAME,
                      ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE,
                      ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION,
                      ALERT_DESCRIPTION_USER_CANCELLED)
    from _ssl import (SSL_ERROR_EOF, SSL_ERROR_INVALID_ERROR_CODE, SSL_ERROR_SSL,
                      SSL_ERROR_SYSCALL, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_READ,
                      SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_ZERO_RETURN)
    from _ssl import VERIFY_CRL_CHECK_CHAIN, VERIFY_CRL_CHECK_LEAF, VERIFY_DEFAULT, VERIFY_X509_STRICT
    from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN
    from _ssl import _OPENSSL_API_VERSION
    from _ssl import PROTOCOL_SSLv23, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2
    from _ssl import PROTOCOL_TLS, PROTOCOL_TLS_CLIENT, PROTOCOL_TLS_SERVER

    class AlertDescription(_IntEnum):
        ALERT_DESCRIPTION_ACCESS_DENIED = 0
        ALERT_DESCRIPTION_BAD_CERTIFICATE = 1
        ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE = 2
        ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE = 3
        ALERT_DESCRIPTION_BAD_RECORD_MAC = 4
        ALERT_DESCRIPTION_CERTIFICATE_EXPIRED = 5
        ALERT_DESCRIPTION_CERTIFICATE_REVOKED = 6
        ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN = 7
        ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE = 8
        ALERT_DESCRIPTION_CLOSE_NOTIFY = 9
        ALERT_DESCRIPTION_DECODE_ERROR = 10
        ALERT_DESCRIPTION_DECOMPRESSION_FAILURE = 11
        ALERT_DESCRIPTION_DECRYPT_ERROR = 12
        ALERT_DESCRIPTION_HANDSHAKE_FAILURE = 13
        ALERT_DESCRIPTION_ILLEGAL_PARAMETER = 14
        ALERT_DESCRIPTION_INSUFFICIENT_SECURITY = 15
        ALERT_DESCRIPTION_INTERNAL_ERROR = 16
        ALERT_DESCRIPTION_NO_RENEGOTIATION = 17
        ALERT_DESCRIPTION_PROTOCOL_VERSION = 18
        ALERT_DESCRIPTION_RECORD_OVERFLOW = 19
        ALERT_DESCRIPTION_UNEXPECTED_MESSAGE = 20
        ALERT_DESCRIPTION_UNKNOWN_CA = 21
        ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY = 22
        ALERT_DESCRIPTION_UNRECOGNIZED_NAME = 23
        ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE = 24
        ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION = 25
        ALERT_DESCRIPTION_USER_CANCELLED = 26

    class SSLErrorNumber(_IntEnum):
        SSL_ERROR_EOF = 0
        SSL_ERROR_INVALID_ERROR_CODE = 1
        SSL_ERROR_SSL = 2
        SSL_ERROR_SYSCALL = 3
        SSL_ERROR_WANT_CONNECT = 4
        SSL_ERROR_WANT_READ = 5
        SSL_ERROR_WANT_WRITE = 6
        SSL_ERROR_WANT_X509_LOOKUP = 7
        SSL_ERROR_ZERO_RETURN = 8

    class VerifyMode(_IntEnum):
        CERT_NONE = 0
        CERT_OPTIONAL = 1
        CERT_REQUIRED = 2
    """
        + _verifyflags_enum()
        + _options_enum()
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "ssl", ssl_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_ssl.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

import textwrap

from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.const import PY39_PLUS, PY310_PLUS, PY311_PLUS
from astroid.manager import AstroidManager


def _subprocess_transform():
    communicate = (bytes("string", "ascii"), bytes("string", "ascii"))
    communicate_signature = "def communicate(self, input=None, timeout=None)"
    args = """\
        self, args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None,
        preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None,
        universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True,
        start_new_session=False, pass_fds=(), *, encoding=None, errors=None, text=None"""

    if PY39_PLUS:
        args += ", user=None, group=None, extra_groups=None, umask=-1"
    if PY310_PLUS:
        args += ", pipesize=-1"
    if PY311_PLUS:
        args += ", process_group=None"

    init = f"""
        def __init__({args}):
            pass"""
    wait_signature = "def wait(self, timeout=None)"
    ctx_manager = """
        def __enter__(self): return self
        def __exit__(self, *args): pass
    """
    py3_args = "args = []"

    check_output_signature = """
    check_output(
        args, *,
        stdin=None,
        stderr=None,
        shell=False,
        cwd=None,
        encoding=None,
        errors=None,
        universal_newlines=False,
        timeout=None,
        env=None,
        text=None,
        restore_signals=True,
        preexec_fn=None,
        pass_fds=(),
        input=None,
        bufsize=0,
        executable=None,
        close_fds=False,
        startupinfo=None,
        creationflags=0,
        start_new_session=False
    ):
    """.strip()

    code = textwrap.dedent(
        f"""
    def {check_output_signature}
        if universal_newlines:
            return ""
        return b""

    class Popen(object):
        returncode = pid = 0
        stdin = stdout = stderr = file()
        {py3_args}

        {communicate_signature}:
            return {communicate!r}
        {wait_signature}:
            return self.returncode
        def poll(self):
            return self.returncode
        def send_signal(self, signal):
            pass
        def terminate(self):
            pass
        def kill(self):
            pass
        {ctx_manager}
       """
    )
    if PY39_PLUS:
        code += """
    @classmethod
    def __class_getitem__(cls, item):
        pass
        """

    init_lines = textwrap.dedent(init).splitlines()
    indented_init = "\n".join(" " * 4 + line for line in init_lines)
    code += indented_init
    return parse(code)


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "subprocess", _subprocess_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_subprocess.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def _thread_transform():
    return parse(
        """
    class lock(object):
        def acquire(self, blocking=True, timeout=-1):
            return False
        def release(self):
            pass
        def __enter__(self):
            return True
        def __exit__(self, *args):
            pass
        def locked(self):
            return False

    def Lock(*args, **kwargs):
        return lock()
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "threading", _thread_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_threading.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Astroid hooks for type support.

Starting from python3.9, type object behaves as it had __class_getitem__ method.
However it was not possible to simply add this method inside type's body, otherwise
all types would also have this method. In this case it would have been possible
to write str[int].
Guido Van Rossum proposed a hack to handle this in the interpreter:
https://github.com/python/cpython/blob/67e394562d67cbcd0ac8114e5439494e7645b8f5/Objects/abstract.c#L181-L184

This brain follows the same logic. It is no wise to add permanently the __class_getitem__ method
to the type object. Instead we choose to add it only in the case of a subscript node
which inside name node is type.
Doing this type[int] is allowed whereas str[int] is not.

Thanks to Lukasz Langa for fruitful discussion.
"""

from __future__ import annotations

from astroid import extract_node, inference_tip, nodes
from astroid.const import PY39_PLUS
from astroid.context import InferenceContext
from astroid.exceptions import UseInferenceDefault
from astroid.manager import AstroidManager


def _looks_like_type_subscript(node) -> bool:
    """
    Try to figure out if a Name node is used inside a type related subscript.

    :param node: node to check
    :type node: astroid.nodes.node_classes.NodeNG
    :return: whether the node is a Name node inside a type related subscript
    """
    if isinstance(node, nodes.Name) and isinstance(node.parent, nodes.Subscript):
        return node.name == "type"
    return False


def infer_type_sub(node, context: InferenceContext | None = None):
    """
    Infer a type[...] subscript.

    :param node: node to infer
    :type node: astroid.nodes.node_classes.NodeNG
    :return: the inferred node
    :rtype: nodes.NodeNG
    """
    node_scope, _ = node.scope().lookup("type")
    if not isinstance(node_scope, nodes.Module) or node_scope.qname() != "builtins":
        raise UseInferenceDefault()
    class_src = """
    class type:
        def __class_getitem__(cls, key):
            return cls
     """
    node = extract_node(class_src)
    return node.infer(context=context)


def register(manager: AstroidManager) -> None:
    if PY39_PLUS:
        manager.register_transform(
            nodes.Name, inference_tip(infer_type_sub), _looks_like_type_subscript
        )

back/venvmsg/Lib/site-packages/astroid/brain/brain_type.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for typing.py support."""

from __future__ import annotations

import textwrap
import typing
from collections.abc import Iterator
from functools import partial
from typing import Final

from astroid import context, extract_node, inference_tip
from astroid.brain.helpers import register_module_extender
from astroid.builder import AstroidBuilder, _extract_single_node
from astroid.const import PY39_PLUS, PY312_PLUS
from astroid.exceptions import (
    AstroidSyntaxError,
    AttributeInferenceError,
    InferenceError,
    UseInferenceDefault,
)
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import (
    Assign,
    AssignName,
    Attribute,
    Call,
    Const,
    JoinedStr,
    Name,
    NodeNG,
    Subscript,
    Tuple,
)
from astroid.nodes.scoped_nodes import ClassDef, FunctionDef

TYPING_TYPEVARS = {"TypeVar", "NewType"}
TYPING_TYPEVARS_QUALIFIED: Final = {
    "typing.TypeVar",
    "typing.NewType",
    "typing_extensions.TypeVar",
}
TYPING_TYPEDDICT_QUALIFIED: Final = {"typing.TypedDict", "typing_extensions.TypedDict"}
TYPING_TYPE_TEMPLATE = """
class Meta(type):
    def __getitem__(self, item):
        return self

    @property
    def __args__(self):
        return ()

class {0}(metaclass=Meta):
    pass
"""
TYPING_MEMBERS = set(getattr(typing, "__all__", []))

TYPING_ALIAS = frozenset(
    (
        "typing.Hashable",
        "typing.Awaitable",
        "typing.Coroutine",
        "typing.AsyncIterable",
        "typing.AsyncIterator",
        "typing.Iterable",
        "typing.Iterator",
        "typing.Reversible",
        "typing.Sized",
        "typing.Container",
        "typing.Collection",
        "typing.Callable",
        "typing.AbstractSet",
        "typing.MutableSet",
        "typing.Mapping",
        "typing.MutableMapping",
        "typing.Sequence",
        "typing.MutableSequence",
        "typing.ByteString",
        "typing.Tuple",
        "typing.List",
        "typing.Deque",
        "typing.Set",
        "typing.FrozenSet",
        "typing.MappingView",
        "typing.KeysView",
        "typing.ItemsView",
        "typing.ValuesView",
        "typing.ContextManager",
        "typing.AsyncContextManager",
        "typing.Dict",
        "typing.DefaultDict",
        "typing.OrderedDict",
        "typing.Counter",
        "typing.ChainMap",
        "typing.Generator",
        "typing.AsyncGenerator",
        "typing.Type",
        "typing.Pattern",
        "typing.Match",
    )
)

CLASS_GETITEM_TEMPLATE = """
@classmethod
def __class_getitem__(cls, item):
    return cls
"""


def looks_like_typing_typevar_or_newtype(node) -> bool:
    func = node.func
    if isinstance(func, Attribute):
        return func.attrname in TYPING_TYPEVARS
    if isinstance(func, Name):
        return func.name in TYPING_TYPEVARS
    return False


def infer_typing_typevar_or_newtype(
    node: Call, context_itton: context.InferenceContext | None = None
) -> Iterator[ClassDef]:
    """Infer a typing.TypeVar(...) or typing.NewType(...) call."""
    try:
        func = next(node.func.infer(context=context_itton))
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc

    if func.qname() not in TYPING_TYPEVARS_QUALIFIED:
        raise UseInferenceDefault
    if not node.args:
        raise UseInferenceDefault
    # Cannot infer from a dynamic class name (f-string)
    if isinstance(node.args[0], JoinedStr):
        raise UseInferenceDefault

    typename = node.args[0].as_string().strip("'")
    try:
        node = extract_node(TYPING_TYPE_TEMPLATE.format(typename))
    except AstroidSyntaxError as exc:
        raise InferenceError from exc
    return node.infer(context=context_itton)


def _looks_like_typing_subscript(node) -> bool:
    """Try to figure out if a Subscript node *might* be a typing-related subscript."""
    if isinstance(node, Name):
        return node.name in TYPING_MEMBERS
    if isinstance(node, Attribute):
        return node.attrname in TYPING_MEMBERS
    if isinstance(node, Subscript):
        return _looks_like_typing_subscript(node.value)
    return False


def infer_typing_attr(
    node: Subscript, ctx: context.InferenceContext | None = None
) -> Iterator[ClassDef]:
    """Infer a typing.X[...] subscript."""
    try:
        value = next(node.value.infer())  # type: ignore[union-attr] # value shouldn't be None for Subscript.
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc

    if not value.qname().startswith("typing.") or value.qname() in TYPING_ALIAS:
        # If typing subscript belongs to an alias handle it separately.
        raise UseInferenceDefault

    if isinstance(value, ClassDef) and value.qname() in {
        "typing.Generic",
        "typing.Annotated",
        "typing_extensions.Annotated",
    }:
        # typing.Generic and typing.Annotated (PY39) are subscriptable
        # through __class_getitem__. Since astroid can't easily
        # infer the native methods, replace them for an easy inference tip
        func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
        value.locals["__class_getitem__"] = [func_to_add]
        if (
            isinstance(node.parent, ClassDef)
            and node in node.parent.bases
            and getattr(node.parent, "__cache", None)
        ):
            # node.parent.slots is evaluated and cached before the inference tip
            # is first applied. Remove the last result to allow a recalculation of slots
            cache = node.parent.__cache  # type: ignore[attr-defined] # Unrecognized getattr
            if cache.get(node.parent.slots) is not None:
                del cache[node.parent.slots]
        return iter([value])

    node = extract_node(TYPING_TYPE_TEMPLATE.format(value.qname().split(".")[-1]))
    return node.infer(context=ctx)


def _looks_like_typedDict(  # pylint: disable=invalid-name
    node: FunctionDef | ClassDef,
) -> bool:
    """Check if node is TypedDict FunctionDef."""
    return node.qname() in TYPING_TYPEDDICT_QUALIFIED


def infer_old_typedDict(  # pylint: disable=invalid-name
    node: ClassDef, ctx: context.InferenceContext | None = None
) -> Iterator[ClassDef]:
    func_to_add = _extract_single_node("dict")
    node.locals["__call__"] = [func_to_add]
    return iter([node])


def infer_typedDict(  # pylint: disable=invalid-name
    node: FunctionDef, ctx: context.InferenceContext | None = None
) -> Iterator[ClassDef]:
    """Replace TypedDict FunctionDef with ClassDef."""
    class_def = ClassDef(
        name="TypedDict",
        lineno=node.lineno,
        col_offset=node.col_offset,
        parent=node.parent,
        end_lineno=node.end_lineno,
        end_col_offset=node.end_col_offset,
    )
    class_def.postinit(bases=[extract_node("dict")], body=[], decorators=None)
    func_to_add = _extract_single_node("dict")
    class_def.locals["__call__"] = [func_to_add]
    return iter([class_def])


def _looks_like_typing_alias(node: Call) -> bool:
    """
    Returns True if the node corresponds to a call to _alias function.

    For example :

    MutableSet = _alias(collections.abc.MutableSet, T)

    :param node: call node
    """
    return (
        isinstance(node.func, Name)
        # TODO: remove _DeprecatedGenericAlias when Py3.14 min
        and node.func.name in {"_alias", "_DeprecatedGenericAlias"}
        and (
            # _alias function works also for builtins object such as list and dict
            isinstance(node.args[0], (Attribute, Name))
        )
    )


def _forbid_class_getitem_access(node: ClassDef) -> None:
    """Disable the access to __class_getitem__ method for the node in parameters."""

    def full_raiser(origin_func, attr, *args, **kwargs):
        """
        Raises an AttributeInferenceError in case of access to __class_getitem__ method.
        Otherwise, just call origin_func.
        """
        if attr == "__class_getitem__":
            raise AttributeInferenceError("__class_getitem__ access is not allowed")
        return origin_func(attr, *args, **kwargs)

    try:
        node.getattr("__class_getitem__")
        # If we are here, then we are sure to modify an object that does have
        # __class_getitem__ method (which origin is the protocol defined in
        # collections module) whereas the typing module considers it should not.
        # We do not want __class_getitem__ to be found in the classdef
        partial_raiser = partial(full_raiser, node.getattr)
        node.getattr = partial_raiser
    except AttributeInferenceError:
        pass


def infer_typing_alias(
    node: Call, ctx: context.InferenceContext | None = None
) -> Iterator[ClassDef]:
    """
    Infers the call to _alias function
    Insert ClassDef, with same name as aliased class,
    in mro to simulate _GenericAlias.

    :param node: call node
    :param context: inference context

    # TODO: evaluate if still necessary when Py3.12 is minimum
    """
    if (
        not isinstance(node.parent, Assign)
        or not len(node.parent.targets) == 1
        or not isinstance(node.parent.targets[0], AssignName)
    ):
        raise UseInferenceDefault
    try:
        res = next(node.args[0].infer(context=ctx))
    except StopIteration as e:
        raise InferenceError(node=node.args[0], context=ctx) from e

    assign_name = node.parent.targets[0]

    class_def = ClassDef(
        name=assign_name.name,
        lineno=assign_name.lineno,
        col_offset=assign_name.col_offset,
        parent=node.parent,
        end_lineno=assign_name.end_lineno,
        end_col_offset=assign_name.end_col_offset,
    )
    if isinstance(res, ClassDef):
        # Only add `res` as base if it's a `ClassDef`
        # This isn't the case for `typing.Pattern` and `typing.Match`
        class_def.postinit(bases=[res], body=[], decorators=None)

    maybe_type_var = node.args[1]
    if (
        not PY39_PLUS
        and not (isinstance(maybe_type_var, Tuple) and not maybe_type_var.elts)
        or PY39_PLUS
        and isinstance(maybe_type_var, Const)
        and maybe_type_var.value > 0
    ):
        # If typing alias is subscriptable, add `__class_getitem__` to ClassDef
        func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
        class_def.locals["__class_getitem__"] = [func_to_add]
    else:
        # If not, make sure that `__class_getitem__` access is forbidden.
        # This is an issue in cases where the aliased class implements it,
        # but the typing alias isn't subscriptable. E.g., `typing.ByteString` for PY39+
        _forbid_class_getitem_access(class_def)

    # Avoid re-instantiating this class every time it's seen
    node._explicit_inference = lambda node, context: iter([class_def])
    return iter([class_def])


def _looks_like_special_alias(node: Call) -> bool:
    """Return True if call is for Tuple or Callable alias.

    In PY37 and PY38 the call is to '_VariadicGenericAlias' with 'tuple' as
    first argument. In PY39+ it is replaced by a call to '_TupleType'.

    PY37: Tuple = _VariadicGenericAlias(tuple, (), inst=False, special=True)
    PY39: Tuple = _TupleType(tuple, -1, inst=False, name='Tuple')

    PY37: Callable = _VariadicGenericAlias(collections.abc.Callable, (), special=True)
    PY39: Callable = _CallableType(collections.abc.Callable, 2)
    """
    return isinstance(node.func, Name) and (
        not PY39_PLUS
        and node.func.name == "_VariadicGenericAlias"
        and (
            isinstance(node.args[0], Name)
            and node.args[0].name == "tuple"
            or isinstance(node.args[0], Attribute)
            and node.args[0].as_string() == "collections.abc.Callable"
        )
        or PY39_PLUS
        and (
            node.func.name == "_TupleType"
            and isinstance(node.args[0], Name)
            and node.args[0].name == "tuple"
            or node.func.name == "_CallableType"
            and isinstance(node.args[0], Attribute)
            and node.args[0].as_string() == "collections.abc.Callable"
        )
    )


def infer_special_alias(
    node: Call, ctx: context.InferenceContext | None = None
) -> Iterator[ClassDef]:
    """Infer call to tuple alias as new subscriptable class typing.Tuple."""
    if not (
        isinstance(node.parent, Assign)
        and len(node.parent.targets) == 1
        and isinstance(node.parent.targets[0], AssignName)
    ):
        raise UseInferenceDefault
    try:
        res = next(node.args[0].infer(context=ctx))
    except StopIteration as e:
        raise InferenceError(node=node.args[0], context=ctx) from e

    assign_name = node.parent.targets[0]
    class_def = ClassDef(
        name=assign_name.name,
        parent=node.parent,
        lineno=assign_name.lineno,
        col_offset=assign_name.col_offset,
        end_lineno=assign_name.end_lineno,
        end_col_offset=assign_name.end_col_offset,
    )
    class_def.postinit(bases=[res], body=[], decorators=None)
    func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE)
    class_def.locals["__class_getitem__"] = [func_to_add]
    return iter([class_def])


def _looks_like_typing_cast(node: Call) -> bool:
    return isinstance(node, Call) and (
        isinstance(node.func, Name)
        and node.func.name == "cast"
        or isinstance(node.func, Attribute)
        and node.func.attrname == "cast"
    )


def infer_typing_cast(
    node: Call, ctx: context.InferenceContext | None = None
) -> Iterator[NodeNG]:
    """Infer call to cast() returning same type as casted-from var."""
    if not isinstance(node.func, (Name, Attribute)):
        raise UseInferenceDefault

    try:
        func = next(node.func.infer(context=ctx))
    except (InferenceError, StopIteration) as exc:
        raise UseInferenceDefault from exc
    if (
        not isinstance(func, FunctionDef)
        or func.qname() != "typing.cast"
        or len(node.args) != 2
    ):
        raise UseInferenceDefault

    return node.args[1].infer(context=ctx)


def _typing_transform():
    return AstroidBuilder(AstroidManager()).string_build(
        textwrap.dedent(
            """
    class Generic:
        @classmethod
        def __class_getitem__(cls, item):  return cls
    class ParamSpec: ...
    class ParamSpecArgs: ...
    class ParamSpecKwargs: ...
    class TypeAlias: ...
    class Type:
        @classmethod
        def __class_getitem__(cls, item):  return cls
    class TypeVar:
        @classmethod
        def __class_getitem__(cls, item):  return cls
    class TypeVarTuple: ...
    """
        )
    )


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        Call,
        inference_tip(infer_typing_typevar_or_newtype),
        looks_like_typing_typevar_or_newtype,
    )
    manager.register_transform(
        Subscript, inference_tip(infer_typing_attr), _looks_like_typing_subscript
    )
    manager.register_transform(
        Call, inference_tip(infer_typing_cast), _looks_like_typing_cast
    )

    if PY39_PLUS:
        manager.register_transform(
            FunctionDef, inference_tip(infer_typedDict), _looks_like_typedDict
        )
    else:
        manager.register_transform(
            ClassDef, inference_tip(infer_old_typedDict), _looks_like_typedDict
        )

    manager.register_transform(
        Call, inference_tip(infer_typing_alias), _looks_like_typing_alias
    )
    manager.register_transform(
        Call, inference_tip(infer_special_alias), _looks_like_special_alias
    )

    if PY312_PLUS:
        register_module_extender(manager, "typing", _typing_transform)

back/venvmsg/Lib/site-packages/astroid/brain/brain_typing.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for unittest module."""
from astroid.brain.helpers import register_module_extender
from astroid.builder import parse
from astroid.manager import AstroidManager


def IsolatedAsyncioTestCaseImport():
    """
    In the unittest package, the IsolatedAsyncioTestCase class is imported lazily.

    I.E. only when the ``__getattr__`` method of the unittest module is called with
    'IsolatedAsyncioTestCase' as argument. Thus the IsolatedAsyncioTestCase
    is not imported statically (during import time).
    This function mocks a classical static import of the IsolatedAsyncioTestCase.

    (see https://github.com/pylint-dev/pylint/issues/4060)
    """
    return parse(
        """
    from .async_case import IsolatedAsyncioTestCase
    """
    )


def register(manager: AstroidManager) -> None:
    register_module_extender(manager, "unittest", IsolatedAsyncioTestCaseImport)

back/venvmsg/Lib/site-packages/astroid/brain/brain_unittest.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Astroid hooks for the UUID module."""
from astroid.manager import AstroidManager
from astroid.nodes.node_classes import Const
from astroid.nodes.scoped_nodes import ClassDef


def _patch_uuid_class(node: ClassDef) -> None:
    # The .int member is patched using __dict__
    node.locals["int"] = [Const(0, parent=node)]


def register(manager: AstroidManager) -> None:
    manager.register_transform(
        ClassDef, _patch_uuid_class, lambda node: node.qname() == "uuid.UUID"
    )

back/venvmsg/Lib/site-packages/astroid/brain/brain_uuid.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from collections.abc import Callable

from astroid.manager import AstroidManager
from astroid.nodes.scoped_nodes import Module


def register_module_extender(
    manager: AstroidManager, module_name: str, get_extension_mod: Callable[[], Module]
) -> None:
    def transform(node: Module) -> None:
        extension_module = get_extension_mod()
        for name, objs in extension_module.locals.items():
            node.locals[name] = objs
            for obj in objs:
                if obj.parent is extension_module:
                    obj.parent = node

    manager.register_transform(Module, transform, lambda n: n.name == module_name)


# pylint: disable-next=too-many-locals
def register_all_brains(manager: AstroidManager) -> None:
    from astroid.brain import (  # pylint: disable=import-outside-toplevel
        brain_argparse,
        brain_attrs,
        brain_boto3,
        brain_builtin_inference,
        brain_collections,
        brain_crypt,
        brain_ctypes,
        brain_curses,
        brain_dataclasses,
        brain_datetime,
        brain_dateutil,
        brain_fstrings,
        brain_functools,
        brain_gi,
        brain_hashlib,
        brain_http,
        brain_hypothesis,
        brain_io,
        brain_mechanize,
        brain_multiprocessing,
        brain_namedtuple_enum,
        brain_nose,
        brain_numpy_core_einsumfunc,
        brain_numpy_core_fromnumeric,
        brain_numpy_core_function_base,
        brain_numpy_core_multiarray,
        brain_numpy_core_numeric,
        brain_numpy_core_numerictypes,
        brain_numpy_core_umath,
        brain_numpy_ma,
        brain_numpy_ndarray,
        brain_numpy_random_mtrand,
        brain_pathlib,
        brain_pkg_resources,
        brain_pytest,
        brain_qt,
        brain_random,
        brain_re,
        brain_regex,
        brain_responses,
        brain_scipy_signal,
        brain_signal,
        brain_six,
        brain_sqlalchemy,
        brain_ssl,
        brain_subprocess,
        brain_threading,
        brain_type,
        brain_typing,
        brain_unittest,
        brain_uuid,
    )

    brain_argparse.register(manager)
    brain_attrs.register(manager)
    brain_boto3.register(manager)
    brain_builtin_inference.register(manager)
    brain_collections.register(manager)
    brain_crypt.register(manager)
    brain_ctypes.register(manager)
    brain_curses.register(manager)
    brain_dataclasses.register(manager)
    brain_datetime.register(manager)
    brain_dateutil.register(manager)
    brain_fstrings.register(manager)
    brain_functools.register(manager)
    brain_gi.register(manager)
    brain_hashlib.register(manager)
    brain_http.register(manager)
    brain_hypothesis.register(manager)
    brain_io.register(manager)
    brain_mechanize.register(manager)
    brain_multiprocessing.register(manager)
    brain_namedtuple_enum.register(manager)
    brain_nose.register(manager)
    brain_numpy_core_einsumfunc.register(manager)
    brain_numpy_core_fromnumeric.register(manager)
    brain_numpy_core_function_base.register(manager)
    brain_numpy_core_multiarray.register(manager)
    brain_numpy_core_numerictypes.register(manager)
    brain_numpy_core_umath.register(manager)
    brain_numpy_random_mtrand.register(manager)
    brain_numpy_ma.register(manager)
    brain_numpy_ndarray.register(manager)
    brain_numpy_core_numeric.register(manager)
    brain_pathlib.register(manager)
    brain_pkg_resources.register(manager)
    brain_pytest.register(manager)
    brain_qt.register(manager)
    brain_random.register(manager)
    brain_re.register(manager)
    brain_regex.register(manager)
    brain_responses.register(manager)
    brain_scipy_signal.register(manager)
    brain_signal.register(manager)
    brain_six.register(manager)
    brain_sqlalchemy.register(manager)
    brain_ssl.register(manager)
    brain_subprocess.register(manager)
    brain_threading.register(manager)
    brain_type.register(manager)
    brain_typing.register(manager)
    brain_unittest.register(manager)
    brain_uuid.register(manager)

back/venvmsg/Lib/site-packages/astroid/brain/helpers.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""The AstroidBuilder makes astroid from living object and / or from _ast.

The builder is not thread safe and can't be used to parse different sources
at the same time.
"""

from __future__ import annotations

import ast
import os
import textwrap
import types
from collections.abc import Iterator, Sequence
from io import TextIOWrapper
from tokenize import detect_encoding

from astroid import bases, modutils, nodes, raw_building, rebuilder, util
from astroid._ast import ParserModule, get_parser_module
from astroid.exceptions import AstroidBuildingError, AstroidSyntaxError, InferenceError
from astroid.manager import AstroidManager

# The name of the transient function that is used to
# wrap expressions to be extracted when calling
# extract_node.
_TRANSIENT_FUNCTION = "__"

# The comment used to select a statement to be extracted
# when calling extract_node.
_STATEMENT_SELECTOR = "#@"
MISPLACED_TYPE_ANNOTATION_ERROR = "misplaced type annotation"


def open_source_file(filename: str) -> tuple[TextIOWrapper, str, str]:
    # pylint: disable=consider-using-with
    with open(filename, "rb") as byte_stream:
        encoding = detect_encoding(byte_stream.readline)[0]
    stream = open(filename, newline=None, encoding=encoding)
    data = stream.read()
    return stream, encoding, data


def _can_assign_attr(node: nodes.ClassDef, attrname: str | None) -> bool:
    try:
        slots = node.slots()
    except NotImplementedError:
        pass
    else:
        if slots and attrname not in {slot.value for slot in slots}:
            return False
    return node.qname() != "builtins.object"


class AstroidBuilder(raw_building.InspectBuilder):
    """Class for building an astroid tree from source code or from a live module.

    The param *manager* specifies the manager class which should be used.
    If no manager is given, then the default one will be used. The
    param *apply_transforms* determines if the transforms should be
    applied after the tree was built from source or from a live object,
    by default being True.
    """

    def __init__(
        self, manager: AstroidManager | None = None, apply_transforms: bool = True
    ) -> None:
        super().__init__(manager)
        self._apply_transforms = apply_transforms
        if not raw_building.InspectBuilder.bootstrapped:
            raw_building._astroid_bootstrapping()

    def module_build(
        self, module: types.ModuleType, modname: str | None = None
    ) -> nodes.Module:
        """Build an astroid from a living module instance."""
        node = None
        path = getattr(module, "__file__", None)
        loader = getattr(module, "__loader__", None)
        # Prefer the loader to get the source rather than assuming we have a
        # filesystem to read the source file from ourselves.
        if loader:
            modname = modname or module.__name__
            source = loader.get_source(modname)
            if source:
                node = self.string_build(source, modname, path=path)
        if node is None and path is not None:
            path_, ext = os.path.splitext(modutils._path_from_filename(path))
            if ext in {".py", ".pyc", ".pyo"} and os.path.exists(path_ + ".py"):
                node = self.file_build(path_ + ".py", modname)
        if node is None:
            # this is a built-in module
            # get a partial representation by introspection
            node = self.inspect_build(module, modname=modname, path=path)
            if self._apply_transforms:
                # We have to handle transformation by ourselves since the
                # rebuilder isn't called for builtin nodes
                node = self._manager.visit_transforms(node)
        assert isinstance(node, nodes.Module)
        return node

    def file_build(self, path: str, modname: str | None = None) -> nodes.Module:
        """Build astroid from a source code file (i.e. from an ast).

        *path* is expected to be a python source file
        """
        try:
            stream, encoding, data = open_source_file(path)
        except OSError as exc:
            raise AstroidBuildingError(
                "Unable to load file {path}:\n{error}",
                modname=modname,
                path=path,
                error=exc,
            ) from exc
        except (SyntaxError, LookupError) as exc:
            raise AstroidSyntaxError(
                "Python 3 encoding specification error or unknown encoding:\n"
                "{error}",
                modname=modname,
                path=path,
                error=exc,
            ) from exc
        except UnicodeError as exc:  # wrong encoding
            # detect_encoding returns utf-8 if no encoding specified
            raise AstroidBuildingError(
                "Wrong or no encoding specified for {filename}.", filename=path
            ) from exc
        with stream:
            # get module name if necessary
            if modname is None:
                try:
                    modname = ".".join(modutils.modpath_from_file(path))
                except ImportError:
                    modname = os.path.splitext(os.path.basename(path))[0]
            # build astroid representation
            module, builder = self._data_build(data, modname, path)
            return self._post_build(module, builder, encoding)

    def string_build(
        self, data: str, modname: str = "", path: str | None = None
    ) -> nodes.Module:
        """Build astroid from source code string."""
        module, builder = self._data_build(data, modname, path)
        module.file_bytes = data.encode("utf-8")
        return self._post_build(module, builder, "utf-8")

    def _post_build(
        self, module: nodes.Module, builder: rebuilder.TreeRebuilder, encoding: str
    ) -> nodes.Module:
        """Handles encoding and delayed nodes after a module has been built."""
        module.file_encoding = encoding
        self._manager.cache_module(module)
        # post tree building steps after we stored the module in the cache:
        for from_node in builder._import_from_nodes:
            if from_node.modname == "__future__":
                for symbol, _ in from_node.names:
                    module.future_imports.add(symbol)
            self.add_from_names_to_locals(from_node)
        # handle delayed assattr nodes
        for delayed in builder._delayed_assattr:
            self.delayed_assattr(delayed)

        # Visit the transforms
        if self._apply_transforms:
            module = self._manager.visit_transforms(module)
        return module

    def _data_build(
        self, data: str, modname: str, path: str | None
    ) -> tuple[nodes.Module, rebuilder.TreeRebuilder]:
        """Build tree node from data and add some informations."""
        try:
            node, parser_module = _parse_string(data, type_comments=True)
        except (TypeError, ValueError, SyntaxError) as exc:
            raise AstroidSyntaxError(
                "Parsing Python code failed:\n{error}",
                source=data,
                modname=modname,
                path=path,
                error=exc,
            ) from exc

        if path is not None:
            node_file = os.path.abspath(path)
        else:
            node_file = "<?>"
        if modname.endswith(".__init__"):
            modname = modname[:-9]
            package = True
        else:
            package = (
                path is not None
                and os.path.splitext(os.path.basename(path))[0] == "__init__"
            )
        builder = rebuilder.TreeRebuilder(self._manager, parser_module, data)
        module = builder.visit_module(node, modname, node_file, package)
        return module, builder

    def add_from_names_to_locals(self, node: nodes.ImportFrom) -> None:
        """Store imported names to the locals.

        Resort the locals if coming from a delayed node
        """

        def _key_func(node: nodes.NodeNG) -> int:
            return node.fromlineno or 0

        def sort_locals(my_list: list[nodes.NodeNG]) -> None:
            my_list.sort(key=_key_func)

        assert node.parent  # It should always default to the module
        for name, asname in node.names:
            if name == "*":
                try:
                    imported = node.do_import_module()
                except AstroidBuildingError:
                    continue
                for name in imported.public_names():
                    node.parent.set_local(name, node)
                    sort_locals(node.parent.scope().locals[name])  # type: ignore[arg-type]
            else:
                node.parent.set_local(asname or name, node)
                sort_locals(node.parent.scope().locals[asname or name])  # type: ignore[arg-type]

    def delayed_assattr(self, node: nodes.AssignAttr) -> None:
        """Visit a AssAttr node.

        This adds name to locals and handle members definition.
        """
        from astroid import objects  # pylint: disable=import-outside-toplevel

        try:
            for inferred in node.expr.infer():
                if isinstance(inferred, util.UninferableBase):
                    continue
                try:
                    # pylint: disable=unidiomatic-typecheck # We want a narrow check on the
                    # parent type, not all of its subclasses
                    if (
                        type(inferred) == bases.Instance
                        or type(inferred) == objects.ExceptionInstance
                    ):
                        inferred = inferred._proxied
                        iattrs = inferred.instance_attrs
                        if not _can_assign_attr(inferred, node.attrname):
                            continue
                    elif isinstance(inferred, bases.Instance):
                        # Const, Tuple or other containers that inherit from
                        # `Instance`
                        continue
                    elif isinstance(inferred, (bases.Proxy, util.UninferableBase)):
                        continue
                    elif inferred.is_function:
                        iattrs = inferred.instance_attrs
                    else:
                        iattrs = inferred.locals
                except AttributeError:
                    # XXX log error
                    continue
                values = iattrs.setdefault(node.attrname, [])
                if node in values:
                    continue
                values.append(node)
        except InferenceError:
            pass


def build_namespace_package_module(name: str, path: Sequence[str]) -> nodes.Module:
    module = nodes.Module(name, path=path, package=True)
    module.postinit(body=[], doc_node=None)
    return module


def parse(
    code: str,
    module_name: str = "",
    path: str | None = None,
    apply_transforms: bool = True,
) -> nodes.Module:
    """Parses a source string in order to obtain an astroid AST from it.

    :param str code: The code for the module.
    :param str module_name: The name for the module, if any
    :param str path: The path for the module
    :param bool apply_transforms:
        Apply the transforms for the give code. Use it if you
        don't want the default transforms to be applied.
    """
    code = textwrap.dedent(code)
    builder = AstroidBuilder(
        manager=AstroidManager(), apply_transforms=apply_transforms
    )
    return builder.string_build(code, modname=module_name, path=path)


def _extract_expressions(node: nodes.NodeNG) -> Iterator[nodes.NodeNG]:
    """Find expressions in a call to _TRANSIENT_FUNCTION and extract them.

    The function walks the AST recursively to search for expressions that
    are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an
    expression, it completely removes the function call node from the tree,
    replacing it by the wrapped expression inside the parent.

    :param node: An astroid node.
    :type node:  astroid.bases.NodeNG
    :yields: The sequence of wrapped expressions on the modified tree
    expression can be found.
    """
    if (
        isinstance(node, nodes.Call)
        and isinstance(node.func, nodes.Name)
        and node.func.name == _TRANSIENT_FUNCTION
    ):
        real_expr = node.args[0]
        assert node.parent
        real_expr.parent = node.parent
        # Search for node in all _astng_fields (the fields checked when
        # get_children is called) of its parent. Some of those fields may
        # be lists or tuples, in which case the elements need to be checked.
        # When we find it, replace it by real_expr, so that the AST looks
        # like no call to _TRANSIENT_FUNCTION ever took place.
        for name in node.parent._astroid_fields:
            child = getattr(node.parent, name)
            if isinstance(child, list):
                for idx, compound_child in enumerate(child):
                    if compound_child is node:
                        child[idx] = real_expr
            elif child is node:
                setattr(node.parent, name, real_expr)
        yield real_expr
    else:
        for child in node.get_children():
            yield from _extract_expressions(child)


def _find_statement_by_line(node: nodes.NodeNG, line: int) -> nodes.NodeNG | None:
    """Extracts the statement on a specific line from an AST.

    If the line number of node matches line, it will be returned;
    otherwise its children are iterated and the function is called
    recursively.

    :param node: An astroid node.
    :type node: astroid.bases.NodeNG
    :param line: The line number of the statement to extract.
    :type line: int
    :returns: The statement on the line, or None if no statement for the line
      can be found.
    :rtype:  astroid.bases.NodeNG or None
    """
    if isinstance(node, (nodes.ClassDef, nodes.FunctionDef, nodes.MatchCase)):
        # This is an inaccuracy in the AST: the nodes that can be
        # decorated do not carry explicit information on which line
        # the actual definition (class/def), but .fromline seems to
        # be close enough.
        node_line = node.fromlineno
    else:
        node_line = node.lineno

    if node_line == line:
        return node

    for child in node.get_children():
        result = _find_statement_by_line(child, line)
        if result:
            return result

    return None


def extract_node(code: str, module_name: str = "") -> nodes.NodeNG | list[nodes.NodeNG]:
    """Parses some Python code as a module and extracts a designated AST node.

    Statements:
     To extract one or more statement nodes, append #@ to the end of the line

     Examples:
       >>> def x():
       >>>   def y():
       >>>     return 1 #@

       The return statement will be extracted.

       >>> class X(object):
       >>>   def meth(self): #@
       >>>     pass

      The function object 'meth' will be extracted.

    Expressions:
     To extract arbitrary expressions, surround them with the fake
     function call __(...). After parsing, the surrounded expression
     will be returned and the whole AST (accessible via the returned
     node's parent attribute) will look like the function call was
     never there in the first place.

     Examples:
       >>> a = __(1)

       The const node will be extracted.

       >>> def x(d=__(foo.bar)): pass

       The node containing the default argument will be extracted.

       >>> def foo(a, b):
       >>>   return 0 < __(len(a)) < b

       The node containing the function call 'len' will be extracted.

    If no statements or expressions are selected, the last toplevel
    statement will be returned.

    If the selected statement is a discard statement, (i.e. an expression
    turned into a statement), the wrapped expression is returned instead.

    For convenience, singleton lists are unpacked.

    :param str code: A piece of Python code that is parsed as
    a module. Will be passed through textwrap.dedent first.
    :param str module_name: The name of the module.
    :returns: The designated node from the parse tree, or a list of nodes.
    """

    def _extract(node: nodes.NodeNG | None) -> nodes.NodeNG | None:
        if isinstance(node, nodes.Expr):
            return node.value

        return node

    requested_lines: list[int] = []
    for idx, line in enumerate(code.splitlines()):
        if line.strip().endswith(_STATEMENT_SELECTOR):
            requested_lines.append(idx + 1)

    tree = parse(code, module_name=module_name)
    if not tree.body:
        raise ValueError("Empty tree, cannot extract from it")

    extracted: list[nodes.NodeNG | None] = []
    if requested_lines:
        extracted = [_find_statement_by_line(tree, line) for line in requested_lines]

    # Modifies the tree.
    extracted.extend(_extract_expressions(tree))

    if not extracted:
        extracted.append(tree.body[-1])

    extracted = [_extract(node) for node in extracted]
    extracted_without_none = [node for node in extracted if node is not None]
    if len(extracted_without_none) == 1:
        return extracted_without_none[0]
    return extracted_without_none


def _extract_single_node(code: str, module_name: str = "") -> nodes.NodeNG:
    """Call extract_node while making sure that only one value is returned."""
    ret = extract_node(code, module_name)
    if isinstance(ret, list):
        return ret[0]
    return ret


def _parse_string(
    data: str, type_comments: bool = True
) -> tuple[ast.Module, ParserModule]:
    parser_module = get_parser_module(type_comments=type_comments)
    try:
        parsed = parser_module.parse(data + "\n", type_comments=type_comments)
    except SyntaxError as exc:
        # If the type annotations are misplaced for some reason, we do not want
        # to fail the entire parsing of the file, so we need to retry the parsing without
        # type comment support.
        if exc.args[0] != MISPLACED_TYPE_ANNOTATION_ERROR or not type_comments:
            raise

        parser_module = get_parser_module(type_comments=False)
        parsed = parser_module.parse(data + "\n", type_comments=False)
    return parsed, parser_module

back/venvmsg/Lib/site-packages/astroid/builder.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

import enum
import sys

PY38 = sys.version_info[:2] == (3, 8)
PY39_PLUS = sys.version_info >= (3, 9)
PY310_PLUS = sys.version_info >= (3, 10)
PY311_PLUS = sys.version_info >= (3, 11)
PY312_PLUS = sys.version_info >= (3, 12)

WIN32 = sys.platform == "win32"

IS_PYPY = sys.implementation.name == "pypy"
IS_JYTHON = sys.implementation.name == "jython"

# pylint: disable-next=no-member
PYPY_7_3_11_PLUS = IS_PYPY and sys.pypy_version_info >= (7, 3, 11)  # type: ignore[attr-defined]


class Context(enum.Enum):
    Load = 1
    Store = 2
    Del = 3


_EMPTY_OBJECT_MARKER = object()

back/venvmsg/Lib/site-packages/astroid/const.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Classes representing different types of constraints on inference values."""
from __future__ import annotations

import sys
from abc import ABC, abstractmethod
from collections.abc import Iterator
from typing import TYPE_CHECKING, Union

from astroid import nodes, util
from astroid.typing import InferenceResult

if sys.version_info >= (3, 11):
    from typing import Self
else:
    from typing_extensions import Self

if TYPE_CHECKING:
    from astroid import bases

_NameNodes = Union[nodes.AssignAttr, nodes.Attribute, nodes.AssignName, nodes.Name]


class Constraint(ABC):
    """Represents a single constraint on a variable."""

    def __init__(self, node: nodes.NodeNG, negate: bool) -> None:
        self.node = node
        """The node that this constraint applies to."""
        self.negate = negate
        """True if this constraint is negated. E.g., "is not" instead of "is"."""

    @classmethod
    @abstractmethod
    def match(
        cls, node: _NameNodes, expr: nodes.NodeNG, negate: bool = False
    ) -> Self | None:
        """Return a new constraint for node matched from expr, if expr matches
        the constraint pattern.

        If negate is True, negate the constraint.
        """

    @abstractmethod
    def satisfied_by(self, inferred: InferenceResult) -> bool:
        """Return True if this constraint is satisfied by the given inferred value."""


class NoneConstraint(Constraint):
    """Represents an "is None" or "is not None" constraint."""

    CONST_NONE: nodes.Const = nodes.Const(None)

    @classmethod
    def match(
        cls, node: _NameNodes, expr: nodes.NodeNG, negate: bool = False
    ) -> Self | None:
        """Return a new constraint for node matched from expr, if expr matches
        the constraint pattern.

        Negate the constraint based on the value of negate.
        """
        if isinstance(expr, nodes.Compare) and len(expr.ops) == 1:
            left = expr.left
            op, right = expr.ops[0]
            if op in {"is", "is not"} and (
                _matches(left, node) and _matches(right, cls.CONST_NONE)
            ):
                negate = (op == "is" and negate) or (op == "is not" and not negate)
                return cls(node=node, negate=negate)

        return None

    def satisfied_by(self, inferred: InferenceResult) -> bool:
        """Return True if this constraint is satisfied by the given inferred value."""
        # Assume true if uninferable
        if isinstance(inferred, util.UninferableBase):
            return True

        # Return the XOR of self.negate and matches(inferred, self.CONST_NONE)
        return self.negate ^ _matches(inferred, self.CONST_NONE)


def get_constraints(
    expr: _NameNodes, frame: nodes.LocalsDictNodeNG
) -> dict[nodes.If, set[Constraint]]:
    """Returns the constraints for the given expression.

    The returned dictionary maps the node where the constraint was generated to the
    corresponding constraint(s).

    Constraints are computed statically by analysing the code surrounding expr.
    Currently this only supports constraints generated from if conditions.
    """
    current_node: nodes.NodeNG | None = expr
    constraints_mapping: dict[nodes.If, set[Constraint]] = {}
    while current_node is not None and current_node is not frame:
        parent = current_node.parent
        if isinstance(parent, nodes.If):
            branch, _ = parent.locate_child(current_node)
            constraints: set[Constraint] | None = None
            if branch == "body":
                constraints = set(_match_constraint(expr, parent.test))
            elif branch == "orelse":
                constraints = set(_match_constraint(expr, parent.test, invert=True))

            if constraints:
                constraints_mapping[parent] = constraints
        current_node = parent

    return constraints_mapping


ALL_CONSTRAINT_CLASSES = frozenset((NoneConstraint,))
"""All supported constraint types."""


def _matches(node1: nodes.NodeNG | bases.Proxy, node2: nodes.NodeNG) -> bool:
    """Returns True if the two nodes match."""
    if isinstance(node1, nodes.Name) and isinstance(node2, nodes.Name):
        return node1.name == node2.name
    if isinstance(node1, nodes.Attribute) and isinstance(node2, nodes.Attribute):
        return node1.attrname == node2.attrname and _matches(node1.expr, node2.expr)
    if isinstance(node1, nodes.Const) and isinstance(node2, nodes.Const):
        return node1.value == node2.value

    return False


def _match_constraint(
    node: _NameNodes, expr: nodes.NodeNG, invert: bool = False
) -> Iterator[Constraint]:
    """Yields all constraint patterns for node that match."""
    for constraint_cls in ALL_CONSTRAINT_CLASSES:
        constraint = constraint_cls.match(node, expr, invert)
        if constraint:
            yield constraint

back/venvmsg/Lib/site-packages/astroid/constraint.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Various context related utilities, including inference and call contexts."""

from __future__ import annotations

import contextlib
import pprint
from collections.abc import Iterator
from typing import TYPE_CHECKING, Dict, Optional, Sequence, Tuple

from astroid.typing import InferenceResult, SuccessfulInferenceResult

if TYPE_CHECKING:
    from astroid import constraint, nodes
    from astroid.nodes.node_classes import Keyword, NodeNG

_InferenceCache = Dict[
    Tuple["NodeNG", Optional[str], Optional[str], Optional[str]], Sequence["NodeNG"]
]

_INFERENCE_CACHE: _InferenceCache = {}


def _invalidate_cache() -> None:
    _INFERENCE_CACHE.clear()


class InferenceContext:
    """Provide context for inference.

    Store already inferred nodes to save time
    Account for already visited nodes to stop infinite recursion
    """

    __slots__ = (
        "path",
        "lookupname",
        "callcontext",
        "boundnode",
        "extra_context",
        "constraints",
        "_nodes_inferred",
    )

    max_inferred = 100

    def __init__(
        self,
        path: set[tuple[nodes.NodeNG, str | None]] | None = None,
        nodes_inferred: list[int] | None = None,
    ) -> None:
        if nodes_inferred is None:
            self._nodes_inferred = [0]
        else:
            self._nodes_inferred = nodes_inferred

        self.path = path or set()
        """Path of visited nodes and their lookupname.

        Currently this key is ``(node, context.lookupname)``
        """
        self.lookupname: str | None = None
        """The original name of the node.

        e.g.
        foo = 1
        The inference of 'foo' is nodes.Const(1) but the lookup name is 'foo'
        """
        self.callcontext: CallContext | None = None
        """The call arguments and keywords for the given context."""
        self.boundnode: SuccessfulInferenceResult | None = None
        """The bound node of the given context.

        e.g. the bound node of object.__new__(cls) is the object node
        """
        self.extra_context: dict[SuccessfulInferenceResult, InferenceContext] = {}
        """Context that needs to be passed down through call stacks for call arguments."""

        self.constraints: dict[str, dict[nodes.If, set[constraint.Constraint]]] = {}
        """The constraints on nodes."""

    @property
    def nodes_inferred(self) -> int:
        """
        Number of nodes inferred in this context and all its clones/descendents.

        Wrap inner value in a mutable cell to allow for mutating a class
        variable in the presence of __slots__
        """
        return self._nodes_inferred[0]

    @nodes_inferred.setter
    def nodes_inferred(self, value: int) -> None:
        self._nodes_inferred[0] = value

    @property
    def inferred(self) -> _InferenceCache:
        """
        Inferred node contexts to their mapped results.

        Currently the key is ``(node, lookupname, callcontext, boundnode)``
        and the value is tuple of the inferred results
        """
        return _INFERENCE_CACHE

    def push(self, node: nodes.NodeNG) -> bool:
        """Push node into inference path.

        Allows one to see if the given node has already
        been looked at for this inference context
        """
        name = self.lookupname
        if (node, name) in self.path:
            return True

        self.path.add((node, name))
        return False

    def clone(self) -> InferenceContext:
        """Clone inference path.

        For example, each side of a binary operation (BinOp)
        starts with the same context but diverge as each side is inferred
        so the InferenceContext will need be cloned
        """
        # XXX copy lookupname/callcontext ?
        clone = InferenceContext(self.path.copy(), nodes_inferred=self._nodes_inferred)
        clone.callcontext = self.callcontext
        clone.boundnode = self.boundnode
        clone.extra_context = self.extra_context
        clone.constraints = self.constraints.copy()
        return clone

    @contextlib.contextmanager
    def restore_path(self) -> Iterator[None]:
        path = set(self.path)
        yield
        self.path = path

    def is_empty(self) -> bool:
        return (
            not self.path
            and not self.nodes_inferred
            and not self.callcontext
            and not self.boundnode
            and not self.lookupname
            and not self.callcontext
            and not self.extra_context
            and not self.constraints
        )

    def __str__(self) -> str:
        state = (
            f"{field}={pprint.pformat(getattr(self, field), width=80 - len(field))}"
            for field in self.__slots__
        )
        return "{}({})".format(type(self).__name__, ",\n    ".join(state))


class CallContext:
    """Holds information for a call site."""

    __slots__ = ("args", "keywords", "callee")

    def __init__(
        self,
        args: list[NodeNG],
        keywords: list[Keyword] | None = None,
        callee: InferenceResult | None = None,
    ):
        self.args = args  # Call positional arguments
        if keywords:
            arg_value_pairs = [(arg.arg, arg.value) for arg in keywords]
        else:
            arg_value_pairs = []
        self.keywords = arg_value_pairs  # Call keyword arguments
        self.callee = callee  # Function being called


def copy_context(context: InferenceContext | None) -> InferenceContext:
    """Clone a context if given, or return a fresh context."""
    if context is not None:
        return context.clone()

    return InferenceContext()


def bind_context_to_node(
    context: InferenceContext | None, node: SuccessfulInferenceResult
) -> InferenceContext:
    """Give a context a boundnode
    to retrieve the correct function name or attribute value
    with from further inference.

    Do not use an existing context since the boundnode could then
    be incorrectly propagated higher up in the call stack.
    """
    context = copy_context(context)
    context.boundnode = node
    return context

back/venvmsg/Lib/site-packages/astroid/context.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""A few useful function/method decorators."""

from __future__ import annotations

import functools
import inspect
import sys
import warnings
from collections.abc import Callable, Generator
from typing import TypeVar

from astroid import util
from astroid.context import InferenceContext
from astroid.exceptions import InferenceError
from astroid.typing import InferenceResult

if sys.version_info >= (3, 10):
    from typing import ParamSpec
else:
    from typing_extensions import ParamSpec

_R = TypeVar("_R")
_P = ParamSpec("_P")


def path_wrapper(func):
    """Return the given infer function wrapped to handle the path.

    Used to stop inference if the node has already been looked
    at for a given `InferenceContext` to prevent infinite recursion
    """

    @functools.wraps(func)
    def wrapped(
        node, context: InferenceContext | None = None, _func=func, **kwargs
    ) -> Generator:
        """Wrapper function handling context."""
        if context is None:
            context = InferenceContext()
        if context.push(node):
            return

        yielded = set()

        for res in _func(node, context, **kwargs):
            # unproxy only true instance, not const, tuple, dict...
            if res.__class__.__name__ == "Instance":
                ares = res._proxied
            else:
                ares = res
            if ares not in yielded:
                yield res
                yielded.add(ares)

    return wrapped


def yes_if_nothing_inferred(
    func: Callable[_P, Generator[InferenceResult, None, None]]
) -> Callable[_P, Generator[InferenceResult, None, None]]:
    def inner(
        *args: _P.args, **kwargs: _P.kwargs
    ) -> Generator[InferenceResult, None, None]:
        generator = func(*args, **kwargs)

        try:
            yield next(generator)
        except StopIteration:
            # generator is empty
            yield util.Uninferable
            return

        yield from generator

    return inner


def raise_if_nothing_inferred(
    func: Callable[_P, Generator[InferenceResult, None, None]],
) -> Callable[_P, Generator[InferenceResult, None, None]]:
    def inner(
        *args: _P.args, **kwargs: _P.kwargs
    ) -> Generator[InferenceResult, None, None]:
        generator = func(*args, **kwargs)
        try:
            yield next(generator)
        except StopIteration as error:
            # generator is empty
            if error.args:
                raise InferenceError(**error.args[0]) from error
            raise InferenceError(
                "StopIteration raised without any error information."
            ) from error
        except RecursionError as error:
            raise InferenceError(
                f"RecursionError raised with limit {sys.getrecursionlimit()}."
            ) from error

        yield from generator

    return inner


# Expensive decorators only used to emit Deprecation warnings.
# If no other than the default DeprecationWarning are enabled,
# fall back to passthrough implementations.
if util.check_warnings_filter():  # noqa: C901

    def deprecate_default_argument_values(
        astroid_version: str = "3.0", **arguments: str
    ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
        """Decorator which emits a DeprecationWarning if any arguments specified
        are None or not passed at all.

        Arguments should be a key-value mapping, with the key being the argument to check
        and the value being a type annotation as string for the value of the argument.

        To improve performance, only used when DeprecationWarnings other than
        the default one are enabled.
        """
        # Helpful links
        # Decorator for DeprecationWarning: https://stackoverflow.com/a/49802489
        # Typing of stacked decorators: https://stackoverflow.com/a/68290080

        def deco(func: Callable[_P, _R]) -> Callable[_P, _R]:
            """Decorator function."""

            @functools.wraps(func)
            def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:
                """Emit DeprecationWarnings if conditions are met."""

                keys = list(inspect.signature(func).parameters.keys())
                for arg, type_annotation in arguments.items():
                    try:
                        index = keys.index(arg)
                    except ValueError:
                        raise ValueError(
                            f"Can't find argument '{arg}' for '{args[0].__class__.__qualname__}'"
                        ) from None
                    if (
                        # Check kwargs
                        # - if found, check it's not None
                        (arg in kwargs and kwargs[arg] is None)
                        # Check args
                        # - make sure not in kwargs
                        # - len(args) needs to be long enough, if too short
                        #   arg can't be in args either
                        # - args[index] should not be None
                        or arg not in kwargs
                        and (
                            index == -1
                            or len(args) <= index
                            or (len(args) > index and args[index] is None)
                        )
                    ):
                        warnings.warn(
                            f"'{arg}' will be a required argument for "
                            f"'{args[0].__class__.__qualname__}.{func.__name__}'"
                            f" in astroid {astroid_version} "
                            f"('{arg}' should be of type: '{type_annotation}')",
                            DeprecationWarning,
                            stacklevel=2,
                        )
                return func(*args, **kwargs)

            return wrapper

        return deco

    def deprecate_arguments(
        astroid_version: str = "3.0", **arguments: str
    ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
        """Decorator which emits a DeprecationWarning if any arguments specified
        are passed.

        Arguments should be a key-value mapping, with the key being the argument to check
        and the value being a string that explains what to do instead of passing the argument.

        To improve performance, only used when DeprecationWarnings other than
        the default one are enabled.
        """

        def deco(func: Callable[_P, _R]) -> Callable[_P, _R]:
            @functools.wraps(func)
            def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:
                keys = list(inspect.signature(func).parameters.keys())
                for arg, note in arguments.items():
                    try:
                        index = keys.index(arg)
                    except ValueError:
                        raise ValueError(
                            f"Can't find argument '{arg}' for '{args[0].__class__.__qualname__}'"
                        ) from None
                    if arg in kwargs or len(args) > index:
                        warnings.warn(
                            f"The argument '{arg}' for "
                            f"'{args[0].__class__.__qualname__}.{func.__name__}' is deprecated "
                            f"and will be removed in astroid {astroid_version} ({note})",
                            DeprecationWarning,
                            stacklevel=2,
                        )
                return func(*args, **kwargs)

            return wrapper

        return deco

else:

    def deprecate_default_argument_values(
        astroid_version: str = "3.0", **arguments: str
    ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
        """Passthrough decorator to improve performance if DeprecationWarnings are
        disabled.
        """

        def deco(func: Callable[_P, _R]) -> Callable[_P, _R]:
            """Decorator function."""
            return func

        return deco

    def deprecate_arguments(
        astroid_version: str = "3.0", **arguments: str
    ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]:
        """Passthrough decorator to improve performance if DeprecationWarnings are
        disabled.
        """

        def deco(func: Callable[_P, _R]) -> Callable[_P, _R]:
            """Decorator function."""
            return func

        return deco

back/venvmsg/Lib/site-packages/astroid/decorators.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""This module contains exceptions used in the astroid library."""

from __future__ import annotations

from collections.abc import Iterable, Iterator
from typing import TYPE_CHECKING, Any

from astroid.typing import InferenceResult, SuccessfulInferenceResult

if TYPE_CHECKING:
    from astroid import arguments, bases, nodes, objects
    from astroid.context import InferenceContext

__all__ = (
    "AstroidBuildingError",
    "AstroidBuildingException",
    "AstroidError",
    "AstroidImportError",
    "AstroidIndexError",
    "AstroidSyntaxError",
    "AstroidTypeError",
    "AstroidValueError",
    "AttributeInferenceError",
    "DuplicateBasesError",
    "InconsistentMroError",
    "InferenceError",
    "InferenceOverwriteError",
    "MroError",
    "NameInferenceError",
    "NoDefault",
    "NotFoundError",
    "ParentMissingError",
    "ResolveError",
    "StatementMissing",
    "SuperArgumentTypeError",
    "SuperError",
    "TooManyLevelsError",
    "UnresolvableName",
    "UseInferenceDefault",
)


class AstroidError(Exception):
    """Base exception class for all astroid related exceptions.

    AstroidError and its subclasses are structured, intended to hold
    objects representing state when the exception is thrown.  Field
    values are passed to the constructor as keyword-only arguments.
    Each subclass has its own set of standard fields, but use your
    best judgment to decide whether a specific exception instance
    needs more or fewer fields for debugging.  Field values may be
    used to lazily generate the error message: self.message.format()
    will be called with the field names and values supplied as keyword
    arguments.
    """

    def __init__(self, message: str = "", **kws: Any) -> None:
        super().__init__(message)
        self.message = message
        for key, value in kws.items():
            setattr(self, key, value)

    def __str__(self) -> str:
        return self.message.format(**vars(self))


class AstroidBuildingError(AstroidError):
    """Exception class when we are unable to build an astroid representation.

    Standard attributes:
        modname: Name of the module that AST construction failed for.
        error: Exception raised during construction.
    """

    def __init__(
        self,
        message: str = "Failed to import module {modname}.",
        modname: str | None = None,
        error: Exception | None = None,
        source: str | None = None,
        path: str | None = None,
        cls: type | None = None,
        class_repr: str | None = None,
        **kws: Any,
    ) -> None:
        self.modname = modname
        self.error = error
        self.source = source
        self.path = path
        self.cls = cls
        self.class_repr = class_repr
        super().__init__(message, **kws)


class AstroidImportError(AstroidBuildingError):
    """Exception class used when a module can't be imported by astroid."""


class TooManyLevelsError(AstroidImportError):
    """Exception class which is raised when a relative import was beyond the top-level.

    Standard attributes:
        level: The level which was attempted.
        name: the name of the module on which the relative import was attempted.
    """

    def __init__(
        self,
        message: str = "Relative import with too many levels "
        "({level}) for module {name!r}",
        level: int | None = None,
        name: str | None = None,
        **kws: Any,
    ) -> None:
        self.level = level
        self.name = name
        super().__init__(message, **kws)


class AstroidSyntaxError(AstroidBuildingError):
    """Exception class used when a module can't be parsed."""

    def __init__(
        self,
        message: str,
        modname: str | None,
        error: Exception,
        path: str | None,
        source: str | None = None,
    ) -> None:
        super().__init__(message, modname, error, source, path)


class NoDefault(AstroidError):
    """Raised by function's `default_value` method when an argument has
    no default value.

    Standard attributes:
        func: Function node.
        name: Name of argument without a default.
    """

    def __init__(
        self,
        message: str = "{func!r} has no default for {name!r}.",
        func: nodes.FunctionDef | None = None,
        name: str | None = None,
        **kws: Any,
    ) -> None:
        self.func = func
        self.name = name
        super().__init__(message, **kws)


class ResolveError(AstroidError):
    """Base class of astroid resolution/inference error.

    ResolveError is not intended to be raised.

    Standard attributes:
        context: InferenceContext object.
    """

    def __init__(
        self, message: str = "", context: InferenceContext | None = None, **kws: Any
    ) -> None:
        self.context = context
        super().__init__(message, **kws)


class MroError(ResolveError):
    """Error raised when there is a problem with method resolution of a class.

    Standard attributes:
        mros: A sequence of sequences containing ClassDef nodes.
        cls: ClassDef node whose MRO resolution failed.
        context: InferenceContext object.
    """

    def __init__(
        self,
        message: str,
        mros: Iterable[Iterable[nodes.ClassDef]],
        cls: nodes.ClassDef,
        context: InferenceContext | None = None,
        **kws: Any,
    ) -> None:
        self.mros = mros
        self.cls = cls
        self.context = context
        super().__init__(message, **kws)

    def __str__(self) -> str:
        mro_names = ", ".join(f"({', '.join(b.name for b in m)})" for m in self.mros)
        return self.message.format(mros=mro_names, cls=self.cls)


class DuplicateBasesError(MroError):
    """Error raised when there are duplicate bases in the same class bases."""


class InconsistentMroError(MroError):
    """Error raised when a class's MRO is inconsistent."""


class SuperError(ResolveError):
    """Error raised when there is a problem with a *super* call.

    Standard attributes:
        *super_*: The Super instance that raised the exception.
        context: InferenceContext object.
    """

    def __init__(self, message: str, super_: objects.Super, **kws: Any) -> None:
        self.super_ = super_
        super().__init__(message, **kws)

    def __str__(self) -> str:
        return self.message.format(**vars(self.super_))


class InferenceError(ResolveError):  # pylint: disable=too-many-instance-attributes
    """Raised when we are unable to infer a node.

    Standard attributes:
        node: The node inference was called on.
        context: InferenceContext object.
    """

    def __init__(  # pylint: disable=too-many-arguments
        self,
        message: str = "Inference failed for {node!r}.",
        node: InferenceResult | None = None,
        context: InferenceContext | None = None,
        target: InferenceResult | None = None,
        targets: InferenceResult | None = None,
        attribute: str | None = None,
        unknown: InferenceResult | None = None,
        assign_path: list[int] | None = None,
        caller: SuccessfulInferenceResult | None = None,
        stmts: Iterator[InferenceResult] | None = None,
        frame: InferenceResult | None = None,
        call_site: arguments.CallSite | None = None,
        func: InferenceResult | None = None,
        arg: str | None = None,
        positional_arguments: list | None = None,
        unpacked_args: list | None = None,
        keyword_arguments: dict | None = None,
        unpacked_kwargs: dict | None = None,
        **kws: Any,
    ) -> None:
        self.node = node
        self.context = context
        self.target = target
        self.targets = targets
        self.attribute = attribute
        self.unknown = unknown
        self.assign_path = assign_path
        self.caller = caller
        self.stmts = stmts
        self.frame = frame
        self.call_site = call_site
        self.func = func
        self.arg = arg
        self.positional_arguments = positional_arguments
        self.unpacked_args = unpacked_args
        self.keyword_arguments = keyword_arguments
        self.unpacked_kwargs = unpacked_kwargs
        super().__init__(message, **kws)


# Why does this inherit from InferenceError rather than ResolveError?
# Changing it causes some inference tests to fail.
class NameInferenceError(InferenceError):
    """Raised when a name lookup fails, corresponds to NameError.

    Standard attributes:
        name: The name for which lookup failed, as a string.
        scope: The node representing the scope in which the lookup occurred.
        context: InferenceContext object.
    """

    def __init__(
        self,
        message: str = "{name!r} not found in {scope!r}.",
        name: str | None = None,
        scope: nodes.LocalsDictNodeNG | None = None,
        context: InferenceContext | None = None,
        **kws: Any,
    ) -> None:
        self.name = name
        self.scope = scope
        self.context = context
        super().__init__(message, **kws)


class AttributeInferenceError(ResolveError):
    """Raised when an attribute lookup fails, corresponds to AttributeError.

    Standard attributes:
        target: The node for which lookup failed.
        attribute: The attribute for which lookup failed, as a string.
        context: InferenceContext object.
    """

    def __init__(
        self,
        message: str = "{attribute!r} not found on {target!r}.",
        attribute: str = "",
        target: nodes.NodeNG | bases.BaseInstance | None = None,
        context: InferenceContext | None = None,
        mros: list[nodes.ClassDef] | None = None,
        super_: nodes.ClassDef | None = None,
        cls: nodes.ClassDef | None = None,
        **kws: Any,
    ) -> None:
        self.attribute = attribute
        self.target = target
        self.context = context
        self.mros = mros
        self.super_ = super_
        self.cls = cls
        super().__init__(message, **kws)


class UseInferenceDefault(Exception):
    """Exception to be raised in custom inference function to indicate that it
    should go back to the default behaviour.
    """


class _NonDeducibleTypeHierarchy(Exception):
    """Raised when is_subtype / is_supertype can't deduce the relation between two
    types.
    """


class AstroidIndexError(AstroidError):
    """Raised when an Indexable / Mapping does not have an index / key."""

    def __init__(
        self,
        message: str = "",
        node: nodes.NodeNG | bases.Instance | None = None,
        index: nodes.Subscript | None = None,
        context: InferenceContext | None = None,
        **kws: Any,
    ) -> None:
        self.node = node
        self.index = index
        self.context = context
        super().__init__(message, **kws)


class AstroidTypeError(AstroidError):
    """Raised when a TypeError would be expected in Python code."""

    def __init__(
        self,
        message: str = "",
        node: nodes.NodeNG | bases.Instance | None = None,
        index: nodes.Subscript | None = None,
        context: InferenceContext | None = None,
        **kws: Any,
    ) -> None:
        self.node = node
        self.index = index
        self.context = context
        super().__init__(message, **kws)


class AstroidValueError(AstroidError):
    """Raised when a ValueError would be expected in Python code."""


class InferenceOverwriteError(AstroidError):
    """Raised when an inference tip is overwritten.

    Currently only used for debugging.
    """


class ParentMissingError(AstroidError):
    """Raised when a node which is expected to have a parent attribute is missing one.

    Standard attributes:
        target: The node for which the parent lookup failed.
    """

    def __init__(self, target: nodes.NodeNG) -> None:
        self.target = target
        super().__init__(message=f"Parent not found on {target!r}.")


class StatementMissing(ParentMissingError):
    """Raised when a call to node.statement() does not return a node.

    This is because a node in the chain does not have a parent attribute
    and therefore does not return a node for statement().

    Standard attributes:
        target: The node for which the parent lookup failed.
    """

    def __init__(self, target: nodes.NodeNG) -> None:
        super(ParentMissingError, self).__init__(
            message=f"Statement not found on {target!r}"
        )


SuperArgumentTypeError = SuperError
UnresolvableName = NameInferenceError
NotFoundError = AttributeInferenceError
AstroidBuildingException = AstroidBuildingError

back/venvmsg/Lib/site-packages/astroid/exceptions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""_filter_stmts and helper functions.

This method gets used in LocalsDictnodes.NodeNG._scope_lookup.
It is not considered public.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes
from astroid.typing import SuccessfulInferenceResult

if TYPE_CHECKING:
    from astroid.nodes import _base_nodes


def _get_filtered_node_statements(
    base_node: nodes.NodeNG, stmt_nodes: list[nodes.NodeNG]
) -> list[tuple[nodes.NodeNG, nodes.Statement]]:
    statements = [(node, node.statement()) for node in stmt_nodes]
    # Next we check if we have ExceptHandlers that are parent
    # of the underlying variable, in which case the last one survives
    if len(statements) > 1 and all(
        isinstance(stmt, nodes.ExceptHandler) for _, stmt in statements
    ):
        statements = [
            (node, stmt) for node, stmt in statements if stmt.parent_of(base_node)
        ]
    return statements


def _is_from_decorator(node) -> bool:
    """Return whether the given node is the child of a decorator."""
    return any(isinstance(parent, nodes.Decorators) for parent in node.node_ancestors())


def _get_if_statement_ancestor(node: nodes.NodeNG) -> nodes.If | None:
    """Return the first parent node that is an If node (or None)."""
    for parent in node.node_ancestors():
        if isinstance(parent, nodes.If):
            return parent
    return None


def _filter_stmts(
    base_node: _base_nodes.LookupMixIn,
    stmts: list[SuccessfulInferenceResult],
    frame: nodes.LocalsDictNodeNG,
    offset: int,
) -> list[nodes.NodeNG]:
    """Filter the given list of statements to remove ignorable statements.

    If base_node is not a frame itself and the name is found in the inner
    frame locals, statements will be filtered to remove ignorable
    statements according to base_node's location.

    :param stmts: The statements to filter.

    :param frame: The frame that all of the given statements belong to.

    :param offset: The line offset to filter statements up to.

    :returns: The filtered statements.
    """
    # if offset == -1, my actual frame is not the inner frame but its parent
    #
    # class A(B): pass
    #
    # we need this to resolve B correctly
    if offset == -1:
        myframe = base_node.frame().parent.frame()
    else:
        myframe = base_node.frame()
        # If the frame of this node is the same as the statement
        # of this node, then the node is part of a class or
        # a function definition and the frame of this node should be the
        # the upper frame, not the frame of the definition.
        # For more information why this is important,
        # see Pylint issue #295.
        # For example, for 'b', the statement is the same
        # as the frame / scope:
        #
        # def test(b=1):
        #     ...
        if base_node.parent and base_node.statement() is myframe and myframe.parent:
            myframe = myframe.parent.frame()

    mystmt: nodes.Statement | None = None
    if base_node.parent:
        mystmt = base_node.statement()

    # line filtering if we are in the same frame
    #
    # take care node may be missing lineno information (this is the case for
    # nodes inserted for living objects)
    if myframe is frame and mystmt and mystmt.fromlineno is not None:
        assert mystmt.fromlineno is not None, mystmt
        mylineno = mystmt.fromlineno + offset
    else:
        # disabling lineno filtering
        mylineno = 0

    _stmts: list[nodes.NodeNG] = []
    _stmt_parents = []
    statements = _get_filtered_node_statements(base_node, stmts)
    for node, stmt in statements:
        # line filtering is on and we have reached our location, break
        if stmt.fromlineno and stmt.fromlineno > mylineno > 0:
            break
        # Ignore decorators with the same name as the
        # decorated function
        # Fixes issue #375
        if mystmt is stmt and _is_from_decorator(base_node):
            continue
        if node.has_base(base_node):
            break

        if isinstance(node, nodes.EmptyNode):
            # EmptyNode does not have assign_type(), so just add it and move on
            _stmts.append(node)
            continue

        assign_type = node.assign_type()
        _stmts, done = assign_type._get_filtered_stmts(base_node, node, _stmts, mystmt)
        if done:
            break

        optional_assign = assign_type.optional_assign
        if optional_assign and assign_type.parent_of(base_node):
            # we are inside a loop, loop var assignment is hiding previous
            # assignment
            _stmts = [node]
            _stmt_parents = [stmt.parent]
            continue

        if isinstance(assign_type, nodes.NamedExpr):
            # If the NamedExpr is in an if statement we do some basic control flow inference
            if_parent = _get_if_statement_ancestor(assign_type)
            if if_parent:
                # If the if statement is within another if statement we append the node
                # to possible statements
                if _get_if_statement_ancestor(if_parent):
                    optional_assign = False
                    _stmts.append(node)
                    _stmt_parents.append(stmt.parent)
                # Else we assume that it will be evaluated
                else:
                    _stmts = [node]
                    _stmt_parents = [stmt.parent]
            else:
                _stmts = [node]
                _stmt_parents = [stmt.parent]

        # XXX comment various branches below!!!
        try:
            pindex = _stmt_parents.index(stmt.parent)
        except ValueError:
            pass
        else:
            # we got a parent index, this means the currently visited node
            # is at the same block level as a previously visited node
            if _stmts[pindex].assign_type().parent_of(assign_type):
                # both statements are not at the same block level
                continue
            # if currently visited node is following previously considered
            # assignment and both are not exclusive, we can drop the
            # previous one. For instance in the following code ::
            #
            #   if a:
            #     x = 1
            #   else:
            #     x = 2
            #   print x
            #
            # we can't remove neither x = 1 nor x = 2 when looking for 'x'
            # of 'print x'; while in the following ::
            #
            #   x = 1
            #   x = 2
            #   print x
            #
            # we can remove x = 1 when we see x = 2
            #
            # moreover, on loop assignment types, assignment won't
            # necessarily be done if the loop has no iteration, so we don't
            # want to clear previous assignments if any (hence the test on
            # optional_assign)
            if not (optional_assign or nodes.are_exclusive(_stmts[pindex], node)):
                del _stmt_parents[pindex]
                del _stmts[pindex]

        # If base_node and node are exclusive, then we can ignore node
        if nodes.are_exclusive(base_node, node):
            continue

        # An AssignName node overrides previous assignments if:
        #   1. node's statement always assigns
        #   2. node and base_node are in the same block (i.e., has the same parent as base_node)
        if isinstance(node, (nodes.NamedExpr, nodes.AssignName)):
            if isinstance(stmt, nodes.ExceptHandler):
                # If node's statement is an ExceptHandler, then it is the variable
                # bound to the caught exception. If base_node is not contained within
                # the exception handler block, node should override previous assignments;
                # otherwise, node should be ignored, as an exception variable
                # is local to the handler block.
                if stmt.parent_of(base_node):
                    _stmts = []
                    _stmt_parents = []
                else:
                    continue
            elif not optional_assign and mystmt and stmt.parent is mystmt.parent:
                _stmts = []
                _stmt_parents = []
        elif isinstance(node, nodes.DelName):
            # Remove all previously stored assignments
            _stmts = []
            _stmt_parents = []
            continue
        # Add the new assignment
        _stmts.append(node)
        if isinstance(node, nodes.Arguments) or isinstance(
            node.parent, nodes.Arguments
        ):
            # Special case for _stmt_parents when node is a function parameter;
            # in this case, stmt is the enclosing FunctionDef, which is what we
            # want to add to _stmt_parents, not stmt.parent. This case occurs when
            # node is an Arguments node (representing varargs or kwargs parameter),
            # and when node.parent is an Arguments node (other parameters).
            # See issue #180.
            _stmt_parents.append(stmt)
        else:
            _stmt_parents.append(stmt.parent)
    return _stmts

back/venvmsg/Lib/site-packages/astroid/filter_statements.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Various helper utilities."""

from __future__ import annotations

import warnings
from collections.abc import Generator

from astroid import bases, manager, nodes, objects, raw_building, util
from astroid.context import CallContext, InferenceContext
from astroid.exceptions import (
    AstroidTypeError,
    AttributeInferenceError,
    InferenceError,
    MroError,
    _NonDeducibleTypeHierarchy,
)
from astroid.nodes import scoped_nodes
from astroid.typing import InferenceResult
from astroid.util import safe_infer as real_safe_infer


def safe_infer(
    node: nodes.NodeNG | bases.Proxy | util.UninferableBase,
    context: InferenceContext | None = None,
) -> InferenceResult | None:
    # When removing, also remove the real_safe_infer alias
    warnings.warn(
        "Import safe_infer from astroid.util; this shim in astroid.helpers will be removed.",
        DeprecationWarning,
        stacklevel=2,
    )
    return real_safe_infer(node, context=context)


def _build_proxy_class(cls_name: str, builtins: nodes.Module) -> nodes.ClassDef:
    proxy = raw_building.build_class(cls_name)
    proxy.parent = builtins
    return proxy


def _function_type(
    function: nodes.Lambda | nodes.FunctionDef | bases.UnboundMethod,
    builtins: nodes.Module,
) -> nodes.ClassDef:
    if isinstance(function, (scoped_nodes.Lambda, scoped_nodes.FunctionDef)):
        if function.root().name == "builtins":
            cls_name = "builtin_function_or_method"
        else:
            cls_name = "function"
    elif isinstance(function, bases.BoundMethod):
        cls_name = "method"
    else:
        cls_name = "function"
    return _build_proxy_class(cls_name, builtins)


def _object_type(
    node: InferenceResult, context: InferenceContext | None = None
) -> Generator[InferenceResult | None, None, None]:
    astroid_manager = manager.AstroidManager()
    builtins = astroid_manager.builtins_module
    context = context or InferenceContext()

    for inferred in node.infer(context=context):
        if isinstance(inferred, scoped_nodes.ClassDef):
            if inferred.newstyle:
                metaclass = inferred.metaclass(context=context)
                if metaclass:
                    yield metaclass
                    continue
            yield builtins.getattr("type")[0]
        elif isinstance(
            inferred,
            (scoped_nodes.Lambda, bases.UnboundMethod, scoped_nodes.FunctionDef),
        ):
            yield _function_type(inferred, builtins)
        elif isinstance(inferred, scoped_nodes.Module):
            yield _build_proxy_class("module", builtins)
        elif isinstance(inferred, nodes.Unknown):
            raise InferenceError
        elif isinstance(inferred, util.UninferableBase):
            yield inferred
        elif isinstance(inferred, (bases.Proxy, nodes.Slice, objects.Super)):
            yield inferred._proxied
        else:  # pragma: no cover
            raise AssertionError(f"We don't handle {type(inferred)} currently")


def object_type(
    node: InferenceResult, context: InferenceContext | None = None
) -> InferenceResult | None:
    """Obtain the type of the given node.

    This is used to implement the ``type`` builtin, which means that it's
    used for inferring type calls, as well as used in a couple of other places
    in the inference.
    The node will be inferred first, so this function can support all
    sorts of objects, as long as they support inference.
    """

    try:
        types = set(_object_type(node, context))
    except InferenceError:
        return util.Uninferable
    if len(types) > 1 or not types:
        return util.Uninferable
    return next(iter(types))


def _object_type_is_subclass(
    obj_type: InferenceResult | None,
    class_or_seq: list[InferenceResult],
    context: InferenceContext | None = None,
) -> util.UninferableBase | bool:
    if isinstance(obj_type, util.UninferableBase) or not isinstance(
        obj_type, nodes.ClassDef
    ):
        return util.Uninferable

    # Instances are not types
    class_seq = [
        item if not isinstance(item, bases.Instance) else util.Uninferable
        for item in class_or_seq
    ]
    # strict compatibility with issubclass
    # issubclass(type, (object, 1)) evaluates to true
    # issubclass(object, (1, type)) raises TypeError
    for klass in class_seq:
        if isinstance(klass, util.UninferableBase):
            raise AstroidTypeError("arg 2 must be a type or tuple of types")

        for obj_subclass in obj_type.mro():
            if obj_subclass == klass:
                return True
    return False


def object_isinstance(
    node: InferenceResult,
    class_or_seq: list[InferenceResult],
    context: InferenceContext | None = None,
) -> util.UninferableBase | bool:
    """Check if a node 'isinstance' any node in class_or_seq.

    :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
    """
    obj_type = object_type(node, context)
    if isinstance(obj_type, util.UninferableBase):
        return util.Uninferable
    return _object_type_is_subclass(obj_type, class_or_seq, context=context)


def object_issubclass(
    node: nodes.NodeNG,
    class_or_seq: list[InferenceResult],
    context: InferenceContext | None = None,
) -> util.UninferableBase | bool:
    """Check if a type is a subclass of any node in class_or_seq.

    :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
    :raises AstroidError: if the type of the given node cannot be inferred
        or its type's mro doesn't work
    """
    if not isinstance(node, nodes.ClassDef):
        raise TypeError(f"{node} needs to be a ClassDef node")
    return _object_type_is_subclass(node, class_or_seq, context=context)


def has_known_bases(klass, context: InferenceContext | None = None) -> bool:
    """Return whether all base classes of a class could be inferred."""
    try:
        return klass._all_bases_known
    except AttributeError:
        pass
    for base in klass.bases:
        result = real_safe_infer(base, context=context)
        # TODO: check for A->B->A->B pattern in class structure too?
        if (
            not isinstance(result, scoped_nodes.ClassDef)
            or result is klass
            or not has_known_bases(result, context=context)
        ):
            klass._all_bases_known = False
            return False
    klass._all_bases_known = True
    return True


def _type_check(type1, type2) -> bool:
    if not all(map(has_known_bases, (type1, type2))):
        raise _NonDeducibleTypeHierarchy

    if not all([type1.newstyle, type2.newstyle]):
        return False
    try:
        return type1 in type2.mro()[:-1]
    except MroError as e:
        # The MRO is invalid.
        raise _NonDeducibleTypeHierarchy from e


def is_subtype(type1, type2) -> bool:
    """Check if *type1* is a subtype of *type2*."""
    return _type_check(type1=type2, type2=type1)


def is_supertype(type1, type2) -> bool:
    """Check if *type2* is a supertype of *type1*."""
    return _type_check(type1, type2)


def class_instance_as_index(node: bases.Instance) -> nodes.Const | None:
    """Get the value as an index for the given instance.

    If an instance provides an __index__ method, then it can
    be used in some scenarios where an integer is expected,
    for instance when multiplying or subscripting a list.
    """
    context = InferenceContext()
    try:
        for inferred in node.igetattr("__index__", context=context):
            if not isinstance(inferred, bases.BoundMethod):
                continue

            context.boundnode = node
            context.callcontext = CallContext(args=[], callee=inferred)
            for result in inferred.infer_call_result(node, context=context):
                if isinstance(result, nodes.Const) and isinstance(result.value, int):
                    return result
    except InferenceError:
        pass
    return None


def object_len(node, context: InferenceContext | None = None):
    """Infer length of given node object.

    :param Union[nodes.ClassDef, nodes.Instance] node:
    :param node: Node to infer length of

    :raises AstroidTypeError: If an invalid node is returned
        from __len__ method or no __len__ method exists
    :raises InferenceError: If the given node cannot be inferred
        or if multiple nodes are inferred or if the code executed in python
        would result in a infinite recursive check for length
    :rtype int: Integer length of node
    """
    # pylint: disable=import-outside-toplevel; circular import
    from astroid.objects import FrozenSet

    inferred_node = real_safe_infer(node, context=context)

    # prevent self referential length calls from causing a recursion error
    # see https://github.com/pylint-dev/astroid/issues/777
    node_frame = node.frame()
    if (
        isinstance(node_frame, scoped_nodes.FunctionDef)
        and node_frame.name == "__len__"
        and isinstance(inferred_node, bases.Proxy)
        and inferred_node._proxied == node_frame.parent
    ):
        message = (
            "Self referential __len__ function will "
            "cause a RecursionError on line {} of {}".format(
                node.lineno, node.root().file
            )
        )
        raise InferenceError(message)

    if inferred_node is None or isinstance(inferred_node, util.UninferableBase):
        raise InferenceError(node=node)
    if isinstance(inferred_node, nodes.Const) and isinstance(
        inferred_node.value, (bytes, str)
    ):
        return len(inferred_node.value)
    if isinstance(inferred_node, (nodes.List, nodes.Set, nodes.Tuple, FrozenSet)):
        return len(inferred_node.elts)
    if isinstance(inferred_node, nodes.Dict):
        return len(inferred_node.items)

    node_type = object_type(inferred_node, context=context)
    if not node_type:
        raise InferenceError(node=node)

    try:
        len_call = next(node_type.igetattr("__len__", context=context))
    except StopIteration as e:
        raise AstroidTypeError(str(e)) from e
    except AttributeInferenceError as e:
        raise AstroidTypeError(
            f"object of type '{node_type.pytype()}' has no len()"
        ) from e

    inferred = len_call.infer_call_result(node, context)
    if isinstance(inferred, util.UninferableBase):
        raise InferenceError(node=node, context=context)
    result_of_len = next(inferred, None)
    if (
        isinstance(result_of_len, nodes.Const)
        and result_of_len.pytype() == "builtins.int"
    ):
        return result_of_len.value
    if (
        result_of_len is None
        or isinstance(result_of_len, bases.Instance)
        and result_of_len.is_subtype_of("builtins.int")
    ):
        # Fake a result as we don't know the arguments of the instance call.
        return 0
    raise AstroidTypeError(
        f"'{result_of_len}' object cannot be interpreted as an integer"
    )


def _higher_function_scope(node: nodes.NodeNG) -> nodes.FunctionDef | None:
    """Search for the first function which encloses the given
    scope.

    This can be used for looking up in that function's
    scope, in case looking up in a lower scope for a particular
    name fails.

    :param node: A scope node.
    :returns:
        ``None``, if no parent function scope was found,
        otherwise an instance of :class:`astroid.nodes.scoped_nodes.Function`,
        which encloses the given node.
    """
    current = node
    while current.parent and not isinstance(current.parent, nodes.FunctionDef):
        current = current.parent
    if current and current.parent:
        return current.parent
    return None

back/venvmsg/Lib/site-packages/astroid/helpers.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Transform utilities (filters and decorator)."""

from __future__ import annotations

from collections import OrderedDict
from collections.abc import Generator
from typing import Any, TypeVar

from astroid.context import InferenceContext
from astroid.exceptions import InferenceOverwriteError, UseInferenceDefault
from astroid.nodes import NodeNG
from astroid.typing import (
    InferenceResult,
    InferFn,
    TransformFn,
)

_cache: OrderedDict[
    tuple[InferFn[Any], NodeNG, InferenceContext | None], list[InferenceResult]
] = OrderedDict()

_CURRENTLY_INFERRING: set[tuple[InferFn[Any], NodeNG]] = set()

_NodesT = TypeVar("_NodesT", bound=NodeNG)


def clear_inference_tip_cache() -> None:
    """Clear the inference tips cache."""
    _cache.clear()


def _inference_tip_cached(func: InferFn[_NodesT]) -> InferFn[_NodesT]:
    """Cache decorator used for inference tips."""

    def inner(
        node: _NodesT,
        context: InferenceContext | None = None,
        **kwargs: Any,
    ) -> Generator[InferenceResult, None, None]:
        partial_cache_key = (func, node)
        if partial_cache_key in _CURRENTLY_INFERRING:
            # If through recursion we end up trying to infer the same
            # func + node we raise here.
            _CURRENTLY_INFERRING.remove(partial_cache_key)
            raise UseInferenceDefault
        if context is not None and context.is_empty():
            # Fresh, empty contexts will defeat the cache.
            context = None
        try:
            yield from _cache[func, node, context]
            return
        except KeyError:
            # Recursion guard with a partial cache key.
            # Using the full key causes a recursion error on PyPy.
            # It's a pragmatic compromise to avoid so much recursive inference
            # with slightly different contexts while still passing the simple
            # test cases included with this commit.
            _CURRENTLY_INFERRING.add(partial_cache_key)
            try:
                # May raise UseInferenceDefault
                result = _cache[func, node, context] = list(
                    func(node, context, **kwargs)
                )
            except Exception as e:
                # Suppress the KeyError from the cache miss.
                raise e from None
            finally:
                # Remove recursion guard.
                try:
                    _CURRENTLY_INFERRING.remove(partial_cache_key)
                except KeyError:
                    pass  # Recursion may beat us to the punch.

                if len(_cache) > 64:
                    _cache.popitem(last=False)

        # https://github.com/pylint-dev/pylint/issues/8686
        yield from result  # pylint: disable=used-before-assignment

    return inner


def inference_tip(
    infer_function: InferFn[_NodesT], raise_on_overwrite: bool = False
) -> TransformFn[_NodesT]:
    """Given an instance specific inference function, return a function to be
    given to AstroidManager().register_transform to set this inference function.

    :param bool raise_on_overwrite: Raise an `InferenceOverwriteError`
        if the inference tip will overwrite another. Used for debugging

    Typical usage

    .. sourcecode:: python

       AstroidManager().register_transform(Call, inference_tip(infer_named_tuple),
                                  predicate)

    .. Note::

        Using an inference tip will override
        any previously set inference tip for the given
        node. Use a predicate in the transform to prevent
        excess overwrites.
    """

    def transform(
        node: _NodesT, infer_function: InferFn[_NodesT] = infer_function
    ) -> _NodesT:
        if (
            raise_on_overwrite
            and node._explicit_inference is not None
            and node._explicit_inference is not infer_function
        ):
            raise InferenceOverwriteError(
                "Inference already set to {existing_inference}. "
                "Trying to overwrite with {new_inference} for {node}".format(
                    existing_inference=infer_function,
                    new_inference=node._explicit_inference,
                    node=node,
                )
            )
        node._explicit_inference = _inference_tip_cached(infer_function)
        return node

    return transform

back/venvmsg/Lib/site-packages/astroid/inference_tip.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import abc
import enum
import importlib
import importlib.machinery
import importlib.util
import os
import pathlib
import sys
import types
import warnings
import zipimport
from collections.abc import Iterator, Sequence
from pathlib import Path
from typing import Any, Literal, NamedTuple, Protocol

from astroid.const import PY310_PLUS
from astroid.modutils import EXT_LIB_DIRS

from . import util


# The MetaPathFinder protocol comes from typeshed, which says:
# Intentionally omits one deprecated and one optional method of `importlib.abc.MetaPathFinder`
class _MetaPathFinder(Protocol):
    def find_spec(
        self,
        fullname: str,
        path: Sequence[str] | None,
        target: types.ModuleType | None = ...,
    ) -> importlib.machinery.ModuleSpec | None:
        ...  # pragma: no cover


class ModuleType(enum.Enum):
    """Python module types used for ModuleSpec."""

    C_BUILTIN = enum.auto()
    C_EXTENSION = enum.auto()
    PKG_DIRECTORY = enum.auto()
    PY_CODERESOURCE = enum.auto()
    PY_COMPILED = enum.auto()
    PY_FROZEN = enum.auto()
    PY_RESOURCE = enum.auto()
    PY_SOURCE = enum.auto()
    PY_ZIPMODULE = enum.auto()
    PY_NAMESPACE = enum.auto()


_MetaPathFinderModuleTypes: dict[str, ModuleType] = {
    # Finders created by setuptools editable installs
    "_EditableFinder": ModuleType.PY_SOURCE,
    "_EditableNamespaceFinder": ModuleType.PY_NAMESPACE,
    # Finders create by six
    "_SixMetaPathImporter": ModuleType.PY_SOURCE,
}

_EditableFinderClasses: set[str] = {
    "_EditableFinder",
    "_EditableNamespaceFinder",
}


class ModuleSpec(NamedTuple):
    """Defines a class similar to PEP 420's ModuleSpec.

    A module spec defines a name of a module, its type, location
    and where submodules can be found, if the module is a package.
    """

    name: str
    type: ModuleType | None
    location: str | None = None
    origin: str | None = None
    submodule_search_locations: Sequence[str] | None = None


class Finder:
    """A finder is a class which knows how to find a particular module."""

    def __init__(self, path: Sequence[str] | None = None) -> None:
        self._path = path or sys.path

    @abc.abstractmethod
    def find_module(
        self,
        modname: str,
        module_parts: Sequence[str],
        processed: list[str],
        submodule_path: Sequence[str] | None,
    ) -> ModuleSpec | None:
        """Find the given module.

        Each finder is responsible for each protocol of finding, as long as
        they all return a ModuleSpec.

        :param modname: The module which needs to be searched.
        :param module_parts: It should be a list of strings,
                                  where each part contributes to the module's
                                  namespace.
        :param processed: What parts from the module parts were processed
                               so far.
        :param submodule_path: A list of paths where the module
                                    can be looked into.
        :returns: A ModuleSpec, describing how and where the module was found,
                  None, otherwise.
        """

    def contribute_to_path(
        self, spec: ModuleSpec, processed: list[str]
    ) -> Sequence[str] | None:
        """Get a list of extra paths where this finder can search."""


class ImportlibFinder(Finder):
    """A finder based on the importlib module."""

    _SUFFIXES: Sequence[tuple[str, ModuleType]] = (
        [(s, ModuleType.C_EXTENSION) for s in importlib.machinery.EXTENSION_SUFFIXES]
        + [(s, ModuleType.PY_SOURCE) for s in importlib.machinery.SOURCE_SUFFIXES]
        + [(s, ModuleType.PY_COMPILED) for s in importlib.machinery.BYTECODE_SUFFIXES]
    )

    def find_module(
        self,
        modname: str,
        module_parts: Sequence[str],
        processed: list[str],
        submodule_path: Sequence[str] | None,
    ) -> ModuleSpec | None:
        if submodule_path is not None:
            submodule_path = list(submodule_path)
        elif modname in sys.builtin_module_names:
            return ModuleSpec(
                name=modname,
                location=None,
                type=ModuleType.C_BUILTIN,
            )
        else:
            try:
                with warnings.catch_warnings():
                    warnings.filterwarnings("ignore", category=UserWarning)
                    spec = importlib.util.find_spec(modname)
                if (
                    spec
                    and spec.loader  # type: ignore[comparison-overlap] # noqa: E501
                    is importlib.machinery.FrozenImporter
                ):
                    # No need for BuiltinImporter; builtins handled above
                    return ModuleSpec(
                        name=modname,
                        location=getattr(spec.loader_state, "filename", None),
                        type=ModuleType.PY_FROZEN,
                    )
            except ValueError:
                pass
            submodule_path = sys.path

        for entry in submodule_path:
            package_directory = os.path.join(entry, modname)
            for suffix in (".py", ".pyi", importlib.machinery.BYTECODE_SUFFIXES[0]):
                package_file_name = "__init__" + suffix
                file_path = os.path.join(package_directory, package_file_name)
                if os.path.isfile(file_path):
                    return ModuleSpec(
                        name=modname,
                        location=package_directory,
                        type=ModuleType.PKG_DIRECTORY,
                    )
            for suffix, type_ in ImportlibFinder._SUFFIXES:
                file_name = modname + suffix
                file_path = os.path.join(entry, file_name)
                if os.path.isfile(file_path):
                    return ModuleSpec(name=modname, location=file_path, type=type_)
        return None

    def contribute_to_path(
        self, spec: ModuleSpec, processed: list[str]
    ) -> Sequence[str] | None:
        if spec.location is None:
            # Builtin.
            return None

        if _is_setuptools_namespace(Path(spec.location)):
            # extend_path is called, search sys.path for module/packages
            # of this name see pkgutil.extend_path documentation
            path = [
                os.path.join(p, *processed)
                for p in sys.path
                if os.path.isdir(os.path.join(p, *processed))
            ]
        elif spec.name == "distutils" and not any(
            spec.location.lower().startswith(ext_lib_dir.lower())
            for ext_lib_dir in EXT_LIB_DIRS
        ):
            # virtualenv below 20.0 patches distutils in an unexpected way
            # so we just find the location of distutils that will be
            # imported to avoid spurious import-error messages
            # https://github.com/pylint-dev/pylint/issues/5645
            # A regression test to create this scenario exists in release-tests.yml
            # and can be triggered manually from GitHub Actions
            distutils_spec = importlib.util.find_spec("distutils")
            if distutils_spec and distutils_spec.origin:
                origin_path = Path(
                    distutils_spec.origin
                )  # e.g. .../distutils/__init__.py
                path = [str(origin_path.parent)]  # e.g. .../distutils
            else:
                path = [spec.location]
        else:
            path = [spec.location]
        return path


class ExplicitNamespacePackageFinder(ImportlibFinder):
    """A finder for the explicit namespace packages."""

    def find_module(
        self,
        modname: str,
        module_parts: Sequence[str],
        processed: list[str],
        submodule_path: Sequence[str] | None,
    ) -> ModuleSpec | None:
        if processed:
            modname = ".".join([*processed, modname])
        if util.is_namespace(modname) and modname in sys.modules:
            submodule_path = sys.modules[modname].__path__
            return ModuleSpec(
                name=modname,
                location="",
                origin="namespace",
                type=ModuleType.PY_NAMESPACE,
                submodule_search_locations=submodule_path,
            )
        return None

    def contribute_to_path(
        self, spec: ModuleSpec, processed: list[str]
    ) -> Sequence[str] | None:
        return spec.submodule_search_locations


class ZipFinder(Finder):
    """Finder that knows how to find a module inside zip files."""

    def __init__(self, path: Sequence[str]) -> None:
        super().__init__(path)
        for entry_path in path:
            if entry_path not in sys.path_importer_cache:
                try:
                    sys.path_importer_cache[entry_path] = zipimport.zipimporter(  # type: ignore[assignment]
                        entry_path
                    )
                except zipimport.ZipImportError:
                    continue

    def find_module(
        self,
        modname: str,
        module_parts: Sequence[str],
        processed: list[str],
        submodule_path: Sequence[str] | None,
    ) -> ModuleSpec | None:
        try:
            file_type, filename, path = _search_zip(module_parts)
        except ImportError:
            return None

        return ModuleSpec(
            name=modname,
            location=filename,
            origin="egg",
            type=file_type,
            submodule_search_locations=path,
        )


class PathSpecFinder(Finder):
    """Finder based on importlib.machinery.PathFinder."""

    def find_module(
        self,
        modname: str,
        module_parts: Sequence[str],
        processed: list[str],
        submodule_path: Sequence[str] | None,
    ) -> ModuleSpec | None:
        spec = importlib.machinery.PathFinder.find_spec(modname, path=submodule_path)
        if spec is not None:
            is_namespace_pkg = spec.origin is None
            location = spec.origin if not is_namespace_pkg else None
            module_type = ModuleType.PY_NAMESPACE if is_namespace_pkg else None
            return ModuleSpec(
                name=spec.name,
                location=location,
                origin=spec.origin,
                type=module_type,
                submodule_search_locations=list(spec.submodule_search_locations or []),
            )
        return spec

    def contribute_to_path(
        self, spec: ModuleSpec, processed: list[str]
    ) -> Sequence[str] | None:
        if spec.type == ModuleType.PY_NAMESPACE:
            return spec.submodule_search_locations
        return None


_SPEC_FINDERS = (
    ImportlibFinder,
    ZipFinder,
    PathSpecFinder,
    ExplicitNamespacePackageFinder,
)


def _is_setuptools_namespace(location: pathlib.Path) -> bool:
    try:
        with open(location / "__init__.py", "rb") as stream:
            data = stream.read(4096)
    except OSError:
        return False
    extend_path = b"pkgutil" in data and b"extend_path" in data
    declare_namespace = (
        b"pkg_resources" in data and b"declare_namespace(__name__)" in data
    )
    return extend_path or declare_namespace


def _get_zipimporters() -> Iterator[tuple[str, zipimport.zipimporter]]:
    for filepath, importer in sys.path_importer_cache.items():
        if isinstance(importer, zipimport.zipimporter):
            yield filepath, importer


def _search_zip(
    modpath: Sequence[str],
) -> tuple[Literal[ModuleType.PY_ZIPMODULE], str, str]:
    for filepath, importer in _get_zipimporters():
        if PY310_PLUS:
            found: Any = importer.find_spec(modpath[0])
        else:
            found = importer.find_module(modpath[0])
        if found:
            if PY310_PLUS:
                if not importer.find_spec(os.path.sep.join(modpath)):
                    raise ImportError(
                        "No module named %s in %s/%s"
                        % (".".join(modpath[1:]), filepath, modpath)
                    )
            elif not importer.find_module(os.path.sep.join(modpath)):
                raise ImportError(
                    "No module named %s in %s/%s"
                    % (".".join(modpath[1:]), filepath, modpath)
                )
            return (
                ModuleType.PY_ZIPMODULE,
                os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath),
                filepath,
            )
    raise ImportError(f"No module named {'.'.join(modpath)}")


def _find_spec_with_path(
    search_path: Sequence[str],
    modname: str,
    module_parts: list[str],
    processed: list[str],
    submodule_path: Sequence[str] | None,
) -> tuple[Finder | _MetaPathFinder, ModuleSpec]:
    for finder in _SPEC_FINDERS:
        finder_instance = finder(search_path)
        spec = finder_instance.find_module(
            modname, module_parts, processed, submodule_path
        )
        if spec is None:
            continue
        return finder_instance, spec

    # Support for custom finders
    for meta_finder in sys.meta_path:
        # See if we support the customer import hook of the meta_finder
        meta_finder_name = meta_finder.__class__.__name__
        if meta_finder_name not in _MetaPathFinderModuleTypes:
            # Setuptools>62 creates its EditableFinders dynamically and have
            # "type" as their __class__.__name__. We check __name__ as well
            # to see if we can support the finder.
            try:
                meta_finder_name = meta_finder.__name__  # type: ignore[attr-defined]
            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/pylint-dev/astroid/pull/1752/
        if not hasattr(meta_finder, "find_spec"):
            continue

        spec = meta_finder.find_spec(modname, submodule_path)
        if spec:
            return (
                meta_finder,
                ModuleSpec(
                    spec.name,
                    module_type,
                    spec.origin,
                    spec.origin,
                    spec.submodule_search_locations,
                ),
            )

    raise ImportError(f"No module named {'.'.join(module_parts)}")


def find_spec(modpath: list[str], path: Sequence[str] | None = None) -> ModuleSpec:
    """Find a spec for the given module.

    :type modpath: list or tuple
    :param modpath:
      split module's name (i.e name of a module or package split
      on '.'), with leading empty strings for explicit relative import

    :type path: list or None
    :param path:
      optional list of path where the module or package should be
      searched (use sys.path if nothing or None is given)

    :rtype: ModuleSpec
    :return: A module spec, which describes how the module was
             found and where.
    """
    _path = path or sys.path

    # Need a copy for not mutating the argument.
    modpath = modpath[:]

    submodule_path = None
    module_parts = modpath[:]
    processed: list[str] = []

    while modpath:
        modname = modpath.pop(0)
        finder, spec = _find_spec_with_path(
            _path, modname, module_parts, processed, submodule_path or path
        )
        processed.append(modname)
        if modpath:
            if isinstance(finder, Finder):
                submodule_path = finder.contribute_to_path(spec, processed)
            # If modname is a package from an editable install, update submodule_path
            # so that the next module in the path will be found inside of it using importlib.
            # Existence of __name__ is guaranteed by _find_spec_with_path.
            elif finder.__name__ in _EditableFinderClasses:  # type: ignore[attr-defined]
                submodule_path = spec.submodule_search_locations

        if spec.type == ModuleType.PKG_DIRECTORY:
            spec = spec._replace(submodule_search_locations=submodule_path)

    return spec

back/venvmsg/Lib/site-packages/astroid/interpreter/_import/spec.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import pathlib
import sys
from functools import lru_cache
from importlib._bootstrap_external import _NamespacePath
from importlib.util import _find_spec_from_path  # type: ignore[attr-defined]

from astroid.const import IS_PYPY


@lru_cache(maxsize=4096)
def is_namespace(modname: str) -> bool:
    from astroid.modutils import (  # pylint: disable=import-outside-toplevel
        EXT_LIB_DIRS,
        STD_LIB_DIRS,
    )

    STD_AND_EXT_LIB_DIRS = STD_LIB_DIRS.union(EXT_LIB_DIRS)

    if modname in sys.builtin_module_names:
        return False

    found_spec = None

    # find_spec() attempts to import parent packages when given dotted paths.
    # That's unacceptable here, so we fallback to _find_spec_from_path(), which does
    # not, but requires instead that each single parent ('astroid', 'nodes', etc.)
    # be specced from left to right.
    processed_components = []
    last_submodule_search_locations: _NamespacePath | None = None
    for component in modname.split("."):
        processed_components.append(component)
        working_modname = ".".join(processed_components)
        try:
            # Both the modname and the path are built iteratively, with the
            # path (e.g. ['a', 'a/b', 'a/b/c']) lagging the modname by one
            found_spec = _find_spec_from_path(
                working_modname, path=last_submodule_search_locations
            )
        except AttributeError:
            return False
        except ValueError:
            if modname == "__main__":
                return False
            try:
                # .pth files will be on sys.modules
                # __spec__ is set inconsistently on PyPy so we can't really on the heuristic here
                # See: https://foss.heptapod.net/pypy/pypy/-/issues/3736
                # Check first fragment of modname, e.g. "astroid", not "astroid.interpreter"
                # because of cffi's behavior
                # See: https://github.com/pylint-dev/astroid/issues/1776
                mod = sys.modules[processed_components[0]]
                return (
                    mod.__spec__ is None
                    and getattr(mod, "__file__", None) is None
                    and hasattr(mod, "__path__")
                    and not IS_PYPY
                )
            except KeyError:
                return False
            except AttributeError:
                # Workaround for "py" module
                # https://github.com/pytest-dev/apipkg/issues/13
                return False
        except KeyError:
            # Intermediate steps might raise KeyErrors
            # https://github.com/python/cpython/issues/93334
            # TODO: update if fixed in importlib
            # For tree a > b > c.py
            # >>> from importlib.machinery import PathFinder
            # >>> PathFinder.find_spec('a.b', ['a'])
            # KeyError: 'a'

            # Repair last_submodule_search_locations
            if last_submodule_search_locations:
                # pylint: disable=unsubscriptable-object
                last_item = last_submodule_search_locations[-1]
                # e.g. for failure example above, add 'a/b' and keep going
                # so that find_spec('a.b.c', path=['a', 'a/b']) succeeds
                assumed_location = pathlib.Path(last_item) / component
                last_submodule_search_locations.append(str(assumed_location))
            continue

        # Update last_submodule_search_locations for next iteration
        if found_spec and found_spec.submodule_search_locations:
            # But immediately return False if we can detect we are in stdlib
            # or external lib (e.g site-packages)
            if any(
                any(location.startswith(lib_dir) for lib_dir in STD_AND_EXT_LIB_DIRS)
                for location in found_spec.submodule_search_locations
            ):
                return False
            last_submodule_search_locations = found_spec.submodule_search_locations

    return (
        found_spec is not None
        and found_spec.submodule_search_locations is not None
        and found_spec.origin is None
    )

back/venvmsg/Lib/site-packages/astroid/interpreter/_import/util.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Contains logic for retrieving special methods.

This implementation does not rely on the dot attribute access
logic, found in ``.getattr()``. The difference between these two
is that the dunder methods are looked with the type slots
(you can find more about these here
http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/)
As such, the lookup for the special methods is actually simpler than
the dot attribute access.
"""
from __future__ import annotations

import itertools
from typing import TYPE_CHECKING

import astroid
from astroid.exceptions import AttributeInferenceError

if TYPE_CHECKING:
    from astroid import nodes
    from astroid.context import InferenceContext


def _lookup_in_mro(node, name) -> list:
    attrs = node.locals.get(name, [])

    nodes = itertools.chain.from_iterable(
        ancestor.locals.get(name, []) for ancestor in node.ancestors(recurs=True)
    )
    values = list(itertools.chain(attrs, nodes))
    if not values:
        raise AttributeInferenceError(attribute=name, target=node)

    return values


def lookup(
    node: nodes.NodeNG, name: str, context: InferenceContext | None = None
) -> list:
    """Lookup the given special method name in the given *node*.

    If the special method was found, then a list of attributes
    will be returned. Otherwise, `astroid.AttributeInferenceError`
    is going to be raised.
    """
    if isinstance(
        node, (astroid.List, astroid.Tuple, astroid.Const, astroid.Dict, astroid.Set)
    ):
        return _builtin_lookup(node, name)
    if isinstance(node, astroid.Instance):
        return _lookup_in_mro(node, name)
    if isinstance(node, astroid.ClassDef):
        return _class_lookup(node, name, context=context)

    raise AttributeInferenceError(attribute=name, target=node)


def _class_lookup(
    node: nodes.ClassDef, name: str, context: InferenceContext | None = None
) -> list:
    metaclass = node.metaclass(context=context)
    if metaclass is None:
        raise AttributeInferenceError(attribute=name, target=node)

    return _lookup_in_mro(metaclass, name)


def _builtin_lookup(node, name) -> list:
    values = node.locals.get(name, [])
    if not values:
        raise AttributeInferenceError(attribute=name, target=node)

    return values

back/venvmsg/Lib/site-packages/astroid/interpreter/dunder_lookup.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Data object model, as per https://docs.python.org/3/reference/datamodel.html.

This module describes, at least partially, a data object model for some
of astroid's nodes. The model contains special attributes that nodes such
as functions, classes, modules etc have, such as __doc__, __class__,
__module__ etc, being used when doing attribute lookups over nodes.

For instance, inferring `obj.__class__` will first trigger an inference
of the `obj` variable. If it was successfully inferred, then an attribute
`__class__ will be looked for in the inferred object. This is the part
where the data model occurs. The model is attached to those nodes
and the lookup mechanism will try to see if attributes such as
`__class__` are defined by the model or not. If they are defined,
the model will be requested to return the corresponding value of that
attribute. Thus the model can be viewed as a special part of the lookup
mechanism.
"""

from __future__ import annotations

import itertools
import os
import pprint
import types
from collections.abc import Iterator
from functools import lru_cache
from typing import TYPE_CHECKING, Any, Literal

import astroid
from astroid import bases, nodes, util
from astroid.context import InferenceContext, copy_context
from astroid.exceptions import AttributeInferenceError, InferenceError, NoDefault
from astroid.manager import AstroidManager
from astroid.nodes import node_classes
from astroid.typing import InferenceResult, SuccessfulInferenceResult

if TYPE_CHECKING:
    from astroid.objects import Property

IMPL_PREFIX = "attr_"
LEN_OF_IMPL_PREFIX = len(IMPL_PREFIX)


def _dunder_dict(instance, attributes):
    obj = node_classes.Dict(
        parent=instance,
        lineno=instance.lineno,
        col_offset=instance.col_offset,
        end_lineno=instance.end_lineno,
        end_col_offset=instance.end_col_offset,
    )

    # Convert the keys to node strings
    keys = [
        node_classes.Const(value=value, parent=obj) for value in list(attributes.keys())
    ]

    # The original attribute has a list of elements for each key,
    # but that is not useful for retrieving the special attribute's value.
    # In this case, we're picking the last value from each list.
    values = [elem[-1] for elem in attributes.values()]

    obj.postinit(list(zip(keys, values)))
    return obj


def _get_bound_node(model: ObjectModel) -> Any:
    # TODO: Use isinstance instead of try ... except after _instance has typing
    try:
        return model._instance._proxied
    except AttributeError:
        return model._instance


class ObjectModel:
    def __init__(self):
        self._instance = None

    def __repr__(self):
        result = []
        cname = type(self).__name__
        string = "%(cname)s(%(fields)s)"
        alignment = len(cname) + 1
        for field in sorted(self.attributes()):
            width = 80 - len(field) - alignment
            lines = pprint.pformat(field, indent=2, width=width).splitlines(True)

            inner = [lines[0]]
            for line in lines[1:]:
                inner.append(" " * alignment + line)
            result.append(field)

        return string % {
            "cname": cname,
            "fields": (",\n" + " " * alignment).join(result),
        }

    def __call__(self, instance):
        self._instance = instance
        return self

    def __get__(self, instance, cls=None):
        # ObjectModel needs to be a descriptor so that just doing
        # `special_attributes = SomeObjectModel` should be enough in the body of a node.
        # But at the same time, node.special_attributes should return an object
        # which can be used for manipulating the special attributes. That's the reason
        # we pass the instance through which it got accessed to ObjectModel.__call__,
        # returning itself afterwards, so we can still have access to the
        # underlying data model and to the instance for which it got accessed.
        return self(instance)

    def __contains__(self, name) -> bool:
        return name in self.attributes()

    @lru_cache  # noqa
    def attributes(self) -> list[str]:
        """Get the attributes which are exported by this object model."""
        return [o[LEN_OF_IMPL_PREFIX:] for o in dir(self) if o.startswith(IMPL_PREFIX)]

    def lookup(self, name):
        """Look up the given *name* in the current model.

        It should return an AST or an interpreter object,
        but if the name is not found, then an AttributeInferenceError will be raised.
        """
        if name in self.attributes():
            return getattr(self, IMPL_PREFIX + name)
        raise AttributeInferenceError(target=self._instance, attribute=name)

    @property
    def attr___new__(self) -> bases.BoundMethod:
        """Calling cls.__new__(type) on an object returns an instance of 'type'."""
        from astroid import builder  # pylint: disable=import-outside-toplevel

        node: nodes.FunctionDef = builder.extract_node(
            """def __new__(self, cls): return cls()"""
        )
        # We set the parent as being the ClassDef of 'object' as that
        # triggers correct inference as a call to __new__ in bases.py
        node.parent = AstroidManager().builtins_module["object"]

        return bases.BoundMethod(proxy=node, bound=_get_bound_node(self))

    @property
    def attr___init__(self) -> bases.BoundMethod:
        """Calling cls.__init__() normally returns None."""
        from astroid import builder  # pylint: disable=import-outside-toplevel

        # The *args and **kwargs are necessary not to trigger warnings about missing
        # or extra parameters for '__init__' methods we don't infer correctly.
        # This BoundMethod is the fallback value for those.
        node: nodes.FunctionDef = builder.extract_node(
            """def __init__(self, *args, **kwargs): return None"""
        )
        # We set the parent as being the ClassDef of 'object' as that
        # is where this method originally comes from
        node.parent = AstroidManager().builtins_module["object"]

        return bases.BoundMethod(proxy=node, bound=_get_bound_node(self))


class ModuleModel(ObjectModel):
    def _builtins(self):
        builtins_ast_module = AstroidManager().builtins_module
        return builtins_ast_module.special_attributes.lookup("__dict__")

    @property
    def attr_builtins(self):
        return self._builtins()

    @property
    def attr___path__(self):
        if not self._instance.package:
            raise AttributeInferenceError(target=self._instance, attribute="__path__")

        path_objs = [
            node_classes.Const(
                value=path
                if not path.endswith("__init__.py")
                else os.path.dirname(path),
                parent=self._instance,
            )
            for path in self._instance.path
        ]

        container = node_classes.List(parent=self._instance)
        container.postinit(path_objs)

        return container

    @property
    def attr___name__(self):
        return node_classes.Const(value=self._instance.name, parent=self._instance)

    @property
    def attr___doc__(self):
        return node_classes.Const(
            value=getattr(self._instance.doc_node, "value", None),
            parent=self._instance,
        )

    @property
    def attr___file__(self):
        return node_classes.Const(value=self._instance.file, parent=self._instance)

    @property
    def attr___dict__(self):
        return _dunder_dict(self._instance, self._instance.globals)

    @property
    def attr___package__(self):
        if not self._instance.package:
            value = ""
        else:
            value = self._instance.name

        return node_classes.Const(value=value, parent=self._instance)

    # These are related to the Python 3 implementation of the
    # import system,
    # https://docs.python.org/3/reference/import.html#import-related-module-attributes

    @property
    def attr___spec__(self):
        # No handling for now.
        return node_classes.Unknown()

    @property
    def attr___loader__(self):
        # No handling for now.
        return node_classes.Unknown()

    @property
    def attr___cached__(self):
        # No handling for now.
        return node_classes.Unknown()


class FunctionModel(ObjectModel):
    @property
    def attr___name__(self):
        return node_classes.Const(value=self._instance.name, parent=self._instance)

    @property
    def attr___doc__(self):
        return node_classes.Const(
            value=getattr(self._instance.doc_node, "value", None),
            parent=self._instance,
        )

    @property
    def attr___qualname__(self):
        return node_classes.Const(value=self._instance.qname(), parent=self._instance)

    @property
    def attr___defaults__(self):
        func = self._instance
        if not func.args.defaults:
            return node_classes.Const(value=None, parent=func)

        defaults_obj = node_classes.Tuple(parent=func)
        defaults_obj.postinit(func.args.defaults)
        return defaults_obj

    @property
    def attr___annotations__(self):
        obj = node_classes.Dict(
            parent=self._instance,
            lineno=self._instance.lineno,
            col_offset=self._instance.col_offset,
            end_lineno=self._instance.end_lineno,
            end_col_offset=self._instance.end_col_offset,
        )

        if not self._instance.returns:
            returns = None
        else:
            returns = self._instance.returns

        args = self._instance.args
        pair_annotations = itertools.chain(
            zip(args.args or [], args.annotations),
            zip(args.kwonlyargs, args.kwonlyargs_annotations),
            zip(args.posonlyargs or [], args.posonlyargs_annotations),
        )

        annotations = {
            arg.name: annotation for (arg, annotation) in pair_annotations if annotation
        }
        if args.varargannotation:
            annotations[args.vararg] = args.varargannotation
        if args.kwargannotation:
            annotations[args.kwarg] = args.kwargannotation
        if returns:
            annotations["return"] = returns

        items = [
            (node_classes.Const(key, parent=obj), value)
            for (key, value) in annotations.items()
        ]

        obj.postinit(items)
        return obj

    @property
    def attr___dict__(self):
        return node_classes.Dict(
            parent=self._instance,
            lineno=self._instance.lineno,
            col_offset=self._instance.col_offset,
            end_lineno=self._instance.end_lineno,
            end_col_offset=self._instance.end_col_offset,
        )

    attr___globals__ = attr___dict__

    @property
    def attr___kwdefaults__(self):
        def _default_args(args, parent):
            for arg in args.kwonlyargs:
                try:
                    default = args.default_value(arg.name)
                except NoDefault:
                    continue

                name = node_classes.Const(arg.name, parent=parent)
                yield name, default

        args = self._instance.args
        obj = node_classes.Dict(
            parent=self._instance,
            lineno=self._instance.lineno,
            col_offset=self._instance.col_offset,
            end_lineno=self._instance.end_lineno,
            end_col_offset=self._instance.end_col_offset,
        )
        defaults = dict(_default_args(args, obj))

        obj.postinit(list(defaults.items()))
        return obj

    @property
    def attr___module__(self):
        return node_classes.Const(self._instance.root().qname())

    @property
    def attr___get__(self):
        func = self._instance

        class DescriptorBoundMethod(bases.BoundMethod):
            """Bound method which knows how to understand calling descriptor
            binding.
            """

            def implicit_parameters(self) -> Literal[0]:
                # Different than BoundMethod since the signature
                # is different.
                return 0

            def infer_call_result(
                self,
                caller: SuccessfulInferenceResult | None,
                context: InferenceContext | None = None,
            ) -> Iterator[bases.BoundMethod]:
                if len(caller.args) > 2 or len(caller.args) < 1:
                    raise InferenceError(
                        "Invalid arguments for descriptor binding",
                        target=self,
                        context=context,
                    )

                context = copy_context(context)
                try:
                    cls = next(caller.args[0].infer(context=context))
                except StopIteration as e:
                    raise InferenceError(context=context, node=caller.args[0]) from e

                if isinstance(cls, util.UninferableBase):
                    raise InferenceError(
                        "Invalid class inferred", target=self, context=context
                    )

                # For some reason func is a Node that the below
                # code is not expecting
                if isinstance(func, bases.BoundMethod):
                    yield func
                    return

                # Rebuild the original value, but with the parent set as the
                # class where it will be bound.
                new_func = func.__class__(
                    name=func.name,
                    lineno=func.lineno,
                    col_offset=func.col_offset,
                    parent=func.parent,
                    end_lineno=func.end_lineno,
                    end_col_offset=func.end_col_offset,
                )
                # pylint: disable=no-member
                new_func.postinit(
                    func.args,
                    func.body,
                    func.decorators,
                    func.returns,
                    doc_node=func.doc_node,
                )

                # Build a proper bound method that points to our newly built function.
                proxy = bases.UnboundMethod(new_func)
                yield bases.BoundMethod(proxy=proxy, bound=cls)

            @property
            def args(self):
                """Overwrite the underlying args to match those of the underlying func.

                Usually the underlying *func* is a function/method, as in:

                    def test(self):
                        pass

                This has only the *self* parameter but when we access test.__get__
                we get a new object which has two parameters, *self* and *type*.
                """
                nonlocal func
                arguments = astroid.Arguments(
                    parent=func.args.parent, vararg=None, kwarg=None
                )

                positional_or_keyword_params = func.args.args.copy()
                positional_or_keyword_params.append(
                    astroid.AssignName(
                        name="type",
                        lineno=0,
                        col_offset=0,
                        parent=arguments,
                        end_lineno=None,
                        end_col_offset=None,
                    )
                )

                positional_only_params = func.args.posonlyargs.copy()

                arguments.postinit(
                    args=positional_or_keyword_params,
                    posonlyargs=positional_only_params,
                    defaults=[],
                    kwonlyargs=[],
                    kw_defaults=[],
                    annotations=[],
                    kwonlyargs_annotations=[],
                    posonlyargs_annotations=[],
                )
                return arguments

        return DescriptorBoundMethod(proxy=self._instance, bound=self._instance)

    # These are here just for completion.
    @property
    def attr___ne__(self):
        return node_classes.Unknown()

    attr___subclasshook__ = attr___ne__
    attr___str__ = attr___ne__
    attr___sizeof__ = attr___ne__
    attr___setattr___ = attr___ne__
    attr___repr__ = attr___ne__
    attr___reduce__ = attr___ne__
    attr___reduce_ex__ = attr___ne__
    attr___lt__ = attr___ne__
    attr___eq__ = attr___ne__
    attr___gt__ = attr___ne__
    attr___format__ = attr___ne__
    attr___delattr___ = attr___ne__
    attr___getattribute__ = attr___ne__
    attr___hash__ = attr___ne__
    attr___dir__ = attr___ne__
    attr___call__ = attr___ne__
    attr___class__ = attr___ne__
    attr___closure__ = attr___ne__
    attr___code__ = attr___ne__


class ClassModel(ObjectModel):
    def __init__(self):
        # Add a context so that inferences called from an instance don't recurse endlessly
        self.context = InferenceContext()

        super().__init__()

    @property
    def attr___module__(self):
        return node_classes.Const(self._instance.root().qname())

    @property
    def attr___name__(self):
        return node_classes.Const(self._instance.name)

    @property
    def attr___qualname__(self):
        return node_classes.Const(self._instance.qname())

    @property
    def attr___doc__(self):
        return node_classes.Const(getattr(self._instance.doc_node, "value", None))

    @property
    def attr___mro__(self):
        if not self._instance.newstyle:
            raise AttributeInferenceError(target=self._instance, attribute="__mro__")

        mro = self._instance.mro()
        obj = node_classes.Tuple(parent=self._instance)
        obj.postinit(mro)
        return obj

    @property
    def attr_mro(self):
        if not self._instance.newstyle:
            raise AttributeInferenceError(target=self._instance, attribute="mro")

        other_self = self

        # Cls.mro is a method and we need to return one in order to have a proper inference.
        # The method we're returning is capable of inferring the underlying MRO though.
        class MroBoundMethod(bases.BoundMethod):
            def infer_call_result(
                self,
                caller: SuccessfulInferenceResult | None,
                context: InferenceContext | None = None,
            ) -> Iterator[node_classes.Tuple]:
                yield other_self.attr___mro__

        implicit_metaclass = self._instance.implicit_metaclass()
        mro_method = implicit_metaclass.locals["mro"][0]
        return MroBoundMethod(proxy=mro_method, bound=implicit_metaclass)

    @property
    def attr___bases__(self):
        obj = node_classes.Tuple()
        context = InferenceContext()
        elts = list(self._instance._inferred_bases(context))
        obj.postinit(elts=elts)
        return obj

    @property
    def attr___class__(self):
        # pylint: disable=import-outside-toplevel; circular import
        from astroid import helpers

        return helpers.object_type(self._instance)

    @property
    def attr___subclasses__(self):
        """Get the subclasses of the underlying class.

        This looks only in the current module for retrieving the subclasses,
        thus it might miss a couple of them.
        """
        if not self._instance.newstyle:
            raise AttributeInferenceError(
                target=self._instance, attribute="__subclasses__"
            )

        qname = self._instance.qname()
        root = self._instance.root()
        classes = [
            cls
            for cls in root.nodes_of_class(nodes.ClassDef)
            if cls != self._instance and cls.is_subtype_of(qname, context=self.context)
        ]

        obj = node_classes.List(parent=self._instance)
        obj.postinit(classes)

        class SubclassesBoundMethod(bases.BoundMethod):
            def infer_call_result(
                self,
                caller: SuccessfulInferenceResult | None,
                context: InferenceContext | None = None,
            ) -> Iterator[node_classes.List]:
                yield obj

        implicit_metaclass = self._instance.implicit_metaclass()
        subclasses_method = implicit_metaclass.locals["__subclasses__"][0]
        return SubclassesBoundMethod(proxy=subclasses_method, bound=implicit_metaclass)

    @property
    def attr___dict__(self):
        return node_classes.Dict(
            parent=self._instance,
            lineno=self._instance.lineno,
            col_offset=self._instance.col_offset,
            end_lineno=self._instance.end_lineno,
            end_col_offset=self._instance.end_col_offset,
        )

    @property
    def attr___call__(self):
        """Calling a class A() returns an instance of A."""
        return self._instance.instantiate_class()


class SuperModel(ObjectModel):
    @property
    def attr___thisclass__(self):
        return self._instance.mro_pointer

    @property
    def attr___self_class__(self):
        return self._instance._self_class

    @property
    def attr___self__(self):
        return self._instance.type

    @property
    def attr___class__(self):
        return self._instance._proxied


class UnboundMethodModel(ObjectModel):
    @property
    def attr___class__(self):
        # pylint: disable=import-outside-toplevel; circular import
        from astroid import helpers

        return helpers.object_type(self._instance)

    @property
    def attr___func__(self):
        return self._instance._proxied

    @property
    def attr___self__(self):
        return node_classes.Const(value=None, parent=self._instance)

    attr_im_func = attr___func__
    attr_im_class = attr___class__
    attr_im_self = attr___self__


class ContextManagerModel(ObjectModel):
    """Model for context managers.

    Based on 3.3.9 of the Data Model documentation:
    https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers
    """

    @property
    def attr___enter__(self) -> bases.BoundMethod:
        """Representation of the base implementation of __enter__.

        As per Python documentation:
        Enter the runtime context related to this object. The with statement
        will bind this method's return value to the target(s) specified in the
        as clause of the statement, if any.
        """
        from astroid import builder  # pylint: disable=import-outside-toplevel

        node: nodes.FunctionDef = builder.extract_node("""def __enter__(self): ...""")
        # We set the parent as being the ClassDef of 'object' as that
        # is where this method originally comes from
        node.parent = AstroidManager().builtins_module["object"]

        return bases.BoundMethod(proxy=node, bound=_get_bound_node(self))

    @property
    def attr___exit__(self) -> bases.BoundMethod:
        """Representation of the base implementation of __exit__.

        As per Python documentation:
        Exit the runtime context related to this object. The parameters describe the
        exception that caused the context to be exited. If the context was exited
        without an exception, all three arguments will be None.
        """
        from astroid import builder  # pylint: disable=import-outside-toplevel

        node: nodes.FunctionDef = builder.extract_node(
            """def __exit__(self, exc_type, exc_value, traceback): ..."""
        )
        # We set the parent as being the ClassDef of 'object' as that
        # is where this method originally comes from
        node.parent = AstroidManager().builtins_module["object"]

        return bases.BoundMethod(proxy=node, bound=_get_bound_node(self))


class BoundMethodModel(FunctionModel):
    @property
    def attr___func__(self):
        return self._instance._proxied._proxied

    @property
    def attr___self__(self):
        return self._instance.bound


class GeneratorModel(FunctionModel, ContextManagerModel):
    def __new__(cls, *args, **kwargs):
        # Append the values from the GeneratorType unto this object.
        ret = super().__new__(cls, *args, **kwargs)
        generator = AstroidManager().builtins_module["generator"]
        for name, values in generator.locals.items():
            method = values[0]

            def patched(cls, meth=method):
                return meth

            setattr(type(ret), IMPL_PREFIX + name, property(patched))

        return ret

    @property
    def attr___name__(self):
        return node_classes.Const(
            value=self._instance.parent.name, parent=self._instance
        )

    @property
    def attr___doc__(self):
        return node_classes.Const(
            value=getattr(self._instance.parent.doc_node, "value", None),
            parent=self._instance,
        )


class AsyncGeneratorModel(GeneratorModel):
    def __new__(cls, *args, **kwargs):
        # Append the values from the AGeneratorType unto this object.
        ret = super().__new__(cls, *args, **kwargs)
        astroid_builtins = AstroidManager().builtins_module
        generator = astroid_builtins.get("async_generator")
        if generator is None:
            # Make it backward compatible.
            generator = astroid_builtins.get("generator")

        for name, values in generator.locals.items():
            method = values[0]

            def patched(cls, meth=method):
                return meth

            setattr(type(ret), IMPL_PREFIX + name, property(patched))

        return ret


class InstanceModel(ObjectModel):
    @property
    def attr___class__(self):
        return self._instance._proxied

    @property
    def attr___module__(self):
        return node_classes.Const(self._instance.root().qname())

    @property
    def attr___doc__(self):
        return node_classes.Const(getattr(self._instance.doc_node, "value", None))

    @property
    def attr___dict__(self):
        return _dunder_dict(self._instance, self._instance.instance_attrs)


# Exception instances


class ExceptionInstanceModel(InstanceModel):
    @property
    def attr_args(self) -> nodes.Tuple:
        return nodes.Tuple(parent=self._instance)

    @property
    def attr___traceback__(self):
        builtins_ast_module = AstroidManager().builtins_module
        traceback_type = builtins_ast_module[types.TracebackType.__name__]
        return traceback_type.instantiate_class()


class SyntaxErrorInstanceModel(ExceptionInstanceModel):
    @property
    def attr_text(self):
        return node_classes.Const("")


class OSErrorInstanceModel(ExceptionInstanceModel):
    @property
    def attr_filename(self):
        return node_classes.Const("")

    @property
    def attr_errno(self):
        return node_classes.Const(0)

    @property
    def attr_strerror(self):
        return node_classes.Const("")

    attr_filename2 = attr_filename


class ImportErrorInstanceModel(ExceptionInstanceModel):
    @property
    def attr_name(self):
        return node_classes.Const("")

    @property
    def attr_path(self):
        return node_classes.Const("")


class UnicodeDecodeErrorInstanceModel(ExceptionInstanceModel):
    @property
    def attr_object(self):
        return node_classes.Const("")


BUILTIN_EXCEPTIONS = {
    "builtins.SyntaxError": SyntaxErrorInstanceModel,
    "builtins.ImportError": ImportErrorInstanceModel,
    "builtins.UnicodeDecodeError": UnicodeDecodeErrorInstanceModel,
    # These are all similar to OSError in terms of attributes
    "builtins.OSError": OSErrorInstanceModel,
    "builtins.BlockingIOError": OSErrorInstanceModel,
    "builtins.BrokenPipeError": OSErrorInstanceModel,
    "builtins.ChildProcessError": OSErrorInstanceModel,
    "builtins.ConnectionAbortedError": OSErrorInstanceModel,
    "builtins.ConnectionError": OSErrorInstanceModel,
    "builtins.ConnectionRefusedError": OSErrorInstanceModel,
    "builtins.ConnectionResetError": OSErrorInstanceModel,
    "builtins.FileExistsError": OSErrorInstanceModel,
    "builtins.FileNotFoundError": OSErrorInstanceModel,
    "builtins.InterruptedError": OSErrorInstanceModel,
    "builtins.IsADirectoryError": OSErrorInstanceModel,
    "builtins.NotADirectoryError": OSErrorInstanceModel,
    "builtins.PermissionError": OSErrorInstanceModel,
    "builtins.ProcessLookupError": OSErrorInstanceModel,
    "builtins.TimeoutError": OSErrorInstanceModel,
}


class DictModel(ObjectModel):
    @property
    def attr___class__(self):
        return self._instance._proxied

    def _generic_dict_attribute(self, obj, name):
        """Generate a bound method that can infer the given *obj*."""

        class DictMethodBoundMethod(astroid.BoundMethod):
            def infer_call_result(
                self,
                caller: SuccessfulInferenceResult | None,
                context: InferenceContext | None = None,
            ) -> Iterator[InferenceResult]:
                yield obj

        meth = next(self._instance._proxied.igetattr(name), None)
        return DictMethodBoundMethod(proxy=meth, bound=self._instance)

    @property
    def attr_items(self):
        from astroid import objects  # pylint: disable=import-outside-toplevel

        elems = []
        obj = node_classes.List(parent=self._instance)
        for key, value in self._instance.items:
            elem = node_classes.Tuple(parent=obj)
            elem.postinit((key, value))
            elems.append(elem)
        obj.postinit(elts=elems)

        items_obj = objects.DictItems(obj)
        return self._generic_dict_attribute(items_obj, "items")

    @property
    def attr_keys(self):
        from astroid import objects  # pylint: disable=import-outside-toplevel

        keys = [key for (key, _) in self._instance.items]
        obj = node_classes.List(parent=self._instance)
        obj.postinit(elts=keys)

        keys_obj = objects.DictKeys(obj)
        return self._generic_dict_attribute(keys_obj, "keys")

    @property
    def attr_values(self):
        from astroid import objects  # pylint: disable=import-outside-toplevel

        values = [value for (_, value) in self._instance.items]
        obj = node_classes.List(parent=self._instance)
        obj.postinit(values)

        values_obj = objects.DictValues(obj)
        return self._generic_dict_attribute(values_obj, "values")


class PropertyModel(ObjectModel):
    """Model for a builtin property."""

    def _init_function(self, name):
        function = nodes.FunctionDef(
            name=name,
            parent=self._instance,
            lineno=self._instance.lineno,
            col_offset=self._instance.col_offset,
            end_lineno=self._instance.end_lineno,
            end_col_offset=self._instance.end_col_offset,
        )

        args = nodes.Arguments(parent=function, vararg=None, kwarg=None)
        args.postinit(
            args=[],
            defaults=[],
            kwonlyargs=[],
            kw_defaults=[],
            annotations=[],
            posonlyargs=[],
            posonlyargs_annotations=[],
            kwonlyargs_annotations=[],
        )

        function.postinit(args=args, body=[])
        return function

    @property
    def attr_fget(self):
        func = self._instance

        class PropertyFuncAccessor(nodes.FunctionDef):
            def infer_call_result(
                self,
                caller: SuccessfulInferenceResult | None,
                context: InferenceContext | None = None,
            ) -> Iterator[InferenceResult]:
                nonlocal func
                if caller and len(caller.args) != 1:
                    raise InferenceError(
                        "fget() needs a single argument", target=self, context=context
                    )

                yield from func.function.infer_call_result(
                    caller=caller, context=context
                )

        property_accessor = PropertyFuncAccessor(
            name="fget",
            parent=self._instance,
            lineno=self._instance.lineno,
            col_offset=self._instance.col_offset,
            end_lineno=self._instance.end_lineno,
            end_col_offset=self._instance.end_col_offset,
        )
        property_accessor.postinit(args=func.args, body=func.body)
        return property_accessor

    @property
    def attr_fset(self):
        func = self._instance

        def find_setter(func: Property) -> astroid.FunctionDef | None:
            """
            Given a property, find the corresponding setter function and returns it.

            :param func: property for which the setter has to be found
            :return: the setter function or None
            """
            for target in [
                t for t in func.parent.get_children() if t.name == func.function.name
            ]:
                for dec_name in target.decoratornames():
                    if dec_name.endswith(func.function.name + ".setter"):
                        return target
            return None

        func_setter = find_setter(func)
        if not func_setter:
            raise InferenceError(
                f"Unable to find the setter of property {func.function.name}"
            )

        class PropertyFuncAccessor(nodes.FunctionDef):
            def infer_call_result(
                self,
                caller: SuccessfulInferenceResult | None,
                context: InferenceContext | None = None,
            ) -> Iterator[InferenceResult]:
                nonlocal func_setter
                if caller and len(caller.args) != 2:
                    raise InferenceError(
                        "fset() needs two arguments", target=self, context=context
                    )
                yield from func_setter.infer_call_result(caller=caller, context=context)

        property_accessor = PropertyFuncAccessor(
            name="fset",
            parent=self._instance,
            lineno=self._instance.lineno,
            col_offset=self._instance.col_offset,
            end_lineno=self._instance.end_lineno,
            end_col_offset=self._instance.end_col_offset,
        )
        property_accessor.postinit(args=func_setter.args, body=func_setter.body)
        return property_accessor

    @property
    def attr_setter(self):
        return self._init_function("setter")

    @property
    def attr_deleter(self):
        return self._init_function("deleter")

    @property
    def attr_getter(self):
        return self._init_function("getter")

    # pylint: enable=import-outside-toplevel

back/venvmsg/Lib/site-packages/astroid/interpreter/objectmodel.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""astroid manager: avoid multiple astroid build of a same module when
possible by providing a class responsible to get astroid representation
from various source and using a cache of built modules)
"""

from __future__ import annotations

import collections
import os
import types
import zipimport
from collections.abc import Callable, Iterator, Sequence
from typing import Any, ClassVar

from astroid import nodes
from astroid.context import InferenceContext, _invalidate_cache
from astroid.exceptions import AstroidBuildingError, AstroidImportError
from astroid.interpreter._import import spec, util
from astroid.modutils import (
    NoSourceFile,
    _cache_normalize_path_,
    file_info_from_modpath,
    get_source_file,
    is_module_name_part_of_extension_package_whitelist,
    is_python_source,
    is_stdlib_module,
    load_module_from_name,
    modpath_from_file,
)
from astroid.transforms import TransformVisitor
from astroid.typing import AstroidManagerBrain, InferenceResult

ZIP_IMPORT_EXTS = (".zip", ".egg", ".whl", ".pyz", ".pyzw")


def safe_repr(obj: Any) -> str:
    try:
        return repr(obj)
    except Exception:  # pylint: disable=broad-except
        return "???"


class AstroidManager:
    """Responsible to build astroid from files or modules.

    Use the Borg (singleton) pattern.
    """

    name = "astroid loader"
    brain: ClassVar[AstroidManagerBrain] = {
        "astroid_cache": {},
        "_mod_file_cache": {},
        "_failed_import_hooks": [],
        "always_load_extensions": False,
        "optimize_ast": False,
        "max_inferable_values": 100,
        "extension_package_whitelist": set(),
        "_transform": TransformVisitor(),
    }

    def __init__(self) -> None:
        # NOTE: cache entries are added by the [re]builder
        self.astroid_cache = AstroidManager.brain["astroid_cache"]
        self._mod_file_cache = AstroidManager.brain["_mod_file_cache"]
        self._failed_import_hooks = AstroidManager.brain["_failed_import_hooks"]
        self.extension_package_whitelist = AstroidManager.brain[
            "extension_package_whitelist"
        ]
        self._transform = AstroidManager.brain["_transform"]

    @property
    def always_load_extensions(self) -> bool:
        return AstroidManager.brain["always_load_extensions"]

    @always_load_extensions.setter
    def always_load_extensions(self, value: bool) -> None:
        AstroidManager.brain["always_load_extensions"] = value

    @property
    def optimize_ast(self) -> bool:
        return AstroidManager.brain["optimize_ast"]

    @optimize_ast.setter
    def optimize_ast(self, value: bool) -> None:
        AstroidManager.brain["optimize_ast"] = value

    @property
    def max_inferable_values(self) -> int:
        return AstroidManager.brain["max_inferable_values"]

    @max_inferable_values.setter
    def max_inferable_values(self, value: int) -> None:
        AstroidManager.brain["max_inferable_values"] = value

    @property
    def register_transform(self):
        # This and unregister_transform below are exported for convenience
        return self._transform.register_transform

    @property
    def unregister_transform(self):
        return self._transform.unregister_transform

    @property
    def builtins_module(self) -> nodes.Module:
        return self.astroid_cache["builtins"]

    def visit_transforms(self, node: nodes.NodeNG) -> InferenceResult:
        """Visit the transforms and apply them to the given *node*."""
        return self._transform.visit(node)

    def ast_from_file(
        self,
        filepath: str,
        modname: str | None = None,
        fallback: bool = True,
        source: bool = False,
    ) -> nodes.Module:
        """Given a module name, return the astroid object."""
        if modname is None:
            try:
                modname = ".".join(modpath_from_file(filepath))
            except ImportError:
                modname = filepath
        if (
            modname in self.astroid_cache
            and self.astroid_cache[modname].file == filepath
        ):
            return self.astroid_cache[modname]
        # Call get_source_file() only after a cache miss,
        # since it calls os.path.exists().
        try:
            filepath = get_source_file(filepath, include_no_ext=True)
            source = True
        except NoSourceFile:
            pass
        # Second attempt on the cache after get_source_file().
        if (
            modname in self.astroid_cache
            and self.astroid_cache[modname].file == filepath
        ):
            return self.astroid_cache[modname]
        if source:
            # pylint: disable=import-outside-toplevel; circular import
            from astroid.builder import AstroidBuilder

            return AstroidBuilder(self).file_build(filepath, modname)
        if fallback and modname:
            return self.ast_from_module_name(modname)
        raise AstroidBuildingError("Unable to build an AST for {path}.", path=filepath)

    def ast_from_string(
        self, data: str, modname: str = "", filepath: str | None = None
    ) -> nodes.Module:
        """Given some source code as a string, return its corresponding astroid
        object.
        """
        # pylint: disable=import-outside-toplevel; circular import
        from astroid.builder import AstroidBuilder

        return AstroidBuilder(self).string_build(data, modname, filepath)

    def _build_stub_module(self, modname: str) -> nodes.Module:
        # pylint: disable=import-outside-toplevel; circular import
        from astroid.builder import AstroidBuilder

        return AstroidBuilder(self).string_build("", modname)

    def _build_namespace_module(
        self, modname: str, path: Sequence[str]
    ) -> nodes.Module:
        # pylint: disable=import-outside-toplevel; circular import
        from astroid.builder import build_namespace_package_module

        return build_namespace_package_module(modname, path)

    def _can_load_extension(self, modname: str) -> bool:
        if self.always_load_extensions:
            return True
        if is_stdlib_module(modname):
            return True
        return is_module_name_part_of_extension_package_whitelist(
            modname, self.extension_package_whitelist
        )

    def ast_from_module_name(  # noqa: C901
        self,
        modname: str | None,
        context_file: str | None = None,
        use_cache: bool = True,
    ) -> nodes.Module:
        """Given a module name, return the astroid object."""
        if modname is None:
            raise AstroidBuildingError("No module name given.")
        # Sometimes we don't want to use the cache. For example, when we're
        # importing a module with the same name as the file that is importing
        # we want to fallback on the import system to make sure we get the correct
        # module.
        if modname in self.astroid_cache and use_cache:
            return self.astroid_cache[modname]
        if modname == "__main__":
            return self._build_stub_module(modname)
        if context_file:
            old_cwd = os.getcwd()
            os.chdir(os.path.dirname(context_file))
        try:
            found_spec = self.file_from_module_name(modname, context_file)
            if found_spec.type == spec.ModuleType.PY_ZIPMODULE:
                module = self.zip_import_data(found_spec.location)
                if module is not None:
                    return module

            elif found_spec.type in (
                spec.ModuleType.C_BUILTIN,
                spec.ModuleType.C_EXTENSION,
            ):
                if (
                    found_spec.type == spec.ModuleType.C_EXTENSION
                    and not self._can_load_extension(modname)
                ):
                    return self._build_stub_module(modname)
                try:
                    named_module = load_module_from_name(modname)
                except Exception as e:
                    raise AstroidImportError(
                        "Loading {modname} failed with:\n{error}",
                        modname=modname,
                        path=found_spec.location,
                    ) from e
                return self.ast_from_module(named_module, modname)

            elif found_spec.type == spec.ModuleType.PY_COMPILED:
                raise AstroidImportError(
                    "Unable to load compiled module {modname}.",
                    modname=modname,
                    path=found_spec.location,
                )

            elif found_spec.type == spec.ModuleType.PY_NAMESPACE:
                return self._build_namespace_module(
                    modname, found_spec.submodule_search_locations or []
                )
            elif found_spec.type == spec.ModuleType.PY_FROZEN:
                if found_spec.location is None:
                    return self._build_stub_module(modname)
                # For stdlib frozen modules we can determine the location and
                # can therefore create a module from the source file
                return self.ast_from_file(found_spec.location, modname, fallback=False)

            if found_spec.location is None:
                raise AstroidImportError(
                    "Can't find a file for module {modname}.", modname=modname
                )

            return self.ast_from_file(found_spec.location, modname, fallback=False)
        except AstroidBuildingError as e:
            for hook in self._failed_import_hooks:
                try:
                    return hook(modname)
                except AstroidBuildingError:
                    pass
            raise e
        finally:
            if context_file:
                os.chdir(old_cwd)

    def zip_import_data(self, filepath: str) -> nodes.Module | None:
        if zipimport is None:
            return None

        # pylint: disable=import-outside-toplevel; circular import
        from astroid.builder import AstroidBuilder

        builder = AstroidBuilder(self)
        for ext in ZIP_IMPORT_EXTS:
            try:
                eggpath, resource = filepath.rsplit(ext + os.path.sep, 1)
            except ValueError:
                continue
            try:
                importer = zipimport.zipimporter(eggpath + ext)
                zmodname = resource.replace(os.path.sep, ".")
                if importer.is_package(resource):
                    zmodname = zmodname + ".__init__"
                module = builder.string_build(
                    importer.get_source(resource), zmodname, filepath
                )
                return module
            except Exception:  # pylint: disable=broad-except
                continue
        return None

    def file_from_module_name(
        self, modname: str, contextfile: str | None
    ) -> spec.ModuleSpec:
        try:
            value = self._mod_file_cache[(modname, contextfile)]
        except KeyError:
            try:
                value = file_info_from_modpath(
                    modname.split("."), context_file=contextfile
                )
            except ImportError as e:
                # pylint: disable-next=redefined-variable-type
                value = AstroidImportError(
                    "Failed to import module {modname} with error:\n{error}.",
                    modname=modname,
                    # we remove the traceback here to save on memory usage (since these exceptions are cached)
                    error=e.with_traceback(None),
                )
            self._mod_file_cache[(modname, contextfile)] = value
        if isinstance(value, AstroidBuildingError):
            # we remove the traceback here to save on memory usage (since these exceptions are cached)
            raise value.with_traceback(None)  # pylint: disable=no-member
        return value

    def ast_from_module(
        self, module: types.ModuleType, modname: str | None = None
    ) -> nodes.Module:
        """Given an imported module, return the astroid object."""
        modname = modname or module.__name__
        if modname in self.astroid_cache:
            return self.astroid_cache[modname]
        try:
            # some builtin modules don't have __file__ attribute
            filepath = module.__file__
            if is_python_source(filepath):
                # Type is checked in is_python_source
                return self.ast_from_file(filepath, modname)  # type: ignore[arg-type]
        except AttributeError:
            pass

        # pylint: disable=import-outside-toplevel; circular import
        from astroid.builder import AstroidBuilder

        return AstroidBuilder(self).module_build(module, modname)

    def ast_from_class(self, klass: type, modname: str | None = None) -> nodes.ClassDef:
        """Get astroid for the given class."""
        if modname is None:
            try:
                modname = klass.__module__
            except AttributeError as exc:
                raise AstroidBuildingError(
                    "Unable to get module for class {class_name}.",
                    cls=klass,
                    class_repr=safe_repr(klass),
                    modname=modname,
                ) from exc
        modastroid = self.ast_from_module_name(modname)
        ret = modastroid.getattr(klass.__name__)[0]
        assert isinstance(ret, nodes.ClassDef)
        return ret

    def infer_ast_from_something(
        self, obj: object, context: InferenceContext | None = None
    ) -> Iterator[InferenceResult]:
        """Infer astroid for the given class."""
        if hasattr(obj, "__class__") and not isinstance(obj, type):
            klass = obj.__class__
        elif isinstance(obj, type):
            klass = obj
        else:
            raise AstroidBuildingError(  # pragma: no cover
                "Unable to get type for {class_repr}.",
                cls=None,
                class_repr=safe_repr(obj),
            )
        try:
            modname = klass.__module__
        except AttributeError as exc:
            raise AstroidBuildingError(
                "Unable to get module for {class_repr}.",
                cls=klass,
                class_repr=safe_repr(klass),
            ) from exc
        except Exception as exc:
            raise AstroidImportError(
                "Unexpected error while retrieving module for {class_repr}:\n"
                "{error}",
                cls=klass,
                class_repr=safe_repr(klass),
            ) from exc
        try:
            name = klass.__name__
        except AttributeError as exc:
            raise AstroidBuildingError(
                "Unable to get name for {class_repr}:\n",
                cls=klass,
                class_repr=safe_repr(klass),
            ) from exc
        except Exception as exc:
            raise AstroidImportError(
                "Unexpected error while retrieving name for {class_repr}:\n{error}",
                cls=klass,
                class_repr=safe_repr(klass),
            ) from exc
        # take care, on living object __module__ is regularly wrong :(
        modastroid = self.ast_from_module_name(modname)
        if klass is obj:
            for inferred in modastroid.igetattr(name, context):
                yield inferred
        else:
            for inferred in modastroid.igetattr(name, context):
                yield inferred.instantiate_class()

    def register_failed_import_hook(self, hook: Callable[[str], nodes.Module]) -> None:
        """Registers a hook to resolve imports that cannot be found otherwise.

        `hook` must be a function that accepts a single argument `modname` which
        contains the name of the module or package that could not be imported.
        If `hook` can resolve the import, must return a node of type `astroid.Module`,
        otherwise, it must raise `AstroidBuildingError`.
        """
        self._failed_import_hooks.append(hook)

    def cache_module(self, module: nodes.Module) -> None:
        """Cache a module if no module with the same name is known yet."""
        self.astroid_cache.setdefault(module.name, module)

    def bootstrap(self) -> None:
        """Bootstrap the required AST modules needed for the manager to work.

        The bootstrap usually involves building the AST for the builtins
        module, which is required by the rest of astroid to work correctly.
        """
        from astroid import raw_building  # pylint: disable=import-outside-toplevel

        raw_building._astroid_bootstrapping()

    def clear_cache(self) -> None:
        """Clear the underlying caches, bootstrap the builtins module and
        re-register transforms.
        """
        # import here because of cyclic imports
        # pylint: disable=import-outside-toplevel
        from astroid.brain.helpers import register_all_brains
        from astroid.inference_tip import clear_inference_tip_cache
        from astroid.interpreter.objectmodel import ObjectModel
        from astroid.nodes._base_nodes import LookupMixIn
        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()
        AstroidManager.brain["_transform"].transforms = collections.defaultdict(list)

        for lru_cache in (
            LookupMixIn.lookup,
            _cache_normalize_path_,
            util.is_namespace,
            ObjectModel.attributes,
            ClassDef._metaclass_lookup_attribute,
        ):
            lru_cache.cache_clear()  # type: ignore[attr-defined]

        self.bootstrap()

        # Reload brain plugins. During initialisation this is done in astroid.manager.py
        register_all_brains(self)

back/venvmsg/Lib/site-packages/astroid/manager.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Python modules manipulation utility functions.

:type PY_SOURCE_EXTS: tuple(str)
:var PY_SOURCE_EXTS: list of possible python source file extension

:type STD_LIB_DIRS: set of str
:var STD_LIB_DIRS: directories where standard modules are located

:type BUILTIN_MODULES: dict
:var BUILTIN_MODULES: dictionary with builtin module names has key
"""

from __future__ import annotations

import importlib
import importlib.machinery
import importlib.util
import io
import itertools
import logging
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, 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__)


if sys.platform.startswith("win"):
    PY_SOURCE_EXTS = ("py", "pyw", "pyi")
    PY_COMPILED_EXTS = ("dll", "pyd")
else:
    PY_SOURCE_EXTS = ("py", "pyi")
    PY_COMPILED_EXTS = ("so",)


# TODO: Adding `platstdlib` is a fix for a workaround in virtualenv. At some point we should
# revisit whether this is still necessary. See https://github.com/pylint-dev/astroid/pull/1323.
STD_LIB_DIRS = {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")}

if os.name == "nt":
    STD_LIB_DIRS.add(os.path.join(sys.prefix, "dlls"))
    try:
        # real_prefix is defined when running inside virtual environments,
        # created with the **virtualenv** library.
        # Deprecated in virtualenv==16.7.9
        # See: https://github.com/pypa/virtualenv/issues/1622
        STD_LIB_DIRS.add(os.path.join(sys.real_prefix, "dlls"))  # type: ignore[attr-defined]
    except AttributeError:
        # sys.base_exec_prefix is always defined, but in a virtual environment
        # created with the stdlib **venv** module, it points to the original
        # installation, if the virtual env is activated.
        try:
            STD_LIB_DIRS.add(os.path.join(sys.base_exec_prefix, "dlls"))
        except AttributeError:
            pass

if IS_PYPY and sys.version_info < (3, 8):
    # PyPy stores the stdlib in two places: sys.prefix/lib_pypy and sys.prefix/lib-python/3
    # sysconfig.get_path on PyPy returns the first, but without an underscore so we patch this manually.
    # Beginning with 3.8 the stdlib is only stored in: sys.prefix/pypy{py_version_short}
    STD_LIB_DIRS.add(str(Path(sysconfig.get_path("stdlib")).parent / "lib_pypy"))
    STD_LIB_DIRS.add(str(Path(sysconfig.get_path("stdlib")).parent / "lib-python/3"))

    # TODO: This is a fix for a workaround in virtualenv. At some point we should revisit
    # whether this is still necessary. See https://github.com/pylint-dev/astroid/pull/1324.
    STD_LIB_DIRS.add(str(Path(sysconfig.get_path("platstdlib")).parent / "lib_pypy"))
    STD_LIB_DIRS.add(
        str(Path(sysconfig.get_path("platstdlib")).parent / "lib-python/3")
    )

if os.name == "posix":
    # Need the real prefix if we're in a virtualenv, otherwise
    # the usual one will do.
    # Deprecated in virtualenv==16.7.9
    # See: https://github.com/pypa/virtualenv/issues/1622
    try:
        prefix: str = sys.real_prefix  # type: ignore[attr-defined]
    except AttributeError:
        prefix = sys.prefix

    def _posix_path(path: str) -> str:
        base_python = "python%d.%d" % sys.version_info[:2]
        return os.path.join(prefix, path, base_python)

    STD_LIB_DIRS.add(_posix_path("lib"))
    if sys.maxsize > 2**32:
        # This tries to fix a problem with /usr/lib64 builds,
        # where systems are running both 32-bit and 64-bit code
        # on the same machine, which reflects into the places where
        # standard library could be found. More details can be found
        # here http://bugs.python.org/issue1294959.
        # An easy reproducing case would be
        # https://github.com/pylint-dev/pylint/issues/712#issuecomment-163178753
        STD_LIB_DIRS.add(_posix_path("lib64"))

EXT_LIB_DIRS = {sysconfig.get_path("purelib"), sysconfig.get_path("platlib")}
BUILTIN_MODULES = dict.fromkeys(sys.builtin_module_names, True)


class NoSourceFile(Exception):
    """Exception raised when we are not able to get a python
    source file for a precompiled file.
    """


def _normalize_path(path: str) -> str:
    """Resolve symlinks in path and convert to absolute path.

    Note that environment variables and ~ in the path need to be expanded in
    advance.

    This can be cached by using _cache_normalize_path.
    """
    return os.path.normcase(os.path.realpath(path))


def _path_from_filename(filename: str, is_jython: bool = IS_JYTHON) -> str:
    if not is_jython:
        return filename
    head, has_pyclass, _ = filename.partition("$py.class")
    if has_pyclass:
        return head + ".py"
    return filename


def _handle_blacklist(
    blacklist: Sequence[str], dirnames: list[str], filenames: list[str]
) -> None:
    """Remove files/directories in the black list.

    dirnames/filenames are usually from os.walk
    """
    for norecurs in blacklist:
        if norecurs in dirnames:
            dirnames.remove(norecurs)
        elif norecurs in filenames:
            filenames.remove(norecurs)


@lru_cache
def _cache_normalize_path_(path: str) -> str:
    return _normalize_path(path)


def _cache_normalize_path(path: str) -> str:
    """Normalize path with caching."""
    # _module_file calls abspath on every path in sys.path every time it's
    # called; on a larger codebase this easily adds up to half a second just
    # assembling path components. This cache alleviates that.
    if not path:  # don't cache result for ''
        return _normalize_path(path)
    return _cache_normalize_path_(path)


def load_module_from_name(dotted_name: str) -> types.ModuleType:
    """Load a Python module from its name.

    :type dotted_name: str
    :param dotted_name: python name of a module or package

    :raise ImportError: if the module or package is not found

    :rtype: module
    :return: the loaded module
    """
    try:
        return sys.modules[dotted_name]
    except KeyError:
        pass

    # Capture and log anything emitted during import to avoid
    # contaminating JSON reports in pylint
    with redirect_stderr(io.StringIO()) as stderr, redirect_stdout(
        io.StringIO()
    ) as stdout:
        module = importlib.import_module(dotted_name)

    stderr_value = stderr.getvalue()
    if stderr_value:
        logger.error(
            "Captured stderr while importing %s:\n%s", dotted_name, stderr_value
        )
    stdout_value = stdout.getvalue()
    if stdout_value:
        logger.info(
            "Captured stdout while importing %s:\n%s", dotted_name, stdout_value
        )

    return module


def load_module_from_modpath(parts: Sequence[str]) -> types.ModuleType:
    """Load a python module from its split name.

    :param parts:
      python name of a module or package split on '.'

    :raise ImportError: if the module or package is not found

    :return: the loaded module
    """
    return load_module_from_name(".".join(parts))


def load_module_from_file(filepath: str) -> types.ModuleType:
    """Load a Python module from it's path.

    :type filepath: str
    :param filepath: path to the python module or package

    :raise ImportError: if the module or package is not found

    :rtype: module
    :return: the loaded module
    """
    modpath = modpath_from_file(filepath)
    return load_module_from_modpath(modpath)


def check_modpath_has_init(path: str, mod_path: list[str]) -> bool:
    """Check there are some __init__.py all along the way."""
    modpath: list[str] = []
    for part in mod_path:
        modpath.append(part)
        path = os.path.join(path, part)
        if not _has_init(path):
            old_namespace = util.is_namespace(".".join(modpath))
            if not old_namespace:
                return False
    return True


def _get_relative_base_path(filename: str, path_to_check: str) -> list[str] | None:
    """Extracts the relative mod path of the file to import from.

    Check if a file is within the passed in path and if so, returns the
    relative mod path from the one passed in.

    If the filename is no in path_to_check, returns None

    Note this function will look for both abs and realpath of the file,
    this allows to find the relative base path even if the file is a
    symlink of a file in the passed in path

    Examples:
        _get_relative_base_path("/a/b/c/d.py", "/a/b") ->  ["c","d"]
        _get_relative_base_path("/a/b/c/d.py", "/dev") ->  None
    """
    importable_path = None
    path_to_check = os.path.normcase(path_to_check)
    abs_filename = os.path.abspath(filename)
    if os.path.normcase(abs_filename).startswith(path_to_check):
        importable_path = abs_filename

    real_filename = os.path.realpath(filename)
    if os.path.normcase(real_filename).startswith(path_to_check):
        importable_path = real_filename

    if importable_path:
        base_path = os.path.splitext(importable_path)[0]
        relative_base_path = base_path[len(path_to_check) :]
        return [pkg for pkg in relative_base_path.split(os.sep) if pkg]

    return None


def modpath_from_file_with_callback(
    filename: str,
    path: Sequence[str] | None = None,
    is_package_cb: Callable[[str, list[str]], bool] | None = None,
) -> list[str]:
    filename = os.path.expanduser(_path_from_filename(filename))
    paths_to_check = sys.path.copy()
    if path:
        paths_to_check += path
    for pathname in itertools.chain(
        paths_to_check, map(_cache_normalize_path, paths_to_check)
    ):
        if not pathname:
            continue
        modpath = _get_relative_base_path(filename, pathname)
        if not modpath:
            continue
        assert is_package_cb is not None
        if is_package_cb(pathname, modpath[:-1]):
            return modpath

    raise ImportError(
        "Unable to find module for {} in {}".format(filename, ", \n".join(sys.path))
    )


def modpath_from_file(filename: str, path: Sequence[str] | None = None) -> list[str]:
    """Get the corresponding split module's name from a filename.

    This function will return the name of a module or package split on `.`.

    :type filename: str
    :param filename: file's path for which we want the module's name

    :type Optional[List[str]] path:
      Optional list of path where the module or package should be
      searched (use sys.path if nothing or None is given)

    :raise ImportError:
      if the corresponding module's name has not been found

    :rtype: list(str)
    :return: the corresponding split module's name
    """
    return modpath_from_file_with_callback(filename, path, check_modpath_has_init)


def file_from_modpath(
    modpath: list[str],
    path: Sequence[str] | None = None,
    context_file: str | None = None,
) -> str | None:
    return file_info_from_modpath(modpath, path, context_file).location


def file_info_from_modpath(
    modpath: list[str],
    path: Sequence[str] | None = None,
    context_file: str | None = None,
) -> spec.ModuleSpec:
    """Given a mod path (i.e. split module / package name), return the
    corresponding file.

    Giving priority to source file over precompiled file if it exists.

    :param modpath:
      split module's name (i.e name of a module or package split
      on '.')
      (this means explicit relative imports that start with dots have
      empty strings in this list!)

    :param path:
      optional list of path where the module or package should be
      searched (use sys.path if nothing or None is given)

    :param context_file:
      context file to consider, necessary if the identifier has been
      introduced using a relative import unresolvable in the actual
      context (i.e. modutils)

    :raise ImportError: if there is no such module in the directory

    :return:
      the path to the module's file or None if it's an integrated
      builtin module such as 'sys'
    """
    if context_file is not None:
        context: str | None = os.path.dirname(context_file)
    else:
        context = context_file
    if modpath[0] == "xml":
        # handle _xmlplus
        try:
            return _spec_from_modpath(["_xmlplus"] + modpath[1:], path, context)
        except ImportError:
            return _spec_from_modpath(modpath, path, context)
    elif modpath == ["os", "path"]:
        # FIXME: currently ignoring search_path...
        return spec.ModuleSpec(
            name="os.path",
            location=os.path.__file__,
            type=spec.ModuleType.PY_SOURCE,
        )
    return _spec_from_modpath(modpath, path, context)


def get_module_part(dotted_name: str, context_file: str | None = None) -> str:
    """Given a dotted name return the module part of the name :

    >>> get_module_part('astroid.as_string.dump')
    'astroid.as_string'

    :param dotted_name: full name of the identifier we are interested in

    :param context_file:
      context file to consider, necessary if the identifier has been
      introduced using a relative import unresolvable in the actual
      context (i.e. modutils)

    :raise ImportError: if there is no such module in the directory

    :return:
      the module part of the name or None if we have not been able at
      all to import the given name

    XXX: deprecated, since it doesn't handle package precedence over module
    (see #10066)
    """
    # os.path trick
    if dotted_name.startswith("os.path"):
        return "os.path"
    parts = dotted_name.split(".")
    if context_file is not None:
        # first check for builtin module which won't be considered latter
        # in that case (path != None)
        if parts[0] in BUILTIN_MODULES:
            if len(parts) > 2:
                raise ImportError(dotted_name)
            return parts[0]
        # don't use += or insert, we want a new list to be created !
    path: list[str] | None = None
    starti = 0
    if parts[0] == "":
        assert (
            context_file is not None
        ), "explicit relative import, but no context_file?"
        path = []  # prevent resolving the import non-relatively
        starti = 1
    # for all further dots: change context
    while starti < len(parts) and parts[starti] == "":
        starti += 1
        assert (
            context_file is not None
        ), "explicit relative import, but no context_file?"
        context_file = os.path.dirname(context_file)
    for i in range(starti, len(parts)):
        try:
            file_from_modpath(
                parts[starti : i + 1], path=path, context_file=context_file
            )
        except ImportError:
            if i < max(1, len(parts) - 2):
                raise
            return ".".join(parts[:i])
    return dotted_name


def get_module_files(
    src_directory: str, blacklist: Sequence[str], list_all: bool = False
) -> list[str]:
    """Given a package directory return a list of all available python
    module's files in the package and its subpackages.

    :param src_directory:
      path of the directory corresponding to the package

    :param blacklist: iterable
      list of files or directories to ignore.

    :param list_all:
        get files from all paths, including ones without __init__.py

    :return:
      the list of all available python module's files in the package and
      its subpackages
    """
    files: list[str] = []
    for directory, dirnames, filenames in os.walk(src_directory):
        if directory in blacklist:
            continue
        _handle_blacklist(blacklist, dirnames, filenames)
        # check for __init__.py
        if not list_all and {"__init__.py", "__init__.pyi"}.isdisjoint(filenames):
            dirnames[:] = ()
            continue
        for filename in filenames:
            if _is_python_file(filename):
                src = os.path.join(directory, filename)
                files.append(src)
    return files


def get_source_file(filename: str, include_no_ext: bool = False) -> str:
    """Given a python module's file name return the matching source file
    name (the filename will be returned identically if it's already an
    absolute path to a python source file).

    :param filename: python module's file name

    :raise NoSourceFile: if no source file exists on the file system

    :return: the absolute path of the source file if it exists
    """
    filename = os.path.abspath(_path_from_filename(filename))
    base, orig_ext = os.path.splitext(filename)
    if orig_ext == ".pyi" and os.path.exists(f"{base}{orig_ext}"):
        return f"{base}{orig_ext}"
    for ext in PY_SOURCE_EXTS:
        source_path = f"{base}.{ext}"
        if os.path.exists(source_path):
            return source_path
    if include_no_ext and not orig_ext and os.path.exists(base):
        return base
    raise NoSourceFile(filename)


def is_python_source(filename: str | None) -> bool:
    """Return: True if the filename is a python source file."""
    if not filename:
        return False
    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).

    :param modname: name of the module we are interested in

    :param std_path: list of path considered has standard

    :return:
      true if the module:
      - 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])
    except ImportError:
        # import failed, i'm probably not so wrong by supposing it's
        # not standard...
        return False
    # modules which are not living in a file are considered standard
    # (sys and __builtin__ for instance)
    if filename is None:
        # we assume there are no namespaces in stdlib
        return not util.is_namespace(modname)
    filename = _normalize_path(filename)
    for path in EXT_LIB_DIRS:
        if filename.startswith(_cache_normalize_path(path)):
            return False
    if std_path is None:
        std_path = STD_LIB_DIRS

    return any(filename.startswith(_cache_normalize_path(path)) for path in std_path)


def is_relative(modname: str, from_file: str) -> bool:
    """Return true if the given module name is relative to the given
    file name.

    :param modname: name of the module we are interested in

    :param from_file:
      path of the module from which modname has been imported

    :return:
      true if the module has been imported relatively to `from_file`
    """
    if not os.path.isdir(from_file):
        from_file = os.path.dirname(from_file)
    if from_file in sys.path:
        return False
    return bool(
        importlib.machinery.PathFinder.find_spec(
            modname.split(".", maxsplit=1)[0], [from_file]
        )
    )


# internal only functions #####################################################


def _spec_from_modpath(
    modpath: list[str],
    path: Sequence[str] | None = None,
    context: str | None = None,
) -> spec.ModuleSpec:
    """Given a mod path (i.e. split module / package name), return the
    corresponding spec.

    this function is used internally, see `file_from_modpath`'s
    documentation for more information
    """
    assert modpath
    location = None
    if context is not None:
        try:
            found_spec = spec.find_spec(modpath, [context])
            location = found_spec.location
        except ImportError:
            found_spec = spec.find_spec(modpath, path)
            location = found_spec.location
    else:
        found_spec = spec.find_spec(modpath, path)
    if found_spec.type == spec.ModuleType.PY_COMPILED:
        try:
            assert found_spec.location is not None
            location = get_source_file(found_spec.location)
            return found_spec._replace(
                location=location, type=spec.ModuleType.PY_SOURCE
            )
        except NoSourceFile:
            return found_spec._replace(location=location)
    elif found_spec.type == spec.ModuleType.C_BUILTIN:
        # integrated builtin module
        return found_spec._replace(location=None)
    elif found_spec.type == spec.ModuleType.PKG_DIRECTORY:
        assert found_spec.location is not None
        location = _has_init(found_spec.location)
        return found_spec._replace(location=location, type=spec.ModuleType.PY_SOURCE)
    return found_spec


def _is_python_file(filename: str) -> bool:
    """Return true if the given filename should be considered as a python file.

    .pyc and .pyo are ignored
    """
    return filename.endswith((".py", ".pyi", ".so", ".pyd", ".pyw"))


def _has_init(directory: str) -> str | None:
    """If the given directory has a valid __init__ file, return its path,
    else return None.
    """
    mod_or_pack = os.path.join(directory, "__init__")
    for ext in (*PY_SOURCE_EXTS, "pyc", "pyo"):
        if os.path.exists(mod_or_pack + "." + ext):
            return mod_or_pack + "." + ext
    return None


def is_namespace(specobj: spec.ModuleSpec) -> bool:
    return specobj.type == spec.ModuleType.PY_NAMESPACE


def is_directory(specobj: spec.ModuleSpec) -> bool:
    return specobj.type == spec.ModuleType.PKG_DIRECTORY


def is_module_name_part_of_extension_package_whitelist(
    module_name: str, package_whitelist: set[str]
) -> bool:
    """
    Returns True if one part of the module name is in the package whitelist.

    >>> is_module_name_part_of_extension_package_whitelist('numpy.core.umath', {'numpy'})
    True
    """
    parts = module_name.split(".")
    return any(
        ".".join(parts[:x]) in package_whitelist for x in range(1, len(parts) + 1)
    )

back/venvmsg/Lib/site-packages/astroid/modutils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Every available node class.

.. seealso::
    :doc:`ast documentation <green_tree_snakes:nodes>`

All nodes inherit from :class:`~astroid.nodes.node_classes.NodeNG`.
"""

# Nodes not present in the builtin ast module:  DictUnpack, Unknown, and EvaluatedObject.
from astroid.nodes.node_classes import (
    CONST_CLS,
    AnnAssign,
    Arguments,
    Assert,
    Assign,
    AssignAttr,
    AssignName,
    AsyncFor,
    AsyncWith,
    Attribute,
    AugAssign,
    Await,
    BaseContainer,
    BinOp,
    BoolOp,
    Break,
    Call,
    Compare,
    Comprehension,
    Const,
    Continue,
    Decorators,
    DelAttr,
    Delete,
    DelName,
    Dict,
    DictUnpack,
    EmptyNode,
    EvaluatedObject,
    ExceptHandler,
    Expr,
    For,
    FormattedValue,
    Global,
    If,
    IfExp,
    Import,
    ImportFrom,
    JoinedStr,
    Keyword,
    List,
    Match,
    MatchAs,
    MatchCase,
    MatchClass,
    MatchMapping,
    MatchOr,
    MatchSequence,
    MatchSingleton,
    MatchStar,
    MatchValue,
    Name,
    NamedExpr,
    NodeNG,
    Nonlocal,
    ParamSpec,
    Pass,
    Pattern,
    Raise,
    Return,
    Set,
    Slice,
    Starred,
    Subscript,
    Try,
    TryStar,
    Tuple,
    TypeAlias,
    TypeVar,
    TypeVarTuple,
    UnaryOp,
    Unknown,
    While,
    With,
    Yield,
    YieldFrom,
    are_exclusive,
    const_factory,
    unpack_infer,
)
from astroid.nodes.scoped_nodes import (
    AsyncFunctionDef,
    ClassDef,
    ComprehensionScope,
    DictComp,
    FunctionDef,
    GeneratorExp,
    Lambda,
    ListComp,
    LocalsDictNodeNG,
    Module,
    SetComp,
    builtin_lookup,
    function_to_method,
    get_wrapping_class,
)
from astroid.nodes.utils import Position

ALL_NODE_CLASSES = (
    BaseContainer,
    AnnAssign,
    Arguments,
    Assert,
    Assign,
    AssignAttr,
    AssignName,
    AsyncFor,
    AsyncFunctionDef,
    AsyncWith,
    Attribute,
    AugAssign,
    Await,
    BinOp,
    BoolOp,
    Break,
    Call,
    ClassDef,
    Compare,
    Comprehension,
    ComprehensionScope,
    Const,
    const_factory,
    Continue,
    Decorators,
    DelAttr,
    Delete,
    DelName,
    Dict,
    DictComp,
    DictUnpack,
    EmptyNode,
    EvaluatedObject,
    ExceptHandler,
    Expr,
    For,
    FormattedValue,
    FunctionDef,
    GeneratorExp,
    Global,
    If,
    IfExp,
    Import,
    ImportFrom,
    JoinedStr,
    Keyword,
    Lambda,
    List,
    ListComp,
    LocalsDictNodeNG,
    Match,
    MatchAs,
    MatchCase,
    MatchClass,
    MatchMapping,
    MatchOr,
    MatchSequence,
    MatchSingleton,
    MatchStar,
    MatchValue,
    Module,
    Name,
    NamedExpr,
    NodeNG,
    Nonlocal,
    ParamSpec,
    Pass,
    Pattern,
    Raise,
    Return,
    Set,
    SetComp,
    Slice,
    Starred,
    Subscript,
    Try,
    TryStar,
    Tuple,
    TypeAlias,
    TypeVar,
    TypeVarTuple,
    UnaryOp,
    Unknown,
    While,
    With,
    Yield,
    YieldFrom,
)

__all__ = (
    "AnnAssign",
    "are_exclusive",
    "Arguments",
    "Assert",
    "Assign",
    "AssignAttr",
    "AssignName",
    "AsyncFor",
    "AsyncFunctionDef",
    "AsyncWith",
    "Attribute",
    "AugAssign",
    "Await",
    "BaseContainer",
    "BinOp",
    "BoolOp",
    "Break",
    "builtin_lookup",
    "Call",
    "ClassDef",
    "CONST_CLS",
    "Compare",
    "Comprehension",
    "ComprehensionScope",
    "Const",
    "const_factory",
    "Continue",
    "Decorators",
    "DelAttr",
    "Delete",
    "DelName",
    "Dict",
    "DictComp",
    "DictUnpack",
    "EmptyNode",
    "EvaluatedObject",
    "ExceptHandler",
    "Expr",
    "For",
    "FormattedValue",
    "FunctionDef",
    "function_to_method",
    "GeneratorExp",
    "get_wrapping_class",
    "Global",
    "If",
    "IfExp",
    "Import",
    "ImportFrom",
    "JoinedStr",
    "Keyword",
    "Lambda",
    "List",
    "ListComp",
    "LocalsDictNodeNG",
    "Match",
    "MatchAs",
    "MatchCase",
    "MatchClass",
    "MatchMapping",
    "MatchOr",
    "MatchSequence",
    "MatchSingleton",
    "MatchStar",
    "MatchValue",
    "Module",
    "Name",
    "NamedExpr",
    "NodeNG",
    "Nonlocal",
    "ParamSpec",
    "Pass",
    "Position",
    "Raise",
    "Return",
    "Set",
    "SetComp",
    "Slice",
    "Starred",
    "Subscript",
    "Try",
    "TryStar",
    "Tuple",
    "TypeAlias",
    "TypeVar",
    "TypeVarTuple",
    "UnaryOp",
    "Unknown",
    "unpack_infer",
    "While",
    "With",
    "Yield",
    "YieldFrom",
)

back/venvmsg/Lib/site-packages/astroid/nodes/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""This module contains some base nodes that can be inherited for the different nodes.

Previously these were called Mixin nodes.
"""

from __future__ import annotations

import itertools
from collections.abc import Generator, Iterator
from functools import cached_property, lru_cache, partial
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, Union

from astroid import bases, nodes, util
from astroid.const import PY310_PLUS
from astroid.context import (
    CallContext,
    InferenceContext,
    bind_context_to_node,
)
from astroid.exceptions import (
    AttributeInferenceError,
    InferenceError,
)
from astroid.interpreter import dunder_lookup
from astroid.nodes.node_ng import NodeNG
from astroid.typing import InferenceResult

if TYPE_CHECKING:
    from astroid.nodes.node_classes import LocalsDictNodeNG

    GetFlowFactory = Callable[
        [
            InferenceResult,
            Optional[InferenceResult],
            Union[nodes.AugAssign, nodes.BinOp],
            InferenceResult,
            Optional[InferenceResult],
            InferenceContext,
            InferenceContext,
        ],
        list[partial[Generator[InferenceResult, None, None]]],
    ]


class Statement(NodeNG):
    """Statement node adding a few attributes.

    NOTE: This class is part of the public API of 'astroid.nodes'.
    """

    is_statement = True
    """Whether this node indicates a statement."""

    def next_sibling(self):
        """The next sibling statement node.

        :returns: The next sibling statement node.
        :rtype: NodeNG or None
        """
        stmts = self.parent.child_sequence(self)
        index = stmts.index(self)
        try:
            return stmts[index + 1]
        except IndexError:
            return None

    def previous_sibling(self):
        """The previous sibling statement.

        :returns: The previous sibling statement node.
        :rtype: NodeNG or None
        """
        stmts = self.parent.child_sequence(self)
        index = stmts.index(self)
        if index >= 1:
            return stmts[index - 1]
        return None


class NoChildrenNode(NodeNG):
    """Base nodes for nodes with no children, e.g. Pass."""

    def get_children(self) -> Iterator[NodeNG]:
        yield from ()


class FilterStmtsBaseNode(NodeNG):
    """Base node for statement filtering and assignment type."""

    def _get_filtered_stmts(self, _, node, _stmts, mystmt: Statement | None):
        """Method used in _filter_stmts to get statements and trigger break."""
        if self.statement() is mystmt:
            # original node's statement is the assignment, only keep
            # current node (gen exp, list comp)
            return [node], True
        return _stmts, False

    def assign_type(self):
        return self


class AssignTypeNode(NodeNG):
    """Base node for nodes that can 'assign' such as AnnAssign."""

    def assign_type(self):
        return self

    def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt: Statement | None):
        """Method used in filter_stmts."""
        if self is mystmt:
            return _stmts, True
        if self.statement() is mystmt:
            # original node's statement is the assignment, only keep
            # current node (gen exp, list comp)
            return [node], True
        return _stmts, False


class ParentAssignNode(AssignTypeNode):
    """Base node for nodes whose assign_type is determined by the parent node."""

    def assign_type(self):
        return self.parent.assign_type()


class ImportNode(FilterStmtsBaseNode, NoChildrenNode, Statement):
    """Base node for From and Import Nodes."""

    modname: str | None
    """The module that is being imported from.

    This is ``None`` for relative imports.
    """

    names: list[tuple[str, str | None]]
    """What is being imported from the module.

    Each entry is a :class:`tuple` of the name being imported,
    and the alias that the name is assigned to (if any).
    """

    def _infer_name(self, frame, name):
        return name

    def do_import_module(self, modname: str | None = None) -> nodes.Module:
        """Return the ast for a module whose name is <modname> imported by <self>."""
        mymodule = self.root()
        level: int | None = getattr(self, "level", None)  # Import has no level
        if modname is None:
            modname = self.modname
        # If the module ImportNode is importing is a module with the same name
        # as the file that contains the ImportNode we don't want to use the cache
        # to make sure we use the import system to get the correct module.
        if (
            modname
            # pylint: disable-next=no-member # pylint doesn't recognize type of mymodule
            and mymodule.relative_to_absolute_name(modname, level) == mymodule.name
        ):
            use_cache = False
        else:
            use_cache = True

        # pylint: disable-next=no-member # pylint doesn't recognize type of mymodule
        return mymodule.import_module(
            modname,
            level=level,
            relative_only=bool(level and level >= 1),
            use_cache=use_cache,
        )

    def real_name(self, asname: str) -> str:
        """Get name from 'as' name."""
        for name, _asname in self.names:
            if name == "*":
                return asname
            if not _asname:
                name = name.split(".", 1)[0]
                _asname = name
            if asname == _asname:
                return name
        raise AttributeInferenceError(
            "Could not find original name for {attribute} in {target!r}",
            target=self,
            attribute=asname,
        )


class MultiLineBlockNode(NodeNG):
    """Base node for multi-line blocks, e.g. For and FunctionDef.

    Note that this does not apply to every node with a `body` field.
    For instance, an If node has a multi-line body, but the body of an
    IfExpr is not multi-line, and hence cannot contain Return nodes,
    Assign nodes, etc.
    """

    _multi_line_block_fields: ClassVar[tuple[str, ...]] = ()

    @cached_property
    def _multi_line_blocks(self):
        return tuple(getattr(self, field) for field in self._multi_line_block_fields)

    def _get_return_nodes_skip_functions(self):
        for block in self._multi_line_blocks:
            for child_node in block:
                if child_node.is_function:
                    continue
                yield from child_node._get_return_nodes_skip_functions()

    def _get_yield_nodes_skip_functions(self):
        for block in self._multi_line_blocks:
            for child_node in block:
                if child_node.is_function:
                    continue
                yield from child_node._get_yield_nodes_skip_functions()

    def _get_yield_nodes_skip_lambdas(self):
        for block in self._multi_line_blocks:
            for child_node in block:
                if child_node.is_lambda:
                    continue
                yield from child_node._get_yield_nodes_skip_lambdas()

    @cached_property
    def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
        children_assign_nodes = (
            child_node._assign_nodes_in_scope
            for block in self._multi_line_blocks
            for child_node in block
        )
        return list(itertools.chain.from_iterable(children_assign_nodes))


class MultiLineWithElseBlockNode(MultiLineBlockNode):
    """Base node for multi-line blocks that can have else statements."""

    @cached_property
    def blockstart_tolineno(self):
        return self.lineno

    def _elsed_block_range(
        self, lineno: int, orelse: list[nodes.NodeNG], last: int | None = None
    ) -> tuple[int, int]:
        """Handle block line numbers range for try/finally, for, if and while
        statements.
        """
        if lineno == self.fromlineno:
            return lineno, lineno
        if orelse:
            if lineno >= orelse[0].fromlineno:
                return lineno, orelse[-1].tolineno
            return lineno, orelse[0].fromlineno - 1
        return lineno, last or self.tolineno


class LookupMixIn(NodeNG):
    """Mixin to look up a name in the right scope."""

    @lru_cache  # noqa
    def lookup(self, name: str) -> tuple[LocalsDictNodeNG, list[NodeNG]]:
        """Lookup where the given variable is assigned.

        The lookup starts from self's scope. If self is not a frame itself
        and the name is found in the inner frame locals, statements will be
        filtered to remove ignorable statements according to self's location.

        :param name: The name of the variable to find assignments for.

        :returns: The scope node and the list of assignments associated to the
            given name according to the scope where it has been found (locals,
            globals or builtin).
        """
        return self.scope().scope_lookup(self, name)

    def ilookup(self, name):
        """Lookup the inferred values of the given variable.

        :param name: The variable name to find values for.
        :type name: str

        :returns: The inferred values of the statements returned from
            :meth:`lookup`.
        :rtype: iterable
        """
        frame, stmts = self.lookup(name)
        context = InferenceContext()
        return bases._infer_stmts(stmts, context, frame)


def _reflected_name(name) -> str:
    return "__r" + name[2:]


def _augmented_name(name) -> str:
    return "__i" + name[2:]


BIN_OP_METHOD = {
    "+": "__add__",
    "-": "__sub__",
    "/": "__truediv__",
    "//": "__floordiv__",
    "*": "__mul__",
    "**": "__pow__",
    "%": "__mod__",
    "&": "__and__",
    "|": "__or__",
    "^": "__xor__",
    "<<": "__lshift__",
    ">>": "__rshift__",
    "@": "__matmul__",
}

REFLECTED_BIN_OP_METHOD = {
    key: _reflected_name(value) for (key, value) in BIN_OP_METHOD.items()
}
AUGMENTED_OP_METHOD = {
    key + "=": _augmented_name(value) for (key, value) in BIN_OP_METHOD.items()
}


class OperatorNode(NodeNG):
    @staticmethod
    def _filter_operation_errors(
        infer_callable: Callable[
            [InferenceContext | None],
            Generator[InferenceResult | util.BadOperationMessage, None, None],
        ],
        context: InferenceContext | None,
        error: type[util.BadOperationMessage],
    ) -> Generator[InferenceResult, None, None]:
        for result in infer_callable(context):
            if isinstance(result, error):
                # For the sake of .infer(), we don't care about operation
                # errors, which is the job of a linter. So return something
                # which shows that we can't infer the result.
                yield util.Uninferable
            else:
                yield result

    @staticmethod
    def _is_not_implemented(const) -> bool:
        """Check if the given const node is NotImplemented."""
        return isinstance(const, nodes.Const) and const.value is NotImplemented

    @staticmethod
    def _infer_old_style_string_formatting(
        instance: nodes.Const, other: nodes.NodeNG, context: InferenceContext
    ) -> tuple[util.UninferableBase | nodes.Const]:
        """Infer the result of '"string" % ...'.

        TODO: Instead of returning Uninferable we should rely
        on the call to '%' to see if the result is actually uninferable.
        """
        if isinstance(other, nodes.Tuple):
            if util.Uninferable in other.elts:
                return (util.Uninferable,)
            inferred_positional = [util.safe_infer(i, context) for i in other.elts]
            if all(isinstance(i, nodes.Const) for i in inferred_positional):
                values = tuple(i.value for i in inferred_positional)
            else:
                values = None
        elif isinstance(other, nodes.Dict):
            values: dict[Any, Any] = {}
            for pair in other.items:
                key = util.safe_infer(pair[0], context)
                if not isinstance(key, nodes.Const):
                    return (util.Uninferable,)
                value = util.safe_infer(pair[1], context)
                if not isinstance(value, nodes.Const):
                    return (util.Uninferable,)
                values[key.value] = value.value
        elif isinstance(other, nodes.Const):
            values = other.value
        else:
            return (util.Uninferable,)

        try:
            return (nodes.const_factory(instance.value % values),)
        except (TypeError, KeyError, ValueError):
            return (util.Uninferable,)

    @staticmethod
    def _invoke_binop_inference(
        instance: InferenceResult,
        opnode: nodes.AugAssign | nodes.BinOp,
        op: str,
        other: InferenceResult,
        context: InferenceContext,
        method_name: str,
    ) -> Generator[InferenceResult, None, None]:
        """Invoke binary operation inference on the given instance."""
        methods = dunder_lookup.lookup(instance, method_name)
        context = bind_context_to_node(context, instance)
        method = methods[0]
        context.callcontext.callee = method

        if (
            isinstance(instance, nodes.Const)
            and isinstance(instance.value, str)
            and op == "%"
        ):
            return iter(
                OperatorNode._infer_old_style_string_formatting(
                    instance, other, context
                )
            )

        try:
            inferred = next(method.infer(context=context))
        except StopIteration as e:
            raise InferenceError(node=method, context=context) from e
        if isinstance(inferred, util.UninferableBase):
            raise InferenceError
        if not isinstance(
            instance,
            (nodes.Const, nodes.Tuple, nodes.List, nodes.ClassDef, bases.Instance),
        ):
            raise InferenceError  # pragma: no cover # Used as a failsafe
        return instance.infer_binary_op(opnode, op, other, context, inferred)

    @staticmethod
    def _aug_op(
        instance: InferenceResult,
        opnode: nodes.AugAssign,
        op: str,
        other: InferenceResult,
        context: InferenceContext,
        reverse: bool = False,
    ) -> partial[Generator[InferenceResult, None, None]]:
        """Get an inference callable for an augmented binary operation."""
        method_name = AUGMENTED_OP_METHOD[op]
        return partial(
            OperatorNode._invoke_binop_inference,
            instance=instance,
            op=op,
            opnode=opnode,
            other=other,
            context=context,
            method_name=method_name,
        )

    @staticmethod
    def _bin_op(
        instance: InferenceResult,
        opnode: nodes.AugAssign | nodes.BinOp,
        op: str,
        other: InferenceResult,
        context: InferenceContext,
        reverse: bool = False,
    ) -> partial[Generator[InferenceResult, None, None]]:
        """Get an inference callable for a normal binary operation.

        If *reverse* is True, then the reflected method will be used instead.
        """
        if reverse:
            method_name = REFLECTED_BIN_OP_METHOD[op]
        else:
            method_name = BIN_OP_METHOD[op]
        return partial(
            OperatorNode._invoke_binop_inference,
            instance=instance,
            op=op,
            opnode=opnode,
            other=other,
            context=context,
            method_name=method_name,
        )

    @staticmethod
    def _bin_op_or_union_type(
        left: bases.UnionType | nodes.ClassDef | nodes.Const,
        right: bases.UnionType | nodes.ClassDef | nodes.Const,
    ) -> Generator[InferenceResult, None, None]:
        """Create a new UnionType instance for binary or, e.g. int | str."""
        yield bases.UnionType(left, right)

    @staticmethod
    def _get_binop_contexts(context, left, right):
        """Get contexts for binary operations.

        This will return two inference contexts, the first one
        for x.__op__(y), the other one for y.__rop__(x), where
        only the arguments are inversed.
        """
        # The order is important, since the first one should be
        # left.__op__(right).
        for arg in (right, left):
            new_context = context.clone()
            new_context.callcontext = CallContext(args=[arg])
            new_context.boundnode = None
            yield new_context

    @staticmethod
    def _same_type(type1, type2) -> bool:
        """Check if type1 is the same as type2."""
        return type1.qname() == type2.qname()

    @staticmethod
    def _get_aug_flow(
        left: InferenceResult,
        left_type: InferenceResult | None,
        aug_opnode: nodes.AugAssign,
        right: InferenceResult,
        right_type: InferenceResult | None,
        context: InferenceContext,
        reverse_context: InferenceContext,
    ) -> list[partial[Generator[InferenceResult, None, None]]]:
        """Get the flow for augmented binary operations.

        The rules are a bit messy:

            * if left and right have the same type, then left.__augop__(right)
            is first tried and then left.__op__(right).
            * if left and right are unrelated typewise, then
            left.__augop__(right) is tried, then left.__op__(right)
            is tried and then right.__rop__(left) is tried.
            * if left is a subtype of right, then left.__augop__(right)
            is tried and then left.__op__(right).
            * if left is a supertype of right, then left.__augop__(right)
            is tried, then right.__rop__(left) and then
            left.__op__(right)
        """
        from astroid import helpers  # pylint: disable=import-outside-toplevel

        bin_op = aug_opnode.op.strip("=")
        aug_op = aug_opnode.op
        if OperatorNode._same_type(left_type, right_type):
            methods = [
                OperatorNode._aug_op(left, aug_opnode, aug_op, right, context),
                OperatorNode._bin_op(left, aug_opnode, bin_op, right, context),
            ]
        elif helpers.is_subtype(left_type, right_type):
            methods = [
                OperatorNode._aug_op(left, aug_opnode, aug_op, right, context),
                OperatorNode._bin_op(left, aug_opnode, bin_op, right, context),
            ]
        elif helpers.is_supertype(left_type, right_type):
            methods = [
                OperatorNode._aug_op(left, aug_opnode, aug_op, right, context),
                OperatorNode._bin_op(
                    right, aug_opnode, bin_op, left, reverse_context, reverse=True
                ),
                OperatorNode._bin_op(left, aug_opnode, bin_op, right, context),
            ]
        else:
            methods = [
                OperatorNode._aug_op(left, aug_opnode, aug_op, right, context),
                OperatorNode._bin_op(left, aug_opnode, bin_op, right, context),
                OperatorNode._bin_op(
                    right, aug_opnode, bin_op, left, reverse_context, reverse=True
                ),
            ]
        return methods

    @staticmethod
    def _get_binop_flow(
        left: InferenceResult,
        left_type: InferenceResult | None,
        binary_opnode: nodes.AugAssign | nodes.BinOp,
        right: InferenceResult,
        right_type: InferenceResult | None,
        context: InferenceContext,
        reverse_context: InferenceContext,
    ) -> list[partial[Generator[InferenceResult, None, None]]]:
        """Get the flow for binary operations.

        The rules are a bit messy:

            * if left and right have the same type, then only one
            method will be called, left.__op__(right)
            * if left and right are unrelated typewise, then first
            left.__op__(right) is tried and if this does not exist
            or returns NotImplemented, then right.__rop__(left) is tried.
            * if left is a subtype of right, then only left.__op__(right)
            is tried.
            * if left is a supertype of right, then right.__rop__(left)
            is first tried and then left.__op__(right)
        """
        from astroid import helpers  # pylint: disable=import-outside-toplevel

        op = binary_opnode.op
        if OperatorNode._same_type(left_type, right_type):
            methods = [OperatorNode._bin_op(left, binary_opnode, op, right, context)]
        elif helpers.is_subtype(left_type, right_type):
            methods = [OperatorNode._bin_op(left, binary_opnode, op, right, context)]
        elif helpers.is_supertype(left_type, right_type):
            methods = [
                OperatorNode._bin_op(
                    right, binary_opnode, op, left, reverse_context, reverse=True
                ),
                OperatorNode._bin_op(left, binary_opnode, op, right, context),
            ]
        else:
            methods = [
                OperatorNode._bin_op(left, binary_opnode, op, right, context),
                OperatorNode._bin_op(
                    right, binary_opnode, op, left, reverse_context, reverse=True
                ),
            ]

        if (
            PY310_PLUS
            and op == "|"
            and (
                isinstance(left, (bases.UnionType, nodes.ClassDef))
                or isinstance(left, nodes.Const)
                and left.value is None
            )
            and (
                isinstance(right, (bases.UnionType, nodes.ClassDef))
                or isinstance(right, nodes.Const)
                and right.value is None
            )
        ):
            methods.extend([partial(OperatorNode._bin_op_or_union_type, left, right)])
        return methods

    @staticmethod
    def _infer_binary_operation(
        left: InferenceResult,
        right: InferenceResult,
        binary_opnode: nodes.AugAssign | nodes.BinOp,
        context: InferenceContext,
        flow_factory: GetFlowFactory,
    ) -> Generator[InferenceResult | util.BadBinaryOperationMessage, None, None]:
        """Infer a binary operation between a left operand and a right operand.

        This is used by both normal binary operations and augmented binary
        operations, the only difference is the flow factory used.
        """
        from astroid import helpers  # pylint: disable=import-outside-toplevel

        context, reverse_context = OperatorNode._get_binop_contexts(
            context, left, right
        )
        left_type = helpers.object_type(left)
        right_type = helpers.object_type(right)
        methods = flow_factory(
            left, left_type, binary_opnode, right, right_type, context, reverse_context
        )
        for method in methods:
            try:
                results = list(method())
            except AttributeError:
                continue
            except AttributeInferenceError:
                continue
            except InferenceError:
                yield util.Uninferable
                return
            else:
                if any(isinstance(result, util.UninferableBase) for result in results):
                    yield util.Uninferable
                    return

                if all(map(OperatorNode._is_not_implemented, results)):
                    continue
                not_implemented = sum(
                    1 for result in results if OperatorNode._is_not_implemented(result)
                )
                if not_implemented and not_implemented != len(results):
                    # Can't infer yet what this is.
                    yield util.Uninferable
                    return

                yield from results
                return

        # The operation doesn't seem to be supported so let the caller know about it
        yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type)

back/venvmsg/Lib/site-packages/astroid/nodes/_base_nodes.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""This module renders Astroid nodes as string"""

from __future__ import annotations

from collections.abc import Iterator
from typing import TYPE_CHECKING

from astroid import nodes

if TYPE_CHECKING:
    from astroid import objects
    from astroid.nodes import Const
    from astroid.nodes.node_classes import (
        Match,
        MatchAs,
        MatchCase,
        MatchClass,
        MatchMapping,
        MatchOr,
        MatchSequence,
        MatchSingleton,
        MatchStar,
        MatchValue,
        Unknown,
    )

# pylint: disable=unused-argument

DOC_NEWLINE = "\0"


# Visitor pattern require argument all the time and is not better with staticmethod
# noinspection PyUnusedLocal,PyMethodMayBeStatic
class AsStringVisitor:
    """Visitor to render an Astroid node as a valid python code string"""

    def __init__(self, indent: str = "    "):
        self.indent: str = indent

    def __call__(self, node) -> str:
        """Makes this visitor behave as a simple function"""
        return node.accept(self).replace(DOC_NEWLINE, "\n")

    def _docs_dedent(self, doc_node: Const | None) -> str:
        """Stop newlines in docs being indented by self._stmt_list"""
        if not doc_node:
            return ""

        return '\n{}"""{}"""'.format(
            self.indent, doc_node.value.replace("\n", DOC_NEWLINE)
        )

    def _stmt_list(self, stmts: list, indent: bool = True) -> str:
        """return a list of nodes to string"""
        stmts_str: str = "\n".join(
            nstr for nstr in [n.accept(self) for n in stmts] if nstr
        )
        if not indent:
            return stmts_str

        return self.indent + stmts_str.replace("\n", "\n" + self.indent)

    def _precedence_parens(self, node, child, is_left: bool = True) -> str:
        """Wrap child in parens only if required to keep same semantics"""
        if self._should_wrap(node, child, is_left):
            return f"({child.accept(self)})"

        return child.accept(self)

    def _should_wrap(self, node, child, is_left: bool) -> bool:
        """Wrap child if:
        - it has lower precedence
        - same precedence with position opposite to associativity direction
        """
        node_precedence = node.op_precedence()
        child_precedence = child.op_precedence()

        if node_precedence > child_precedence:
            # 3 * (4 + 5)
            return True

        if (
            node_precedence == child_precedence
            and is_left != node.op_left_associative()
        ):
            # 3 - (4 - 5)
            # (2**3)**4
            return True

        return False

    # visit_<node> methods ###########################################

    def visit_await(self, node) -> str:
        return f"await {node.value.accept(self)}"

    def visit_asyncwith(self, node) -> str:
        return f"async {self.visit_with(node)}"

    def visit_asyncfor(self, node) -> str:
        return f"async {self.visit_for(node)}"

    def visit_arguments(self, node) -> str:
        """return an astroid.Function node as string"""
        return node.format_args()

    def visit_assignattr(self, node) -> str:
        """return an astroid.AssAttr node as string"""
        return self.visit_attribute(node)

    def visit_assert(self, node) -> str:
        """return an astroid.Assert node as string"""
        if node.fail:
            return f"assert {node.test.accept(self)}, {node.fail.accept(self)}"
        return f"assert {node.test.accept(self)}"

    def visit_assignname(self, node) -> str:
        """return an astroid.AssName node as string"""
        return node.name

    def visit_assign(self, node) -> str:
        """return an astroid.Assign node as string"""
        lhs = " = ".join(n.accept(self) for n in node.targets)
        return f"{lhs} = {node.value.accept(self)}"

    def visit_augassign(self, node) -> str:
        """return an astroid.AugAssign node as string"""
        return f"{node.target.accept(self)} {node.op} {node.value.accept(self)}"

    def visit_annassign(self, node) -> str:
        """Return an astroid.AugAssign node as string"""

        target = node.target.accept(self)
        annotation = node.annotation.accept(self)
        if node.value is None:
            return f"{target}: {annotation}"
        return f"{target}: {annotation} = {node.value.accept(self)}"

    def visit_binop(self, node) -> str:
        """return an astroid.BinOp node as string"""
        left = self._precedence_parens(node, node.left)
        right = self._precedence_parens(node, node.right, is_left=False)
        if node.op == "**":
            return f"{left}{node.op}{right}"

        return f"{left} {node.op} {right}"

    def visit_boolop(self, node) -> str:
        """return an astroid.BoolOp node as string"""
        values = [f"{self._precedence_parens(node, n)}" for n in node.values]
        return (f" {node.op} ").join(values)

    def visit_break(self, node) -> str:
        """return an astroid.Break node as string"""
        return "break"

    def visit_call(self, node) -> str:
        """return an astroid.Call node as string"""
        expr_str = self._precedence_parens(node, node.func)
        args = [arg.accept(self) for arg in node.args]
        if node.keywords:
            keywords = [kwarg.accept(self) for kwarg in node.keywords]
        else:
            keywords = []

        args.extend(keywords)
        return f"{expr_str}({', '.join(args)})"

    def visit_classdef(self, node) -> str:
        """return an astroid.ClassDef node as string"""
        decorate = node.decorators.accept(self) if node.decorators else ""
        args = [n.accept(self) for n in node.bases]
        if node._metaclass and not node.has_metaclass_hack():
            args.append("metaclass=" + node._metaclass.accept(self))
        args += [n.accept(self) for n in node.keywords]
        args_str = f"({', '.join(args)})" if args else ""
        docs = self._docs_dedent(node.doc_node)
        # TODO: handle type_params
        return "\n\n{}class {}{}:{}\n{}\n".format(
            decorate, node.name, args_str, docs, self._stmt_list(node.body)
        )

    def visit_compare(self, node) -> str:
        """return an astroid.Compare node as string"""
        rhs_str = " ".join(
            f"{op} {self._precedence_parens(node, expr, is_left=False)}"
            for op, expr in node.ops
        )
        return f"{self._precedence_parens(node, node.left)} {rhs_str}"

    def visit_comprehension(self, node) -> str:
        """return an astroid.Comprehension node as string"""
        ifs = "".join(f" if {n.accept(self)}" for n in node.ifs)
        generated = f"for {node.target.accept(self)} in {node.iter.accept(self)}{ifs}"
        return f"{'async ' if node.is_async else ''}{generated}"

    def visit_const(self, node) -> str:
        """return an astroid.Const node as string"""
        if node.value is Ellipsis:
            return "..."
        return repr(node.value)

    def visit_continue(self, node) -> str:
        """return an astroid.Continue node as string"""
        return "continue"

    def visit_delete(self, node) -> str:  # XXX check if correct
        """return an astroid.Delete node as string"""
        return f"del {', '.join(child.accept(self) for child in node.targets)}"

    def visit_delattr(self, node) -> str:
        """return an astroid.DelAttr node as string"""
        return self.visit_attribute(node)

    def visit_delname(self, node) -> str:
        """return an astroid.DelName node as string"""
        return node.name

    def visit_decorators(self, node) -> str:
        """return an astroid.Decorators node as string"""
        return "@%s\n" % "\n@".join(item.accept(self) for item in node.nodes)

    def visit_dict(self, node) -> str:
        """return an astroid.Dict node as string"""
        return "{%s}" % ", ".join(self._visit_dict(node))

    def _visit_dict(self, node) -> Iterator[str]:
        for key, value in node.items:
            key = key.accept(self)
            value = value.accept(self)
            if key == "**":
                # It can only be a DictUnpack node.
                yield key + value
            else:
                yield f"{key}: {value}"

    def visit_dictunpack(self, node) -> str:
        return "**"

    def visit_dictcomp(self, node) -> str:
        """return an astroid.DictComp node as string"""
        return "{{{}: {} {}}}".format(
            node.key.accept(self),
            node.value.accept(self),
            " ".join(n.accept(self) for n in node.generators),
        )

    def visit_expr(self, node) -> str:
        """return an astroid.Discard node as string"""
        return node.value.accept(self)

    def visit_emptynode(self, node) -> str:
        """dummy method for visiting an Empty node"""
        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"{n} {node.type.accept(self)} as {node.name.accept(self)}"
            else:
                excs = f"{n} {node.type.accept(self)}"
        else:
            excs = f"{n}"
        return f"{excs}:\n{self._stmt_list(node.body)}"

    def visit_empty(self, node) -> str:
        """return an Empty node as string"""
        return ""

    def visit_for(self, node) -> str:
        """return an astroid.For node as string"""
        fors = "for {} in {}:\n{}".format(
            node.target.accept(self), node.iter.accept(self), self._stmt_list(node.body)
        )
        if node.orelse:
            fors = f"{fors}\nelse:\n{self._stmt_list(node.orelse)}"
        return fors

    def visit_importfrom(self, node) -> str:
        """return an astroid.ImportFrom node as string"""
        return "from {} import {}".format(
            "." * (node.level or 0) + node.modname, _import_string(node.names)
        )

    def visit_joinedstr(self, node) -> str:
        string = "".join(
            # Use repr on the string literal parts
            # to get proper escapes, e.g. \n, \\, \"
            # But strip the quotes off the ends
            # (they will always be one character: ' or ")
            repr(value.value)[1:-1]
            # Literal braces must be doubled to escape them
            .replace("{", "{{").replace("}", "}}")
            # Each value in values is either a string literal (Const)
            # or a FormattedValue
            if type(value).__name__ == "Const" else value.accept(self)
            for value in node.values
        )

        # Try to find surrounding quotes that don't appear at all in the string.
        # Because the formatted values inside {} can't contain backslash (\)
        # using a triple quote is sometimes necessary
        for quote in ("'", '"', '"""', "'''"):
            if quote not in string:
                break

        return "f" + quote + string + quote

    def visit_formattedvalue(self, node) -> str:
        result = node.value.accept(self)
        if node.conversion and node.conversion >= 0:
            # e.g. if node.conversion == 114: result += "!r"
            result += "!" + chr(node.conversion)
        if node.format_spec:
            # The format spec is itself a JoinedString, i.e. an f-string
            # We strip the f and quotes of the ends
            result += ":" + node.format_spec.accept(self)[2:-1]
        return "{%s}" % result

    def handle_functiondef(self, node: nodes.FunctionDef, keyword: str) -> str:
        """return a (possibly async) function definition node as string"""
        decorate = node.decorators.accept(self) if node.decorators else ""
        docs = self._docs_dedent(node.doc_node)
        trailer = ":"
        if node.returns:
            return_annotation = " -> " + node.returns.as_string()
            trailer = return_annotation + ":"
        # TODO: handle type_params
        def_format = "\n%s%s %s(%s)%s%s\n%s"
        return def_format % (
            decorate,
            keyword,
            node.name,
            node.args.accept(self),
            trailer,
            docs,
            self._stmt_list(node.body),
        )

    def visit_functiondef(self, node: nodes.FunctionDef) -> str:
        """return an astroid.FunctionDef node as string"""
        return self.handle_functiondef(node, "def")

    def visit_asyncfunctiondef(self, node: nodes.AsyncFunctionDef) -> str:
        """return an astroid.AsyncFunction node as string"""
        return self.handle_functiondef(node, "async def")

    def visit_generatorexp(self, node) -> str:
        """return an astroid.GeneratorExp node as string"""
        return "({} {})".format(
            node.elt.accept(self), " ".join(n.accept(self) for n in node.generators)
        )

    def visit_attribute(self, node) -> str:
        """return an astroid.Getattr node as string"""
        left = self._precedence_parens(node, node.expr)
        if left.isdigit():
            left = f"({left})"
        return f"{left}.{node.attrname}"

    def visit_global(self, node) -> str:
        """return an astroid.Global node as string"""
        return f"global {', '.join(node.names)}"

    def visit_if(self, node) -> str:
        """return an astroid.If node as string"""
        ifs = [f"if {node.test.accept(self)}:\n{self._stmt_list(node.body)}"]
        if node.has_elif_block():
            ifs.append(f"el{self._stmt_list(node.orelse, indent=False)}")
        elif node.orelse:
            ifs.append(f"else:\n{self._stmt_list(node.orelse)}")
        return "\n".join(ifs)

    def visit_ifexp(self, node) -> str:
        """return an astroid.IfExp node as string"""
        return "{} if {} else {}".format(
            self._precedence_parens(node, node.body, is_left=True),
            self._precedence_parens(node, node.test, is_left=True),
            self._precedence_parens(node, node.orelse, is_left=False),
        )

    def visit_import(self, node) -> str:
        """return an astroid.Import node as string"""
        return f"import {_import_string(node.names)}"

    def visit_keyword(self, node) -> str:
        """return an astroid.Keyword node as string"""
        if node.arg is None:
            return f"**{node.value.accept(self)}"
        return f"{node.arg}={node.value.accept(self)}"

    def visit_lambda(self, node) -> str:
        """return an astroid.Lambda node as string"""
        args = node.args.accept(self)
        body = node.body.accept(self)
        if args:
            return f"lambda {args}: {body}"

        return f"lambda: {body}"

    def visit_list(self, node) -> str:
        """return an astroid.List node as string"""
        return f"[{', '.join(child.accept(self) for child in node.elts)}]"

    def visit_listcomp(self, node) -> str:
        """return an astroid.ListComp node as string"""
        return "[{} {}]".format(
            node.elt.accept(self), " ".join(n.accept(self) for n in node.generators)
        )

    def visit_module(self, node) -> str:
        """return an astroid.Module node as string"""
        docs = f'"""{node.doc_node.value}"""\n\n' if node.doc_node else ""
        return docs + "\n".join(n.accept(self) for n in node.body) + "\n\n"

    def visit_name(self, node) -> str:
        """return an astroid.Name node as string"""
        return node.name

    def visit_namedexpr(self, node) -> str:
        """Return an assignment expression node as string"""
        target = node.target.accept(self)
        value = node.value.accept(self)
        return f"{target} := {value}"

    def visit_nonlocal(self, node) -> str:
        """return an astroid.Nonlocal node as string"""
        return f"nonlocal {', '.join(node.names)}"

    def visit_paramspec(self, node: nodes.ParamSpec) -> str:
        """return an astroid.ParamSpec node as string"""
        return node.name.accept(self)

    def visit_pass(self, node) -> str:
        """return an astroid.Pass node as string"""
        return "pass"

    def visit_partialfunction(self, node: objects.PartialFunction) -> str:
        """Return an objects.PartialFunction as string."""
        return self.visit_functiondef(node)

    def visit_raise(self, node) -> str:
        """return an astroid.Raise node as string"""
        if node.exc:
            if node.cause:
                return f"raise {node.exc.accept(self)} from {node.cause.accept(self)}"
            return f"raise {node.exc.accept(self)}"
        return "raise"

    def visit_return(self, node) -> str:
        """return an astroid.Return node as string"""
        if node.is_tuple_return() and len(node.value.elts) > 1:
            elts = [child.accept(self) for child in node.value.elts]
            return f"return {', '.join(elts)}"

        if node.value:
            return f"return {node.value.accept(self)}"

        return "return"

    def visit_set(self, node) -> str:
        """return an astroid.Set node as string"""
        return "{%s}" % ", ".join(child.accept(self) for child in node.elts)

    def visit_setcomp(self, node) -> str:
        """return an astroid.SetComp node as string"""
        return "{{{} {}}}".format(
            node.elt.accept(self), " ".join(n.accept(self) for n in node.generators)
        )

    def visit_slice(self, node) -> str:
        """return an astroid.Slice node as string"""
        lower = node.lower.accept(self) if node.lower else ""
        upper = node.upper.accept(self) if node.upper else ""
        step = node.step.accept(self) if node.step else ""
        if step:
            return f"{lower}:{upper}:{step}"
        return f"{lower}:{upper}"

    def visit_subscript(self, node) -> str:
        """return an astroid.Subscript node as string"""
        idx = node.slice
        if idx.__class__.__name__.lower() == "index":
            idx = idx.value
        idxstr = idx.accept(self)
        if idx.__class__.__name__.lower() == "tuple" and idx.elts:
            # Remove parenthesis in tuple and extended slice.
            # a[(::1, 1:)] is not valid syntax.
            idxstr = idxstr[1:-1]
        return f"{self._precedence_parens(node, node.value)}[{idxstr}]"

    def visit_try(self, node) -> str:
        """return an astroid.Try 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_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:
            return f"({node.elts[0].accept(self)}, )"
        return f"({', '.join(child.accept(self) for child in node.elts)})"

    def visit_typealias(self, node: nodes.TypeAlias) -> str:
        """return an astroid.TypeAlias node as string"""
        return node.name.accept(self) if node.name else "_"

    def visit_typevar(self, node: nodes.TypeVar) -> str:
        """return an astroid.TypeVar node as string"""
        return node.name.accept(self) if node.name else "_"

    def visit_typevartuple(self, node: nodes.TypeVarTuple) -> str:
        """return an astroid.TypeVarTuple node as string"""
        return "*" + node.name.accept(self) if node.name else ""

    def visit_unaryop(self, node) -> str:
        """return an astroid.UnaryOp node as string"""
        if node.op == "not":
            operator = "not "
        else:
            operator = node.op
        return f"{operator}{self._precedence_parens(node, node.operand)}"

    def visit_while(self, node) -> str:
        """return an astroid.While node as string"""
        whiles = f"while {node.test.accept(self)}:\n{self._stmt_list(node.body)}"
        if node.orelse:
            whiles = f"{whiles}\nelse:\n{self._stmt_list(node.orelse)}"
        return whiles

    def visit_with(self, node) -> str:  # 'with' without 'as' is possible
        """return an astroid.With node as string"""
        items = ", ".join(
            f"{expr.accept(self)}" + (v and f" as {v.accept(self)}" or "")
            for expr, v in node.items
        )
        return f"with {items}:\n{self._stmt_list(node.body)}"

    def visit_yield(self, node) -> str:
        """yield an ast.Yield node as string"""
        yi_val = (" " + node.value.accept(self)) if node.value else ""
        expr = "yield" + yi_val
        if node.parent.is_statement:
            return expr

        return f"({expr})"

    def visit_yieldfrom(self, node) -> str:
        """Return an astroid.YieldFrom node as string."""
        yi_val = (" " + node.value.accept(self)) if node.value else ""
        expr = "yield from" + yi_val
        if node.parent.is_statement:
            return expr

        return f"({expr})"

    def visit_starred(self, node) -> str:
        """return Starred node as string"""
        return "*" + node.value.accept(self)

    def visit_match(self, node: Match) -> str:
        """Return an astroid.Match node as string."""
        return f"match {node.subject.accept(self)}:\n{self._stmt_list(node.cases)}"

    def visit_matchcase(self, node: MatchCase) -> str:
        """Return an astroid.MatchCase node as string."""
        guard_str = f" if {node.guard.accept(self)}" if node.guard else ""
        return (
            f"case {node.pattern.accept(self)}{guard_str}:\n"
            f"{self._stmt_list(node.body)}"
        )

    def visit_matchvalue(self, node: MatchValue) -> str:
        """Return an astroid.MatchValue node as string."""
        return node.value.accept(self)

    @staticmethod
    def visit_matchsingleton(node: MatchSingleton) -> str:
        """Return an astroid.MatchSingleton node as string."""
        return str(node.value)

    def visit_matchsequence(self, node: MatchSequence) -> str:
        """Return an astroid.MatchSequence node as string."""
        if node.patterns is None:
            return "[]"
        return f"[{', '.join(p.accept(self) for p in node.patterns)}]"

    def visit_matchmapping(self, node: MatchMapping) -> str:
        """Return an astroid.MatchMapping node as string."""
        mapping_strings: list[str] = []
        if node.keys and node.patterns:
            mapping_strings.extend(
                f"{key.accept(self)}: {p.accept(self)}"
                for key, p in zip(node.keys, node.patterns)
            )
        if node.rest:
            mapping_strings.append(f"**{node.rest.accept(self)}")
        return f"{'{'}{', '.join(mapping_strings)}{'}'}"

    def visit_matchclass(self, node: MatchClass) -> str:
        """Return an astroid.MatchClass node as string."""
        if node.cls is None:
            raise AssertionError(f"{node} does not have a 'cls' node")
        class_strings: list[str] = []
        if node.patterns:
            class_strings.extend(p.accept(self) for p in node.patterns)
        if node.kwd_attrs and node.kwd_patterns:
            for attr, pattern in zip(node.kwd_attrs, node.kwd_patterns):
                class_strings.append(f"{attr}={pattern.accept(self)}")
        return f"{node.cls.accept(self)}({', '.join(class_strings)})"

    def visit_matchstar(self, node: MatchStar) -> str:
        """Return an astroid.MatchStar node as string."""
        return f"*{node.name.accept(self) if node.name else '_'}"

    def visit_matchas(self, node: MatchAs) -> str:
        """Return an astroid.MatchAs node as string."""
        # pylint: disable=import-outside-toplevel
        # Prevent circular dependency
        from astroid.nodes.node_classes import MatchClass, MatchMapping, MatchSequence

        if isinstance(node.parent, (MatchSequence, MatchMapping, MatchClass)):
            return node.name.accept(self) if node.name else "_"
        return (
            f"{node.pattern.accept(self) if node.pattern else '_'}"
            f"{f' as {node.name.accept(self)}' if node.name else ''}"
        )

    def visit_matchor(self, node: MatchOr) -> str:
        """Return an astroid.MatchOr node as string."""
        if node.patterns is None:
            raise AssertionError(f"{node} does not have pattern nodes")
        return " | ".join(p.accept(self) for p in node.patterns)

    # These aren't for real AST nodes, but for inference objects.

    def visit_frozenset(self, node):
        return node.parent.accept(self)

    def visit_super(self, node):
        return node.parent.accept(self)

    def visit_uninferable(self, node):
        return str(node)

    def visit_property(self, node):
        return node.function.accept(self)

    def visit_evaluatedobject(self, node):
        return node.original.accept(self)

    def visit_unknown(self, node: Unknown) -> str:
        return str(node)


def _import_string(names) -> str:
    """return a list of (name, asname) formatted as a string"""
    _names = []
    for name, asname in names:
        if asname is not None:
            _names.append(f"{name} as {asname}")
        else:
            _names.append(name)
    return ", ".join(_names)


# This sets the default indent to 4 spaces.
to_code = AsStringVisitor("    ")

back/venvmsg/Lib/site-packages/astroid/nodes/as_string.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

OP_PRECEDENCE = {
    op: precedence
    for precedence, ops in enumerate(
        [
            ["Lambda"],  # lambda x: x + 1
            ["IfExp"],  # 1 if True else 2
            ["or"],
            ["and"],
            ["not"],
            ["Compare"],  # in, not in, is, is not, <, <=, >, >=, !=, ==
            ["|"],
            ["^"],
            ["&"],
            ["<<", ">>"],
            ["+", "-"],
            ["*", "@", "/", "//", "%"],
            ["UnaryOp"],  # +, -, ~
            ["**"],
            ["Await"],
        ]
    )
    for op in ops
}

back/venvmsg/Lib/site-packages/astroid/nodes/const.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Module for some node classes. More nodes in scoped_nodes.py"""

from __future__ import annotations

import abc
import ast
import itertools
import operator
import sys
import typing
import warnings
from collections.abc import Generator, Iterable, Iterator, Mapping
from functools import cached_property
from typing import (
    TYPE_CHECKING,
    Any,
    Callable,
    ClassVar,
    Literal,
    Optional,
    Union,
)

from astroid import decorators, protocols, util
from astroid.bases import Instance, _infer_stmts
from astroid.const import _EMPTY_OBJECT_MARKER, Context
from astroid.context import CallContext, InferenceContext, copy_context
from astroid.exceptions import (
    AstroidBuildingError,
    AstroidError,
    AstroidIndexError,
    AstroidTypeError,
    AstroidValueError,
    AttributeInferenceError,
    InferenceError,
    NameInferenceError,
    NoDefault,
    ParentMissingError,
    _NonDeducibleTypeHierarchy,
)
from astroid.interpreter import dunder_lookup
from astroid.manager import AstroidManager
from astroid.nodes import _base_nodes
from astroid.nodes.const import OP_PRECEDENCE
from astroid.nodes.node_ng import NodeNG
from astroid.typing import (
    ConstFactoryResult,
    InferenceErrorInfo,
    InferenceResult,
    SuccessfulInferenceResult,
)

if sys.version_info >= (3, 11):
    from typing import Self
else:
    from typing_extensions import Self

if TYPE_CHECKING:
    from astroid import nodes
    from astroid.nodes import LocalsDictNodeNG


def _is_const(value) -> bool:
    return isinstance(value, tuple(CONST_CLS))


_NodesT = typing.TypeVar("_NodesT", bound=NodeNG)
_BadOpMessageT = typing.TypeVar("_BadOpMessageT", bound=util.BadOperationMessage)

AssignedStmtsPossibleNode = Union["List", "Tuple", "AssignName", "AssignAttr", None]
AssignedStmtsCall = Callable[
    [
        _NodesT,
        AssignedStmtsPossibleNode,
        Optional[InferenceContext],
        Optional[typing.List[int]],
    ],
    Any,
]
InferBinaryOperation = Callable[
    [_NodesT, Optional[InferenceContext]],
    typing.Generator[Union[InferenceResult, _BadOpMessageT], None, None],
]
InferLHS = Callable[
    [_NodesT, Optional[InferenceContext]],
    typing.Generator[InferenceResult, None, Optional[InferenceErrorInfo]],
]
InferUnaryOp = Callable[[_NodesT, str], ConstFactoryResult]


@decorators.raise_if_nothing_inferred
def unpack_infer(stmt, context: InferenceContext | None = None):
    """recursively generate nodes inferred by the given statement.
    If the inferred value is a list or a tuple, recurse on the elements
    """
    if isinstance(stmt, (List, Tuple)):
        for elt in stmt.elts:
            if elt is util.Uninferable:
                yield elt
                continue
            yield from unpack_infer(elt, context)
        return {"node": stmt, "context": context}
    # if inferred is a final node, return it and stop
    inferred = next(stmt.infer(context), util.Uninferable)
    if inferred is stmt:
        yield inferred
        return {"node": stmt, "context": context}
    # else, infer recursively, except Uninferable object that should be returned as is
    for inferred in stmt.infer(context):
        if isinstance(inferred, util.UninferableBase):
            yield inferred
        else:
            yield from unpack_infer(inferred, context)

    return {"node": stmt, "context": context}


def are_exclusive(stmt1, stmt2, exceptions: list[str] | None = None) -> bool:
    """return true if the two given statements are mutually exclusive

    `exceptions` may be a list of exception names. If specified, discard If
    branches and check one of the statement is in an exception handler catching
    one of the given exceptions.

    algorithm :
     1) index stmt1's parents
     2) climb among stmt2's parents until we find a common parent
     3) if the common parent is a If or Try statement, look if nodes are
        in exclusive branches
    """
    # index stmt1's parents
    stmt1_parents = {}
    children = {}
    previous = stmt1
    for node in stmt1.node_ancestors():
        stmt1_parents[node] = 1
        children[node] = previous
        previous = node
    # climb among stmt2's parents until we find a common parent
    previous = stmt2
    for node in stmt2.node_ancestors():
        if node in stmt1_parents:
            # if the common parent is a If or Try statement, look if
            # nodes are in exclusive branches
            if isinstance(node, If) and exceptions is None:
                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, Try):
                c2attr, c2node = node.locate_child(previous)
                c1attr, c1node = node.locate_child(children[node])
                if c1node is not c2node:
                    first_in_body_caught_by_handlers = (
                        c2attr == "handlers"
                        and c1attr == "body"
                        and previous.catch(exceptions)
                    )
                    second_in_body_caught_by_handlers = (
                        c2attr == "body"
                        and c1attr == "handlers"
                        and children[node].catch(exceptions)
                    )
                    first_in_else_other_in_handlers = (
                        c2attr == "handlers" and c1attr == "orelse"
                    )
                    second_in_else_other_in_handlers = (
                        c2attr == "orelse" and c1attr == "handlers"
                    )
                    if any(
                        (
                            first_in_body_caught_by_handlers,
                            second_in_body_caught_by_handlers,
                            first_in_else_other_in_handlers,
                            second_in_else_other_in_handlers,
                        )
                    ):
                        return True
                elif c2attr == "handlers" and c1attr == "handlers":
                    return previous is not children[node]
            return False
        previous = node
    return False


# getitem() helpers.

_SLICE_SENTINEL = object()


def _slice_value(index, context: InferenceContext | None = None):
    """Get the value of the given slice index."""

    if isinstance(index, Const):
        if isinstance(index.value, (int, type(None))):
            return index.value
    elif index is None:
        return None
    else:
        # Try to infer what the index actually is.
        # Since we can't return all the possible values,
        # we'll stop at the first possible value.
        try:
            inferred = next(index.infer(context=context))
        except (InferenceError, StopIteration):
            pass
        else:
            if isinstance(inferred, Const):
                if isinstance(inferred.value, (int, type(None))):
                    return inferred.value

    # Use a sentinel, because None can be a valid
    # value that this function can return,
    # as it is the case for unspecified bounds.
    return _SLICE_SENTINEL


def _infer_slice(node, context: InferenceContext | None = None):
    lower = _slice_value(node.lower, context)
    upper = _slice_value(node.upper, context)
    step = _slice_value(node.step, context)
    if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)):
        return slice(lower, upper, step)

    raise AstroidTypeError(
        message="Could not infer slice used in subscript",
        node=node,
        index=node.parent,
        context=context,
    )


def _container_getitem(instance, elts, index, context: InferenceContext | None = None):
    """Get a slice or an item, using the given *index*, for the given sequence."""
    try:
        if isinstance(index, Slice):
            index_slice = _infer_slice(index, context=context)
            new_cls = instance.__class__()
            new_cls.elts = elts[index_slice]
            new_cls.parent = instance.parent
            return new_cls
        if isinstance(index, Const):
            return elts[index.value]
    except ValueError as exc:
        raise AstroidValueError(
            message="Slice {index!r} cannot index container",
            node=instance,
            index=index,
            context=context,
        ) from exc
    except IndexError as exc:
        raise AstroidIndexError(
            message="Index {index!s} out of range",
            node=instance,
            index=index,
            context=context,
        ) from exc
    except TypeError as exc:
        raise AstroidTypeError(
            message="Type error {error!r}", node=instance, index=index, context=context
        ) from exc

    raise AstroidTypeError(f"Could not use {index} as subscript index")


class BaseContainer(_base_nodes.ParentAssignNode, Instance, metaclass=abc.ABCMeta):
    """Base class for Set, FrozenSet, Tuple and List."""

    _astroid_fields = ("elts",)

    def __init__(
        self,
        lineno: int | None,
        col_offset: int | None,
        parent: NodeNG | None,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.elts: list[SuccessfulInferenceResult] = []
        """The elements in the node."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, elts: list[SuccessfulInferenceResult]) -> None:
        self.elts = elts

    @classmethod
    def from_elements(cls, elts: Iterable[Any]) -> Self:
        """Create a node of this type from the given list of elements.

        :param elts: The list of elements that the node should contain.

        :returns: A new node containing the given elements.
        """
        node = cls(
            lineno=None,
            col_offset=None,
            parent=None,
            end_lineno=None,
            end_col_offset=None,
        )
        node.elts = [const_factory(e) if _is_const(e) else e for e in elts]
        return node

    def itered(self):
        """An iterator over the elements this node contains.

        :returns: The contents of this node.
        :rtype: iterable(NodeNG)
        """
        return self.elts

    def bool_value(self, context: InferenceContext | None = None) -> bool:
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
        """
        return bool(self.elts)

    @abc.abstractmethod
    def pytype(self) -> str:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """

    def get_children(self):
        yield from self.elts

    @decorators.raise_if_nothing_inferred
    def _infer(
        self,
        context: InferenceContext | None = None,
        **kwargs: Any,
    ) -> Iterator[Self]:
        has_starred_named_expr = any(
            isinstance(e, (Starred, NamedExpr)) for e in self.elts
        )
        if has_starred_named_expr:
            values = self._infer_sequence_helper(context)
            new_seq = type(self)(
                lineno=self.lineno,
                col_offset=self.col_offset,
                parent=self.parent,
                end_lineno=self.end_lineno,
                end_col_offset=self.end_col_offset,
            )
            new_seq.postinit(values)

            yield new_seq
        else:
            yield self

    def _infer_sequence_helper(
        self, context: InferenceContext | None = None
    ) -> list[SuccessfulInferenceResult]:
        """Infer all values based on BaseContainer.elts."""
        values = []

        for elt in self.elts:
            if isinstance(elt, Starred):
                starred = util.safe_infer(elt.value, context)
                if not starred:
                    raise InferenceError(node=self, context=context)
                if not hasattr(starred, "elts"):
                    raise InferenceError(node=self, context=context)
                # TODO: fresh context?
                values.extend(starred._infer_sequence_helper(context))
            elif isinstance(elt, NamedExpr):
                value = util.safe_infer(elt.value, context)
                if not value:
                    raise InferenceError(node=self, context=context)
                values.append(value)
            else:
                values.append(elt)
        return values


# Name classes


class AssignName(
    _base_nodes.NoChildrenNode,
    _base_nodes.LookupMixIn,
    _base_nodes.ParentAssignNode,
):
    """Variation of :class:`ast.Assign` representing assignment to a name.

    An :class:`AssignName` is the name of something that is assigned to.
    This includes variables defined in a function signature or in a loop.

    >>> import astroid
    >>> node = astroid.extract_node('variable = range(10)')
    >>> node
    <Assign l.1 at 0x7effe1db8550>
    >>> list(node.get_children())
    [<AssignName.variable l.1 at 0x7effe1db8748>, <Call l.1 at 0x7effe1db8630>]
    >>> list(node.get_children())[0].as_string()
    'variable'
    """

    _other_fields = ("name",)

    def __init__(
        self,
        name: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.name = name
        """The name that is assigned to."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    assigned_stmts = protocols.assend_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
        """Infer an AssignName: need to inspect the RHS part of the
        assign node.
        """
        if isinstance(self.parent, AugAssign):
            return self.parent.infer(context)

        stmts = list(self.assigned_stmts(context=context))
        return _infer_stmts(stmts, context)

    @decorators.raise_if_nothing_inferred
    def infer_lhs(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
        """Infer a Name: use name lookup rules.

        Same implementation as Name._infer."""
        # pylint: disable=import-outside-toplevel
        from astroid.constraint import get_constraints
        from astroid.helpers import _higher_function_scope

        frame, stmts = self.lookup(self.name)
        if not stmts:
            # Try to see if the name is enclosed in a nested function
            # and use the higher (first function) scope for searching.
            parent_function = _higher_function_scope(self.scope())
            if parent_function:
                _, stmts = parent_function.lookup(self.name)

            if not stmts:
                raise NameInferenceError(
                    name=self.name, scope=self.scope(), context=context
                )
        context = copy_context(context)
        context.lookupname = self.name
        context.constraints[self.name] = get_constraints(self, frame)

        return _infer_stmts(stmts, context, frame)


class DelName(
    _base_nodes.NoChildrenNode, _base_nodes.LookupMixIn, _base_nodes.ParentAssignNode
):
    """Variation of :class:`ast.Delete` representing deletion of a name.

    A :class:`DelName` is the name of something that is deleted.

    >>> import astroid
    >>> node = astroid.extract_node("del variable #@")
    >>> list(node.get_children())
    [<DelName.variable l.1 at 0x7effe1da4d30>]
    >>> list(node.get_children())[0].as_string()
    'variable'
    """

    _other_fields = ("name",)

    def __init__(
        self,
        name: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.name = name
        """The name that is being deleted."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )


class Name(_base_nodes.LookupMixIn, _base_nodes.NoChildrenNode):
    """Class representing an :class:`ast.Name` node.

    A :class:`Name` node is something that is named, but not covered by
    :class:`AssignName` or :class:`DelName`.

    >>> import astroid
    >>> node = astroid.extract_node('range(10)')
    >>> node
    <Call l.1 at 0x7effe1db8710>
    >>> list(node.get_children())
    [<Name.range l.1 at 0x7effe1db86a0>, <Const.int l.1 at 0x7effe1db8518>]
    >>> list(node.get_children())[0].as_string()
    'range'
    """

    _other_fields = ("name",)

    def __init__(
        self,
        name: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.name = name
        """The name that this node refers to."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def _get_name_nodes(self):
        yield self

        for child_node in self.get_children():
            yield from child_node._get_name_nodes()

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
        """Infer a Name: use name lookup rules

        Same implementation as AssignName._infer_lhs."""
        # pylint: disable=import-outside-toplevel
        from astroid.constraint import get_constraints
        from astroid.helpers import _higher_function_scope

        frame, stmts = self.lookup(self.name)
        if not stmts:
            # Try to see if the name is enclosed in a nested function
            # and use the higher (first function) scope for searching.
            parent_function = _higher_function_scope(self.scope())
            if parent_function:
                _, stmts = parent_function.lookup(self.name)

            if not stmts:
                raise NameInferenceError(
                    name=self.name, scope=self.scope(), context=context
                )
        context = copy_context(context)
        context.lookupname = self.name
        context.constraints[self.name] = get_constraints(self, frame)

        return _infer_stmts(stmts, context, frame)


DEPRECATED_ARGUMENT_DEFAULT = "DEPRECATED_ARGUMENT_DEFAULT"


class Arguments(
    _base_nodes.AssignTypeNode
):  # pylint: disable=too-many-instance-attributes
    """Class representing an :class:`ast.arguments` node.

    An :class:`Arguments` node represents that arguments in a
    function definition.

    >>> import astroid
    >>> node = astroid.extract_node('def foo(bar): pass')
    >>> node
    <FunctionDef.foo l.1 at 0x7effe1db8198>
    >>> node.args
    <Arguments l.1 at 0x7effe1db82e8>
    """

    # Python 3.4+ uses a different approach regarding annotations,
    # each argument is a new class, _ast.arg, which exposes an
    # 'annotation' attribute. In astroid though, arguments are exposed
    # as is in the Arguments node and the only way to expose annotations
    # is by using something similar with Python 3.3:
    #  - we expose 'varargannotation' and 'kwargannotation' of annotations
    #    of varargs and kwargs.
    #  - we expose 'annotation', a list with annotations for
    #    for each normal argument. If an argument doesn't have an
    #    annotation, its value will be None.
    _astroid_fields = (
        "args",
        "defaults",
        "kwonlyargs",
        "posonlyargs",
        "posonlyargs_annotations",
        "kw_defaults",
        "annotations",
        "varargannotation",
        "kwargannotation",
        "kwonlyargs_annotations",
        "type_comment_args",
        "type_comment_kwonlyargs",
        "type_comment_posonlyargs",
    )

    _other_fields = ("vararg", "kwarg")

    args: list[AssignName] | None
    """The names of the required arguments.

    Can be None if the associated function does not have a retrievable
    signature and the arguments are therefore unknown.
    This can happen with (builtin) functions implemented in C that have
    incomplete signature information.
    """

    defaults: list[NodeNG] | None
    """The default values for arguments that can be passed positionally."""

    kwonlyargs: list[AssignName]
    """The keyword arguments that cannot be passed positionally."""

    posonlyargs: list[AssignName]
    """The arguments that can only be passed positionally."""

    kw_defaults: list[NodeNG | None] | None
    """The default values for keyword arguments that cannot be passed positionally."""

    annotations: list[NodeNG | None]
    """The type annotations of arguments that can be passed positionally."""

    posonlyargs_annotations: list[NodeNG | None]
    """The type annotations of arguments that can only be passed positionally."""

    kwonlyargs_annotations: list[NodeNG | None]
    """The type annotations of arguments that cannot be passed positionally."""

    type_comment_args: list[NodeNG | None]
    """The type annotation, passed by a type comment, of each argument.

    If an argument does not have a type comment,
    the value for that argument will be None.
    """

    type_comment_kwonlyargs: list[NodeNG | None]
    """The type annotation, passed by a type comment, of each keyword only argument.

    If an argument does not have a type comment,
    the value for that argument will be None.
    """

    type_comment_posonlyargs: list[NodeNG | None]
    """The type annotation, passed by a type comment, of each positional argument.

    If an argument does not have a type comment,
    the value for that argument will be None.
    """

    varargannotation: NodeNG | None
    """The type annotation for the variable length arguments."""

    kwargannotation: NodeNG | None
    """The type annotation for the variable length keyword arguments."""

    vararg_node: AssignName | None
    """The node for variable length arguments"""

    kwarg_node: AssignName | None
    """The node for variable keyword arguments"""

    def __init__(
        self,
        vararg: str | None,
        kwarg: str | None,
        parent: NodeNG,
        vararg_node: AssignName | None = None,
        kwarg_node: AssignName | None = None,
    ) -> None:
        """Almost all attributes can be None for living objects where introspection failed."""
        super().__init__(
            parent=parent,
            lineno=None,
            col_offset=None,
            end_lineno=None,
            end_col_offset=None,
        )

        self.vararg = vararg
        """The name of the variable length arguments."""

        self.kwarg = kwarg
        """The name of the variable length keyword arguments."""

        self.vararg_node = vararg_node
        self.kwarg_node = kwarg_node

    # pylint: disable=too-many-arguments
    def postinit(
        self,
        args: list[AssignName] | None,
        defaults: list[NodeNG] | None,
        kwonlyargs: list[AssignName],
        kw_defaults: list[NodeNG | None] | None,
        annotations: list[NodeNG | None],
        posonlyargs: list[AssignName],
        kwonlyargs_annotations: list[NodeNG | None],
        posonlyargs_annotations: list[NodeNG | None],
        varargannotation: NodeNG | None = None,
        kwargannotation: NodeNG | None = None,
        type_comment_args: list[NodeNG | None] | None = None,
        type_comment_kwonlyargs: list[NodeNG | None] | None = None,
        type_comment_posonlyargs: list[NodeNG | None] | None = None,
    ) -> None:
        self.args = args
        self.defaults = defaults
        self.kwonlyargs = kwonlyargs
        self.posonlyargs = posonlyargs
        self.kw_defaults = kw_defaults
        self.annotations = annotations
        self.kwonlyargs_annotations = kwonlyargs_annotations
        self.posonlyargs_annotations = posonlyargs_annotations

        # Parameters that got added later and need a default
        self.varargannotation = varargannotation
        self.kwargannotation = kwargannotation
        if type_comment_args is None:
            type_comment_args = []
        self.type_comment_args = type_comment_args
        if type_comment_kwonlyargs is None:
            type_comment_kwonlyargs = []
        self.type_comment_kwonlyargs = type_comment_kwonlyargs
        if type_comment_posonlyargs is None:
            type_comment_posonlyargs = []
        self.type_comment_posonlyargs = type_comment_posonlyargs

    assigned_stmts = protocols.arguments_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    def _infer_name(self, frame, name):
        if self.parent is frame:
            return name
        return None

    @cached_property
    def fromlineno(self) -> int:
        """The first line that this node appears on in the source code.

        Can also return 0 if the line can not be determined.
        """
        lineno = super().fromlineno
        return max(lineno, self.parent.fromlineno or 0)

    @cached_property
    def arguments(self):
        """Get all the arguments for this node. This includes:
        * Positional only arguments
        * Positional arguments
        * Keyword arguments
        * Variable arguments (.e.g *args)
        * Variable keyword arguments (e.g **kwargs)
        """
        retval = list(itertools.chain((self.posonlyargs or ()), (self.args or ())))
        if self.vararg_node:
            retval.append(self.vararg_node)
        retval += self.kwonlyargs or ()
        if self.kwarg_node:
            retval.append(self.kwarg_node)

        return retval

    def format_args(self, *, skippable_names: set[str] | None = None) -> str:
        """Get the arguments formatted as string.

        :returns: The formatted arguments.
        :rtype: str
        """
        result = []
        positional_only_defaults = []
        positional_or_keyword_defaults = self.defaults
        if self.defaults:
            args = self.args or []
            positional_or_keyword_defaults = self.defaults[-len(args) :]
            positional_only_defaults = self.defaults[: len(self.defaults) - len(args)]

        if self.posonlyargs:
            result.append(
                _format_args(
                    self.posonlyargs,
                    positional_only_defaults,
                    self.posonlyargs_annotations,
                    skippable_names=skippable_names,
                )
            )
            result.append("/")
        if self.args:
            result.append(
                _format_args(
                    self.args,
                    positional_or_keyword_defaults,
                    getattr(self, "annotations", None),
                    skippable_names=skippable_names,
                )
            )
        if self.vararg:
            result.append(f"*{self.vararg}")
        if self.kwonlyargs:
            if not self.vararg:
                result.append("*")
            result.append(
                _format_args(
                    self.kwonlyargs,
                    self.kw_defaults,
                    self.kwonlyargs_annotations,
                    skippable_names=skippable_names,
                )
            )
        if self.kwarg:
            result.append(f"**{self.kwarg}")
        return ", ".join(result)

    def _get_arguments_data(
        self,
    ) -> tuple[
        dict[str, tuple[str | None, str | None]],
        dict[str, tuple[str | None, str | None]],
    ]:
        """Get the arguments as dictionary with information about typing and defaults.

        The return tuple contains a dictionary for positional and keyword arguments with their typing
        and their default value, if any.
        The method follows a similar order as format_args but instead of formatting into a string it
        returns the data that is used to do so.
        """
        pos_only: dict[str, tuple[str | None, str | None]] = {}
        kw_only: dict[str, tuple[str | None, str | None]] = {}

        # Setup and match defaults with arguments
        positional_only_defaults = []
        positional_or_keyword_defaults = self.defaults
        if self.defaults:
            args = self.args or []
            positional_or_keyword_defaults = self.defaults[-len(args) :]
            positional_only_defaults = self.defaults[: len(self.defaults) - len(args)]

        for index, posonly in enumerate(self.posonlyargs):
            annotation, default = self.posonlyargs_annotations[index], None
            if annotation is not None:
                annotation = annotation.as_string()
            if positional_only_defaults:
                default = positional_only_defaults[index].as_string()
            pos_only[posonly.name] = (annotation, default)

        for index, arg in enumerate(self.args):
            annotation, default = self.annotations[index], None
            if annotation is not None:
                annotation = annotation.as_string()
            if positional_or_keyword_defaults:
                defaults_offset = len(self.args) - len(positional_or_keyword_defaults)
                default_index = index - defaults_offset
                if (
                    default_index > -1
                    and positional_or_keyword_defaults[default_index] is not None
                ):
                    default = positional_or_keyword_defaults[default_index].as_string()
            pos_only[arg.name] = (annotation, default)

        if self.vararg:
            annotation = self.varargannotation
            if annotation is not None:
                annotation = annotation.as_string()
            pos_only[self.vararg] = (annotation, None)

        for index, kwarg in enumerate(self.kwonlyargs):
            annotation = self.kwonlyargs_annotations[index]
            if annotation is not None:
                annotation = annotation.as_string()
            default = self.kw_defaults[index]
            if default is not None:
                default = default.as_string()
            kw_only[kwarg.name] = (annotation, default)

        if self.kwarg:
            annotation = self.kwargannotation
            if annotation is not None:
                annotation = annotation.as_string()
            kw_only[self.kwarg] = (annotation, None)

        return pos_only, kw_only

    def default_value(self, argname):
        """Get the default value for an argument.

        :param argname: The name of the argument to get the default value for.
        :type argname: str

        :raises NoDefault: If there is no default value defined for the
            given argument.
        """
        args = [
            arg for arg in self.arguments if arg.name not in [self.vararg, self.kwarg]
        ]

        index = _find_arg(argname, self.kwonlyargs)[0]
        if (index is not None) and (len(self.kw_defaults) > index):
            if self.kw_defaults[index] is not None:
                return self.kw_defaults[index]
            raise NoDefault(func=self.parent, name=argname)

        index = _find_arg(argname, args)[0]
        if index is not None:
            idx = index - (len(args) - len(self.defaults) - len(self.kw_defaults))
            if idx >= 0:
                return self.defaults[idx]

        raise NoDefault(func=self.parent, name=argname)

    def is_argument(self, name) -> bool:
        """Check if the given name is defined in the arguments.

        :param name: The name to check for.
        :type name: str

        :returns: Whether the given name is defined in the arguments,
        """
        if name == self.vararg:
            return True
        if name == self.kwarg:
            return True
        return self.find_argname(name)[1] is not None

    def find_argname(self, argname, rec=DEPRECATED_ARGUMENT_DEFAULT):
        """Get the index and :class:`AssignName` node for given name.

        :param argname: The name of the argument to search for.
        :type argname: str

        :returns: The index and node for the argument.
        :rtype: tuple(str or None, AssignName or None)
        """
        if rec != DEPRECATED_ARGUMENT_DEFAULT:  # pragma: no cover
            warnings.warn(
                "The rec argument will be removed in astroid 3.1.",
                DeprecationWarning,
                stacklevel=2,
            )
        if self.arguments:
            index, argument = _find_arg(argname, self.arguments)
            if argument:
                return index, argument
        return None, None

    def get_children(self):
        yield from self.posonlyargs or ()

        for elt in self.posonlyargs_annotations:
            if elt is not None:
                yield elt

        yield from self.args or ()

        if self.defaults is not None:
            yield from self.defaults
        yield from self.kwonlyargs

        for elt in self.kw_defaults or ():
            if elt is not None:
                yield elt

        for elt in self.annotations:
            if elt is not None:
                yield elt

        if self.varargannotation is not None:
            yield self.varargannotation

        if self.kwargannotation is not None:
            yield self.kwargannotation

        for elt in self.kwonlyargs_annotations:
            if elt is not None:
                yield elt

    @decorators.raise_if_nothing_inferred
    def _infer(
        self: nodes.Arguments, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, None]:
        # pylint: disable-next=import-outside-toplevel
        from astroid.protocols import _arguments_infer_argname

        if context is None or context.lookupname is None:
            raise InferenceError(node=self, context=context)
        return _arguments_infer_argname(self, context.lookupname, context)


def _find_arg(argname, args):
    for i, arg in enumerate(args):
        if arg.name == argname:
            return i, arg
    return None, None


def _format_args(
    args, defaults=None, annotations=None, skippable_names: set[str] | None = None
) -> str:
    if skippable_names is None:
        skippable_names = set()
    values = []
    if args is None:
        return ""
    if annotations is None:
        annotations = []
    if defaults is not None:
        default_offset = len(args) - len(defaults)
    packed = itertools.zip_longest(args, annotations)
    for i, (arg, annotation) in enumerate(packed):
        if arg.name in skippable_names:
            continue
        if isinstance(arg, Tuple):
            values.append(f"({_format_args(arg.elts)})")
        else:
            argname = arg.name
            default_sep = "="
            if annotation is not None:
                argname += ": " + annotation.as_string()
                default_sep = " = "
            values.append(argname)

            if defaults is not None and i >= default_offset:
                if defaults[i - default_offset] is not None:
                    values[-1] += default_sep + defaults[i - default_offset].as_string()
    return ", ".join(values)


def _infer_attribute(
    node: nodes.AssignAttr | nodes.Attribute,
    context: InferenceContext | None = None,
    **kwargs: Any,
) -> Generator[InferenceResult, None, InferenceErrorInfo]:
    """Infer an AssignAttr/Attribute node by using getattr on the associated object."""
    # pylint: disable=import-outside-toplevel
    from astroid.constraint import get_constraints
    from astroid.nodes import ClassDef

    for owner in node.expr.infer(context):
        if isinstance(owner, util.UninferableBase):
            yield owner
            continue

        context = copy_context(context)
        old_boundnode = context.boundnode
        try:
            context.boundnode = owner
            if isinstance(owner, (ClassDef, Instance)):
                frame = owner if isinstance(owner, ClassDef) else owner._proxied
                context.constraints[node.attrname] = get_constraints(node, frame=frame)
            if node.attrname == "argv" and owner.name == "sys":
                # sys.argv will never be inferable during static analysis
                # It's value would be the args passed to the linter itself
                yield util.Uninferable
            else:
                yield from owner.igetattr(node.attrname, context)
        except (
            AttributeInferenceError,
            InferenceError,
            AttributeError,
        ):
            pass
        finally:
            context.boundnode = old_boundnode
    return InferenceErrorInfo(node=node, context=context)


class AssignAttr(_base_nodes.LookupMixIn, _base_nodes.ParentAssignNode):
    """Variation of :class:`ast.Assign` representing assignment to an attribute.

    >>> import astroid
    >>> node = astroid.extract_node('self.attribute = range(10)')
    >>> node
    <Assign l.1 at 0x7effe1d521d0>
    >>> list(node.get_children())
    [<AssignAttr.attribute l.1 at 0x7effe1d52320>, <Call l.1 at 0x7effe1d522e8>]
    >>> list(node.get_children())[0].as_string()
    'self.attribute'
    """

    expr: NodeNG

    _astroid_fields = ("expr",)
    _other_fields = ("attrname",)

    def __init__(
        self,
        attrname: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.attrname = attrname
        """The name of the attribute being assigned to."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, expr: NodeNG) -> None:
        self.expr = expr

    assigned_stmts = protocols.assend_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    def get_children(self):
        yield self.expr

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
        """Infer an AssignAttr: need to inspect the RHS part of the
        assign node.
        """
        if isinstance(self.parent, AugAssign):
            return self.parent.infer(context)

        stmts = list(self.assigned_stmts(context=context))
        return _infer_stmts(stmts, context)

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def infer_lhs(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
        return _infer_attribute(self, context, **kwargs)


class Assert(_base_nodes.Statement):
    """Class representing an :class:`ast.Assert` node.

    An :class:`Assert` node represents an assert statement.

    >>> import astroid
    >>> node = astroid.extract_node('assert len(things) == 10, "Not enough things"')
    >>> node
    <Assert l.1 at 0x7effe1d527b8>
    """

    _astroid_fields = ("test", "fail")

    test: NodeNG
    """The test that passes or fails the assertion."""

    fail: NodeNG | None
    """The message shown when the assertion fails."""

    def postinit(self, test: NodeNG, fail: NodeNG | None) -> None:
        self.fail = fail
        self.test = test

    def get_children(self):
        yield self.test

        if self.fail is not None:
            yield self.fail


class Assign(_base_nodes.AssignTypeNode, _base_nodes.Statement):
    """Class representing an :class:`ast.Assign` node.

    An :class:`Assign` is a statement where something is explicitly
    asssigned to.

    >>> import astroid
    >>> node = astroid.extract_node('variable = range(10)')
    >>> node
    <Assign l.1 at 0x7effe1db8550>
    """

    targets: list[NodeNG]
    """What is being assigned to."""

    value: NodeNG
    """The value being assigned to the variables."""

    type_annotation: NodeNG | None
    """If present, this will contain the type annotation passed by a type comment"""

    _astroid_fields = ("targets", "value")
    _other_other_fields = ("type_annotation",)

    def postinit(
        self,
        targets: list[NodeNG],
        value: NodeNG,
        type_annotation: NodeNG | None,
    ) -> None:
        self.targets = targets
        self.value = value
        self.type_annotation = type_annotation

    assigned_stmts = protocols.assign_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    def get_children(self):
        yield from self.targets

        yield self.value

    @cached_property
    def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
        return [self, *self.value._assign_nodes_in_scope]

    def _get_yield_nodes_skip_functions(self):
        yield from self.value._get_yield_nodes_skip_functions()

    def _get_yield_nodes_skip_lambdas(self):
        yield from self.value._get_yield_nodes_skip_lambdas()


class AnnAssign(_base_nodes.AssignTypeNode, _base_nodes.Statement):
    """Class representing an :class:`ast.AnnAssign` node.

    An :class:`AnnAssign` is an assignment with a type annotation.

    >>> import astroid
    >>> node = astroid.extract_node('variable: List[int] = range(10)')
    >>> node
    <AnnAssign l.1 at 0x7effe1d4c630>
    """

    _astroid_fields = ("target", "annotation", "value")
    _other_fields = ("simple",)

    target: Name | Attribute | Subscript
    """What is being assigned to."""

    annotation: NodeNG
    """The type annotation of what is being assigned to."""

    value: NodeNG | None
    """The value being assigned to the variables."""

    simple: int
    """Whether :attr:`target` is a pure name or a complex statement."""

    def postinit(
        self,
        target: Name | Attribute | Subscript,
        annotation: NodeNG,
        simple: int,
        value: NodeNG | None,
    ) -> None:
        self.target = target
        self.annotation = annotation
        self.value = value
        self.simple = simple

    assigned_stmts = protocols.assign_annassigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    def get_children(self):
        yield self.target
        yield self.annotation

        if self.value is not None:
            yield self.value


class AugAssign(
    _base_nodes.AssignTypeNode, _base_nodes.OperatorNode, _base_nodes.Statement
):
    """Class representing an :class:`ast.AugAssign` node.

    An :class:`AugAssign` is an assignment paired with an operator.

    >>> import astroid
    >>> node = astroid.extract_node('variable += 1')
    >>> node
    <AugAssign l.1 at 0x7effe1db4d68>
    """

    _astroid_fields = ("target", "value")
    _other_fields = ("op",)

    target: Name | Attribute | Subscript
    """What is being assigned to."""

    value: NodeNG
    """The value being assigned to the variable."""

    def __init__(
        self,
        op: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.op = op
        """The operator that is being combined with the assignment.

        This includes the equals sign.
        """

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, target: Name | Attribute | Subscript, value: NodeNG) -> None:
        self.target = target
        self.value = value

    assigned_stmts = protocols.assign_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    def type_errors(self, context: InferenceContext | None = None):
        """Get a list of type errors which can occur during inference.

        Each TypeError is represented by a :class:`BadBinaryOperationMessage` ,
        which holds the original exception.

        :returns: The list of possible type errors.
        :rtype: list(BadBinaryOperationMessage)
        """
        try:
            results = self._infer_augassign(context=context)
            return [
                result
                for result in results
                if isinstance(result, util.BadBinaryOperationMessage)
            ]
        except InferenceError:
            return []

    def get_children(self):
        yield self.target
        yield self.value

    def _get_yield_nodes_skip_functions(self):
        """An AugAssign node can contain a Yield node in the value"""
        yield from self.value._get_yield_nodes_skip_functions()
        yield from super()._get_yield_nodes_skip_functions()

    def _get_yield_nodes_skip_lambdas(self):
        """An AugAssign node can contain a Yield node in the value"""
        yield from self.value._get_yield_nodes_skip_lambdas()
        yield from super()._get_yield_nodes_skip_lambdas()

    def _infer_augassign(
        self, context: InferenceContext | None = None
    ) -> Generator[InferenceResult | util.BadBinaryOperationMessage, None, None]:
        """Inference logic for augmented binary operations."""
        context = context or InferenceContext()

        rhs_context = context.clone()

        lhs_iter = self.target.infer_lhs(context=context)
        rhs_iter = self.value.infer(context=rhs_context)

        for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
            if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
                # Don't know how to process this.
                yield util.Uninferable
                return

            try:
                yield from self._infer_binary_operation(
                    left=lhs,
                    right=rhs,
                    binary_opnode=self,
                    context=context,
                    flow_factory=self._get_aug_flow,
                )
            except _NonDeducibleTypeHierarchy:
                yield util.Uninferable

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self: nodes.AugAssign, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, None]:
        return self._filter_operation_errors(
            self._infer_augassign, context, util.BadBinaryOperationMessage
        )


class BinOp(_base_nodes.OperatorNode):
    """Class representing an :class:`ast.BinOp` node.

    A :class:`BinOp` node is an application of a binary operator.

    >>> import astroid
    >>> node = astroid.extract_node('a + b')
    >>> node
    <BinOp l.1 at 0x7f23b2e8cfd0>
    """

    _astroid_fields = ("left", "right")
    _other_fields = ("op",)

    left: NodeNG
    """What is being applied to the operator on the left side."""

    right: NodeNG
    """What is being applied to the operator on the right side."""

    def __init__(
        self,
        op: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.op = op
        """The operator."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, left: NodeNG, right: NodeNG) -> None:
        self.left = left
        self.right = right

    def type_errors(self, context: InferenceContext | None = None):
        """Get a list of type errors which can occur during inference.

        Each TypeError is represented by a :class:`BadBinaryOperationMessage`,
        which holds the original exception.

        :returns: The list of possible type errors.
        :rtype: list(BadBinaryOperationMessage)
        """
        try:
            results = self._infer_binop(context=context)
            return [
                result
                for result in results
                if isinstance(result, util.BadBinaryOperationMessage)
            ]
        except InferenceError:
            return []

    def get_children(self):
        yield self.left
        yield self.right

    def op_precedence(self):
        return OP_PRECEDENCE[self.op]

    def op_left_associative(self) -> bool:
        # 2**3**4 == 2**(3**4)
        return self.op != "**"

    def _infer_binop(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, None]:
        """Binary operation inference logic."""
        left = self.left
        right = self.right

        # we use two separate contexts for evaluating lhs and rhs because
        # 1. evaluating lhs may leave some undesired entries in context.path
        #    which may not let us infer right value of rhs
        context = context or InferenceContext()
        lhs_context = copy_context(context)
        rhs_context = copy_context(context)
        lhs_iter = left.infer(context=lhs_context)
        rhs_iter = right.infer(context=rhs_context)
        for lhs, rhs in itertools.product(lhs_iter, rhs_iter):
            if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)):
                # Don't know how to process this.
                yield util.Uninferable
                return

            try:
                yield from self._infer_binary_operation(
                    lhs, rhs, self, context, self._get_binop_flow
                )
            except _NonDeducibleTypeHierarchy:
                yield util.Uninferable

    @decorators.yes_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self: nodes.BinOp, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, None]:
        return self._filter_operation_errors(
            self._infer_binop, context, util.BadBinaryOperationMessage
        )


class BoolOp(NodeNG):
    """Class representing an :class:`ast.BoolOp` node.

    A :class:`BoolOp` is an application of a boolean operator.

    >>> import astroid
    >>> node = astroid.extract_node('a and b')
    >>> node
    <BinOp l.1 at 0x7f23b2e71c50>
    """

    _astroid_fields = ("values",)
    _other_fields = ("op",)

    @decorators.deprecate_default_argument_values(op="str")
    def __init__(
        self,
        op: str | None = None,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """
        :param op: The operator.

        :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.op: str | None = op
        """The operator."""

        self.values: list[NodeNG] = []
        """The values being applied to the operator."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, values: list[NodeNG] | None = None) -> None:
        """Do some setup after initialisation.

        :param values: The values being applied to the operator.
        """
        if values is not None:
            self.values = values

    def get_children(self):
        yield from self.values

    def op_precedence(self):
        return OP_PRECEDENCE[self.op]

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self: nodes.BoolOp, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
        """Infer a boolean operation (and / or / not).

        The function will calculate the boolean operation
        for all pairs generated through inference for each component
        node.
        """
        values = self.values
        if self.op == "or":
            predicate = operator.truth
        else:
            predicate = operator.not_

        try:
            inferred_values = [value.infer(context=context) for value in values]
        except InferenceError:
            yield util.Uninferable
            return None

        for pair in itertools.product(*inferred_values):
            if any(isinstance(item, util.UninferableBase) for item in pair):
                # Can't infer the final result, just yield Uninferable.
                yield util.Uninferable
                continue

            bool_values = [item.bool_value() for item in pair]
            if any(isinstance(item, util.UninferableBase) for item in bool_values):
                # Can't infer the final result, just yield Uninferable.
                yield util.Uninferable
                continue

            # Since the boolean operations are short circuited operations,
            # this code yields the first value for which the predicate is True
            # and if no value respected the predicate, then the last value will
            # be returned (or Uninferable if there was no last value).
            # This is conforming to the semantics of `and` and `or`:
            #   1 and 0 -> 1
            #   0 and 1 -> 0
            #   1 or 0 -> 1
            #   0 or 1 -> 1
            value = util.Uninferable
            for value, bool_value in zip(pair, bool_values):
                if predicate(bool_value):
                    yield value
                    break
            else:
                yield value

        return InferenceErrorInfo(node=self, context=context)


class Break(_base_nodes.NoChildrenNode, _base_nodes.Statement):
    """Class representing an :class:`ast.Break` node.

    >>> import astroid
    >>> node = astroid.extract_node('break')
    >>> node
    <Break l.1 at 0x7f23b2e9e5c0>
    """


class Call(NodeNG):
    """Class representing an :class:`ast.Call` node.

    A :class:`Call` node is a call to a function, method, etc.

    >>> import astroid
    >>> node = astroid.extract_node('function()')
    >>> node
    <Call l.1 at 0x7f23b2e71eb8>
    """

    _astroid_fields = ("func", "args", "keywords")

    func: NodeNG
    """What is being called."""

    args: list[NodeNG]
    """The positional arguments being given to the call."""

    keywords: list[Keyword]
    """The keyword arguments being given to the call."""

    def postinit(
        self, func: NodeNG, args: list[NodeNG], keywords: list[Keyword]
    ) -> None:
        self.func = func
        self.args = args
        self.keywords = keywords

    @property
    def starargs(self) -> list[Starred]:
        """The positional arguments that unpack something."""
        return [arg for arg in self.args if isinstance(arg, Starred)]

    @property
    def kwargs(self) -> list[Keyword]:
        """The keyword arguments that unpack something."""
        return [keyword for keyword in self.keywords if keyword.arg is None]

    def get_children(self):
        yield self.func

        yield from self.args

        yield from self.keywords

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
        """Infer a Call node by trying to guess what the function returns."""
        callcontext = copy_context(context)
        callcontext.boundnode = None
        if context is not None:
            callcontext.extra_context = self._populate_context_lookup(context.clone())

        for callee in self.func.infer(context):
            if isinstance(callee, util.UninferableBase):
                yield callee
                continue
            try:
                if hasattr(callee, "infer_call_result"):
                    callcontext.callcontext = CallContext(
                        args=self.args, keywords=self.keywords, callee=callee
                    )
                    yield from callee.infer_call_result(
                        caller=self, context=callcontext
                    )
            except InferenceError:
                continue
        return InferenceErrorInfo(node=self, context=context)

    def _populate_context_lookup(self, context: InferenceContext | None):
        """Allows context to be saved for later for inference inside a function."""
        context_lookup: dict[InferenceResult, InferenceContext] = {}
        if context is None:
            return context_lookup
        for arg in self.args:
            if isinstance(arg, Starred):
                context_lookup[arg.value] = context
            else:
                context_lookup[arg] = context
        keywords = self.keywords if self.keywords is not None else []
        for keyword in keywords:
            context_lookup[keyword.value] = context
        return context_lookup


COMPARE_OPS: dict[str, Callable[[Any, Any], bool]] = {
    "==": operator.eq,
    "!=": operator.ne,
    "<": operator.lt,
    "<=": operator.le,
    ">": operator.gt,
    ">=": operator.ge,
    "in": lambda a, b: a in b,
    "not in": lambda a, b: a not in b,
}
UNINFERABLE_OPS = {
    "is",
    "is not",
}


class Compare(NodeNG):
    """Class representing an :class:`ast.Compare` node.

    A :class:`Compare` node indicates a comparison.

    >>> import astroid
    >>> node = astroid.extract_node('a <= b <= c')
    >>> node
    <Compare l.1 at 0x7f23b2e9e6d8>
    >>> node.ops
    [('<=', <Name.b l.1 at 0x7f23b2e9e2b0>), ('<=', <Name.c l.1 at 0x7f23b2e9e390>)]
    """

    _astroid_fields = ("left", "ops")

    left: NodeNG
    """The value at the left being applied to a comparison operator."""

    ops: list[tuple[str, NodeNG]]
    """The remainder of the operators and their relevant right hand value."""

    def postinit(self, left: NodeNG, ops: list[tuple[str, NodeNG]]) -> None:
        self.left = left
        self.ops = ops

    def get_children(self):
        """Get the child nodes below this node.

        Overridden to handle the tuple fields and skip returning the operator
        strings.

        :returns: The children.
        :rtype: iterable(NodeNG)
        """
        yield self.left
        for _, comparator in self.ops:
            yield comparator  # we don't want the 'op'

    def last_child(self):
        """An optimized version of list(get_children())[-1]

        :returns: The last child.
        :rtype: NodeNG
        """
        # XXX maybe if self.ops:
        return self.ops[-1][1]
        # return self.left

    # TODO: move to util?
    @staticmethod
    def _to_literal(node: SuccessfulInferenceResult) -> Any:
        # Can raise SyntaxError or ValueError from ast.literal_eval
        # Can raise AttributeError from node.as_string() as not all nodes have a visitor
        # Is this the stupidest idea or the simplest idea?
        return ast.literal_eval(node.as_string())

    def _do_compare(
        self,
        left_iter: Iterable[InferenceResult],
        op: str,
        right_iter: Iterable[InferenceResult],
    ) -> bool | util.UninferableBase:
        """
        If all possible combinations are either True or False, return that:
        >>> _do_compare([1, 2], '<=', [3, 4])
        True
        >>> _do_compare([1, 2], '==', [3, 4])
        False

        If any item is uninferable, or if some combinations are True and some
        are False, return Uninferable:
        >>> _do_compare([1, 3], '<=', [2, 4])
        util.Uninferable
        """
        retval: bool | None = None
        if op in UNINFERABLE_OPS:
            return util.Uninferable
        op_func = COMPARE_OPS[op]

        for left, right in itertools.product(left_iter, right_iter):
            if isinstance(left, util.UninferableBase) or isinstance(
                right, util.UninferableBase
            ):
                return util.Uninferable

            try:
                left, right = self._to_literal(left), self._to_literal(right)
            except (SyntaxError, ValueError, AttributeError):
                return util.Uninferable

            try:
                expr = op_func(left, right)
            except TypeError as exc:
                raise AstroidTypeError from exc

            if retval is None:
                retval = expr
            elif retval != expr:
                return util.Uninferable
                # (or both, but "True | False" is basically the same)

        assert retval is not None
        return retval  # it was all the same value

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[nodes.Const | util.UninferableBase, None, None]:
        """Chained comparison inference logic."""
        retval: bool | util.UninferableBase = True

        ops = self.ops
        left_node = self.left
        lhs = list(left_node.infer(context=context))
        # should we break early if first element is uninferable?
        for op, right_node in ops:
            # eagerly evaluate rhs so that values can be re-used as lhs
            rhs = list(right_node.infer(context=context))
            try:
                retval = self._do_compare(lhs, op, rhs)
            except AstroidTypeError:
                retval = util.Uninferable
                break
            if retval is not True:
                break  # short-circuit
            lhs = rhs  # continue
        if retval is util.Uninferable:
            yield retval  # type: ignore[misc]
        else:
            yield Const(retval)


class Comprehension(NodeNG):
    """Class representing an :class:`ast.comprehension` node.

    A :class:`Comprehension` indicates the loop inside any type of
    comprehension including generator expressions.

    >>> import astroid
    >>> node = astroid.extract_node('[x for x in some_values]')
    >>> list(node.get_children())
    [<Name.x l.1 at 0x7f23b2e352b0>, <Comprehension l.1 at 0x7f23b2e35320>]
    >>> list(node.get_children())[1].as_string()
    'for x in some_values'
    """

    _astroid_fields = ("target", "iter", "ifs")
    _other_fields = ("is_async",)

    optional_assign = True
    """Whether this node optionally assigns a variable."""

    target: NodeNG
    """What is assigned to by the comprehension."""

    iter: NodeNG
    """What is iterated over by the comprehension."""

    ifs: list[NodeNG]
    """The contents of any if statements that filter the comprehension."""

    is_async: bool
    """Whether this is an asynchronous comprehension or not."""

    def postinit(
        self,
        target: NodeNG,
        iter: NodeNG,  # pylint: disable = redefined-builtin
        ifs: list[NodeNG],
        is_async: bool,
    ) -> None:
        self.target = target
        self.iter = iter
        self.ifs = ifs
        self.is_async = is_async

    assigned_stmts = protocols.for_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    def assign_type(self):
        """The type of assignment that this node performs.

        :returns: The assignment type.
        :rtype: NodeNG
        """
        return self

    def _get_filtered_stmts(
        self, lookup_node, node, stmts, mystmt: _base_nodes.Statement | None
    ):
        """method used in filter_stmts"""
        if self is mystmt:
            if isinstance(lookup_node, (Const, Name)):
                return [lookup_node], True

        elif self.statement() is mystmt:
            # original node's statement is the assignment, only keeps
            # current node (gen exp, list comp)

            return [node], True

        return stmts, False

    def get_children(self):
        yield self.target
        yield self.iter

        yield from self.ifs


class Const(_base_nodes.NoChildrenNode, Instance):
    """Class representing any constant including num, str, bool, None, bytes.

    >>> import astroid
    >>> node = astroid.extract_node('(5, "This is a string.", True, None, b"bytes")')
    >>> node
    <Tuple.tuple l.1 at 0x7f23b2e358d0>
    >>> list(node.get_children())
    [<Const.int l.1 at 0x7f23b2e35940>,
    <Const.str l.1 at 0x7f23b2e35978>,
    <Const.bool l.1 at 0x7f23b2e359b0>,
    <Const.NoneType l.1 at 0x7f23b2e359e8>,
    <Const.bytes l.1 at 0x7f23b2e35a20>]
    """

    _other_fields = ("value", "kind")

    def __init__(
        self,
        value: Any,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        kind: str | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """
        :param value: The value that the constant represents.

        :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 kind: The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only.

        :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.value: Any = value
        """The value that the constant represents."""

        self.kind: str | None = kind  # can be None
        """"The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

        Instance.__init__(self, None)

    infer_unary_op = protocols.const_infer_unary_op
    infer_binary_op = protocols.const_infer_binary_op

    def __getattr__(self, name):
        # This is needed because of Proxy's __getattr__ method.
        # Calling object.__new__ on this class without calling
        # __init__ would result in an infinite loop otherwise
        # since __getattr__ is called when an attribute doesn't
        # exist and self._proxied indirectly calls self.value
        # and Proxy __getattr__ calls self.value
        if name == "value":
            raise AttributeError
        return super().__getattr__(name)

    def getitem(self, index, context: InferenceContext | None = None):
        """Get an item from this node if subscriptable.

        :param index: The node to use as a subscript index.
        :type index: Const or Slice

        :raises AstroidTypeError: When the given index cannot be used as a
            subscript index, or if this node is not subscriptable.
        """
        if isinstance(index, Const):
            index_value = index.value
        elif isinstance(index, Slice):
            index_value = _infer_slice(index, context=context)

        else:
            raise AstroidTypeError(
                f"Could not use type {type(index)} as subscript index"
            )

        try:
            if isinstance(self.value, (str, bytes)):
                return Const(self.value[index_value])
        except ValueError as exc:
            raise AstroidValueError(
                f"Could not index {self.value!r} with {index_value!r}"
            ) from exc
        except IndexError as exc:
            raise AstroidIndexError(
                message="Index {index!r} out of range",
                node=self,
                index=index,
                context=context,
            ) from exc
        except TypeError as exc:
            raise AstroidTypeError(
                message="Type error {error!r}", node=self, index=index, context=context
            ) from exc

        raise AstroidTypeError(f"{self!r} (value={self.value})")

    def has_dynamic_getattr(self) -> bool:
        """Check if the node has a custom __getattr__ or __getattribute__.

        :returns: Whether the class has a custom __getattr__ or __getattribute__.
            For a :class:`Const` this is always ``False``.
        """
        return False

    def itered(self):
        """An iterator over the elements this node contains.

        :returns: The contents of this node.
        :rtype: iterable(Const)

        :raises TypeError: If this node does not represent something that is iterable.
        """
        if isinstance(self.value, str):
            return [const_factory(elem) for elem in self.value]
        raise TypeError(f"Cannot iterate over type {type(self.value)!r}")

    def pytype(self) -> str:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        return self._proxied.qname()

    def bool_value(self, context: InferenceContext | None = None):
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
        :rtype: bool
        """
        return bool(self.value)

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Iterator[Const]:
        yield self


class Continue(_base_nodes.NoChildrenNode, _base_nodes.Statement):
    """Class representing an :class:`ast.Continue` node.

    >>> import astroid
    >>> node = astroid.extract_node('continue')
    >>> node
    <Continue l.1 at 0x7f23b2e35588>
    """


class Decorators(NodeNG):
    """A node representing a list of decorators.

    A :class:`Decorators` is the decorators that are applied to
    a method or function.

    >>> import astroid
    >>> node = astroid.extract_node('''
    @property
    def my_property(self):
        return 3
    ''')
    >>> node
    <FunctionDef.my_property l.2 at 0x7f23b2e35d30>
    >>> list(node.get_children())[0]
    <Decorators l.1 at 0x7f23b2e35d68>
    """

    _astroid_fields = ("nodes",)

    nodes: list[NodeNG]
    """The decorators that this node contains."""

    def postinit(self, nodes: list[NodeNG]) -> None:
        self.nodes = nodes

    def scope(self) -> LocalsDictNodeNG:
        """The first parent node defining a new scope.
        These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.

        :returns: The first parent scope node.
        """
        # skip the function node to go directly to the upper level scope
        if not self.parent:
            raise ParentMissingError(target=self)
        if not self.parent.parent:
            raise ParentMissingError(target=self.parent)
        return self.parent.parent.scope()

    def get_children(self):
        yield from self.nodes


class DelAttr(_base_nodes.ParentAssignNode):
    """Variation of :class:`ast.Delete` representing deletion of an attribute.

    >>> import astroid
    >>> node = astroid.extract_node('del self.attr')
    >>> node
    <Delete l.1 at 0x7f23b2e35f60>
    >>> list(node.get_children())[0]
    <DelAttr.attr l.1 at 0x7f23b2e411d0>
    """

    _astroid_fields = ("expr",)
    _other_fields = ("attrname",)

    expr: NodeNG
    """The name that this node represents."""

    def __init__(
        self,
        attrname: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.attrname = attrname
        """The name of the attribute that is being deleted."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, expr: NodeNG) -> None:
        self.expr = expr

    def get_children(self):
        yield self.expr


class Delete(_base_nodes.AssignTypeNode, _base_nodes.Statement):
    """Class representing an :class:`ast.Delete` node.

    A :class:`Delete` is a ``del`` statement this is deleting something.

    >>> import astroid
    >>> node = astroid.extract_node('del self.attr')
    >>> node
    <Delete l.1 at 0x7f23b2e35f60>
    """

    _astroid_fields = ("targets",)

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.targets: list[NodeNG] = []
        """What is being deleted."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, targets: list[NodeNG]) -> None:
        self.targets = targets

    def get_children(self):
        yield from self.targets


class Dict(NodeNG, Instance):
    """Class representing an :class:`ast.Dict` node.

    A :class:`Dict` is a dictionary that is created with ``{}`` syntax.

    >>> import astroid
    >>> node = astroid.extract_node('{1: "1"}')
    >>> node
    <Dict.dict l.1 at 0x7f23b2e35cc0>
    """

    _astroid_fields = ("items",)

    def __init__(
        self,
        lineno: int | None,
        col_offset: int | None,
        parent: NodeNG | None,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.items: list[tuple[InferenceResult, InferenceResult]] = []
        """The key-value pairs contained in the dictionary."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, items: list[tuple[InferenceResult, InferenceResult]]) -> None:
        """Do some setup after initialisation.

        :param items: The key-value pairs contained in the dictionary.
        """
        self.items = items

    infer_unary_op = protocols.dict_infer_unary_op

    def pytype(self) -> Literal["builtins.dict"]:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        return "builtins.dict"

    def get_children(self):
        """Get the key and value nodes below this node.

        Children are returned in the order that they are defined in the source
        code, key first then the value.

        :returns: The children.
        :rtype: iterable(NodeNG)
        """
        for key, value in self.items:
            yield key
            yield value

    def last_child(self):
        """An optimized version of list(get_children())[-1]

        :returns: The last child, or None if no children exist.
        :rtype: NodeNG or None
        """
        if self.items:
            return self.items[-1][1]
        return None

    def itered(self):
        """An iterator over the keys this node contains.

        :returns: The keys of this node.
        :rtype: iterable(NodeNG)
        """
        return [key for (key, _) in self.items]

    def getitem(
        self, index: Const | Slice, context: InferenceContext | None = None
    ) -> NodeNG:
        """Get an item from this node.

        :param index: The node to use as a subscript index.

        :raises AstroidTypeError: When the given index cannot be used as a
            subscript index, or if this node is not subscriptable.
        :raises AstroidIndexError: If the given index does not exist in the
            dictionary.
        """
        for key, value in self.items:
            # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}.
            if isinstance(key, DictUnpack):
                inferred_value = util.safe_infer(value, context)
                if not isinstance(inferred_value, Dict):
                    continue

                try:
                    return inferred_value.getitem(index, context)
                except (AstroidTypeError, AstroidIndexError):
                    continue

            for inferredkey in key.infer(context):
                if isinstance(inferredkey, util.UninferableBase):
                    continue
                if isinstance(inferredkey, Const) and isinstance(index, Const):
                    if inferredkey.value == index.value:
                        return value

        raise AstroidIndexError(index)

    def bool_value(self, context: InferenceContext | None = None):
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
        :rtype: bool
        """
        return bool(self.items)

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Iterator[nodes.Dict]:
        if not any(isinstance(k, DictUnpack) for k, _ in self.items):
            yield self
        else:
            items = self._infer_map(context)
            new_seq = type(self)(
                lineno=self.lineno,
                col_offset=self.col_offset,
                parent=self.parent,
                end_lineno=self.end_lineno,
                end_col_offset=self.end_col_offset,
            )
            new_seq.postinit(list(items.items()))
            yield new_seq

    @staticmethod
    def _update_with_replacement(
        lhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
        rhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult],
    ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
        """Delete nodes that equate to duplicate keys.

        Since an astroid node doesn't 'equal' another node with the same value,
        this function uses the as_string method to make sure duplicate keys
        don't get through

        Note that both the key and the value are astroid nodes

        Fixes issue with DictUnpack causing duplicate keys
        in inferred Dict items

        :param lhs_dict: Dictionary to 'merge' nodes into
        :param rhs_dict: Dictionary with nodes to pull from
        :return : merged dictionary of nodes
        """
        combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items())
        # Overwrite keys which have the same string values
        string_map = {key.as_string(): (key, value) for key, value in combined_dict}
        # Return to dictionary
        return dict(string_map.values())

    def _infer_map(
        self, context: InferenceContext | None
    ) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]:
        """Infer all values based on Dict.items."""
        values: dict[SuccessfulInferenceResult, SuccessfulInferenceResult] = {}
        for name, value in self.items:
            if isinstance(name, DictUnpack):
                double_starred = util.safe_infer(value, context)
                if not double_starred:
                    raise InferenceError
                if not isinstance(double_starred, Dict):
                    raise InferenceError(node=self, context=context)
                unpack_items = double_starred._infer_map(context)
                values = self._update_with_replacement(values, unpack_items)
            else:
                key = util.safe_infer(name, context=context)
                safe_value = util.safe_infer(value, context=context)
                if any(not elem for elem in (key, safe_value)):
                    raise InferenceError(node=self, context=context)
                # safe_value is SuccessfulInferenceResult as bool(Uninferable) == False
                values = self._update_with_replacement(values, {key: safe_value})
        return values


class Expr(_base_nodes.Statement):
    """Class representing an :class:`ast.Expr` node.

    An :class:`Expr` is any expression that does not have its value used or
    stored.

    >>> import astroid
    >>> node = astroid.extract_node('method()')
    >>> node
    <Call l.1 at 0x7f23b2e352b0>
    >>> node.parent
    <Expr l.1 at 0x7f23b2e35278>
    """

    _astroid_fields = ("value",)

    value: NodeNG
    """What the expression does."""

    def postinit(self, value: NodeNG) -> None:
        self.value = value

    def get_children(self):
        yield self.value

    def _get_yield_nodes_skip_functions(self):
        if not self.value.is_function:
            yield from self.value._get_yield_nodes_skip_functions()

    def _get_yield_nodes_skip_lambdas(self):
        if not self.value.is_lambda:
            yield from self.value._get_yield_nodes_skip_lambdas()


class EmptyNode(_base_nodes.NoChildrenNode):
    """Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`."""

    object = None

    def __init__(
        self,
        lineno: None = None,
        col_offset: None = None,
        parent: None = None,
        *,
        end_lineno: None = None,
        end_col_offset: None = None,
    ) -> None:
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def has_underlying_object(self) -> bool:
        return self.object is not None and self.object is not _EMPTY_OBJECT_MARKER

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, None]:
        if not self.has_underlying_object():
            yield util.Uninferable
        else:
            try:
                yield from AstroidManager().infer_ast_from_something(
                    self.object, context=context
                )
            except AstroidError:
                yield util.Uninferable


class ExceptHandler(
    _base_nodes.MultiLineBlockNode, _base_nodes.AssignTypeNode, _base_nodes.Statement
):
    """Class representing an :class:`ast.ExceptHandler`. node.

    An :class:`ExceptHandler` is an ``except`` block on a try-except.

    >>> import astroid
    >>> node = astroid.extract_node('''
        try:
            do_something()
        except Exception as error:
            print("Error!")
        ''')
    >>> node
    <Try l.2 at 0x7f23b2e9d908>
    >>> node.handlers
    [<ExceptHandler l.4 at 0x7f23b2e9e860>]
    """

    _astroid_fields = ("type", "name", "body")
    _multi_line_block_fields = ("body",)

    type: NodeNG | None
    """The types that the block handles."""

    name: AssignName | None
    """The name that the caught exception is assigned to."""

    body: list[NodeNG]
    """The contents of the block."""

    assigned_stmts = protocols.excepthandler_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    def postinit(
        self,
        type: NodeNG | None,  # pylint: disable = redefined-builtin
        name: AssignName | None,
        body: list[NodeNG],
    ) -> None:
        self.type = type
        self.name = name
        self.body = body

    def get_children(self):
        if self.type is not None:
            yield self.type

        if self.name is not None:
            yield self.name

        yield from self.body

    @cached_property
    def blockstart_tolineno(self):
        """The line on which the beginning of this block ends.

        :type: int
        """
        if self.name:
            return self.name.tolineno
        if self.type:
            return self.type.tolineno
        return self.lineno

    def catch(self, exceptions: list[str] | None) -> bool:
        """Check if this node handles any of the given

        :param exceptions: The names of the exceptions to check for.
        """
        if self.type is None or exceptions is None:
            return True
        return any(node.name in exceptions for node in self.type._get_name_nodes())


class For(
    _base_nodes.MultiLineWithElseBlockNode,
    _base_nodes.AssignTypeNode,
    _base_nodes.Statement,
):
    """Class representing an :class:`ast.For` node.

    >>> import astroid
    >>> node = astroid.extract_node('for thing in things: print(thing)')
    >>> node
    <For l.1 at 0x7f23b2e8cf28>
    """

    _astroid_fields = ("target", "iter", "body", "orelse")
    _other_other_fields = ("type_annotation",)
    _multi_line_block_fields = ("body", "orelse")

    optional_assign = True
    """Whether this node optionally assigns a variable.

    This is always ``True`` for :class:`For` nodes.
    """

    target: NodeNG
    """What the loop assigns to."""

    iter: NodeNG
    """What the loop iterates over."""

    body: list[NodeNG]
    """The contents of the body of the loop."""

    orelse: list[NodeNG]
    """The contents of the ``else`` block of the loop."""

    type_annotation: NodeNG | None
    """If present, this will contain the type annotation passed by a type comment"""

    def postinit(
        self,
        target: NodeNG,
        iter: NodeNG,  # pylint: disable = redefined-builtin
        body: list[NodeNG],
        orelse: list[NodeNG],
        type_annotation: NodeNG | None,
    ) -> None:
        self.target = target
        self.iter = iter
        self.body = body
        self.orelse = orelse
        self.type_annotation = type_annotation

    assigned_stmts = protocols.for_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    @cached_property
    def blockstart_tolineno(self):
        """The line on which the beginning of this block ends.

        :type: int
        """
        return self.iter.tolineno

    def get_children(self):
        yield self.target
        yield self.iter

        yield from self.body
        yield from self.orelse


class AsyncFor(For):
    """Class representing an :class:`ast.AsyncFor` node.

    An :class:`AsyncFor` is an asynchronous :class:`For` built with
    the ``async`` keyword.

    >>> import astroid
    >>> node = astroid.extract_node('''
    async def func(things):
        async for thing in things:
            print(thing)
    ''')
    >>> node
    <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
    >>> node.body[0]
    <AsyncFor l.3 at 0x7f23b2e417b8>
    """


class Await(NodeNG):
    """Class representing an :class:`ast.Await` node.

    An :class:`Await` is the ``await`` keyword.

    >>> import astroid
    >>> node = astroid.extract_node('''
    async def func(things):
        await other_func()
    ''')
    >>> node
    <AsyncFunctionDef.func l.2 at 0x7f23b2e41748>
    >>> node.body[0]
    <Expr l.3 at 0x7f23b2e419e8>
    >>> list(node.body[0].get_children())[0]
    <Await l.3 at 0x7f23b2e41a20>
    """

    _astroid_fields = ("value",)

    value: NodeNG
    """What to wait for."""

    def postinit(self, value: NodeNG) -> None:
        self.value = value

    def get_children(self):
        yield self.value


class ImportFrom(_base_nodes.ImportNode):
    """Class representing an :class:`ast.ImportFrom` node.

    >>> import astroid
    >>> node = astroid.extract_node('from my_package import my_module')
    >>> node
    <ImportFrom l.1 at 0x7f23b2e415c0>
    """

    _other_fields = ("modname", "names", "level")

    def __init__(
        self,
        fromname: str | None,
        names: list[tuple[str, str | None]],
        level: int | None = 0,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """
        :param fromname: The module that is being imported from.

        :param names: What is being imported from the module.

        :param level: The level of relative import.

        :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.modname: str | None = fromname  # can be None
        """The module that is being imported from.

        This is ``None`` for relative imports.
        """

        self.names: list[tuple[str, str | None]] = names
        """What is being imported from the module.

        Each entry is a :class:`tuple` of the name being imported,
        and the alias that the name is assigned to (if any).
        """

        # TODO When is 'level' None?
        self.level: int | None = level  # can be None
        """The level of relative import.

        Essentially this is the number of dots in the import.
        This is always 0 for absolute imports.
        """

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self,
        context: InferenceContext | None = None,
        asname: bool = True,
        **kwargs: Any,
    ) -> Generator[InferenceResult, None, None]:
        """Infer a ImportFrom node: return the imported module/object."""
        context = context or InferenceContext()
        name = context.lookupname
        if name is None:
            raise InferenceError(node=self, context=context)
        if asname:
            try:
                name = self.real_name(name)
            except AttributeInferenceError as exc:
                # See https://github.com/pylint-dev/pylint/issues/4692
                raise InferenceError(node=self, context=context) from exc
        try:
            module = self.do_import_module()
        except AstroidBuildingError as exc:
            raise InferenceError(node=self, context=context) from exc

        try:
            context = copy_context(context)
            context.lookupname = name
            stmts = module.getattr(name, ignore_locals=module is self.root())
            return _infer_stmts(stmts, context)
        except AttributeInferenceError as error:
            raise InferenceError(
                str(error), target=self, attribute=name, context=context
            ) from error


class Attribute(NodeNG):
    """Class representing an :class:`ast.Attribute` node."""

    expr: NodeNG

    _astroid_fields = ("expr",)
    _other_fields = ("attrname",)

    def __init__(
        self,
        attrname: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.attrname = attrname
        """The name of the attribute."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, expr: NodeNG) -> None:
        self.expr = expr

    def get_children(self):
        yield self.expr

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
        return _infer_attribute(self, context, **kwargs)


class Global(_base_nodes.NoChildrenNode, _base_nodes.Statement):
    """Class representing an :class:`ast.Global` node.

    >>> import astroid
    >>> node = astroid.extract_node('global a_global')
    >>> node
    <Global l.1 at 0x7f23b2e9de10>
    """

    _other_fields = ("names",)

    def __init__(
        self,
        names: list[str],
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """
        :param names: The names being declared as global.

        :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.names: list[str] = names
        """The names being declared as global."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def _infer_name(self, frame, name):
        return name

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, None]:
        if context is None or context.lookupname is None:
            raise InferenceError(node=self, context=context)
        try:
            # pylint: disable-next=no-member
            return _infer_stmts(self.root().getattr(context.lookupname), context)
        except AttributeInferenceError as error:
            raise InferenceError(
                str(error), target=self, attribute=context.lookupname, context=context
            ) from error


class If(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
    """Class representing an :class:`ast.If` node.

    >>> import astroid
    >>> node = astroid.extract_node('if condition: print(True)')
    >>> node
    <If l.1 at 0x7f23b2e9dd30>
    """

    _astroid_fields = ("test", "body", "orelse")
    _multi_line_block_fields = ("body", "orelse")

    test: NodeNG
    """The condition that the statement tests."""

    body: list[NodeNG]
    """The contents of the block."""

    orelse: list[NodeNG]
    """The contents of the ``else`` block."""

    def postinit(self, test: NodeNG, body: list[NodeNG], orelse: list[NodeNG]) -> None:
        self.test = test
        self.body = body
        self.orelse = orelse

    @cached_property
    def blockstart_tolineno(self):
        """The line on which the beginning of this block ends.

        :type: int
        """
        return self.test.tolineno

    def block_range(self, lineno: int) -> tuple[int, int]:
        """Get a range from the given line number to where this node ends.

        :param lineno: The line number to start the range at.

        :returns: The range of line numbers that this node belongs to,
            starting at the given line number.
        """
        if lineno == self.body[0].fromlineno:
            return lineno, lineno
        if lineno <= self.body[-1].tolineno:
            return lineno, self.body[-1].tolineno
        return self._elsed_block_range(lineno, self.orelse, self.body[0].fromlineno - 1)

    def get_children(self):
        yield self.test

        yield from self.body
        yield from self.orelse

    def has_elif_block(self):
        return len(self.orelse) == 1 and isinstance(self.orelse[0], If)

    def _get_yield_nodes_skip_functions(self):
        """An If node can contain a Yield node in the test"""
        yield from self.test._get_yield_nodes_skip_functions()
        yield from super()._get_yield_nodes_skip_functions()

    def _get_yield_nodes_skip_lambdas(self):
        """An If node can contain a Yield node in the test"""
        yield from self.test._get_yield_nodes_skip_lambdas()
        yield from super()._get_yield_nodes_skip_lambdas()


class IfExp(NodeNG):
    """Class representing an :class:`ast.IfExp` node.
    >>> import astroid
    >>> node = astroid.extract_node('value if condition else other')
    >>> node
    <IfExp l.1 at 0x7f23b2e9dbe0>
    """

    _astroid_fields = ("test", "body", "orelse")

    test: NodeNG
    """The condition that the statement tests."""

    body: NodeNG
    """The contents of the block."""

    orelse: NodeNG
    """The contents of the ``else`` block."""

    def postinit(self, test: NodeNG, body: NodeNG, orelse: NodeNG) -> None:
        self.test = test
        self.body = body
        self.orelse = orelse

    def get_children(self):
        yield self.test
        yield self.body
        yield self.orelse

    def op_left_associative(self) -> Literal[False]:
        # `1 if True else 2 if False else 3` is parsed as
        # `1 if True else (2 if False else 3)`
        return False

    @decorators.raise_if_nothing_inferred
    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, None]:
        """Support IfExp inference.

        If we can't infer the truthiness of the condition, we default
        to inferring both branches. Otherwise, we infer either branch
        depending on the condition.
        """
        both_branches = False
        # We use two separate contexts for evaluating lhs and rhs because
        # evaluating lhs may leave some undesired entries in context.path
        # which may not let us infer right value of rhs.

        context = context or InferenceContext()
        lhs_context = copy_context(context)
        rhs_context = copy_context(context)
        try:
            test = next(self.test.infer(context=context.clone()))
        except (InferenceError, StopIteration):
            both_branches = True
        else:
            if not isinstance(test, util.UninferableBase):
                if test.bool_value():
                    yield from self.body.infer(context=lhs_context)
                else:
                    yield from self.orelse.infer(context=rhs_context)
            else:
                both_branches = True
        if both_branches:
            yield from self.body.infer(context=lhs_context)
            yield from self.orelse.infer(context=rhs_context)


class Import(_base_nodes.ImportNode):
    """Class representing an :class:`ast.Import` node.
    >>> import astroid
    >>> node = astroid.extract_node('import astroid')
    >>> node
    <Import l.1 at 0x7f23b2e4e5c0>
    """

    _other_fields = ("names",)

    @decorators.deprecate_default_argument_values(names="list[tuple[str, str | None]]")
    def __init__(
        self,
        names: list[tuple[str, str | None]] | None = None,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """
        :param names: The names being imported.

        :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.names: list[tuple[str, str | None]] = names or []
        """The names being imported.

        Each entry is a :class:`tuple` of the name being imported,
        and the alias that the name is assigned to (if any).
        """

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self,
        context: InferenceContext | None = None,
        asname: bool = True,
        **kwargs: Any,
    ) -> Generator[nodes.Module, None, None]:
        """Infer an Import node: return the imported module/object."""
        context = context or InferenceContext()
        name = context.lookupname
        if name is None:
            raise InferenceError(node=self, context=context)

        try:
            if asname:
                yield self.do_import_module(self.real_name(name))
            else:
                yield self.do_import_module(name)
        except AstroidBuildingError as exc:
            raise InferenceError(node=self, context=context) from exc


class Keyword(NodeNG):
    """Class representing an :class:`ast.keyword` node.

    >>> import astroid
    >>> node = astroid.extract_node('function(a_kwarg=True)')
    >>> node
    <Call l.1 at 0x7f23b2e9e320>
    >>> node.keywords
    [<Keyword l.1 at 0x7f23b2e9e9b0>]
    """

    _astroid_fields = ("value",)
    _other_fields = ("arg",)

    value: NodeNG
    """The value being assigned to the keyword argument."""

    def __init__(
        self,
        arg: str | None,
        lineno: int | None,
        col_offset: int | None,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.arg = arg
        """The argument being assigned to."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, value: NodeNG) -> None:
        self.value = value

    def get_children(self):
        yield self.value


class List(BaseContainer):
    """Class representing an :class:`ast.List` node.

    >>> import astroid
    >>> node = astroid.extract_node('[1, 2, 3]')
    >>> node
    <List.list l.1 at 0x7f23b2e9e128>
    """

    _other_fields = ("ctx",)

    def __init__(
        self,
        ctx: Context | None = None,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """
        :param ctx: Whether the list is assigned to or loaded from.

        :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.ctx: Context | None = ctx
        """Whether the list is assigned to or loaded from."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    assigned_stmts = protocols.sequence_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    infer_unary_op = protocols.list_infer_unary_op
    infer_binary_op = protocols.tl_infer_binary_op

    def pytype(self) -> Literal["builtins.list"]:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        return "builtins.list"

    def getitem(self, index, context: InferenceContext | None = None):
        """Get an item from this node.

        :param index: The node to use as a subscript index.
        :type index: Const or Slice
        """
        return _container_getitem(self, self.elts, index, context=context)


class Nonlocal(_base_nodes.NoChildrenNode, _base_nodes.Statement):
    """Class representing an :class:`ast.Nonlocal` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    def function():
        nonlocal var
    ''')
    >>> node
    <FunctionDef.function l.2 at 0x7f23b2e9e208>
    >>> node.body[0]
    <Nonlocal l.3 at 0x7f23b2e9e908>
    """

    _other_fields = ("names",)

    def __init__(
        self,
        names: list[str],
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """
        :param names: The names being declared as not local.

        :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.names: list[str] = names
        """The names being declared as not local."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def _infer_name(self, frame, name):
        return name


class ParamSpec(_base_nodes.AssignTypeNode):
    """Class representing a :class:`ast.ParamSpec` node.

    >>> import astroid
    >>> node = astroid.extract_node('type Alias[**P] = Callable[P, int]')
    >>> node.type_params[0]
    <ParamSpec l.1 at 0x7f23b2e4e198>
    """

    _astroid_fields = ("name",)

    name: AssignName

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int,
        end_col_offset: int,
    ) -> None:
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, *, name: AssignName) -> None:
        self.name = name

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Iterator[ParamSpec]:
        yield self


class Pass(_base_nodes.NoChildrenNode, _base_nodes.Statement):
    """Class representing an :class:`ast.Pass` node.

    >>> import astroid
    >>> node = astroid.extract_node('pass')
    >>> node
    <Pass l.1 at 0x7f23b2e9e748>
    """


class Raise(_base_nodes.Statement):
    """Class representing an :class:`ast.Raise` node.

    >>> import astroid
    >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")')
    >>> node
    <Raise l.1 at 0x7f23b2e9e828>
    """

    _astroid_fields = ("exc", "cause")

    exc: NodeNG | None
    """What is being raised."""

    cause: NodeNG | None
    """The exception being used to raise this one."""

    def postinit(
        self,
        exc: NodeNG | None,
        cause: NodeNG | None,
    ) -> None:
        self.exc = exc
        self.cause = cause

    def raises_not_implemented(self) -> bool:
        """Check if this node raises a :class:`NotImplementedError`.

        :returns: Whether this node raises a :class:`NotImplementedError`.
        """
        if not self.exc:
            return False
        return any(
            name.name == "NotImplementedError" for name in self.exc._get_name_nodes()
        )

    def get_children(self):
        if self.exc is not None:
            yield self.exc

        if self.cause is not None:
            yield self.cause


class Return(_base_nodes.Statement):
    """Class representing an :class:`ast.Return` node.

    >>> import astroid
    >>> node = astroid.extract_node('return True')
    >>> node
    <Return l.1 at 0x7f23b8211908>
    """

    _astroid_fields = ("value",)

    value: NodeNG | None
    """The value being returned."""

    def postinit(self, value: NodeNG | None) -> None:
        self.value = value

    def get_children(self):
        if self.value is not None:
            yield self.value

    def is_tuple_return(self):
        return isinstance(self.value, Tuple)

    def _get_return_nodes_skip_functions(self):
        yield self


class Set(BaseContainer):
    """Class representing an :class:`ast.Set` node.

    >>> import astroid
    >>> node = astroid.extract_node('{1, 2, 3}')
    >>> node
    <Set.set l.1 at 0x7f23b2e71d68>
    """

    infer_unary_op = protocols.set_infer_unary_op

    def pytype(self) -> Literal["builtins.set"]:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        return "builtins.set"


class Slice(NodeNG):
    """Class representing an :class:`ast.Slice` node.

    >>> import astroid
    >>> node = astroid.extract_node('things[1:3]')
    >>> node
    <Subscript l.1 at 0x7f23b2e71f60>
    >>> node.slice
    <Slice l.1 at 0x7f23b2e71e80>
    """

    _astroid_fields = ("lower", "upper", "step")

    lower: NodeNG | None
    """The lower index in the slice."""

    upper: NodeNG | None
    """The upper index in the slice."""

    step: NodeNG | None
    """The step to take between indexes."""

    def postinit(
        self,
        lower: NodeNG | None,
        upper: NodeNG | None,
        step: NodeNG | None,
    ) -> None:
        self.lower = lower
        self.upper = upper
        self.step = step

    def _wrap_attribute(self, attr):
        """Wrap the empty attributes of the Slice in a Const node."""
        if not attr:
            const = const_factory(attr)
            const.parent = self
            return const
        return attr

    @cached_property
    def _proxied(self) -> nodes.ClassDef:
        builtins = AstroidManager().builtins_module
        return builtins.getattr("slice")[0]

    def pytype(self) -> Literal["builtins.slice"]:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        return "builtins.slice"

    def igetattr(
        self, attrname: str, context: InferenceContext | None = None
    ) -> Iterator[SuccessfulInferenceResult]:
        """Infer the possible values of the given attribute on the slice.

        :param attrname: The name of the attribute to infer.

        :returns: The inferred possible values.
        """
        if attrname == "start":
            yield self._wrap_attribute(self.lower)
        elif attrname == "stop":
            yield self._wrap_attribute(self.upper)
        elif attrname == "step":
            yield self._wrap_attribute(self.step)
        else:
            yield from self.getattr(attrname, context=context)

    def getattr(self, attrname, context: InferenceContext | None = None):
        return self._proxied.getattr(attrname, context)

    def get_children(self):
        if self.lower is not None:
            yield self.lower

        if self.upper is not None:
            yield self.upper

        if self.step is not None:
            yield self.step

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Iterator[Slice]:
        yield self


class Starred(_base_nodes.ParentAssignNode):
    """Class representing an :class:`ast.Starred` node.

    >>> import astroid
    >>> node = astroid.extract_node('*args')
    >>> node
    <Starred l.1 at 0x7f23b2e41978>
    """

    _astroid_fields = ("value",)
    _other_fields = ("ctx",)

    value: NodeNG
    """What is being unpacked."""

    def __init__(
        self,
        ctx: Context,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.ctx = ctx
        """Whether the starred item is assigned to or loaded from."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, value: NodeNG) -> None:
        self.value = value

    assigned_stmts = protocols.starred_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    def get_children(self):
        yield self.value


class Subscript(NodeNG):
    """Class representing an :class:`ast.Subscript` node.

    >>> import astroid
    >>> node = astroid.extract_node('things[1:3]')
    >>> node
    <Subscript l.1 at 0x7f23b2e71f60>
    """

    _SUBSCRIPT_SENTINEL = object()
    _astroid_fields = ("value", "slice")
    _other_fields = ("ctx",)

    value: NodeNG
    """What is being indexed."""

    slice: NodeNG
    """The slice being used to lookup."""

    def __init__(
        self,
        ctx: Context,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.ctx = ctx
        """Whether the subscripted item is assigned to or loaded from."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    # pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
    def postinit(self, value: NodeNG, slice: NodeNG) -> None:
        self.value = value
        self.slice = slice

    def get_children(self):
        yield self.value
        yield self.slice

    def _infer_subscript(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
        """Inference for subscripts.

        We're understanding if the index is a Const
        or a slice, passing the result of inference
        to the value's `getitem` method, which should
        handle each supported index type accordingly.
        """
        from astroid import helpers  # pylint: disable=import-outside-toplevel

        found_one = False
        for value in self.value.infer(context):
            if isinstance(value, util.UninferableBase):
                yield util.Uninferable
                return None
            for index in self.slice.infer(context):
                if isinstance(index, util.UninferableBase):
                    yield util.Uninferable
                    return None

                # Try to deduce the index value.
                index_value = self._SUBSCRIPT_SENTINEL
                if value.__class__ == Instance:
                    index_value = index
                elif index.__class__ == Instance:
                    instance_as_index = helpers.class_instance_as_index(index)
                    if instance_as_index:
                        index_value = instance_as_index
                else:
                    index_value = index

                if index_value is self._SUBSCRIPT_SENTINEL:
                    raise InferenceError(node=self, context=context)

                try:
                    assigned = value.getitem(index_value, context)
                except (
                    AstroidTypeError,
                    AstroidIndexError,
                    AstroidValueError,
                    AttributeInferenceError,
                    AttributeError,
                ) as exc:
                    raise InferenceError(node=self, context=context) from exc

                # Prevent inferring if the inferred subscript
                # is the same as the original subscripted object.
                if self is assigned or isinstance(assigned, util.UninferableBase):
                    yield util.Uninferable
                    return None
                yield from assigned.infer(context)
                found_one = True

        if found_one:
            return InferenceErrorInfo(node=self, context=context)
        return None

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(self, context: InferenceContext | None = None, **kwargs: Any):
        return self._infer_subscript(context, **kwargs)

    @decorators.raise_if_nothing_inferred
    def infer_lhs(self, context: InferenceContext | None = None, **kwargs: Any):
        return self._infer_subscript(context, **kwargs)


class Try(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
    """Class representing a :class:`ast.Try` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
        try:
            do_something()
        except Exception as error:
            print("Error!")
        finally:
            print("Cleanup!")
        ''')
    >>> node
    <Try l.2 at 0x7f23b2e41d68>
    """

    _astroid_fields = ("body", "handlers", "orelse", "finalbody")
    _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody")

    def __init__(
        self,
        *,
        lineno: int,
        col_offset: int,
        end_lineno: int,
        end_col_offset: int,
        parent: NodeNG,
    ) -> 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],
        handlers: list[ExceptHandler],
        orelse: list[NodeNG],
        finalbody: list[NodeNG],
    ) -> 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.
        """
        self.body = body
        self.handlers = handlers
        self.orelse = orelse
        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 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.

    >>> import astroid
    >>> node = astroid.extract_node('(1, 2, 3)')
    >>> node
    <Tuple.tuple l.1 at 0x7f23b2e41780>
    """

    _other_fields = ("ctx",)

    def __init__(
        self,
        ctx: Context | None = None,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """
        :param ctx: Whether the tuple is assigned to or loaded from.

        :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.ctx: Context | None = ctx
        """Whether the tuple is assigned to or loaded from."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    assigned_stmts = protocols.sequence_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    infer_unary_op = protocols.tuple_infer_unary_op
    infer_binary_op = protocols.tl_infer_binary_op

    def pytype(self) -> Literal["builtins.tuple"]:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        return "builtins.tuple"

    def getitem(self, index, context: InferenceContext | None = None):
        """Get an item from this node.

        :param index: The node to use as a subscript index.
        :type index: Const or Slice
        """
        return _container_getitem(self, self.elts, index, context=context)


class TypeAlias(_base_nodes.AssignTypeNode, _base_nodes.Statement):
    """Class representing a :class:`ast.TypeAlias` node.

    >>> import astroid
    >>> node = astroid.extract_node('type Point = tuple[float, float]')
    >>> node
    <TypeAlias l.1 at 0x7f23b2e4e198>
    """

    _astroid_fields = ("name", "type_params", "value")

    name: AssignName
    type_params: list[TypeVar | ParamSpec | TypeVarTuple]
    value: NodeNG

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int,
        end_col_offset: int,
    ) -> None:
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(
        self,
        *,
        name: AssignName,
        type_params: list[TypeVar | ParamSpec | TypeVarTuple],
        value: NodeNG,
    ) -> None:
        self.name = name
        self.type_params = type_params
        self.value = value

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Iterator[TypeAlias]:
        yield self

    assigned_stmts: ClassVar[
        Callable[
            [
                TypeAlias,
                AssignName,
                InferenceContext | None,
                None,
            ],
            Generator[NodeNG, None, None],
        ]
    ] = protocols.assign_assigned_stmts


class TypeVar(_base_nodes.AssignTypeNode):
    """Class representing a :class:`ast.TypeVar` node.

    >>> import astroid
    >>> node = astroid.extract_node('type Point[T] = tuple[float, float]')
    >>> node.type_params[0]
    <TypeVar l.1 at 0x7f23b2e4e198>
    """

    _astroid_fields = ("name", "bound")

    name: AssignName
    bound: NodeNG | None

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int,
        end_col_offset: int,
    ) -> None:
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, *, name: AssignName, bound: NodeNG | None) -> None:
        self.name = name
        self.bound = bound

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Iterator[TypeVar]:
        yield self


class TypeVarTuple(_base_nodes.AssignTypeNode):
    """Class representing a :class:`ast.TypeVarTuple` node.

    >>> import astroid
    >>> node = astroid.extract_node('type Alias[*Ts] = tuple[*Ts]')
    >>> node.type_params[0]
    <TypeVarTuple l.1 at 0x7f23b2e4e198>
    """

    _astroid_fields = ("name",)

    name: AssignName

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int,
        end_col_offset: int,
    ) -> None:
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, *, name: AssignName) -> None:
        self.name = name

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Iterator[TypeVarTuple]:
        yield self


UNARY_OP_METHOD = {
    "+": "__pos__",
    "-": "__neg__",
    "~": "__invert__",
    "not": None,  # XXX not '__nonzero__'
}


class UnaryOp(_base_nodes.OperatorNode):
    """Class representing an :class:`ast.UnaryOp` node.

    >>> import astroid
    >>> node = astroid.extract_node('-5')
    >>> node
    <UnaryOp l.1 at 0x7f23b2e4e198>
    """

    _astroid_fields = ("operand",)
    _other_fields = ("op",)

    operand: NodeNG
    """What the unary operator is applied to."""

    def __init__(
        self,
        op: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.op = op
        """The operator."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, operand: NodeNG) -> None:
        self.operand = operand

    def type_errors(self, context: InferenceContext | None = None):
        """Get a list of type errors which can occur during inference.

        Each TypeError is represented by a :class:`BadUnaryOperationMessage`,
        which holds the original exception.

        :returns: The list of possible type errors.
        :rtype: list(BadUnaryOperationMessage)
        """
        try:
            results = self._infer_unaryop(context=context)
            return [
                result
                for result in results
                if isinstance(result, util.BadUnaryOperationMessage)
            ]
        except InferenceError:
            return []

    def get_children(self):
        yield self.operand

    def op_precedence(self):
        if self.op == "not":
            return OP_PRECEDENCE[self.op]

        return super().op_precedence()

    def _infer_unaryop(
        self: nodes.UnaryOp, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[
        InferenceResult | util.BadUnaryOperationMessage, None, InferenceErrorInfo
    ]:
        """Infer what an UnaryOp should return when evaluated."""
        from astroid.nodes import ClassDef  # pylint: disable=import-outside-toplevel

        for operand in self.operand.infer(context):
            try:
                yield operand.infer_unary_op(self.op)
            except TypeError as exc:
                # The operand doesn't support this operation.
                yield util.BadUnaryOperationMessage(operand, self.op, exc)
            except AttributeError as exc:
                meth = UNARY_OP_METHOD[self.op]
                if meth is None:
                    # `not node`. Determine node's boolean
                    # value and negate its result, unless it is
                    # Uninferable, which will be returned as is.
                    bool_value = operand.bool_value()
                    if not isinstance(bool_value, util.UninferableBase):
                        yield const_factory(not bool_value)
                    else:
                        yield util.Uninferable
                else:
                    if not isinstance(operand, (Instance, ClassDef)):
                        # The operation was used on something which
                        # doesn't support it.
                        yield util.BadUnaryOperationMessage(operand, self.op, exc)
                        continue

                    try:
                        try:
                            methods = dunder_lookup.lookup(operand, meth)
                        except AttributeInferenceError:
                            yield util.BadUnaryOperationMessage(operand, self.op, exc)
                            continue

                        meth = methods[0]
                        inferred = next(meth.infer(context=context), None)
                        if (
                            isinstance(inferred, util.UninferableBase)
                            or not inferred.callable()
                        ):
                            continue

                        context = copy_context(context)
                        context.boundnode = operand
                        context.callcontext = CallContext(args=[], callee=inferred)

                        call_results = inferred.infer_call_result(self, context=context)
                        result = next(call_results, None)
                        if result is None:
                            # Failed to infer, return the same type.
                            yield operand
                        else:
                            yield result
                    except AttributeInferenceError as inner_exc:
                        # The unary operation special method was not found.
                        yield util.BadUnaryOperationMessage(operand, self.op, inner_exc)
                    except InferenceError:
                        yield util.Uninferable

    @decorators.raise_if_nothing_inferred
    @decorators.path_wrapper
    def _infer(
        self: nodes.UnaryOp, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo]:
        """Infer what an UnaryOp should return when evaluated."""
        yield from self._filter_operation_errors(
            self._infer_unaryop, context, util.BadUnaryOperationMessage
        )
        return InferenceErrorInfo(node=self, context=context)


class While(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement):
    """Class representing an :class:`ast.While` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    while condition():
        print("True")
    ''')
    >>> node
    <While l.2 at 0x7f23b2e4e390>
    """

    _astroid_fields = ("test", "body", "orelse")
    _multi_line_block_fields = ("body", "orelse")

    test: NodeNG
    """The condition that the loop tests."""

    body: list[NodeNG]
    """The contents of the loop."""

    orelse: list[NodeNG]
    """The contents of the ``else`` block."""

    def postinit(
        self,
        test: NodeNG,
        body: list[NodeNG],
        orelse: list[NodeNG],
    ) -> None:
        self.test = test
        self.body = body
        self.orelse = orelse

    @cached_property
    def blockstart_tolineno(self):
        """The line on which the beginning of this block ends.

        :type: int
        """
        return self.test.tolineno

    def block_range(self, lineno: int) -> tuple[int, int]:
        """Get a range from the given line number to where this node ends.

        :param lineno: The line number to start the range at.

        :returns: The range of line numbers that this node belongs to,
            starting at the given line number.
        """
        return self._elsed_block_range(lineno, self.orelse)

    def get_children(self):
        yield self.test

        yield from self.body
        yield from self.orelse

    def _get_yield_nodes_skip_functions(self):
        """A While node can contain a Yield node in the test"""
        yield from self.test._get_yield_nodes_skip_functions()
        yield from super()._get_yield_nodes_skip_functions()

    def _get_yield_nodes_skip_lambdas(self):
        """A While node can contain a Yield node in the test"""
        yield from self.test._get_yield_nodes_skip_lambdas()
        yield from super()._get_yield_nodes_skip_lambdas()


class With(
    _base_nodes.MultiLineWithElseBlockNode,
    _base_nodes.AssignTypeNode,
    _base_nodes.Statement,
):
    """Class representing an :class:`ast.With` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    with open(file_path) as file_:
        print(file_.read())
    ''')
    >>> node
    <With l.2 at 0x7f23b2e4e710>
    """

    _astroid_fields = ("items", "body")
    _other_other_fields = ("type_annotation",)
    _multi_line_block_fields = ("body",)

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | 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.items: list[tuple[NodeNG, NodeNG | None]] = []
        """The pairs of context managers and the names they are assigned to."""

        self.body: list[NodeNG] = []
        """The contents of the ``with`` block."""

        self.type_annotation: NodeNG | None = None  # can be None
        """If present, this will contain the type annotation passed by a type comment"""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(
        self,
        items: list[tuple[NodeNG, NodeNG | None]] | None = None,
        body: list[NodeNG] | None = None,
        type_annotation: NodeNG | None = None,
    ) -> None:
        """Do some setup after initialisation.

        :param items: The pairs of context managers and the names
            they are assigned to.

        :param body: The contents of the ``with`` block.
        """
        if items is not None:
            self.items = items
        if body is not None:
            self.body = body
        self.type_annotation = type_annotation

    assigned_stmts = protocols.with_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    @cached_property
    def blockstart_tolineno(self):
        """The line on which the beginning of this block ends.

        :type: int
        """
        return self.items[-1][0].tolineno

    def get_children(self):
        """Get the child nodes below this node.

        :returns: The children.
        :rtype: iterable(NodeNG)
        """
        for expr, var in self.items:
            yield expr
            if var:
                yield var
        yield from self.body


class AsyncWith(With):
    """Asynchronous ``with`` built with the ``async`` keyword."""


class Yield(NodeNG):
    """Class representing an :class:`ast.Yield` node.

    >>> import astroid
    >>> node = astroid.extract_node('yield True')
    >>> node
    <Yield l.1 at 0x7f23b2e4e5f8>
    """

    _astroid_fields = ("value",)

    value: NodeNG | None
    """The value to yield."""

    def postinit(self, value: NodeNG | None) -> None:
        self.value = value

    def get_children(self):
        if self.value is not None:
            yield self.value

    def _get_yield_nodes_skip_functions(self):
        yield self

    def _get_yield_nodes_skip_lambdas(self):
        yield self


class YieldFrom(Yield):  # TODO value is required, not optional
    """Class representing an :class:`ast.YieldFrom` node."""


class DictUnpack(_base_nodes.NoChildrenNode):
    """Represents the unpacking of dicts into dicts using :pep:`448`."""


class FormattedValue(NodeNG):
    """Class representing an :class:`ast.FormattedValue` node.

    Represents a :pep:`498` format string.

    >>> import astroid
    >>> node = astroid.extract_node('f"Format {type_}"')
    >>> node
    <JoinedStr l.1 at 0x7f23b2e4ed30>
    >>> node.values
    [<Const.str l.1 at 0x7f23b2e4eda0>, <FormattedValue l.1 at 0x7f23b2e4edd8>]
    """

    _astroid_fields = ("value", "format_spec")
    _other_fields = ("conversion",)

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | 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.value: NodeNG
        """The value to be formatted into the string."""

        self.conversion: int
        """The type of formatting to be applied to the value.

        .. seealso::
            :class:`ast.FormattedValue`
        """

        self.format_spec: JoinedStr | None = None
        """The formatting to be applied to the value.

        .. seealso::
            :class:`ast.FormattedValue`
        """

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(
        self,
        *,
        value: NodeNG,
        conversion: int,
        format_spec: JoinedStr | None = None,
    ) -> None:
        """Do some setup after initialisation.

        :param value: The value to be formatted into the string.

        :param conversion: The type of formatting to be applied to the value.

        :param format_spec: The formatting to be applied to the value.
        :type format_spec: JoinedStr or None
        """
        self.value = value
        self.conversion = conversion
        self.format_spec = format_spec

    def get_children(self):
        yield self.value

        if self.format_spec is not None:
            yield self.format_spec


class JoinedStr(NodeNG):
    """Represents a list of string expressions to be joined.

    >>> import astroid
    >>> node = astroid.extract_node('f"Format {type_}"')
    >>> node
    <JoinedStr l.1 at 0x7f23b2e4ed30>
    """

    _astroid_fields = ("values",)

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | 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.values: list[NodeNG] = []
        """The string expressions to be joined.

        :type: list(FormattedValue or Const)
        """

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, values: list[NodeNG] | None = None) -> None:
        """Do some setup after initialisation.

        :param value: The string expressions to be joined.

        :type: list(FormattedValue or Const)
        """
        if values is not None:
            self.values = values

    def get_children(self):
        yield from self.values


class NamedExpr(_base_nodes.AssignTypeNode):
    """Represents the assignment from the assignment expression

    >>> import astroid
    >>> module = astroid.parse('if a := 1: pass')
    >>> module.body[0].test
    <NamedExpr l.1 at 0x7f23b2e4ed30>
    """

    _astroid_fields = ("target", "value")

    optional_assign = True
    """Whether this node optionally assigns a variable.

    Since NamedExpr are not always called they do not always assign."""

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | 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.target: NodeNG
        """The assignment target

        :type: Name
        """

        self.value: NodeNG
        """The value that gets assigned in the expression"""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, target: NodeNG, value: NodeNG) -> None:
        self.target = target
        self.value = value

    assigned_stmts = protocols.named_expr_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """

    def frame(
        self, *, future: Literal[None, True] = None
    ) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda:
        """The first parent frame node.

        A frame node is a :class:`Module`, :class:`FunctionDef`,
        or :class:`ClassDef`.

        :returns: The first parent frame node.
        """
        if future is not None:
            warnings.warn(
                "The future arg will be removed in astroid 4.0.",
                DeprecationWarning,
                stacklevel=2,
            )
        if not self.parent:
            raise ParentMissingError(target=self)

        # For certain parents NamedExpr evaluate to the scope of the parent
        if isinstance(self.parent, (Arguments, Keyword, Comprehension)):
            if not self.parent.parent:
                raise ParentMissingError(target=self.parent)
            if not self.parent.parent.parent:
                raise ParentMissingError(target=self.parent.parent)
            return self.parent.parent.parent.frame()

        return self.parent.frame()

    def scope(self) -> LocalsDictNodeNG:
        """The first parent node defining a new scope.
        These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.

        :returns: The first parent scope node.
        """
        if not self.parent:
            raise ParentMissingError(target=self)

        # For certain parents NamedExpr evaluate to the scope of the parent
        if isinstance(self.parent, (Arguments, Keyword, Comprehension)):
            if not self.parent.parent:
                raise ParentMissingError(target=self.parent)
            if not self.parent.parent.parent:
                raise ParentMissingError(target=self.parent.parent)
            return self.parent.parent.parent.scope()

        return self.parent.scope()

    def set_local(self, name: str, stmt: NodeNG) -> None:
        """Define that the given name is declared in the given statement node.
        NamedExpr's in Arguments, Keyword or Comprehension are evaluated in their
        parent's parent scope. So we add to their frame's locals.

        .. seealso:: :meth:`scope`

        :param name: The name that is being defined.

        :param stmt: The statement that defines the given name.
        """
        self.frame().set_local(name, stmt)


class Unknown(_base_nodes.AssignTypeNode):
    """This node represents a node in a constructed AST where
    introspection is not possible.  At the moment, it's only used in
    the args attribute of FunctionDef nodes where function signature
    introspection failed.
    """

    name = "Unknown"

    def __init__(
        self,
        lineno: None = None,
        col_offset: None = None,
        parent: None = None,
        *,
        end_lineno: None = None,
        end_col_offset: None = None,
    ) -> None:
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def qname(self) -> Literal["Unknown"]:
        return "Unknown"

    def _infer(self, context: InferenceContext | None = None, **kwargs):
        """Inference on an Unknown node immediately terminates."""
        yield util.Uninferable


class EvaluatedObject(NodeNG):
    """Contains an object that has already been inferred

    This class is useful to pre-evaluate a particular node,
    with the resulting class acting as the non-evaluated node.
    """

    name = "EvaluatedObject"
    _astroid_fields = ("original",)
    _other_fields = ("value",)

    def __init__(
        self, original: SuccessfulInferenceResult, value: InferenceResult
    ) -> None:
        self.original: SuccessfulInferenceResult = original
        """The original node that has already been evaluated"""

        self.value: InferenceResult = value
        """The inferred value"""

        super().__init__(
            lineno=self.original.lineno,
            col_offset=self.original.col_offset,
            parent=self.original.parent,
            end_lineno=self.original.end_lineno,
            end_col_offset=self.original.end_col_offset,
        )

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[NodeNG | util.UninferableBase, None, None]:
        yield self.value


# Pattern matching #######################################################


class Match(_base_nodes.Statement, _base_nodes.MultiLineBlockNode):
    """Class representing a :class:`ast.Match` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case 200:
            ...
        case _:
            ...
    ''')
    >>> node
    <Match l.2 at 0x10c24e170>
    """

    _astroid_fields = ("subject", "cases")
    _multi_line_block_fields = ("cases",)

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        self.subject: NodeNG
        self.cases: list[MatchCase]
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(
        self,
        *,
        subject: NodeNG,
        cases: list[MatchCase],
    ) -> None:
        self.subject = subject
        self.cases = cases


class Pattern(NodeNG):
    """Base class for all Pattern nodes."""


class MatchCase(_base_nodes.MultiLineBlockNode):
    """Class representing a :class:`ast.match_case` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case 200:
            ...
    ''')
    >>> node.cases[0]
    <MatchCase l.3 at 0x10c24e590>
    """

    _astroid_fields = ("pattern", "guard", "body")
    _multi_line_block_fields = ("body",)

    lineno: None
    col_offset: None
    end_lineno: None
    end_col_offset: None

    def __init__(self, *, parent: NodeNG | None = None) -> None:
        self.pattern: Pattern
        self.guard: NodeNG | None
        self.body: list[NodeNG]
        super().__init__(
            parent=parent,
            lineno=None,
            col_offset=None,
            end_lineno=None,
            end_col_offset=None,
        )

    def postinit(
        self,
        *,
        pattern: Pattern,
        guard: NodeNG | None,
        body: list[NodeNG],
    ) -> None:
        self.pattern = pattern
        self.guard = guard
        self.body = body


class MatchValue(Pattern):
    """Class representing a :class:`ast.MatchValue` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case 200:
            ...
    ''')
    >>> node.cases[0].pattern
    <MatchValue l.3 at 0x10c24e200>
    """

    _astroid_fields = ("value",)

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        self.value: NodeNG
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, *, value: NodeNG) -> None:
        self.value = value


class MatchSingleton(Pattern):
    """Class representing a :class:`ast.MatchSingleton` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case True:
            ...
        case False:
            ...
        case None:
            ...
    ''')
    >>> node.cases[0].pattern
    <MatchSingleton l.3 at 0x10c2282e0>
    >>> node.cases[1].pattern
    <MatchSingleton l.5 at 0x10c228af0>
    >>> node.cases[2].pattern
    <MatchSingleton l.7 at 0x10c229f90>
    """

    _other_fields = ("value",)

    def __init__(
        self,
        *,
        value: Literal[True, False, None],
        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:
        self.value = value
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )


class MatchSequence(Pattern):
    """Class representing a :class:`ast.MatchSequence` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case [1, 2]:
            ...
        case (1, 2, *_):
            ...
    ''')
    >>> node.cases[0].pattern
    <MatchSequence l.3 at 0x10ca80d00>
    >>> node.cases[1].pattern
    <MatchSequence l.5 at 0x10ca80b20>
    """

    _astroid_fields = ("patterns",)

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        self.patterns: list[Pattern]
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, *, patterns: list[Pattern]) -> None:
        self.patterns = patterns


class MatchMapping(_base_nodes.AssignTypeNode, Pattern):
    """Class representing a :class:`ast.MatchMapping` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case {1: "Hello", 2: "World", 3: _, **rest}:
            ...
    ''')
    >>> node.cases[0].pattern
    <MatchMapping l.3 at 0x10c8a8850>
    """

    _astroid_fields = ("keys", "patterns", "rest")

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        self.keys: list[NodeNG]
        self.patterns: list[Pattern]
        self.rest: AssignName | None
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(
        self,
        *,
        keys: list[NodeNG],
        patterns: list[Pattern],
        rest: AssignName | None,
    ) -> None:
        self.keys = keys
        self.patterns = patterns
        self.rest = rest

    assigned_stmts = protocols.match_mapping_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """


class MatchClass(Pattern):
    """Class representing a :class:`ast.MatchClass` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case Point2D(0, 0):
            ...
        case Point3D(x=0, y=0, z=0):
            ...
    ''')
    >>> node.cases[0].pattern
    <MatchClass l.3 at 0x10ca83940>
    >>> node.cases[1].pattern
    <MatchClass l.5 at 0x10ca80880>
    """

    _astroid_fields = ("cls", "patterns", "kwd_patterns")
    _other_fields = ("kwd_attrs",)

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        self.cls: NodeNG
        self.patterns: list[Pattern]
        self.kwd_attrs: list[str]
        self.kwd_patterns: list[Pattern]
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(
        self,
        *,
        cls: NodeNG,
        patterns: list[Pattern],
        kwd_attrs: list[str],
        kwd_patterns: list[Pattern],
    ) -> None:
        self.cls = cls
        self.patterns = patterns
        self.kwd_attrs = kwd_attrs
        self.kwd_patterns = kwd_patterns


class MatchStar(_base_nodes.AssignTypeNode, Pattern):
    """Class representing a :class:`ast.MatchStar` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case [1, *_]:
            ...
    ''')
    >>> node.cases[0].pattern.patterns[1]
    <MatchStar l.3 at 0x10ca809a0>
    """

    _astroid_fields = ("name",)

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        self.name: AssignName | None
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, *, name: AssignName | None) -> None:
        self.name = name

    assigned_stmts = protocols.match_star_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """


class MatchAs(_base_nodes.AssignTypeNode, Pattern):
    """Class representing a :class:`ast.MatchAs` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case [1, a]:
            ...
        case {'key': b}:
            ...
        case Point2D(0, 0) as c:
            ...
        case d:
            ...
    ''')
    >>> node.cases[0].pattern.patterns[1]
    <MatchAs l.3 at 0x10d0b2da0>
    >>> node.cases[1].pattern.patterns[0]
    <MatchAs l.5 at 0x10d0b2920>
    >>> node.cases[2].pattern
    <MatchAs l.7 at 0x10d0b06a0>
    >>> node.cases[3].pattern
    <MatchAs l.9 at 0x10d09b880>
    """

    _astroid_fields = ("pattern", "name")

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        self.pattern: Pattern | None
        self.name: AssignName | None
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(
        self,
        *,
        pattern: Pattern | None,
        name: AssignName | None,
    ) -> None:
        self.pattern = pattern
        self.name = name

    assigned_stmts = protocols.match_as_assigned_stmts
    """Returns the assigned statement (non inferred) according to the assignment type.
    See astroid/protocols.py for actual implementation.
    """


class MatchOr(Pattern):
    """Class representing a :class:`ast.MatchOr` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    match x:
        case 400 | 401 | 402:
            ...
    ''')
    >>> node.cases[0].pattern
    <MatchOr l.3 at 0x10d0b0b50>
    """

    _astroid_fields = ("patterns",)

    def __init__(
        self,
        lineno: int | None = None,
        col_offset: int | None = None,
        parent: NodeNG | None = None,
        *,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        self.patterns: list[Pattern]
        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, *, patterns: list[Pattern]) -> None:
        self.patterns = patterns


# constants ##############################################################

# The _proxied attribute of all container types (List, Tuple, etc.)
# are set during bootstrapping by _astroid_bootstrapping().
CONST_CLS: dict[type, type[NodeNG]] = {
    list: List,
    tuple: Tuple,
    dict: Dict,
    set: Set,
    type(None): Const,
    type(NotImplemented): Const,
    type(...): Const,
    bool: Const,
    int: Const,
    float: Const,
    complex: Const,
    str: Const,
    bytes: Const,
}


def _create_basic_elements(
    value: Iterable[Any], node: List | Set | Tuple
) -> list[NodeNG]:
    """Create a list of nodes to function as the elements of a new node."""
    elements: list[NodeNG] = []
    for element in value:
        element_node = const_factory(element)
        element_node.parent = node
        elements.append(element_node)
    return elements


def _create_dict_items(
    values: Mapping[Any, Any], node: Dict
) -> list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]]:
    """Create a list of node pairs to function as the items of a new dict node."""
    elements: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] = []
    for key, value in values.items():
        key_node = const_factory(key)
        key_node.parent = node
        value_node = const_factory(value)
        value_node.parent = node
        elements.append((key_node, value_node))
    return elements


def const_factory(value: Any) -> ConstFactoryResult:
    """Return an astroid node for a python value."""
    assert not isinstance(value, NodeNG)

    # This only handles instances of the CONST types. Any
    # subclasses get inferred as EmptyNode.
    # TODO: See if we should revisit these with the normal builder.
    if value.__class__ not in CONST_CLS:
        node = EmptyNode()
        node.object = value
        return node

    instance: List | Set | Tuple | Dict
    initializer_cls = CONST_CLS[value.__class__]
    if issubclass(initializer_cls, (List, Set, Tuple)):
        instance = initializer_cls(
            lineno=None,
            col_offset=None,
            parent=None,
            end_lineno=None,
            end_col_offset=None,
        )
        instance.postinit(_create_basic_elements(value, instance))
        return instance
    if issubclass(initializer_cls, Dict):
        instance = initializer_cls(
            lineno=None,
            col_offset=None,
            parent=None,
            end_lineno=None,
            end_col_offset=None,
        )
        instance.postinit(_create_dict_items(value, instance))
        return instance
    return Const(value)

back/venvmsg/Lib/site-packages/astroid/nodes/node_classes.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import pprint
import sys
import warnings
from collections.abc import Generator, Iterator
from functools import cached_property
from functools import singledispatch as _singledispatch
from typing import (
    TYPE_CHECKING,
    Any,
    ClassVar,
    Literal,
    Tuple,
    Type,
    TypeVar,
    Union,
    cast,
    overload,
)

from astroid import util
from astroid.context import InferenceContext
from astroid.exceptions import (
    AstroidError,
    InferenceError,
    ParentMissingError,
    StatementMissing,
    UseInferenceDefault,
)
from astroid.manager import AstroidManager
from astroid.nodes.as_string import AsStringVisitor
from astroid.nodes.const import OP_PRECEDENCE
from astroid.nodes.utils import Position
from astroid.typing import InferenceErrorInfo, InferenceResult, InferFn

if sys.version_info >= (3, 11):
    from typing import Self
else:
    from typing_extensions import Self


if TYPE_CHECKING:
    from astroid import nodes


# Types for 'NodeNG.nodes_of_class()'
_NodesT = TypeVar("_NodesT", bound="NodeNG")
_NodesT2 = TypeVar("_NodesT2", bound="NodeNG")
_NodesT3 = TypeVar("_NodesT3", bound="NodeNG")
SkipKlassT = Union[None, Type["NodeNG"], Tuple[Type["NodeNG"], ...]]


class NodeNG:
    """A node of the new Abstract Syntax Tree (AST).

    This is the base class for all Astroid node classes.
    """

    is_statement: ClassVar[bool] = False
    """Whether this node indicates a statement."""
    optional_assign: ClassVar[
        bool
    ] = False  # True for For (and for Comprehension if py <3.0)
    """Whether this node optionally assigns a variable.

    This is for loop assignments because loop won't necessarily perform an
    assignment if the loop has no iterations.
    This is also the case from comprehensions in Python 2.
    """
    is_function: ClassVar[bool] = False  # True for FunctionDef nodes
    """Whether this node indicates a function."""
    is_lambda: ClassVar[bool] = False

    # Attributes below are set by the builder module or by raw factories
    _astroid_fields: ClassVar[tuple[str, ...]] = ()
    """Node attributes that contain child nodes.

    This is redefined in most concrete classes.
    """
    _other_fields: ClassVar[tuple[str, ...]] = ()
    """Node attributes that do not contain child nodes."""
    _other_other_fields: ClassVar[tuple[str, ...]] = ()
    """Attributes that contain AST-dependent fields."""
    # instance specific inference function infer(node, context)
    _explicit_inference: InferFn[Self] | None = None

    def __init__(
        self,
        lineno: int | None,
        col_offset: int | None,
        parent: NodeNG | None,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.lineno = lineno
        """The line that this node appears on in the source code."""

        self.col_offset = col_offset
        """The column that this node appears on in the source code."""

        self.parent = parent
        """The parent node in the syntax tree."""

        self.end_lineno = end_lineno
        """The last line this node appears on in the source code."""

        self.end_col_offset = end_col_offset
        """The end column this node appears on in the source code.

        Note: This is after the last symbol.
        """

        self.position: Position | None = None
        """Position of keyword(s) and name.

        Used as fallback for block nodes which might not provide good
        enough positional information. E.g. ClassDef, FunctionDef.
        """

    def infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, None]:
        """Get a generator of the inferred values.

        This is the main entry point to the inference system.

        .. seealso:: :ref:`inference`

        If the instance has some explicit inference function set, it will be
        called instead of the default interface.

        :returns: The inferred values.
        :rtype: iterable
        """
        if context is None:
            context = InferenceContext()
        else:
            context = context.extra_context.get(self, context)
        if self._explicit_inference is not None:
            # explicit_inference is not bound, give it self explicitly
            try:
                for result in self._explicit_inference(
                    self,  # type: ignore[arg-type]
                    context,
                    **kwargs,
                ):
                    context.nodes_inferred += 1
                    yield result
                return
            except UseInferenceDefault:
                pass

        key = (self, context.lookupname, context.callcontext, context.boundnode)
        if key in context.inferred:
            yield from context.inferred[key]
            return

        results = []

        # Limit inference amount to help with performance issues with
        # exponentially exploding possible results.
        limit = AstroidManager().max_inferable_values
        for i, result in enumerate(self._infer(context=context, **kwargs)):
            if i >= limit or (context.nodes_inferred > context.max_inferred):
                results.append(util.Uninferable)
                yield util.Uninferable
                break
            results.append(result)
            yield result
            context.nodes_inferred += 1

        # Cache generated results for subsequent inferences of the
        # same node using the same context
        context.inferred[key] = tuple(results)
        return

    def repr_name(self) -> str:
        """Get a name for nice representation.

        This is either :attr:`name`, :attr:`attrname`, or the empty string.
        """
        if all(name not in self._astroid_fields for name in ("name", "attrname")):
            return getattr(self, "name", "") or getattr(self, "attrname", "")
        return ""

    def __str__(self) -> str:
        rname = self.repr_name()
        cname = type(self).__name__
        if rname:
            string = "%(cname)s.%(rname)s(%(fields)s)"
            alignment = len(cname) + len(rname) + 2
        else:
            string = "%(cname)s(%(fields)s)"
            alignment = len(cname) + 1
        result = []
        for field in self._other_fields + self._astroid_fields:
            value = getattr(self, field, "Unknown")
            width = 80 - len(field) - alignment
            lines = pprint.pformat(value, indent=2, width=width).splitlines(True)

            inner = [lines[0]]
            for line in lines[1:]:
                inner.append(" " * alignment + line)
            result.append(f"{field}={''.join(inner)}")

        return string % {
            "cname": cname,
            "rname": rname,
            "fields": (",\n" + " " * alignment).join(result),
        }

    def __repr__(self) -> str:
        rname = self.repr_name()
        # The dependencies used to calculate fromlineno (if not cached) may not exist at the time
        try:
            lineno = self.fromlineno
        except AttributeError:
            lineno = 0
        if rname:
            string = "<%(cname)s.%(rname)s l.%(lineno)s at 0x%(id)x>"
        else:
            string = "<%(cname)s l.%(lineno)s at 0x%(id)x>"
        return string % {
            "cname": type(self).__name__,
            "rname": rname,
            "lineno": lineno,
            "id": id(self),
        }

    def accept(self, visitor: AsStringVisitor) -> str:
        """Visit this node using the given visitor."""
        func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
        return func(self)

    def get_children(self) -> Iterator[NodeNG]:
        """Get the child nodes below this node."""
        for field in self._astroid_fields:
            attr = getattr(self, field)
            if attr is None:
                continue
            if isinstance(attr, (list, tuple)):
                yield from attr
            else:
                yield attr
        yield from ()

    def last_child(self) -> NodeNG | None:
        """An optimized version of list(get_children())[-1]."""
        for field in self._astroid_fields[::-1]:
            attr = getattr(self, field)
            if not attr:  # None or empty list / tuple
                continue
            if isinstance(attr, (list, tuple)):
                return attr[-1]
            return attr
        return None

    def node_ancestors(self) -> Iterator[NodeNG]:
        """Yield parent, grandparent, etc until there are no more."""
        parent = self.parent
        while parent is not None:
            yield parent
            parent = parent.parent

    def parent_of(self, node) -> bool:
        """Check if this node is the parent of the given node.

        :param node: The node to check if it is the child.
        :type node: NodeNG

        :returns: Whether this node is the parent of the given node.
        """
        return any(self is parent for parent in node.node_ancestors())

    def statement(self, *, future: Literal[None, True] = None) -> nodes.Statement:
        """The first parent node, including self, marked as statement node.

        :raises StatementMissing: If self has no parent attribute.
        """
        if future is not None:
            warnings.warn(
                "The future arg will be removed in astroid 4.0.",
                DeprecationWarning,
                stacklevel=2,
            )
        if self.is_statement:
            return cast("nodes.Statement", self)
        if not self.parent:
            raise StatementMissing(target=self)
        return self.parent.statement()

    def frame(
        self, *, future: Literal[None, True] = None
    ) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda:
        """The first parent frame node.

        A frame node is a :class:`Module`, :class:`FunctionDef`,
        :class:`ClassDef` or :class:`Lambda`.

        :returns: The first parent frame node.
        :raises ParentMissingError: If self has no parent attribute.
        """
        if future is not None:
            warnings.warn(
                "The future arg will be removed in astroid 4.0.",
                DeprecationWarning,
                stacklevel=2,
            )
        if self.parent is None:
            raise ParentMissingError(target=self)
        return self.parent.frame(future=future)

    def scope(self) -> nodes.LocalsDictNodeNG:
        """The first parent node defining a new scope.

        These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.

        :returns: The first parent scope node.
        """
        if not self.parent:
            raise ParentMissingError(target=self)
        return self.parent.scope()

    def root(self) -> nodes.Module:
        """Return the root node of the syntax tree.

        :returns: The root node.
        """
        if not (parent := self.parent):
            return self  # type: ignore[return-value] # Only 'Module' does not have a parent node.

        while parent.parent:
            parent = parent.parent
        return parent  # type: ignore[return-value] # Only 'Module' does not have a parent node.

    def child_sequence(self, child):
        """Search for the sequence that contains this child.

        :param child: The child node to search sequences for.
        :type child: NodeNG

        :returns: The sequence containing the given child node.
        :rtype: iterable(NodeNG)

        :raises AstroidError: If no sequence could be found that contains
            the given child.
        """
        for field in self._astroid_fields:
            node_or_sequence = getattr(self, field)
            if node_or_sequence is child:
                return [node_or_sequence]
            # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
            if (
                isinstance(node_or_sequence, (tuple, list))
                and child in node_or_sequence
            ):
                return node_or_sequence

        msg = "Could not find %s in %s's children"
        raise AstroidError(msg % (repr(child), repr(self)))

    def locate_child(self, child):
        """Find the field of this node that contains the given child.

        :param child: The child node to search fields for.
        :type child: NodeNG

        :returns: A tuple of the name of the field that contains the child,
            and the sequence or node that contains the child node.
        :rtype: tuple(str, iterable(NodeNG) or NodeNG)

        :raises AstroidError: If no field could be found that contains
            the given child.
        """
        for field in self._astroid_fields:
            node_or_sequence = getattr(self, field)
            # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
            if child is node_or_sequence:
                return field, child
            if (
                isinstance(node_or_sequence, (tuple, list))
                and child in node_or_sequence
            ):
                return field, node_or_sequence
        msg = "Could not find %s in %s's children"
        raise AstroidError(msg % (repr(child), repr(self)))

    # FIXME : should we merge child_sequence and locate_child ? locate_child
    # is only used in are_exclusive, child_sequence one time in pylint.

    def next_sibling(self):
        """The next sibling statement node.

        :returns: The next sibling statement node.
        :rtype: NodeNG or None
        """
        return self.parent.next_sibling()

    def previous_sibling(self):
        """The previous sibling statement.

        :returns: The previous sibling statement node.
        :rtype: NodeNG or None
        """
        return self.parent.previous_sibling()

    # these are lazy because they're relatively expensive to compute for every
    # single node, and they rarely get looked at

    @cached_property
    def fromlineno(self) -> int:
        """The first line that this node appears on in the source code.

        Can also return 0 if the line can not be determined.
        """
        if self.lineno is None:
            return self._fixed_source_line()
        return self.lineno

    @cached_property
    def tolineno(self) -> int:
        """The last line that this node appears on in the source code.

        Can also return 0 if the line can not be determined.
        """
        if self.end_lineno is not None:
            return self.end_lineno
        if not self._astroid_fields:
            # can't have children
            last_child = None
        else:
            last_child = self.last_child()
        if last_child is None:
            return self.fromlineno
        return last_child.tolineno

    def _fixed_source_line(self) -> int:
        """Attempt to find the line that this node appears on.

        We need this method since not all nodes have :attr:`lineno` set.
        Will return 0 if the line number can not be determined.
        """
        line = self.lineno
        _node = self
        try:
            while line is None:
                _node = next(_node.get_children())
                line = _node.lineno
        except StopIteration:
            parent = self.parent
            while parent and line is None:
                line = parent.lineno
                parent = parent.parent
        return line or 0

    def block_range(self, lineno: int) -> tuple[int, int]:
        """Get a range from the given line number to where this node ends.

        :param lineno: The line number to start the range at.

        :returns: The range of line numbers that this node belongs to,
            starting at the given line number.
        """
        return lineno, self.tolineno

    def set_local(self, name: str, stmt: NodeNG) -> None:
        """Define that the given name is declared in the given statement node.

        This definition is stored on the parent scope node.

        .. seealso:: :meth:`scope`

        :param name: The name that is being defined.

        :param stmt: The statement that defines the given name.
        """
        assert self.parent
        self.parent.set_local(name, stmt)

    @overload
    def nodes_of_class(
        self,
        klass: type[_NodesT],
        skip_klass: SkipKlassT = ...,
    ) -> Iterator[_NodesT]:
        ...

    @overload
    def nodes_of_class(
        self,
        klass: tuple[type[_NodesT], type[_NodesT2]],
        skip_klass: SkipKlassT = ...,
    ) -> Iterator[_NodesT] | Iterator[_NodesT2]:
        ...

    @overload
    def nodes_of_class(
        self,
        klass: tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]],
        skip_klass: SkipKlassT = ...,
    ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]:
        ...

    @overload
    def nodes_of_class(
        self,
        klass: tuple[type[_NodesT], ...],
        skip_klass: SkipKlassT = ...,
    ) -> Iterator[_NodesT]:
        ...

    def nodes_of_class(  # type: ignore[misc] # mypy doesn't correctly recognize the overloads
        self,
        klass: (
            type[_NodesT]
            | tuple[type[_NodesT], type[_NodesT2]]
            | tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]]
            | tuple[type[_NodesT], ...]
        ),
        skip_klass: SkipKlassT = None,
    ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]:
        """Get the nodes (including this one or below) of the given types.

        :param klass: The types of node to search for.

        :param skip_klass: The types of node to ignore. This is useful to ignore
            subclasses of :attr:`klass`.

        :returns: The node of the given types.
        """
        if isinstance(self, klass):
            yield self

        if skip_klass is None:
            for child_node in self.get_children():
                yield from child_node.nodes_of_class(klass, skip_klass)

            return

        for child_node in self.get_children():
            if isinstance(child_node, skip_klass):
                continue
            yield from child_node.nodes_of_class(klass, skip_klass)

    @cached_property
    def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
        return []

    def _get_name_nodes(self):
        for child_node in self.get_children():
            yield from child_node._get_name_nodes()

    def _get_return_nodes_skip_functions(self):
        yield from ()

    def _get_yield_nodes_skip_functions(self):
        yield from ()

    def _get_yield_nodes_skip_lambdas(self):
        yield from ()

    def _infer_name(self, frame, name):
        # overridden for ImportFrom, Import, Global, Try, TryStar and Arguments
        pass

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
        """We don't know how to resolve a statement by default."""
        # this method is overridden by most concrete classes
        raise InferenceError(
            "No inference function for {node!r}.", node=self, context=context
        )

    def inferred(self):
        """Get a list of the inferred values.

        .. seealso:: :ref:`inference`

        :returns: The inferred values.
        :rtype: list
        """
        return list(self.infer())

    def instantiate_class(self):
        """Instantiate an instance of the defined class.

        .. note::

            On anything other than a :class:`ClassDef` this will return self.

        :returns: An instance of the defined class.
        :rtype: object
        """
        return self

    def has_base(self, node) -> bool:
        """Check if this node inherits from the given type.

        :param node: The node defining the base to look for.
            Usually this is a :class:`Name` node.
        :type node: NodeNG
        """
        return False

    def callable(self) -> bool:
        """Whether this node defines something that is callable.

        :returns: Whether this defines something that is callable.
        """
        return False

    def eq(self, value) -> bool:
        return False

    def as_string(self) -> str:
        """Get the source code that this node represents."""
        return AsStringVisitor()(self)

    def repr_tree(
        self,
        ids=False,
        include_linenos=False,
        ast_state=False,
        indent="   ",
        max_depth=0,
        max_width=80,
    ) -> str:
        """Get a string representation of the AST from this node.

        :param ids: If true, includes the ids with the node type names.
        :type ids: bool

        :param include_linenos: If true, includes the line numbers and
            column offsets.
        :type include_linenos: bool

        :param ast_state: If true, includes information derived from
            the whole AST like local and global variables.
        :type ast_state: bool

        :param indent: A string to use to indent the output string.
        :type indent: str

        :param max_depth: If set to a positive integer, won't return
            nodes deeper than max_depth in the string.
        :type max_depth: int

        :param max_width: Attempt to format the output string to stay
            within this number of characters, but can exceed it under some
            circumstances. Only positive integer values are valid, the default is 80.
        :type max_width: int

        :returns: The string representation of the AST.
        :rtype: str
        """

        @_singledispatch
        def _repr_tree(node, result, done, cur_indent="", depth=1):
            """Outputs a representation of a non-tuple/list, non-node that's
            contained within an AST, including strings.
            """
            lines = pprint.pformat(
                node, width=max(max_width - len(cur_indent), 1)
            ).splitlines(True)
            result.append(lines[0])
            result.extend([cur_indent + line for line in lines[1:]])
            return len(lines) != 1

        # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch
        @_repr_tree.register(tuple)
        @_repr_tree.register(list)
        def _repr_seq(node, result, done, cur_indent="", depth=1):
            """Outputs a representation of a sequence that's contained within an
            AST.
            """
            cur_indent += indent
            result.append("[")
            if not node:
                broken = False
            elif len(node) == 1:
                broken = _repr_tree(node[0], result, done, cur_indent, depth)
            elif len(node) == 2:
                broken = _repr_tree(node[0], result, done, cur_indent, depth)
                if not broken:
                    result.append(", ")
                else:
                    result.append(",\n")
                    result.append(cur_indent)
                broken = _repr_tree(node[1], result, done, cur_indent, depth) or broken
            else:
                result.append("\n")
                result.append(cur_indent)
                for child in node[:-1]:
                    _repr_tree(child, result, done, cur_indent, depth)
                    result.append(",\n")
                    result.append(cur_indent)
                _repr_tree(node[-1], result, done, cur_indent, depth)
                broken = True
            result.append("]")
            return broken

        # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch
        @_repr_tree.register(NodeNG)
        def _repr_node(node, result, done, cur_indent="", depth=1):
            """Outputs a strings representation of an astroid node."""
            if node in done:
                result.append(
                    indent + f"<Recursion on {type(node).__name__} with id={id(node)}"
                )
                return False
            done.add(node)

            if max_depth and depth > max_depth:
                result.append("...")
                return False
            depth += 1
            cur_indent += indent
            if ids:
                result.append(f"{type(node).__name__}<0x{id(node):x}>(\n")
            else:
                result.append(f"{type(node).__name__}(")
            fields = []
            if include_linenos:
                fields.extend(("lineno", "col_offset"))
            fields.extend(node._other_fields)
            fields.extend(node._astroid_fields)
            if ast_state:
                fields.extend(node._other_other_fields)
            if not fields:
                broken = False
            elif len(fields) == 1:
                result.append(f"{fields[0]}=")
                broken = _repr_tree(
                    getattr(node, fields[0]), result, done, cur_indent, depth
                )
            else:
                result.append("\n")
                result.append(cur_indent)
                for field in fields[:-1]:
                    # TODO: Remove this after removal of the 'doc' attribute
                    if field == "doc":
                        continue
                    result.append(f"{field}=")
                    _repr_tree(getattr(node, field), result, done, cur_indent, depth)
                    result.append(",\n")
                    result.append(cur_indent)
                result.append(f"{fields[-1]}=")
                _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, depth)
                broken = True
            result.append(")")
            return broken

        result: list[str] = []
        _repr_tree(self, result, set())
        return "".join(result)

    def bool_value(self, context: InferenceContext | None = None):
        """Determine the boolean value of this node.

        The boolean value of a node can have three
        possible values:

            * False: For instance, empty data structures,
              False, empty strings, instances which return
              explicitly False from the __nonzero__ / __bool__
              method.
            * True: Most of constructs are True by default:
              classes, functions, modules etc
            * Uninferable: The inference engine is uncertain of the
              node's value.

        :returns: The boolean value of this node.
        :rtype: bool or Uninferable
        """
        return util.Uninferable

    def op_precedence(self):
        # Look up by class name or default to highest precedence
        return OP_PRECEDENCE.get(self.__class__.__name__, len(OP_PRECEDENCE))

    def op_left_associative(self) -> bool:
        # Everything is left associative except `**` and IfExp
        return True

back/venvmsg/Lib/site-packages/astroid/nodes/node_ng.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""This module contains all classes that are considered a "scoped" node and anything
related.

A scope node is a node that opens a new local scope in the language definition:
Module, ClassDef, FunctionDef (and Lambda, GeneratorExp, DictComp and SetComp to some extent).
"""

from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG
from astroid.nodes.scoped_nodes.scoped_nodes import (
    AsyncFunctionDef,
    ClassDef,
    DictComp,
    FunctionDef,
    GeneratorExp,
    Lambda,
    ListComp,
    Module,
    SetComp,
    _is_metaclass,
    function_to_method,
    get_wrapping_class,
)
from astroid.nodes.scoped_nodes.utils import builtin_lookup

__all__ = (
    "AsyncFunctionDef",
    "ClassDef",
    "ComprehensionScope",
    "DictComp",
    "FunctionDef",
    "GeneratorExp",
    "Lambda",
    "ListComp",
    "LocalsDictNodeNG",
    "Module",
    "SetComp",
    "builtin_lookup",
    "function_to_method",
    "get_wrapping_class",
    "_is_metaclass",
)

back/venvmsg/Lib/site-packages/astroid/nodes/scoped_nodes/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""This module contains mixin classes for scoped nodes."""

from __future__ import annotations

from typing import TYPE_CHECKING, TypeVar, overload

from astroid.exceptions import ParentMissingError
from astroid.filter_statements import _filter_stmts
from astroid.nodes import _base_nodes, scoped_nodes
from astroid.nodes.scoped_nodes.utils import builtin_lookup
from astroid.typing import InferenceResult, SuccessfulInferenceResult

if TYPE_CHECKING:
    from astroid import nodes

_T = TypeVar("_T")


class LocalsDictNodeNG(_base_nodes.LookupMixIn):
    """this class provides locals handling common to Module, FunctionDef
    and ClassDef nodes, including a dict like interface for direct access
    to locals information
    """

    # attributes below are set by the builder module or by raw factories
    locals: dict[str, list[InferenceResult]]
    """A map of the name of a local variable to the node defining the local."""

    def qname(self) -> str:
        """Get the 'qualified' name of the node.

        For example: module.name, module.class.name ...

        :returns: The qualified name.
        :rtype: str
        """
        # pylint: disable=no-member; github.com/pylint-dev/astroid/issues/278
        if self.parent is None:
            return self.name
        try:
            return f"{self.parent.frame().qname()}.{self.name}"
        except ParentMissingError:
            return self.name

    def scope(self: _T) -> _T:
        """The first parent node defining a new scope.

        :returns: The first parent scope node.
        :rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr
        """
        return self

    def scope_lookup(
        self, node: _base_nodes.LookupMixIn, name: str, offset: int = 0
    ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]:
        """Lookup where the given variable is assigned.

        :param node: The node to look for assignments up to.
            Any assignments after the given node are ignored.

        :param name: The name of the variable to find assignments for.

        :param offset: The line offset to filter statements up to.

        :returns: This scope node and the list of assignments associated to the
            given name according to the scope where it has been found (locals,
            globals or builtin).
        """
        raise NotImplementedError

    def _scope_lookup(
        self, node: _base_nodes.LookupMixIn, name: str, offset: int = 0
    ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]:
        """XXX method for interfacing the scope lookup"""
        try:
            stmts = _filter_stmts(node, self.locals[name], self, offset)
        except KeyError:
            stmts = ()
        if stmts:
            return self, stmts

        # Handle nested scopes: since class names do not extend to nested
        # scopes (e.g., methods), we find the next enclosing non-class scope
        pscope = self.parent and self.parent.scope()
        while pscope is not None:
            if not isinstance(pscope, scoped_nodes.ClassDef):
                return pscope.scope_lookup(node, name)
            pscope = pscope.parent and pscope.parent.scope()

        # self is at the top level of a module, or is enclosed only by ClassDefs
        return builtin_lookup(name)

    def set_local(self, name: str, stmt: nodes.NodeNG) -> None:
        """Define that the given name is declared in the given statement node.

        .. seealso:: :meth:`scope`

        :param name: The name that is being defined.

        :param stmt: The statement that defines the given name.
        """
        # assert not stmt in self.locals.get(name, ()), (self, stmt)
        self.locals.setdefault(name, []).append(stmt)

    __setitem__ = set_local

    def _append_node(self, child: nodes.NodeNG) -> None:
        """append a child, linking it in the tree"""
        # pylint: disable=no-member; depending by the class
        # which uses the current class as a mixin or base class.
        # It's rewritten in 2.0, so it makes no sense for now
        # to spend development time on it.
        self.body.append(child)  # type: ignore[attr-defined]
        child.parent = self

    @overload
    def add_local_node(
        self, child_node: nodes.ClassDef, name: str | None = ...
    ) -> None:
        ...

    @overload
    def add_local_node(self, child_node: nodes.NodeNG, name: str) -> None:
        ...

    def add_local_node(self, child_node: nodes.NodeNG, name: str | None = None) -> None:
        """Append a child that should alter the locals of this scope node.

        :param child_node: The child node that will alter locals.

        :param name: The name of the local that will be altered by
            the given child node.
        """
        if name != "__class__":
            # add __class__ node as a child will cause infinite recursion later!
            self._append_node(child_node)
        self.set_local(name or child_node.name, child_node)  # type: ignore[attr-defined]

    def __getitem__(self, item: str) -> SuccessfulInferenceResult:
        """The first node the defines the given local.

        :param item: The name of the locally defined object.

        :raises KeyError: If the name is not defined.
        """
        return self.locals[item][0]

    def __iter__(self):
        """Iterate over the names of locals defined in this scoped node.

        :returns: The names of the defined locals.
        :rtype: iterable(str)
        """
        return iter(self.keys())

    def keys(self):
        """The names of locals defined in this scoped node.

        :returns: The names of the defined locals.
        :rtype: list(str)
        """
        return list(self.locals.keys())

    def values(self):
        """The nodes that define the locals in this scoped node.

        :returns: The nodes that define locals.
        :rtype: list(NodeNG)
        """
        # pylint: disable=consider-using-dict-items
        # It look like this class override items/keys/values,
        # probably not worth the headache
        return [self[key] for key in self.keys()]

    def items(self):
        """Get the names of the locals and the node that defines the local.

        :returns: The names of locals and their associated node.
        :rtype: list(tuple(str, NodeNG))
        """
        return list(zip(self.keys(), self.values()))

    def __contains__(self, name) -> bool:
        """Check if a local is defined in this scope.

        :param name: The name of the local to check for.
        :type name: str

        :returns: Whether this node has a local of the given name,
        """
        return name in self.locals


class ComprehensionScope(LocalsDictNodeNG):
    """Scoping for different types of comprehensions."""

    scope_lookup = LocalsDictNodeNG._scope_lookup

    generators: list[nodes.Comprehension]
    """The generators that are looped through."""

back/venvmsg/Lib/site-packages/astroid/nodes/scoped_nodes/mixin.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
This module contains the classes for "scoped" node, i.e. which are opening a
new local scope in the language definition : Module, ClassDef, FunctionDef (and
Lambda, GeneratorExp, DictComp and SetComp to some extent).
"""

from __future__ import annotations

import io
import itertools
import os
import warnings
from collections.abc import Generator, Iterable, Iterator, Sequence
from functools import cached_property, lru_cache
from typing import TYPE_CHECKING, Any, ClassVar, Literal, NoReturn, TypeVar

from astroid import bases, protocols, util
from astroid.const import IS_PYPY, PY38, PY39_PLUS, PYPY_7_3_11_PLUS
from astroid.context import (
    CallContext,
    InferenceContext,
    bind_context_to_node,
    copy_context,
)
from astroid.exceptions import (
    AstroidBuildingError,
    AstroidTypeError,
    AttributeInferenceError,
    DuplicateBasesError,
    InconsistentMroError,
    InferenceError,
    MroError,
    ParentMissingError,
    StatementMissing,
    TooManyLevelsError,
)
from astroid.interpreter.dunder_lookup import lookup
from astroid.interpreter.objectmodel import ClassModel, FunctionModel, ModuleModel
from astroid.manager import AstroidManager
from astroid.nodes import Arguments, Const, NodeNG, Unknown, _base_nodes, node_classes
from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG
from astroid.nodes.scoped_nodes.utils import builtin_lookup
from astroid.nodes.utils import Position
from astroid.typing import (
    InferBinaryOp,
    InferenceErrorInfo,
    InferenceResult,
    SuccessfulInferenceResult,
)

if TYPE_CHECKING:
    from astroid import nodes, objects
    from astroid.nodes._base_nodes import LookupMixIn


ITER_METHODS = ("__iter__", "__getitem__")
EXCEPTION_BASE_CLASSES = frozenset({"Exception", "BaseException"})
BUILTIN_DESCRIPTORS = frozenset(
    {"classmethod", "staticmethod", "builtins.classmethod", "builtins.staticmethod"}
)

_T = TypeVar("_T")


def _c3_merge(sequences, cls, context):
    """Merges MROs in *sequences* to a single MRO using the C3 algorithm.

    Adapted from http://www.python.org/download/releases/2.3/mro/.

    """
    result = []
    while True:
        sequences = [s for s in sequences if s]  # purge empty sequences
        if not sequences:
            return result
        for s1 in sequences:  # find merge candidates among seq heads
            candidate = s1[0]
            for s2 in sequences:
                if candidate in s2[1:]:
                    candidate = None
                    break  # reject the current head, it appears later
            else:
                break
        if not candidate:
            # Show all the remaining bases, which were considered as
            # candidates for the next mro sequence.
            raise InconsistentMroError(
                message="Cannot create a consistent method resolution order "
                "for MROs {mros} of class {cls!r}.",
                mros=sequences,
                cls=cls,
                context=context,
            )

        result.append(candidate)
        # remove the chosen candidate
        for seq in sequences:
            if seq[0] == candidate:
                del seq[0]
    return None


def clean_typing_generic_mro(sequences: list[list[ClassDef]]) -> None:
    """A class can inherit from typing.Generic directly, as base,
    and as base of bases. The merged MRO must however only contain the last entry.
    To prepare for _c3_merge, remove some typing.Generic entries from
    sequences if multiple are present.

    This method will check if Generic is in inferred_bases and also
    part of bases_mro. If true, remove it from inferred_bases
    as well as its entry the bases_mro.

    Format sequences: [[self]] + bases_mro + [inferred_bases]
    """
    bases_mro = sequences[1:-1]
    inferred_bases = sequences[-1]
    # Check if Generic is part of inferred_bases
    for i, base in enumerate(inferred_bases):
        if base.qname() == "typing.Generic":
            position_in_inferred_bases = i
            break
    else:
        return
    # Check if also part of bases_mro
    # Ignore entry for typing.Generic
    for i, seq in enumerate(bases_mro):
        if i == position_in_inferred_bases:
            continue
        if any(base.qname() == "typing.Generic" for base in seq):
            break
    else:
        return
    # Found multiple Generics in mro, remove entry from inferred_bases
    # and the corresponding one from bases_mro
    inferred_bases.pop(position_in_inferred_bases)
    bases_mro.pop(position_in_inferred_bases)


def clean_duplicates_mro(
    sequences: list[list[ClassDef]],
    cls: ClassDef,
    context: InferenceContext | None,
) -> list[list[ClassDef]]:
    for sequence in sequences:
        seen = set()
        for node in sequence:
            lineno_and_qname = (node.lineno, node.qname())
            if lineno_and_qname in seen:
                raise DuplicateBasesError(
                    message="Duplicates found in MROs {mros} for {cls!r}.",
                    mros=sequences,
                    cls=cls,
                    context=context,
                )
            seen.add(lineno_and_qname)
    return sequences


def function_to_method(n, klass):
    if isinstance(n, FunctionDef):
        if n.type == "classmethod":
            return bases.BoundMethod(n, klass)
        if n.type == "property":
            return n
        if n.type != "staticmethod":
            return bases.UnboundMethod(n)
    return n


class Module(LocalsDictNodeNG):
    """Class representing an :class:`ast.Module` node.

    >>> import astroid
    >>> node = astroid.extract_node('import astroid')
    >>> node
    <Import l.1 at 0x7f23b2e4e5c0>
    >>> node.parent
    <Module l.0 at 0x7f23b2e4eda0>
    """

    _astroid_fields = ("doc_node", "body")

    doc_node: Const | None
    """The doc node associated with this node."""

    # attributes below are set by the builder module or by raw factories

    file_bytes: str | bytes | None = None
    """The string/bytes that this ast was built from."""

    file_encoding: str | None = None
    """The encoding of the source file.

    This is used to get unicode out of a source file.
    Python 2 only.
    """

    special_attributes = ModuleModel()
    """The names of special attributes that this module has."""

    # names of module attributes available through the global scope
    scope_attrs: ClassVar[set[str]] = {
        "__name__",
        "__doc__",
        "__file__",
        "__path__",
        "__package__",
    }
    """The names of module attributes available through the global scope."""

    _other_fields = (
        "name",
        "file",
        "path",
        "package",
        "pure_python",
        "future_imports",
    )
    _other_other_fields = ("locals", "globals")

    def __init__(
        self,
        name: str,
        file: str | None = None,
        path: Sequence[str] | None = None,
        package: bool = False,
        pure_python: bool = True,
    ) -> None:
        self.name = name
        """The name of the module."""

        self.file = file
        """The path to the file that this ast has been extracted from.

        This will be ``None`` when the representation has been built from a
        built-in module.
        """

        self.path = path

        self.package = package
        """Whether the node represents a package or a module."""

        self.pure_python = pure_python
        """Whether the ast was built from source."""

        self.globals: dict[str, list[InferenceResult]]
        """A map of the name of a global variable to the node defining the global."""

        self.locals = self.globals = {}
        """A map of the name of a local variable to the node defining the local."""

        self.body: list[node_classes.NodeNG] = []
        """The contents of the module."""

        self.future_imports: set[str] = set()
        """The imports from ``__future__``."""

        super().__init__(
            lineno=0, parent=None, col_offset=0, end_lineno=None, end_col_offset=None
        )

    # pylint: enable=redefined-builtin

    def postinit(
        self, body: list[node_classes.NodeNG], *, doc_node: Const | None = None
    ):
        self.body = body
        self.doc_node = doc_node

    def _get_stream(self):
        if self.file_bytes is not None:
            return io.BytesIO(self.file_bytes)
        if self.file is not None:
            # pylint: disable=consider-using-with
            stream = open(self.file, "rb")
            return stream
        return None

    def stream(self):
        """Get a stream to the underlying file or bytes.

        :type: file or io.BytesIO or None
        """
        return self._get_stream()

    def block_range(self, lineno: int) -> tuple[int, int]:
        """Get a range from where this node starts to where this node ends.

        :param lineno: Unused.

        :returns: The range of line numbers that this node belongs to.
        """
        return self.fromlineno, self.tolineno

    def scope_lookup(
        self, node: LookupMixIn, name: str, offset: int = 0
    ) -> tuple[LocalsDictNodeNG, list[node_classes.NodeNG]]:
        """Lookup where the given variable is assigned.

        :param node: The node to look for assignments up to.
            Any assignments after the given node are ignored.

        :param name: The name of the variable to find assignments for.

        :param offset: The line offset to filter statements up to.

        :returns: This scope node and the list of assignments associated to the
            given name according to the scope where it has been found (locals,
            globals or builtin).
        """
        if name in self.scope_attrs and name not in self.locals:
            try:
                return self, self.getattr(name)
            except AttributeInferenceError:
                return self, []
        return self._scope_lookup(node, name, offset)

    def pytype(self) -> Literal["builtins.module"]:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        return "builtins.module"

    def display_type(self) -> str:
        """A human readable type of this node.

        :returns: The type of this node.
        :rtype: str
        """
        return "Module"

    def getattr(
        self, name, context: InferenceContext | None = None, ignore_locals=False
    ):
        if not name:
            raise AttributeInferenceError(target=self, attribute=name, context=context)

        result = []
        name_in_locals = name in self.locals

        if name in self.special_attributes and not ignore_locals and not name_in_locals:
            result = [self.special_attributes.lookup(name)]
        elif not ignore_locals and name_in_locals:
            result = self.locals[name]
        elif self.package:
            try:
                result = [self.import_module(name, relative_only=True)]
            except (AstroidBuildingError, SyntaxError) as exc:
                raise AttributeInferenceError(
                    target=self, attribute=name, context=context
                ) from exc
        result = [n for n in result if not isinstance(n, node_classes.DelName)]
        if result:
            return result
        raise AttributeInferenceError(target=self, attribute=name, context=context)

    def igetattr(
        self, name: str, context: InferenceContext | None = None
    ) -> Iterator[InferenceResult]:
        """Infer the possible values of the given variable.

        :param name: The name of the variable to infer.

        :returns: The inferred possible values.
        """
        # set lookup name since this is necessary to infer on import nodes for
        # instance
        context = copy_context(context)
        context.lookupname = name
        try:
            return bases._infer_stmts(self.getattr(name, context), context, frame=self)
        except AttributeInferenceError as error:
            raise InferenceError(
                str(error), target=self, attribute=name, context=context
            ) from error

    def fully_defined(self) -> bool:
        """Check if this module has been build from a .py file.

        If so, the module contains a complete representation,
        including the code.

        :returns: Whether the module has been built from a .py file.
        """
        return self.file is not None and self.file.endswith(".py")

    def statement(self, *, future: Literal[None, True] = None) -> NoReturn:
        """The first parent node, including self, marked as statement node.

        When called on a :class:`Module` this raises a StatementMissing.
        """
        if future is not None:
            warnings.warn(
                "The future arg will be removed in astroid 4.0.",
                DeprecationWarning,
                stacklevel=2,
            )
        raise StatementMissing(target=self)

    def previous_sibling(self):
        """The previous sibling statement.

        :returns: The previous sibling statement node.
        :rtype: NodeNG or None
        """

    def next_sibling(self):
        """The next sibling statement node.

        :returns: The next sibling statement node.
        :rtype: NodeNG or None
        """

    _absolute_import_activated = True

    def absolute_import_activated(self) -> bool:
        """Whether :pep:`328` absolute import behaviour has been enabled.

        :returns: Whether :pep:`328` has been enabled.
        """
        return self._absolute_import_activated

    def import_module(
        self,
        modname: str,
        relative_only: bool = False,
        level: int | None = None,
        use_cache: bool = True,
    ) -> Module:
        """Get the ast for a given module as if imported from this module.

        :param modname: The name of the module to "import".

        :param relative_only: Whether to only consider relative imports.

        :param level: The level of relative import.

        :param use_cache: Whether to use the astroid_cache of modules.

        :returns: The imported module ast.
        """
        if relative_only and level is None:
            level = 0
        absmodname = self.relative_to_absolute_name(modname, level)

        try:
            return AstroidManager().ast_from_module_name(
                absmodname, use_cache=use_cache
            )
        except AstroidBuildingError:
            # we only want to import a sub module or package of this module,
            # skip here
            if relative_only:
                raise
            # Don't repeat the same operation, e.g. for missing modules
            # like "_winapi" or "nt" on POSIX systems.
            if modname == absmodname:
                raise
        return AstroidManager().ast_from_module_name(modname, use_cache=use_cache)

    def relative_to_absolute_name(self, modname: str, level: int | None) -> str:
        """Get the absolute module name for a relative import.

        The relative import can be implicit or explicit.

        :param modname: The module name to convert.

        :param level: The level of relative import.

        :returns: The absolute module name.

        :raises TooManyLevelsError: When the relative import refers to a
            module too far above this one.
        """
        # XXX this returns non sens when called on an absolute import
        # like 'pylint.checkers.astroid.utils'
        # XXX doesn't return absolute name if self.name isn't absolute name
        if self.absolute_import_activated() and level is None:
            return modname
        if level:
            if self.package:
                level = level - 1
                package_name = self.name.rsplit(".", level)[0]
            elif (
                self.path
                and not os.path.exists(os.path.dirname(self.path[0]) + "/__init__.py")
                and os.path.exists(
                    os.path.dirname(self.path[0]) + "/" + modname.split(".")[0]
                )
            ):
                level = level - 1
                package_name = ""
            else:
                package_name = self.name.rsplit(".", level)[0]
            if level and self.name.count(".") < level:
                raise TooManyLevelsError(level=level, name=self.name)

        elif self.package:
            package_name = self.name
        else:
            package_name = self.name.rsplit(".", 1)[0]

        if package_name:
            if not modname:
                return package_name
            return f"{package_name}.{modname}"
        return modname

    def wildcard_import_names(self):
        """The list of imported names when this module is 'wildcard imported'.

        It doesn't include the '__builtins__' name which is added by the
        current CPython implementation of wildcard imports.

        :returns: The list of imported names.
        :rtype: list(str)
        """
        # We separate the different steps of lookup in try/excepts
        # to avoid catching too many Exceptions
        default = [name for name in self.keys() if not name.startswith("_")]
        try:
            all_values = self["__all__"]
        except KeyError:
            return default

        try:
            explicit = next(all_values.assigned_stmts())
        except (InferenceError, StopIteration):
            return default
        except AttributeError:
            # not an assignment node
            # XXX infer?
            return default

        # Try our best to detect the exported name.
        inferred = []
        try:
            explicit = next(explicit.infer())
        except (InferenceError, StopIteration):
            return default
        if not isinstance(explicit, (node_classes.Tuple, node_classes.List)):
            return default

        def str_const(node) -> bool:
            return isinstance(node, node_classes.Const) and isinstance(node.value, str)

        for node in explicit.elts:
            if str_const(node):
                inferred.append(node.value)
            else:
                try:
                    inferred_node = next(node.infer())
                except (InferenceError, StopIteration):
                    continue
                if str_const(inferred_node):
                    inferred.append(inferred_node.value)
        return inferred

    def public_names(self):
        """The list of the names that are publicly available in this module.

        :returns: The list of public names.
        :rtype: list(str)
        """
        return [name for name in self.keys() if not name.startswith("_")]

    def bool_value(self, context: InferenceContext | None = None) -> bool:
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
            For a :class:`Module` this is always ``True``.
        """
        return True

    def get_children(self):
        yield from self.body

    def frame(self: _T, *, future: Literal[None, True] = None) -> _T:
        """The node's frame node.

        A frame node is a :class:`Module`, :class:`FunctionDef`,
        :class:`ClassDef` or :class:`Lambda`.

        :returns: The node itself.
        """
        return self

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[Module, None, None]:
        yield self


class GeneratorExp(ComprehensionScope):
    """Class representing an :class:`ast.GeneratorExp` node.

    >>> import astroid
    >>> node = astroid.extract_node('(thing for thing in things if thing)')
    >>> node
    <GeneratorExp l.1 at 0x7f23b2e4e400>
    """

    _astroid_fields = ("elt", "generators")
    _other_other_fields = ("locals",)
    elt: NodeNG
    """The element that forms the output of the expression."""

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.locals = {}
        """A map of the name of a local variable to the node defining the local."""

        self.generators: list[nodes.Comprehension] = []
        """The generators that are looped through."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]) -> None:
        self.elt = elt
        self.generators = generators

    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
            For a :class:`GeneratorExp` this is always ``True``.
        """
        return True

    def get_children(self):
        yield self.elt

        yield from self.generators


class DictComp(ComprehensionScope):
    """Class representing an :class:`ast.DictComp` node.

    >>> import astroid
    >>> node = astroid.extract_node('{k:v for k, v in things if k > v}')
    >>> node
    <DictComp l.1 at 0x7f23b2e41d68>
    """

    _astroid_fields = ("key", "value", "generators")
    _other_other_fields = ("locals",)
    key: NodeNG
    """What produces the keys."""

    value: NodeNG
    """What produces the values."""

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.locals = {}
        """A map of the name of a local variable to the node defining the local."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(
        self, key: NodeNG, value: NodeNG, generators: list[nodes.Comprehension]
    ) -> None:
        self.key = key
        self.value = value
        self.generators = generators

    def bool_value(self, context: InferenceContext | None = None):
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
            For a :class:`DictComp` this is always :class:`Uninferable`.
        :rtype: Uninferable
        """
        return util.Uninferable

    def get_children(self):
        yield self.key
        yield self.value

        yield from self.generators


class SetComp(ComprehensionScope):
    """Class representing an :class:`ast.SetComp` node.

    >>> import astroid
    >>> node = astroid.extract_node('{thing for thing in things if thing}')
    >>> node
    <SetComp l.1 at 0x7f23b2e41898>
    """

    _astroid_fields = ("elt", "generators")
    _other_other_fields = ("locals",)
    elt: NodeNG
    """The element that forms the output of the expression."""

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.locals = {}
        """A map of the name of a local variable to the node defining the local."""

        self.generators: list[nodes.Comprehension] = []
        """The generators that are looped through."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]) -> None:
        self.elt = elt
        self.generators = generators

    def bool_value(self, context: InferenceContext | None = None):
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
            For a :class:`SetComp` this is always :class:`Uninferable`.
        :rtype: Uninferable
        """
        return util.Uninferable

    def get_children(self):
        yield self.elt

        yield from self.generators


class ListComp(ComprehensionScope):
    """Class representing an :class:`ast.ListComp` node.

    >>> import astroid
    >>> node = astroid.extract_node('[thing for thing in things if thing]')
    >>> node
    <ListComp l.1 at 0x7f23b2e418d0>
    """

    _astroid_fields = ("elt", "generators")
    _other_other_fields = ("locals",)

    elt: NodeNG
    """The element that forms the output of the expression."""

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.locals = {}
        """A map of the name of a local variable to the node defining it."""

        self.generators: list[nodes.Comprehension] = []
        """The generators that are looped through."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]):
        self.elt = elt
        self.generators = generators

    def bool_value(self, context: InferenceContext | None = None):
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
            For a :class:`ListComp` this is always :class:`Uninferable`.
        :rtype: Uninferable
        """
        return util.Uninferable

    def get_children(self):
        yield self.elt

        yield from self.generators


def _infer_decorator_callchain(node):
    """Detect decorator call chaining and see if the end result is a
    static or a classmethod.
    """
    if not isinstance(node, FunctionDef):
        return None
    if not node.parent:
        return None
    try:
        result = next(node.infer_call_result(node.parent), None)
    except InferenceError:
        return None
    if isinstance(result, bases.Instance):
        result = result._proxied
    if isinstance(result, ClassDef):
        if result.is_subtype_of("builtins.classmethod"):
            return "classmethod"
        if result.is_subtype_of("builtins.staticmethod"):
            return "staticmethod"
    if isinstance(result, FunctionDef):
        if not result.decorators:
            return None
        # Determine if this function is decorated with one of the builtin descriptors we want.
        for decorator in result.decorators.nodes:
            if isinstance(decorator, node_classes.Name):
                if decorator.name in BUILTIN_DESCRIPTORS:
                    return decorator.name
            if (
                isinstance(decorator, node_classes.Attribute)
                and isinstance(decorator.expr, node_classes.Name)
                and decorator.expr.name == "builtins"
                and decorator.attrname in BUILTIN_DESCRIPTORS
            ):
                return decorator.attrname
    return None


class Lambda(_base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG):
    """Class representing an :class:`ast.Lambda` node.

    >>> import astroid
    >>> node = astroid.extract_node('lambda arg: arg + 1')
    >>> node
    <Lambda.<lambda> l.1 at 0x7f23b2e41518>
    """

    _astroid_fields: ClassVar[tuple[str, ...]] = ("args", "body")
    _other_other_fields: ClassVar[tuple[str, ...]] = ("locals",)
    name = "<lambda>"
    is_lambda = True
    special_attributes = FunctionModel()
    """The names of special attributes that this function has."""

    args: Arguments
    """The arguments that the function takes."""

    body: NodeNG
    """The contents of the function body."""

    def implicit_parameters(self) -> Literal[0]:
        return 0

    @property
    def type(self) -> Literal["method", "function"]:
        """Whether this is a method or function.

        :returns: 'method' if this is a method, 'function' otherwise.
        """
        if self.args.arguments and self.args.arguments[0].name == "self":
            if self.parent and isinstance(self.parent.scope(), ClassDef):
                return "method"
        return "function"

    def __init__(
        self,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ):
        self.locals = {}
        """A map of the name of a local variable to the node defining it."""

        self.instance_attrs: dict[str, list[NodeNG]] = {}

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )

    def postinit(self, args: Arguments, body: NodeNG) -> None:
        self.args = args
        self.body = body

    def pytype(self) -> Literal["builtins.instancemethod", "builtins.function"]:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        if "method" in self.type:
            return "builtins.instancemethod"
        return "builtins.function"

    def display_type(self) -> str:
        """A human readable type of this node.

        :returns: The type of this node.
        :rtype: str
        """
        if "method" in self.type:
            return "Method"
        return "Function"

    def callable(self) -> Literal[True]:
        """Whether this node defines something that is callable.

        :returns: Whether this defines something that is callable
            For a :class:`Lambda` this is always ``True``.
        """
        return True

    def argnames(self) -> list[str]:
        """Get the names of each of the arguments, including that
        of the collections of variable-length arguments ("args", "kwargs",
        etc.), as well as positional-only and keyword-only arguments.

        :returns: The names of the arguments.
        :rtype: list(str)
        """
        if self.args.arguments:  # maybe None with builtin functions
            names = [elt.name for elt in self.args.arguments]
        else:
            names = []

        return names

    def infer_call_result(
        self,
        caller: SuccessfulInferenceResult | None,
        context: InferenceContext | None = None,
    ) -> Iterator[InferenceResult]:
        """Infer what the function returns when called."""
        return self.body.infer(context)

    def scope_lookup(
        self, node: LookupMixIn, name: str, offset: int = 0
    ) -> tuple[LocalsDictNodeNG, list[NodeNG]]:
        """Lookup where the given names is assigned.

        :param node: The node to look for assignments up to.
            Any assignments after the given node are ignored.

        :param name: The name to find assignments for.

        :param offset: The line offset to filter statements up to.

        :returns: This scope node and the list of assignments associated to the
            given name according to the scope where it has been found (locals,
            globals or builtin).
        """
        if (self.args.defaults and node in self.args.defaults) or (
            self.args.kw_defaults and node in self.args.kw_defaults
        ):
            if not self.parent:
                raise ParentMissingError(target=self)
            frame = self.parent.frame()
            # line offset to avoid that def func(f=func) resolve the default
            # value to the defined function
            offset = -1
        else:
            # check this is not used in function decorators
            frame = self
        return frame._scope_lookup(node, name, offset)

    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
            For a :class:`Lambda` this is always ``True``.
        """
        return True

    def get_children(self):
        yield self.args
        yield self.body

    def frame(self: _T, *, future: Literal[None, True] = None) -> _T:
        """The node's frame node.

        A frame node is a :class:`Module`, :class:`FunctionDef`,
        :class:`ClassDef` or :class:`Lambda`.

        :returns: The node itself.
        """
        return self

    def getattr(
        self, name: str, context: InferenceContext | None = None
    ) -> list[NodeNG]:
        if not name:
            raise AttributeInferenceError(target=self, attribute=name, context=context)

        found_attrs = []
        if name in self.instance_attrs:
            found_attrs = self.instance_attrs[name]
        if name in self.special_attributes:
            found_attrs.append(self.special_attributes.lookup(name))
        if found_attrs:
            return found_attrs
        raise AttributeInferenceError(target=self, attribute=name)

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[Lambda, None, None]:
        yield self

    def _get_yield_nodes_skip_functions(self):
        """A Lambda node can contain a Yield node in the body."""
        yield from self.body._get_yield_nodes_skip_functions()


class FunctionDef(
    _base_nodes.MultiLineBlockNode,
    _base_nodes.FilterStmtsBaseNode,
    _base_nodes.Statement,
    LocalsDictNodeNG,
):
    """Class representing an :class:`ast.FunctionDef`.

    >>> import astroid
    >>> node = astroid.extract_node('''
    ... def my_func(arg):
    ...     return arg + 1
    ... ''')
    >>> node
    <FunctionDef.my_func l.2 at 0x7f23b2e71e10>
    """

    _astroid_fields = (
        "decorators",
        "args",
        "returns",
        "type_params",
        "doc_node",
        "body",
    )
    _multi_line_block_fields = ("body",)
    returns = None

    decorators: node_classes.Decorators | None
    """The decorators that are applied to this method or function."""

    doc_node: Const | None
    """The doc node associated with this node."""

    args: Arguments
    """The arguments that the function takes."""

    is_function = True
    """Whether this node indicates a function.

    For a :class:`FunctionDef` this is always ``True``.

    :type: bool
    """
    type_annotation = None
    """If present, this will contain the type annotation passed by a type comment

    :type: NodeNG or None
    """
    type_comment_args = None
    """
    If present, this will contain the type annotation for arguments
    passed by a type comment
    """
    type_comment_returns = None
    """If present, this will contain the return type annotation, passed by a type comment"""
    # attributes below are set by the builder module or by raw factories
    _other_fields = ("name", "position")
    _other_other_fields = (
        "locals",
        "_type",
        "type_comment_returns",
        "type_comment_args",
    )
    _type = None

    name = "<functiondef>"

    special_attributes = FunctionModel()
    """The names of special attributes that this function has."""

    def __init__(
        self,
        name: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.name = name
        """The name of the function."""

        self.locals = {}
        """A map of the name of a local variable to the node defining it."""

        self.body: list[NodeNG] = []
        """The contents of the function body."""

        self.type_params: list[
            nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple
        ] = []
        """PEP 695 (Python 3.12+) type params, e.g. first 'T' in def func[T]() -> T: ..."""

        self.instance_attrs: dict[str, list[NodeNG]] = {}

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )
        if parent and not isinstance(parent, Unknown):
            frame = parent.frame()
            frame.set_local(name, self)

    def postinit(
        self,
        args: Arguments,
        body: list[NodeNG],
        decorators: node_classes.Decorators | None = None,
        returns=None,
        type_comment_returns=None,
        type_comment_args=None,
        *,
        position: Position | None = None,
        doc_node: Const | None = None,
        type_params: list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple]
        | None = None,
    ):
        """Do some setup after initialisation.

        :param args: The arguments that the function takes.

        :param body: The contents of the function body.

        :param decorators: The decorators that are applied to this
            method or function.
        :params type_comment_returns:
            The return type annotation passed via a type comment.
        :params type_comment_args:
            The args type annotation passed via a type comment.
        :params position:
            Position of function keyword(s) and name.
        :param doc_node:
            The doc node associated with this node.
        :param type_params:
            The type_params associated with this node.
        """
        self.args = args
        self.body = body
        self.decorators = decorators
        self.returns = returns
        self.type_comment_returns = type_comment_returns
        self.type_comment_args = type_comment_args
        self.position = position
        self.doc_node = doc_node
        self.type_params = type_params or []

    @cached_property
    def extra_decorators(self) -> list[node_classes.Call]:
        """The extra decorators that this function can have.

        Additional decorators are considered when they are used as
        assignments, as in ``method = staticmethod(method)``.
        The property will return all the callables that are used for
        decoration.
        """
        if not self.parent or not isinstance(frame := self.parent.frame(), ClassDef):
            return []

        decorators: list[node_classes.Call] = []
        for assign in frame._assign_nodes_in_scope:
            if isinstance(assign.value, node_classes.Call) and isinstance(
                assign.value.func, node_classes.Name
            ):
                for assign_node in assign.targets:
                    if not isinstance(assign_node, node_classes.AssignName):
                        # Support only `name = callable(name)`
                        continue

                    if assign_node.name != self.name:
                        # Interested only in the assignment nodes that
                        # decorates the current method.
                        continue
                    try:
                        meth = frame[self.name]
                    except KeyError:
                        continue
                    else:
                        # Must be a function and in the same frame as the
                        # original method.
                        if (
                            isinstance(meth, FunctionDef)
                            and assign_node.frame() == frame
                        ):
                            decorators.append(assign.value)
        return decorators

    def pytype(self) -> Literal["builtins.instancemethod", "builtins.function"]:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        if "method" in self.type:
            return "builtins.instancemethod"
        return "builtins.function"

    def display_type(self) -> str:
        """A human readable type of this node.

        :returns: The type of this node.
        :rtype: str
        """
        if "method" in self.type:
            return "Method"
        return "Function"

    def callable(self) -> Literal[True]:
        return True

    def argnames(self) -> list[str]:
        """Get the names of each of the arguments, including that
        of the collections of variable-length arguments ("args", "kwargs",
        etc.), as well as positional-only and keyword-only arguments.

        :returns: The names of the arguments.
        :rtype: list(str)
        """
        if self.args.arguments:  # maybe None with builtin functions
            names = [elt.name for elt in self.args.arguments]
        else:
            names = []

        return names

    def getattr(
        self, name: str, context: InferenceContext | None = None
    ) -> list[NodeNG]:
        if not name:
            raise AttributeInferenceError(target=self, attribute=name, context=context)

        found_attrs = []
        if name in self.instance_attrs:
            found_attrs = self.instance_attrs[name]
        if name in self.special_attributes:
            found_attrs.append(self.special_attributes.lookup(name))
        if found_attrs:
            return found_attrs
        raise AttributeInferenceError(target=self, attribute=name)

    @cached_property
    def type(self) -> str:  # pylint: disable=too-many-return-statements # noqa: C901
        """The function type for this node.

        Possible values are: method, function, staticmethod, classmethod.
        """
        for decorator in self.extra_decorators:
            if decorator.func.name in BUILTIN_DESCRIPTORS:
                return decorator.func.name

        if not self.parent:
            raise ParentMissingError(target=self)

        frame = self.parent.frame()
        type_name = "function"
        if isinstance(frame, ClassDef):
            if self.name == "__new__":
                return "classmethod"
            if self.name == "__init_subclass__":
                return "classmethod"
            if self.name == "__class_getitem__":
                return "classmethod"

            type_name = "method"

        if not self.decorators:
            return type_name

        for node in self.decorators.nodes:
            if isinstance(node, node_classes.Name):
                if node.name in BUILTIN_DESCRIPTORS:
                    return node.name
            if (
                isinstance(node, node_classes.Attribute)
                and isinstance(node.expr, node_classes.Name)
                and node.expr.name == "builtins"
                and node.attrname in BUILTIN_DESCRIPTORS
            ):
                return node.attrname

            if isinstance(node, node_classes.Call):
                # Handle the following case:
                # @some_decorator(arg1, arg2)
                # def func(...)
                #
                try:
                    current = next(node.func.infer())
                except (InferenceError, StopIteration):
                    continue
                _type = _infer_decorator_callchain(current)
                if _type is not None:
                    return _type

            try:
                for inferred in node.infer():
                    # Check to see if this returns a static or a class method.
                    _type = _infer_decorator_callchain(inferred)
                    if _type is not None:
                        return _type

                    if not isinstance(inferred, ClassDef):
                        continue
                    for ancestor in inferred.ancestors():
                        if not isinstance(ancestor, ClassDef):
                            continue
                        if ancestor.is_subtype_of("builtins.classmethod"):
                            return "classmethod"
                        if ancestor.is_subtype_of("builtins.staticmethod"):
                            return "staticmethod"
            except InferenceError:
                pass
        return type_name

    @cached_property
    def fromlineno(self) -> int:
        """The first line that this node appears on in the source code.

        Can also return 0 if the line can not be determined.
        """
        # lineno is the line number of the first decorator, we want the def
        # statement lineno. Similar to 'ClassDef.fromlineno'
        lineno = self.lineno or 0
        if self.decorators is not None:
            lineno += sum(
                node.tolineno - (node.lineno or 0) + 1 for node in self.decorators.nodes
            )

        return lineno or 0

    @cached_property
    def blockstart_tolineno(self):
        """The line on which the beginning of this block ends.

        :type: int
        """
        return self.args.tolineno

    def implicit_parameters(self) -> Literal[0, 1]:
        return 1 if self.is_bound() else 0

    def block_range(self, lineno: int) -> tuple[int, int]:
        """Get a range from the given line number to where this node ends.

        :param lineno: Unused.

        :returns: The range of line numbers that this node belongs to,
        """
        return self.fromlineno, self.tolineno

    def igetattr(
        self, name: str, context: InferenceContext | None = None
    ) -> Iterator[InferenceResult]:
        """Inferred getattr, which returns an iterator of inferred statements."""
        try:
            return bases._infer_stmts(self.getattr(name, context), context, frame=self)
        except AttributeInferenceError as error:
            raise InferenceError(
                str(error), target=self, attribute=name, context=context
            ) from error

    def is_method(self) -> bool:
        """Check if this function node represents a method.

        :returns: Whether this is a method.
        """
        # check we are defined in a ClassDef, because this is usually expected
        # (e.g. pylint...) when is_method() return True
        return (
            self.type != "function"
            and self.parent is not None
            and isinstance(self.parent.frame(), ClassDef)
        )

    def decoratornames(self, context: InferenceContext | None = None) -> set[str]:
        """Get the qualified names of each of the decorators on this function.

        :param context:
            An inference context that can be passed to inference functions
        :returns: The names of the decorators.
        """
        result = set()
        decoratornodes = []
        if self.decorators is not None:
            decoratornodes += self.decorators.nodes
        decoratornodes += self.extra_decorators
        for decnode in decoratornodes:
            try:
                for infnode in decnode.infer(context=context):
                    result.add(infnode.qname())
            except InferenceError:
                continue
        return result

    def is_bound(self) -> bool:
        """Check if the function is bound to an instance or class.

        :returns: Whether the function is bound to an instance or class.
        """
        return self.type in {"method", "classmethod"}

    def is_abstract(self, pass_is_abstract=True, any_raise_is_abstract=False) -> bool:
        """Check if the method is abstract.

        A method is considered abstract if any of the following is true:
        * The only statement is 'raise NotImplementedError'
        * The only statement is 'raise <SomeException>' and any_raise_is_abstract is True
        * The only statement is 'pass' and pass_is_abstract is True
        * The method is annotated with abc.astractproperty/abc.abstractmethod

        :returns: Whether the method is abstract.
        """
        if self.decorators:
            for node in self.decorators.nodes:
                try:
                    inferred = next(node.infer())
                except (InferenceError, StopIteration):
                    continue
                if inferred and inferred.qname() in {
                    "abc.abstractproperty",
                    "abc.abstractmethod",
                }:
                    return True

        for child_node in self.body:
            if isinstance(child_node, node_classes.Raise):
                if any_raise_is_abstract:
                    return True
                if child_node.raises_not_implemented():
                    return True
            return pass_is_abstract and isinstance(child_node, node_classes.Pass)
        # empty function is the same as function with a single "pass" statement
        if pass_is_abstract:
            return True

        return False

    def is_generator(self) -> bool:
        """Check if this is a generator function.

        :returns: Whether this is a generator function.
        """
        yields_without_lambdas = set(self._get_yield_nodes_skip_lambdas())
        yields_without_functions = set(self._get_yield_nodes_skip_functions())
        # Want an intersecting member that is neither in a lambda nor a function
        return bool(yields_without_lambdas & yields_without_functions)

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[objects.Property | FunctionDef, None, InferenceErrorInfo]:
        from astroid import objects  # pylint: disable=import-outside-toplevel

        if not self.decorators or not bases._is_property(self):
            yield self
            return InferenceErrorInfo(node=self, context=context)

        # When inferring a property, we instantiate a new `objects.Property` object,
        # which in turn, because it inherits from `FunctionDef`, sets itself in the locals
        # of the wrapping frame. This means that every time we infer a property, the locals
        # are mutated with a new instance of the property. To avoid this, we detect this
        # scenario and avoid passing the `parent` argument to the constructor.
        if not self.parent:
            raise ParentMissingError(target=self)
        parent_frame = self.parent.frame()
        property_already_in_parent_locals = self.name in parent_frame.locals and any(
            isinstance(val, objects.Property) for val in parent_frame.locals[self.name]
        )
        # We also don't want to pass parent if the definition is within a Try node
        if isinstance(
            self.parent,
            (node_classes.Try, node_classes.If),
        ):
            property_already_in_parent_locals = True

        prop_func = objects.Property(
            function=self,
            name=self.name,
            lineno=self.lineno,
            parent=self.parent if not property_already_in_parent_locals else None,
            col_offset=self.col_offset,
        )
        if property_already_in_parent_locals:
            prop_func.parent = self.parent
        prop_func.postinit(body=[], args=self.args, doc_node=self.doc_node)
        yield prop_func
        return InferenceErrorInfo(node=self, context=context)

    def infer_yield_result(self, context: InferenceContext | None = None):
        """Infer what the function yields when called

        :returns: What the function yields
        :rtype: iterable(NodeNG or Uninferable) or None
        """
        # pylint: disable=not-an-iterable
        # https://github.com/pylint-dev/astroid/issues/1015
        for yield_ in self.nodes_of_class(node_classes.Yield):
            if yield_.value is None:
                const = node_classes.Const(None)
                const.parent = yield_
                const.lineno = yield_.lineno
                yield const
            elif yield_.scope() == self:
                yield from yield_.value.infer(context=context)

    def infer_call_result(
        self,
        caller: SuccessfulInferenceResult | None,
        context: InferenceContext | None = None,
    ) -> Iterator[InferenceResult]:
        """Infer what the function returns when called."""
        if self.is_generator():
            if isinstance(self, AsyncFunctionDef):
                generator_cls: type[bases.Generator] = bases.AsyncGenerator
            else:
                generator_cls = bases.Generator
            result = generator_cls(self, generator_initial_context=context)
            yield result
            return
        # This is really a gigantic hack to work around metaclass generators
        # that return transient class-generating functions. Pylint's AST structure
        # cannot handle a base class object that is only used for calling __new__,
        # but does not contribute to the inheritance structure itself. We inject
        # a fake class into the hierarchy here for several well-known metaclass
        # generators, and filter it out later.
        if (
            self.name == "with_metaclass"
            and caller is not None
            and self.args.args
            and len(self.args.args) == 1
            and self.args.vararg is not None
        ):
            if isinstance(caller.args, Arguments):
                assert caller.args.args is not None
                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 = [
                        # Find the first non-None inferred base value
                        next(
                            b
                            for b in arg.infer(
                                context=context.clone() if context else context
                            )
                            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",
                    lineno=0,
                    col_offset=0,
                    end_lineno=0,
                    end_col_offset=0,
                    parent=self,
                )
                new_class.hide = True
                new_class.postinit(
                    bases=[
                        base
                        for base in class_bases
                        if not isinstance(base, util.UninferableBase)
                    ],
                    body=[],
                    decorators=None,
                    metaclass=metaclass,
                )
                yield new_class
                return
        returns = self._get_return_nodes_skip_functions()

        first_return = next(returns, None)
        if not first_return:
            if self.body:
                if self.is_abstract(pass_is_abstract=True, any_raise_is_abstract=True):
                    yield util.Uninferable
                else:
                    yield node_classes.Const(None)
                return

            raise InferenceError("The function does not have any return statements")

        for returnnode in itertools.chain((first_return,), returns):
            if returnnode.value is None:
                yield node_classes.Const(None)
            else:
                try:
                    yield from returnnode.value.infer(context)
                except InferenceError:
                    yield util.Uninferable

    def bool_value(self, context: InferenceContext | None = None) -> bool:
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
            For a :class:`FunctionDef` this is always ``True``.
        """
        return True

    def get_children(self):
        if self.decorators is not None:
            yield self.decorators

        yield self.args

        if self.returns is not None:
            yield self.returns

        yield from self.body

    def scope_lookup(
        self, node: LookupMixIn, name: str, offset: int = 0
    ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]:
        """Lookup where the given name is assigned."""
        if name == "__class__":
            # __class__ is an implicit closure reference created by the compiler
            # if any methods in a class body refer to either __class__ or super.
            # In our case, we want to be able to look it up in the current scope
            # when `__class__` is being used.
            if self.parent and isinstance(frame := self.parent.frame(), ClassDef):
                return self, [frame]

        if (self.args.defaults and node in self.args.defaults) or (
            self.args.kw_defaults and node in self.args.kw_defaults
        ):
            if not self.parent:
                raise ParentMissingError(target=self)
            frame = self.parent.frame()
            # line offset to avoid that def func(f=func) resolve the default
            # value to the defined function
            offset = -1
        else:
            # check this is not used in function decorators
            frame = self
        return frame._scope_lookup(node, name, offset)

    def frame(self: _T, *, future: Literal[None, True] = None) -> _T:
        """The node's frame node.

        A frame node is a :class:`Module`, :class:`FunctionDef`,
        :class:`ClassDef` or :class:`Lambda`.

        :returns: The node itself.
        """
        return self


class AsyncFunctionDef(FunctionDef):
    """Class representing an :class:`ast.FunctionDef` node.

    A :class:`AsyncFunctionDef` is an asynchronous function
    created with the `async` keyword.

    >>> import astroid
    >>> node = astroid.extract_node('''
    async def func(things):
        async for thing in things:
            print(thing)
    ''')
    >>> node
    <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
    >>> node.body[0]
    <AsyncFor l.3 at 0x7f23b2e417b8>
    """


def _is_metaclass(klass, seen=None, context: InferenceContext | None = None) -> bool:
    """Return if the given class can be
    used as a metaclass.
    """
    if klass.name == "type":
        return True
    if seen is None:
        seen = set()
    for base in klass.bases:
        try:
            for baseobj in base.infer(context=context):
                baseobj_name = baseobj.qname()
                if baseobj_name in seen:
                    continue

                seen.add(baseobj_name)
                if isinstance(baseobj, bases.Instance):
                    # not abstract
                    return False
                if baseobj is klass:
                    continue
                if not isinstance(baseobj, ClassDef):
                    continue
                if baseobj._type == "metaclass":
                    return True
                if _is_metaclass(baseobj, seen, context=context):
                    return True
        except InferenceError:
            continue
    return False


def _class_type(klass, ancestors=None, context: InferenceContext | None = None):
    """return a ClassDef node type to differ metaclass and exception
    from 'regular' classes
    """
    # XXX we have to store ancestors in case we have an ancestor loop
    if klass._type is not None:
        return klass._type
    if _is_metaclass(klass, context=context):
        klass._type = "metaclass"
    elif klass.name.endswith("Exception"):
        klass._type = "exception"
    else:
        if ancestors is None:
            ancestors = set()
        klass_name = klass.qname()
        if klass_name in ancestors:
            # XXX we are in loop ancestors, and have found no type
            klass._type = "class"
            return "class"
        ancestors.add(klass_name)
        for base in klass.ancestors(recurs=False):
            name = _class_type(base, ancestors)
            if name != "class":
                if name == "metaclass" and not _is_metaclass(klass):
                    # don't propagate it if the current class
                    # can't be a metaclass
                    continue
                klass._type = base.type
                break
    if klass._type is None:
        klass._type = "class"
    return klass._type


def get_wrapping_class(node):
    """Get the class that wraps the given node.

    We consider that a class wraps a node if the class
    is a parent for the said node.

    :returns: The class that wraps the given node
    :rtype: ClassDef or None
    """

    klass = node.frame()
    while klass is not None and not isinstance(klass, ClassDef):
        if klass.parent is None:
            klass = None
        else:
            klass = klass.parent.frame()
    return klass


class ClassDef(  # pylint: disable=too-many-instance-attributes
    _base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG, _base_nodes.Statement
):
    """Class representing an :class:`ast.ClassDef` node.

    >>> import astroid
    >>> node = astroid.extract_node('''
    class Thing:
        def my_meth(self, arg):
            return arg + self.offset
    ''')
    >>> node
    <ClassDef.Thing l.2 at 0x7f23b2e9e748>
    """

    # some of the attributes below are set by the builder module or
    # by a raw factories

    # a dictionary of class instances attributes
    _astroid_fields = (
        "decorators",
        "bases",
        "keywords",
        "doc_node",
        "body",
        "type_params",
    )  # name

    decorators = None
    """The decorators that are applied to this class.

    :type: Decorators or None
    """
    special_attributes = ClassModel()
    """The names of special attributes that this class has.

    :type: objectmodel.ClassModel
    """

    _type = None
    _metaclass: NodeNG | None = None
    _metaclass_hack = False
    hide = False
    type = property(
        _class_type,
        doc=(
            "The class type for this node.\n\n"
            "Possible values are: class, metaclass, exception.\n\n"
            ":type: str"
        ),
    )
    _other_fields = ("name", "is_dataclass", "position")
    _other_other_fields = ("locals", "_newstyle")
    _newstyle: bool | None = None

    def __init__(
        self,
        name: str,
        lineno: int,
        col_offset: int,
        parent: NodeNG,
        *,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        self.instance_attrs: dict[str, NodeNG] = {}
        self.locals = {}
        """A map of the name of a local variable to the node defining it."""

        self.keywords: list[node_classes.Keyword] = []
        """The keywords given to the class definition.

        This is usually for :pep:`3115` style metaclass declaration.
        """

        self.bases: list[SuccessfulInferenceResult] = []
        """What the class inherits from."""

        self.body: list[NodeNG] = []
        """The contents of the class body."""

        self.name = name
        """The name of the class."""

        self.decorators = None
        """The decorators that are applied to this class."""

        self.doc_node: Const | None = None
        """The doc node associated with this node."""

        self.is_dataclass: bool = False
        """Whether this class is a dataclass."""

        self.type_params: list[
            nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple
        ] = []
        """PEP 695 (Python 3.12+) type params, e.g. class MyClass[T]: ..."""

        super().__init__(
            lineno=lineno,
            col_offset=col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )
        if parent and not isinstance(parent, Unknown):
            parent.frame().set_local(name, self)

        for local_name, node in self.implicit_locals():
            self.add_local_node(node, local_name)

    infer_binary_op: ClassVar[
        InferBinaryOp[ClassDef]
    ] = protocols.instance_class_infer_binary_op

    def implicit_parameters(self) -> Literal[1]:
        return 1

    def implicit_locals(self):
        """Get implicitly defined class definition locals.

        :returns: the the name and Const pair for each local
        :rtype: tuple(tuple(str, node_classes.Const), ...)
        """
        locals_ = (("__module__", self.special_attributes.attr___module__),)
        # __qualname__ is defined in PEP3155
        locals_ += (("__qualname__", self.special_attributes.attr___qualname__),)
        return locals_

    # pylint: disable=redefined-outer-name
    def postinit(
        self,
        bases: list[SuccessfulInferenceResult],
        body: list[NodeNG],
        decorators: node_classes.Decorators | None,
        newstyle: bool | None = None,
        metaclass: NodeNG | None = None,
        keywords: list[node_classes.Keyword] | None = None,
        *,
        position: Position | None = None,
        doc_node: Const | None = None,
        type_params: list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple]
        | None = None,
    ) -> None:
        if keywords is not None:
            self.keywords = keywords
        self.bases = bases
        self.body = body
        self.decorators = decorators
        self._newstyle = newstyle
        self._metaclass = metaclass
        self.position = position
        self.doc_node = doc_node
        self.type_params = type_params or []

    def _newstyle_impl(self, context: InferenceContext | None = None):
        if context is None:
            context = InferenceContext()
        if self._newstyle is not None:
            return self._newstyle
        for base in self.ancestors(recurs=False, context=context):
            if base._newstyle_impl(context):
                self._newstyle = True
                break
        klass = self.declared_metaclass()
        # could be any callable, we'd need to infer the result of klass(name,
        # bases, dict).  punt if it's not a class node.
        if klass is not None and isinstance(klass, ClassDef):
            self._newstyle = klass._newstyle_impl(context)
        if self._newstyle is None:
            self._newstyle = False
        return self._newstyle

    _newstyle = None
    newstyle = property(
        _newstyle_impl,
        doc=("Whether this is a new style class or not\n\n" ":type: bool or None"),
    )

    @cached_property
    def fromlineno(self) -> int:
        """The first line that this node appears on in the source code.

        Can also return 0 if the line can not be determined.
        """
        if IS_PYPY and PY38 and not PYPY_7_3_11_PLUS:
            # For Python < 3.8 the lineno is the line number of the first decorator.
            # We want the class statement lineno. Similar to 'FunctionDef.fromlineno'
            # PyPy (3.8): Fixed with version v7.3.11
            lineno = self.lineno or 0
            if self.decorators is not None:
                lineno += sum(
                    node.tolineno - (node.lineno or 0) + 1
                    for node in self.decorators.nodes
                )

            return lineno or 0
        return super().fromlineno

    @cached_property
    def blockstart_tolineno(self):
        """The line on which the beginning of this block ends.

        :type: int
        """
        if self.bases:
            return self.bases[-1].tolineno

        return self.fromlineno

    def block_range(self, lineno: int) -> tuple[int, int]:
        """Get a range from the given line number to where this node ends.

        :param lineno: Unused.

        :returns: The range of line numbers that this node belongs to,
        """
        return self.fromlineno, self.tolineno

    def pytype(self) -> Literal["builtins.type", "builtins.classobj"]:
        """Get the name of the type that this node represents.

        :returns: The name of the type.
        """
        if self.newstyle:
            return "builtins.type"
        return "builtins.classobj"

    def display_type(self) -> str:
        """A human readable type of this node.

        :returns: The type of this node.
        :rtype: str
        """
        return "Class"

    def callable(self) -> bool:
        """Whether this node defines something that is callable.

        :returns: Whether this defines something that is callable.
            For a :class:`ClassDef` this is always ``True``.
        """
        return True

    def is_subtype_of(self, type_name, context: InferenceContext | None = None) -> bool:
        """Whether this class is a subtype of the given type.

        :param type_name: The name of the type of check against.
        :type type_name: str

        :returns: Whether this class is a subtype of the given type.
        """
        if self.qname() == type_name:
            return True

        return any(anc.qname() == type_name for anc in self.ancestors(context=context))

    def _infer_type_call(self, caller, context):
        try:
            name_node = next(caller.args[0].infer(context))
        except StopIteration as e:
            raise InferenceError(node=caller.args[0], context=context) from e
        if isinstance(name_node, node_classes.Const) and isinstance(
            name_node.value, str
        ):
            name = name_node.value
        else:
            return util.Uninferable

        result = ClassDef(
            name,
            lineno=0,
            col_offset=0,
            end_lineno=0,
            end_col_offset=0,
            parent=Unknown(),
        )

        # Get the bases of the class.
        try:
            class_bases = next(caller.args[1].infer(context))
        except StopIteration as e:
            raise InferenceError(node=caller.args[1], context=context) from e
        if isinstance(class_bases, (node_classes.Tuple, node_classes.List)):
            bases = []
            for base in class_bases.itered():
                inferred = next(base.infer(context=context), None)
                if inferred:
                    bases.append(
                        node_classes.EvaluatedObject(original=base, value=inferred)
                    )
            result.bases = bases
        else:
            # There is currently no AST node that can represent an 'unknown'
            # node (Uninferable is not an AST node), therefore we simply return Uninferable here
            # although we know at least the name of the class.
            return util.Uninferable

        # Get the members of the class
        try:
            members = next(caller.args[2].infer(context))
        except (InferenceError, StopIteration):
            members = None

        if members and isinstance(members, node_classes.Dict):
            for attr, value in members.items:
                if isinstance(attr, node_classes.Const) and isinstance(attr.value, str):
                    result.locals[attr.value] = [value]

        result.parent = caller.parent
        return result

    def infer_call_result(
        self,
        caller: SuccessfulInferenceResult | None,
        context: InferenceContext | None = None,
    ) -> Iterator[InferenceResult]:
        """infer what a class is returning when called"""
        if self.is_subtype_of("builtins.type", context) and len(caller.args) == 3:
            result = self._infer_type_call(caller, context)
            yield result
            return

        dunder_call = None
        try:
            metaclass = self.metaclass(context=context)
            if metaclass is not None:
                # Only get __call__ if it's defined locally for the metaclass.
                # Otherwise we will find ObjectModel.__call__ which will
                # return an instance of the metaclass. Instantiating the class is
                # handled later.
                if "__call__" in metaclass.locals:
                    dunder_call = next(metaclass.igetattr("__call__", context))
        except (AttributeInferenceError, StopIteration):
            pass

        if dunder_call and dunder_call.qname() != "builtins.type.__call__":
            # Call type.__call__ if not set metaclass
            # (since type is the default metaclass)
            context = bind_context_to_node(context, self)
            context.callcontext.callee = dunder_call
            yield from dunder_call.infer_call_result(caller, context)
        else:
            yield self.instantiate_class()

    def scope_lookup(
        self, node: LookupMixIn, name: str, offset: int = 0
    ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]:
        """Lookup where the given name is assigned.

        :param node: The node to look for assignments up to.
            Any assignments after the given node are ignored.

        :param name: The name to find assignments for.

        :param offset: The line offset to filter statements up to.

        :returns: This scope node and the list of assignments associated to the
            given name according to the scope where it has been found (locals,
            globals or builtin).
        """
        # If the name looks like a builtin name, just try to look
        # into the upper scope of this class. We might have a
        # decorator that it's poorly named after a builtin object
        # inside this class.
        lookup_upper_frame = (
            isinstance(node.parent, node_classes.Decorators)
            and name in AstroidManager().builtins_module
        )
        if (
            any(node == base or base.parent_of(node) for base in self.bases)
            or lookup_upper_frame
        ):
            # Handle the case where we have either a name
            # in the bases of a class, which exists before
            # the actual definition or the case where we have
            # a Getattr node, with that name.
            #
            # name = ...
            # class A(name):
            #     def name(self): ...
            #
            # import name
            # class A(name.Name):
            #     def name(self): ...
            if not self.parent:
                raise ParentMissingError(target=self)
            frame = self.parent.frame()
            # line offset to avoid that class A(A) resolve the ancestor to
            # the defined class
            offset = -1
        else:
            frame = self
        return frame._scope_lookup(node, name, offset)

    @property
    def basenames(self):
        """The names of the parent classes

        Names are given in the order they appear in the class definition.

        :type: list(str)
        """
        return [bnode.as_string() for bnode in self.bases]

    def ancestors(
        self, recurs: bool = True, context: InferenceContext | None = None
    ) -> Generator[ClassDef, None, None]:
        """Iterate over the base classes in prefixed depth first order.

        :param recurs: Whether to recurse or return direct ancestors only.

        :returns: The base classes
        """
        # FIXME: should be possible to choose the resolution order
        # FIXME: inference make infinite loops possible here
        yielded = {self}
        if context is None:
            context = InferenceContext()
        if not self.bases and self.qname() != "builtins.object":
            # This should always be a ClassDef (which we don't assert for)
            yield builtin_lookup("object")[1][0]  # type: ignore[misc]
            return

        for stmt in self.bases:
            with context.restore_path():
                try:
                    for baseobj in stmt.infer(context):
                        if not isinstance(baseobj, ClassDef):
                            if isinstance(baseobj, bases.Instance):
                                baseobj = baseobj._proxied
                            else:
                                continue
                        if not baseobj.hide:
                            if baseobj in yielded:
                                continue
                            yielded.add(baseobj)
                            yield baseobj
                        if not recurs:
                            continue
                        for grandpa in baseobj.ancestors(recurs=True, context=context):
                            if grandpa is self:
                                # This class is the ancestor of itself.
                                break
                            if grandpa in yielded:
                                continue
                            yielded.add(grandpa)
                            yield grandpa
                except InferenceError:
                    continue

    def local_attr_ancestors(self, name, context: InferenceContext | None = None):
        """Iterate over the parents that define the given name.

        :param name: The name to find definitions for.
        :type name: str

        :returns: The parents that define the given name.
        :rtype: iterable(NodeNG)
        """
        # Look up in the mro if we can. This will result in the
        # attribute being looked up just as Python does it.
        try:
            ancestors: Iterable[ClassDef] = self.mro(context)[1:]
        except MroError:
            # Fallback to use ancestors, we can't determine
            # a sane MRO.
            ancestors = self.ancestors(context=context)
        for astroid in ancestors:
            if name in astroid:
                yield astroid

    def instance_attr_ancestors(self, name, context: InferenceContext | None = None):
        """Iterate over the parents that define the given name as an attribute.

        :param name: The name to find definitions for.
        :type name: str

        :returns: The parents that define the given name as
            an instance attribute.
        :rtype: iterable(NodeNG)
        """
        for astroid in self.ancestors(context=context):
            if name in astroid.instance_attrs:
                yield astroid

    def has_base(self, node) -> bool:
        """Whether this class directly inherits from the given node.

        :param node: The node to check for.
        :type node: NodeNG

        :returns: Whether this class directly inherits from the given node.
        """
        return node in self.bases

    def local_attr(self, name, context: InferenceContext | None = None):
        """Get the list of assign nodes associated to the given name.

        Assignments are looked for in both this class and in parents.

        :returns: The list of assignments to the given name.
        :rtype: list(NodeNG)

        :raises AttributeInferenceError: If no attribute with this name
            can be found in this class or parent classes.
        """
        result = []
        if name in self.locals:
            result = self.locals[name]
        else:
            class_node = next(self.local_attr_ancestors(name, context), None)
            if class_node:
                result = class_node.locals[name]
        result = [n for n in result if not isinstance(n, node_classes.DelAttr)]
        if result:
            return result
        raise AttributeInferenceError(target=self, attribute=name, context=context)

    def instance_attr(self, name, context: InferenceContext | None = None):
        """Get the list of nodes associated to the given attribute name.

        Assignments are looked for in both this class and in parents.

        :returns: The list of assignments to the given name.
        :rtype: list(NodeNG)

        :raises AttributeInferenceError: If no attribute with this name
            can be found in this class or parent classes.
        """
        # Return a copy, so we don't modify self.instance_attrs,
        # which could lead to infinite loop.
        values = list(self.instance_attrs.get(name, []))
        # get all values from parents
        for class_node in self.instance_attr_ancestors(name, context):
            values += class_node.instance_attrs[name]
        values = [n for n in values if not isinstance(n, node_classes.DelAttr)]
        if values:
            return values
        raise AttributeInferenceError(target=self, attribute=name, context=context)

    def instantiate_class(self) -> bases.Instance:
        """Get an :class:`Instance` of the :class:`ClassDef` node.

        :returns: An :class:`Instance` of the :class:`ClassDef` node
        """
        from astroid import objects  # pylint: disable=import-outside-toplevel

        try:
            if any(cls.name in EXCEPTION_BASE_CLASSES for cls in self.mro()):
                # Subclasses of exceptions can be exception instances
                return objects.ExceptionInstance(self)
        except MroError:
            pass
        return bases.Instance(self)

    def getattr(
        self,
        name: str,
        context: InferenceContext | None = None,
        class_context: bool = True,
    ) -> list[InferenceResult]:
        """Get an attribute from this class, using Python's attribute semantic.

        This method doesn't look in the :attr:`instance_attrs` dictionary
        since it is done by an :class:`Instance` proxy at inference time.
        It may return an :class:`Uninferable` object if
        the attribute has not been
        found, but a ``__getattr__`` or ``__getattribute__`` method is defined.
        If ``class_context`` is given, then it is considered that the
        attribute is accessed from a class context,
        e.g. ClassDef.attribute, otherwise it might have been accessed
        from an instance as well. If ``class_context`` is used in that
        case, then a lookup in the implicit metaclass and the explicit
        metaclass will be done.

        :param name: The attribute to look for.

        :param class_context: Whether the attribute can be accessed statically.

        :returns: The attribute.

        :raises AttributeInferenceError: If the attribute cannot be inferred.
        """
        if not name:
            raise AttributeInferenceError(target=self, attribute=name, context=context)

        # don't modify the list in self.locals!
        values: list[InferenceResult] = list(self.locals.get(name, []))
        for classnode in self.ancestors(recurs=True, context=context):
            values += classnode.locals.get(name, [])

        if name in self.special_attributes and class_context and not values:
            result = [self.special_attributes.lookup(name)]
            if name == "__bases__":
                # Need special treatment, since they are mutable
                # and we need to return all the values.
                result += values
            return result

        if class_context:
            values += self._metaclass_lookup_attribute(name, context)

        # Remove AnnAssigns without value, which are not attributes in the purest sense.
        for value in values.copy():
            if isinstance(value, node_classes.AssignName):
                stmt = value.statement()
                if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None:
                    values.pop(values.index(value))

        if not values:
            raise AttributeInferenceError(target=self, attribute=name, context=context)

        return values

    @lru_cache(maxsize=1024)  # noqa
    def _metaclass_lookup_attribute(self, name, context):
        """Search the given name in the implicit and the explicit metaclass."""
        attrs = set()
        implicit_meta = self.implicit_metaclass()
        context = copy_context(context)
        metaclass = self.metaclass(context=context)
        for cls in (implicit_meta, metaclass):
            if cls and cls != self and isinstance(cls, ClassDef):
                cls_attributes = self._get_attribute_from_metaclass(cls, name, context)
                attrs.update(set(cls_attributes))
        return attrs

    def _get_attribute_from_metaclass(self, cls, name, context):
        from astroid import objects  # pylint: disable=import-outside-toplevel

        try:
            attrs = cls.getattr(name, context=context, class_context=True)
        except AttributeInferenceError:
            return

        for attr in bases._infer_stmts(attrs, context, frame=cls):
            if not isinstance(attr, FunctionDef):
                yield attr
                continue

            if isinstance(attr, objects.Property):
                yield attr
                continue
            if attr.type == "classmethod":
                # If the method is a classmethod, then it will
                # be bound to the metaclass, not to the class
                # from where the attribute is retrieved.
                # get_wrapping_class could return None, so just
                # default to the current class.
                frame = get_wrapping_class(attr) or self
                yield bases.BoundMethod(attr, frame)
            elif attr.type == "staticmethod":
                yield attr
            else:
                yield bases.BoundMethod(attr, self)

    def igetattr(
        self,
        name: str,
        context: InferenceContext | None = None,
        class_context: bool = True,
    ) -> Iterator[InferenceResult]:
        """Infer the possible values of the given variable.

        :param name: The name of the variable to infer.

        :returns: The inferred possible values.
        """
        from astroid import objects  # pylint: disable=import-outside-toplevel

        # set lookup name since this is necessary to infer on import nodes for
        # instance
        context = copy_context(context)
        context.lookupname = name

        metaclass = self.metaclass(context=context)
        try:
            attributes = self.getattr(name, context, class_context=class_context)
            # If we have more than one attribute, make sure that those starting from
            # the second one are from the same scope. This is to account for modifications
            # to the attribute happening *after* the attribute's definition (e.g. AugAssigns on lists)
            if len(attributes) > 1:
                first_attr, attributes = attributes[0], attributes[1:]
                first_scope = first_attr.scope()
                attributes = [first_attr] + [
                    attr
                    for attr in attributes
                    if attr.parent and attr.parent.scope() == first_scope
                ]

            for inferred in bases._infer_stmts(attributes, context, frame=self):
                # yield Uninferable object instead of descriptors when necessary
                if not isinstance(inferred, node_classes.Const) and isinstance(
                    inferred, bases.Instance
                ):
                    try:
                        inferred._proxied.getattr("__get__", context)
                    except AttributeInferenceError:
                        yield inferred
                    else:
                        yield util.Uninferable
                elif isinstance(inferred, objects.Property):
                    function = inferred.function
                    if not class_context:
                        # Through an instance so we can solve the property
                        yield from function.infer_call_result(
                            caller=self, context=context
                        )
                    # If we're in a class context, we need to determine if the property
                    # was defined in the metaclass (a derived class must be a subclass of
                    # the metaclass of all its bases), in which case we can resolve the
                    # property. If not, i.e. the property is defined in some base class
                    # instead, then we return the property object
                    elif metaclass and function.parent.scope() is metaclass:
                        # Resolve a property as long as it is not accessed through
                        # the class itself.
                        yield from function.infer_call_result(
                            caller=self, context=context
                        )
                    else:
                        yield inferred
                else:
                    yield function_to_method(inferred, self)
        except AttributeInferenceError as error:
            if not name.startswith("__") and self.has_dynamic_getattr(context):
                # class handle some dynamic attributes, return a Uninferable object
                yield util.Uninferable
            else:
                raise InferenceError(
                    str(error), target=self, attribute=name, context=context
                ) from error

    def has_dynamic_getattr(self, context: InferenceContext | None = None) -> bool:
        """Check if the class has a custom __getattr__ or __getattribute__.

        If any such method is found and it is not from
        builtins, nor from an extension module, then the function
        will return True.

        :returns: Whether the class has a custom __getattr__ or __getattribute__.
        """

        def _valid_getattr(node):
            root = node.root()
            return root.name != "builtins" and getattr(root, "pure_python", None)

        try:
            return _valid_getattr(self.getattr("__getattr__", context)[0])
        except AttributeInferenceError:
            # if self.newstyle: XXX cause an infinite recursion error
            try:
                getattribute = self.getattr("__getattribute__", context)[0]
                return _valid_getattr(getattribute)
            except AttributeInferenceError:
                pass
        return False

    def getitem(self, index, context: InferenceContext | None = None):
        """Return the inference of a subscript.

        This is basically looking up the method in the metaclass and calling it.

        :returns: The inferred value of a subscript to this class.
        :rtype: NodeNG

        :raises AstroidTypeError: If this class does not define a
            ``__getitem__`` method.
        """
        try:
            methods = lookup(self, "__getitem__", context=context)
        except AttributeInferenceError as exc:
            if isinstance(self, ClassDef):
                # subscripting a class definition may be
                # achieved thanks to __class_getitem__ method
                # which is a classmethod defined in the class
                # that supports subscript and not in the metaclass
                try:
                    methods = self.getattr("__class_getitem__")
                    # Here it is assumed that the __class_getitem__ node is
                    # a FunctionDef. One possible improvement would be to deal
                    # with more generic inference.
                except AttributeInferenceError:
                    raise AstroidTypeError(node=self, context=context) from exc
            else:
                raise AstroidTypeError(node=self, context=context) from exc

        method = methods[0]

        # Create a new callcontext for providing index as an argument.
        new_context = bind_context_to_node(context, self)
        new_context.callcontext = CallContext(args=[index], callee=method)

        try:
            return next(method.infer_call_result(self, new_context), util.Uninferable)
        except AttributeError:
            # Starting with python3.9, builtin types list, dict etc...
            # are subscriptable thanks to __class_getitem___ classmethod.
            # However in such case the method is bound to an EmptyNode and
            # EmptyNode doesn't have infer_call_result method yielding to
            # AttributeError
            if (
                isinstance(method, node_classes.EmptyNode)
                and self.pytype() == "builtins.type"
                and PY39_PLUS
            ):
                return self
            raise
        except InferenceError:
            return util.Uninferable

    def methods(self):
        """Iterate over all of the method defined in this class and its parents.

        :returns: The methods defined on the class.
        :rtype: iterable(FunctionDef)
        """
        done = {}
        for astroid in itertools.chain(iter((self,)), self.ancestors()):
            for meth in astroid.mymethods():
                if meth.name in done:
                    continue
                done[meth.name] = None
                yield meth

    def mymethods(self):
        """Iterate over all of the method defined in this class only.

        :returns: The methods defined on the class.
        :rtype: iterable(FunctionDef)
        """
        for member in self.values():
            if isinstance(member, FunctionDef):
                yield member

    def implicit_metaclass(self):
        """Get the implicit metaclass of the current class.

        For newstyle classes, this will return an instance of builtins.type.
        For oldstyle classes, it will simply return None, since there's
        no implicit metaclass there.

        :returns: The metaclass.
        :rtype: builtins.type or None
        """
        if self.newstyle:
            return builtin_lookup("type")[1][0]
        return None

    def declared_metaclass(
        self, context: InferenceContext | None = None
    ) -> SuccessfulInferenceResult | None:
        """Return the explicit declared metaclass for the current class.

        An explicit declared metaclass is defined
        either by passing the ``metaclass`` keyword argument
        in the class definition line (Python 3) or (Python 2) by
        having a ``__metaclass__`` class attribute, or if there are
        no explicit bases but there is a global ``__metaclass__`` variable.

        :returns: The metaclass of this class,
            or None if one could not be found.
        """
        for base in self.bases:
            try:
                for baseobj in base.infer(context=context):
                    if isinstance(baseobj, ClassDef) and baseobj.hide:
                        self._metaclass = baseobj._metaclass
                        self._metaclass_hack = True
                        break
            except InferenceError:
                pass

        if self._metaclass:
            # Expects this from Py3k TreeRebuilder
            try:
                return next(
                    node
                    for node in self._metaclass.infer(context=context)
                    if not isinstance(node, util.UninferableBase)
                )
            except (InferenceError, StopIteration):
                return None

        return None

    def _find_metaclass(
        self, seen: set[ClassDef] | None = None, context: InferenceContext | None = None
    ) -> SuccessfulInferenceResult | None:
        if seen is None:
            seen = set()
        seen.add(self)

        klass = self.declared_metaclass(context=context)
        if klass is None:
            for parent in self.ancestors(context=context):
                if parent not in seen:
                    klass = parent._find_metaclass(seen)
                    if klass is not None:
                        break
        return klass

    def metaclass(
        self, context: InferenceContext | None = None
    ) -> SuccessfulInferenceResult | None:
        """Get the metaclass of this class.

        If this class does not define explicitly a metaclass,
        then the first defined metaclass in ancestors will be used
        instead.

        :returns: The metaclass of this class.
        """
        return self._find_metaclass(context=context)

    def has_metaclass_hack(self):
        return self._metaclass_hack

    def _islots(self):
        """Return an iterator with the inferred slots."""
        if "__slots__" not in self.locals:
            return None
        for slots in self.igetattr("__slots__"):
            # check if __slots__ is a valid type
            for meth in ITER_METHODS:
                try:
                    slots.getattr(meth)
                    break
                except AttributeInferenceError:
                    continue
            else:
                continue

            if isinstance(slots, node_classes.Const):
                # a string. Ignore the following checks,
                # but yield the node, only if it has a value
                if slots.value:
                    yield slots
                continue
            if not hasattr(slots, "itered"):
                # we can't obtain the values, maybe a .deque?
                continue

            if isinstance(slots, node_classes.Dict):
                values = [item[0] for item in slots.items]
            else:
                values = slots.itered()
            if isinstance(values, util.UninferableBase):
                continue
            if not values:
                # Stop the iteration, because the class
                # has an empty list of slots.
                return values

            for elt in values:
                try:
                    for inferred in elt.infer():
                        if not isinstance(
                            inferred, node_classes.Const
                        ) or not isinstance(inferred.value, str):
                            continue
                        if not inferred.value:
                            continue
                        yield inferred
                except InferenceError:
                    continue

        return None

    def _slots(self):
        if not self.newstyle:
            raise NotImplementedError(
                "The concept of slots is undefined for old-style classes."
            )

        slots = self._islots()
        try:
            first = next(slots)
        except StopIteration as exc:
            # The class doesn't have a __slots__ definition or empty slots.
            if exc.args and exc.args[0] not in ("", None):
                return exc.args[0]
            return None
        return [first, *slots]

    # Cached, because inferring them all the time is expensive
    @cached_property
    def _all_slots(self):
        """Get all the slots for this node.

        :returns: The names of slots for this class.
            If the class doesn't define any slot, through the ``__slots__``
            variable, then this function will return a None.
            Also, it will return None in the case the slots were not inferred.
        :rtype: list(str) or None
        """

        def grouped_slots(
            mro: list[ClassDef],
        ) -> Iterator[node_classes.NodeNG | None]:
            for cls in mro:
                # Not interested in object, since it can't have slots.
                if cls.qname() == "builtins.object":
                    continue
                try:
                    cls_slots = cls._slots()
                except NotImplementedError:
                    continue
                if cls_slots is not None:
                    yield from cls_slots
                else:
                    yield None

        if not self.newstyle:
            raise NotImplementedError(
                "The concept of slots is undefined for old-style classes."
            )

        try:
            mro = self.mro()
        except MroError as e:
            raise NotImplementedError(
                "Cannot get slots while parsing mro fails."
            ) from e

        slots = list(grouped_slots(mro))
        if not all(slot is not None for slot in slots):
            return None

        return sorted(set(slots), key=lambda item: item.value)

    def slots(self):
        return self._all_slots

    def _inferred_bases(self, context: InferenceContext | None = None):
        # Similar with .ancestors, but the difference is when one base is inferred,
        # only the first object is wanted. That's because
        # we aren't interested in superclasses, as in the following
        # example:
        #
        # class SomeSuperClass(object): pass
        # class SomeClass(SomeSuperClass): pass
        # class Test(SomeClass): pass
        #
        # Inferring SomeClass from the Test's bases will give
        # us both SomeClass and SomeSuperClass, but we are interested
        # only in SomeClass.

        if context is None:
            context = InferenceContext()
        if not self.bases and self.qname() != "builtins.object":
            yield builtin_lookup("object")[1][0]
            return

        for stmt in self.bases:
            try:
                # Find the first non-None inferred base value
                baseobj = next(
                    b
                    for b in stmt.infer(context=context.clone())
                    if not (isinstance(b, Const) and b.value is None)
                )
            except (InferenceError, StopIteration):
                continue
            if isinstance(baseobj, bases.Instance):
                baseobj = baseobj._proxied
            if not isinstance(baseobj, ClassDef):
                continue
            if not baseobj.hide:
                yield baseobj
            else:
                yield from baseobj.bases

    def _compute_mro(self, context: InferenceContext | None = None):
        if self.qname() == "builtins.object":
            return [self]

        inferred_bases = list(self._inferred_bases(context=context))
        bases_mro = []
        for base in inferred_bases:
            if base is self:
                continue

            try:
                mro = base._compute_mro(context=context)
                bases_mro.append(mro)
            except NotImplementedError:
                # Some classes have in their ancestors both newstyle and
                # old style classes. For these we can't retrieve the .mro,
                # although in Python it's possible, since the class we are
                # currently working is in fact new style.
                # So, we fallback to ancestors here.
                ancestors = list(base.ancestors(context=context))
                bases_mro.append(ancestors)

        unmerged_mro: list[list[ClassDef]] = [[self], *bases_mro, inferred_bases]
        unmerged_mro = clean_duplicates_mro(unmerged_mro, self, context)
        clean_typing_generic_mro(unmerged_mro)
        return _c3_merge(unmerged_mro, self, context)

    def mro(self, context: InferenceContext | None = None) -> list[ClassDef]:
        """Get the method resolution order, using C3 linearization.

        :returns: The list of ancestors, sorted by the mro.
        :rtype: list(NodeNG)
        :raises DuplicateBasesError: Duplicate bases in the same class base
        :raises InconsistentMroError: A class' MRO is inconsistent
        """
        return self._compute_mro(context=context)

    def bool_value(self, context: InferenceContext | None = None) -> Literal[True]:
        """Determine the boolean value of this node.

        :returns: The boolean value of this node.
            For a :class:`ClassDef` this is always ``True``.
        """
        return True

    def get_children(self):
        if self.decorators is not None:
            yield self.decorators

        yield from self.bases
        if self.keywords is not None:
            yield from self.keywords
        yield from self.body

    @cached_property
    def _assign_nodes_in_scope(self):
        children_assign_nodes = (
            child_node._assign_nodes_in_scope for child_node in self.body
        )
        return list(itertools.chain.from_iterable(children_assign_nodes))

    def frame(self: _T, *, future: Literal[None, True] = None) -> _T:
        """The node's frame node.

        A frame node is a :class:`Module`, :class:`FunctionDef`,
        :class:`ClassDef` or :class:`Lambda`.

        :returns: The node itself.
        """
        return self

    def _infer(
        self, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[ClassDef, None, None]:
        yield self

back/venvmsg/Lib/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""This module contains utility functions for scoped nodes."""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid.manager import AstroidManager

if TYPE_CHECKING:
    from astroid import nodes


def builtin_lookup(name: str) -> tuple[nodes.Module, list[nodes.NodeNG]]:
    """Lookup a name in the builtin module.

    Return the list of matching statements and the ast for the builtin module
    """
    manager = AstroidManager()
    try:
        _builtin_astroid = manager.builtins_module
    except KeyError:
        # User manipulated the astroid cache directly! Rebuild everything.
        manager.clear_cache()
        _builtin_astroid = manager.builtins_module
    if name == "__dict__":
        return _builtin_astroid, ()
    try:
        stmts: list[nodes.NodeNG] = _builtin_astroid.locals[name]  # type: ignore[assignment]
    except KeyError:
        stmts = []
    return _builtin_astroid, stmts

back/venvmsg/Lib/site-packages/astroid/nodes/scoped_nodes/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from typing import NamedTuple


class Position(NamedTuple):
    """Position with line and column information."""

    lineno: int
    col_offset: int
    end_lineno: int
    end_col_offset: int

back/venvmsg/Lib/site-packages/astroid/nodes/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""
Inference objects are a way to represent composite AST nodes,
which are used only as inference results, so they can't be found in the
original AST tree. For instance, inferring the following frozenset use,
leads to an inferred FrozenSet:

    Call(func=Name('frozenset'), args=Tuple(...))
"""

from __future__ import annotations

from collections.abc import Generator, Iterator
from functools import cached_property
from typing import Any, Literal, NoReturn, TypeVar

from astroid import bases, decorators, util
from astroid.context import InferenceContext
from astroid.exceptions import (
    AttributeInferenceError,
    InferenceError,
    MroError,
    SuperError,
)
from astroid.interpreter import objectmodel
from astroid.manager import AstroidManager
from astroid.nodes import node_classes, scoped_nodes
from astroid.typing import InferenceResult, SuccessfulInferenceResult

_T = TypeVar("_T")


class FrozenSet(node_classes.BaseContainer):
    """Class representing a FrozenSet composite node."""

    def pytype(self) -> Literal["builtins.frozenset"]:
        return "builtins.frozenset"

    def _infer(self, context: InferenceContext | None = None, **kwargs: Any):
        yield self

    @cached_property
    def _proxied(self):  # pylint: disable=method-hidden
        ast_builtins = AstroidManager().builtins_module
        return ast_builtins.getattr("frozenset")[0]


class Super(node_classes.NodeNG):
    """Proxy class over a super call.

    This class offers almost the same behaviour as Python's super,
    which is MRO lookups for retrieving attributes from the parents.

    The *mro_pointer* is the place in the MRO from where we should
    start looking, not counting it. *mro_type* is the object which
    provides the MRO, it can be both a type or an instance.
    *self_class* is the class where the super call is, while
    *scope* is the function where the super call is.
    """

    special_attributes = objectmodel.SuperModel()

    def __init__(
        self,
        mro_pointer: SuccessfulInferenceResult,
        mro_type: SuccessfulInferenceResult,
        self_class: scoped_nodes.ClassDef,
        scope: scoped_nodes.FunctionDef,
        call: node_classes.Call,
    ) -> None:
        self.type = mro_type
        self.mro_pointer = mro_pointer
        self._class_based = False
        self._self_class = self_class
        self._scope = scope
        super().__init__(
            parent=scope,
            lineno=scope.lineno,
            col_offset=scope.col_offset,
            end_lineno=scope.end_lineno,
            end_col_offset=scope.end_col_offset,
        )

    def _infer(self, context: InferenceContext | None = None, **kwargs: Any):
        yield self

    def super_mro(self):
        """Get the MRO which will be used to lookup attributes in this super."""
        if not isinstance(self.mro_pointer, scoped_nodes.ClassDef):
            raise SuperError(
                "The first argument to super must be a subtype of "
                "type, not {mro_pointer}.",
                super_=self,
            )

        if isinstance(self.type, scoped_nodes.ClassDef):
            # `super(type, type)`, most likely in a class method.
            self._class_based = True
            mro_type = self.type
        else:
            mro_type = getattr(self.type, "_proxied", None)
            if not isinstance(mro_type, (bases.Instance, scoped_nodes.ClassDef)):
                raise SuperError(
                    "The second argument to super must be an "
                    "instance or subtype of type, not {type}.",
                    super_=self,
                )

        if not mro_type.newstyle:
            raise SuperError("Unable to call super on old-style classes.", super_=self)

        mro = mro_type.mro()
        if self.mro_pointer not in mro:
            raise SuperError(
                "The second argument to super must be an "
                "instance or subtype of type, not {type}.",
                super_=self,
            )

        index = mro.index(self.mro_pointer)
        return mro[index + 1 :]

    @cached_property
    def _proxied(self):
        ast_builtins = AstroidManager().builtins_module
        return ast_builtins.getattr("super")[0]

    def pytype(self) -> Literal["builtins.super"]:
        return "builtins.super"

    def display_type(self) -> str:
        return "Super of"

    @property
    def name(self):
        """Get the name of the MRO pointer."""
        return self.mro_pointer.name

    def qname(self) -> Literal["super"]:
        return "super"

    def igetattr(  # noqa: C901
        self, name: str, context: InferenceContext | None = None
    ) -> Iterator[InferenceResult]:
        """Retrieve the inferred values of the given attribute name."""
        # '__class__' is a special attribute that should be taken directly
        # from the special attributes dict
        if name == "__class__":
            yield self.special_attributes.lookup(name)
            return

        try:
            mro = self.super_mro()
        # Don't let invalid MROs or invalid super calls
        # leak out as is from this function.
        except SuperError as exc:
            raise AttributeInferenceError(
                (
                    "Lookup for {name} on {target!r} because super call {super!r} "
                    "is invalid."
                ),
                target=self,
                attribute=name,
                context=context,
                super_=exc.super_,
            ) from exc
        except MroError as exc:
            raise AttributeInferenceError(
                (
                    "Lookup for {name} on {target!r} failed because {cls!r} has an "
                    "invalid MRO."
                ),
                target=self,
                attribute=name,
                context=context,
                mros=exc.mros,
                cls=exc.cls,
            ) from exc
        found = False
        for cls in mro:
            if name not in cls.locals:
                continue

            found = True
            for inferred in bases._infer_stmts([cls[name]], context, frame=self):
                if not isinstance(inferred, scoped_nodes.FunctionDef):
                    yield inferred
                    continue

                # We can obtain different descriptors from a super depending
                # on what we are accessing and where the super call is.
                if inferred.type == "classmethod":
                    yield bases.BoundMethod(inferred, cls)
                elif self._scope.type == "classmethod" and inferred.type == "method":
                    yield inferred
                elif self._class_based or inferred.type == "staticmethod":
                    yield inferred
                elif isinstance(inferred, Property):
                    function = inferred.function
                    try:
                        yield from function.infer_call_result(
                            caller=self, context=context
                        )
                    except InferenceError:
                        yield util.Uninferable
                elif bases._is_property(inferred):
                    # TODO: support other descriptors as well.
                    try:
                        yield from inferred.infer_call_result(self, context)
                    except InferenceError:
                        yield util.Uninferable
                else:
                    yield bases.BoundMethod(inferred, cls)

        # Only if we haven't found any explicit overwrites for the
        # attribute we look it up in the special attributes
        if not found and name in self.special_attributes:
            yield self.special_attributes.lookup(name)
            return

        if not found:
            raise AttributeInferenceError(target=self, attribute=name, context=context)

    def getattr(self, name, context: InferenceContext | None = None):
        return list(self.igetattr(name, context=context))


class ExceptionInstance(bases.Instance):
    """Class for instances of exceptions.

    It has special treatment for some of the exceptions's attributes,
    which are transformed at runtime into certain concrete objects, such as
    the case of .args.
    """

    @cached_property
    def special_attributes(self):
        qname = self.qname()
        instance = objectmodel.BUILTIN_EXCEPTIONS.get(
            qname, objectmodel.ExceptionInstanceModel
        )
        return instance()(self)


class DictInstance(bases.Instance):
    """Special kind of instances for dictionaries.

    This instance knows the underlying object model of the dictionaries, which means
    that methods such as .values or .items can be properly inferred.
    """

    special_attributes = objectmodel.DictModel()


# Custom objects tailored for dictionaries, which are used to
# disambiguate between the types of Python 2 dict's method returns
# and Python 3 (where they return set like objects).
class DictItems(bases.Proxy):
    __str__ = node_classes.NodeNG.__str__
    __repr__ = node_classes.NodeNG.__repr__


class DictKeys(bases.Proxy):
    __str__ = node_classes.NodeNG.__str__
    __repr__ = node_classes.NodeNG.__repr__


class DictValues(bases.Proxy):
    __str__ = node_classes.NodeNG.__str__
    __repr__ = node_classes.NodeNG.__repr__


class PartialFunction(scoped_nodes.FunctionDef):
    """A class representing partial function obtained via functools.partial."""

    @decorators.deprecate_arguments(doc="Use the postinit arg 'doc_node' instead")
    def __init__(
        self, call, name=None, doc=None, lineno=None, col_offset=None, parent=None
    ):
        # TODO: Pass end_lineno, end_col_offset and parent as well
        super().__init__(
            name,
            lineno=lineno,
            col_offset=col_offset,
            parent=node_classes.Unknown(),
            end_col_offset=0,
            end_lineno=0,
        )
        # Assigned directly to prevent triggering the DeprecationWarning.
        self._doc = doc
        # A typical FunctionDef automatically adds its name to the parent scope,
        # but a partial should not, so defer setting parent until after init
        self.parent = parent
        self.filled_args = call.positional_arguments[1:]
        self.filled_keywords = call.keyword_arguments

        wrapped_function = call.positional_arguments[0]
        inferred_wrapped_function = next(wrapped_function.infer())
        if isinstance(inferred_wrapped_function, PartialFunction):
            self.filled_args = inferred_wrapped_function.filled_args + self.filled_args
            self.filled_keywords = {
                **inferred_wrapped_function.filled_keywords,
                **self.filled_keywords,
            }

        self.filled_positionals = len(self.filled_args)

    def infer_call_result(
        self,
        caller: SuccessfulInferenceResult | None,
        context: InferenceContext | None = None,
    ) -> Iterator[InferenceResult]:
        if context:
            assert (
                context.callcontext
            ), "CallContext should be set before inferring call result"
            current_passed_keywords = {
                keyword for (keyword, _) in context.callcontext.keywords
            }
            for keyword, value in self.filled_keywords.items():
                if keyword not in current_passed_keywords:
                    context.callcontext.keywords.append((keyword, value))

            call_context_args = context.callcontext.args or []
            context.callcontext.args = self.filled_args + call_context_args

        return super().infer_call_result(caller=caller, context=context)

    def qname(self) -> str:
        return self.__class__.__name__


# TODO: Hack to solve the circular import problem between node_classes and objects
# This is not needed in 2.0, which has a cleaner design overall
node_classes.Dict.__bases__ = (node_classes.NodeNG, DictInstance)


class Property(scoped_nodes.FunctionDef):
    """Class representing a Python property."""

    @decorators.deprecate_arguments(doc="Use the postinit arg 'doc_node' instead")
    def __init__(
        self, function, name=None, doc=None, lineno=None, col_offset=None, parent=None
    ):
        self.function = function
        super().__init__(
            name,
            lineno=lineno,
            col_offset=col_offset,
            parent=parent,
            end_col_offset=function.end_col_offset,
            end_lineno=function.end_lineno,
        )
        # Assigned directly to prevent triggering the DeprecationWarning.
        self._doc = doc

    special_attributes = objectmodel.PropertyModel()
    type = "property"

    def pytype(self) -> Literal["builtins.property"]:
        return "builtins.property"

    def infer_call_result(
        self,
        caller: SuccessfulInferenceResult | None,
        context: InferenceContext | None = None,
    ) -> NoReturn:
        raise InferenceError("Properties are not callable")

    def _infer(
        self: _T, context: InferenceContext | None = None, **kwargs: Any
    ) -> Generator[_T, None, None]:
        yield self

back/venvmsg/Lib/site-packages/astroid/objects.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""This module contains a set of functions to handle python protocols for nodes
where it makes sense.
"""

from __future__ import annotations

import collections
import itertools
import operator as operator_mod
from collections.abc import Callable, Generator, Iterator, Sequence
from typing import TYPE_CHECKING, Any, TypeVar

from astroid import bases, decorators, nodes, util
from astroid.const import Context
from astroid.context import InferenceContext, copy_context
from astroid.exceptions import (
    AstroidIndexError,
    AstroidTypeError,
    AttributeInferenceError,
    InferenceError,
    NoDefault,
)
from astroid.nodes import node_classes
from astroid.typing import (
    ConstFactoryResult,
    InferenceResult,
    SuccessfulInferenceResult,
)

if TYPE_CHECKING:
    _TupleListNodeT = TypeVar("_TupleListNodeT", nodes.Tuple, nodes.List)

_CONTEXTLIB_MGR = "contextlib.contextmanager"

_UNARY_OPERATORS: dict[str, Callable[[Any], Any]] = {
    "+": operator_mod.pos,
    "-": operator_mod.neg,
    "~": operator_mod.invert,
    "not": operator_mod.not_,
}


def _infer_unary_op(obj: Any, op: str) -> ConstFactoryResult:
    """Perform unary operation on `obj`, unless it is `NotImplemented`.

    Can raise TypeError if operation is unsupported.
    """
    if obj is NotImplemented:
        value = obj
    else:
        func = _UNARY_OPERATORS[op]
        value = func(obj)
    return nodes.const_factory(value)


def tuple_infer_unary_op(self, op):
    return _infer_unary_op(tuple(self.elts), op)


def list_infer_unary_op(self, op):
    return _infer_unary_op(self.elts, op)


def set_infer_unary_op(self, op):
    return _infer_unary_op(set(self.elts), op)


def const_infer_unary_op(self, op):
    return _infer_unary_op(self.value, op)


def dict_infer_unary_op(self, op):
    return _infer_unary_op(dict(self.items), op)


# Binary operations

BIN_OP_IMPL = {
    "+": lambda a, b: a + b,
    "-": lambda a, b: a - b,
    "/": lambda a, b: a / b,
    "//": lambda a, b: a // b,
    "*": lambda a, b: a * b,
    "**": lambda a, b: a**b,
    "%": lambda a, b: a % b,
    "&": lambda a, b: a & b,
    "|": lambda a, b: a | b,
    "^": lambda a, b: a ^ b,
    "<<": lambda a, b: a << b,
    ">>": lambda a, b: a >> b,
    "@": operator_mod.matmul,
}
for _KEY, _IMPL in list(BIN_OP_IMPL.items()):
    BIN_OP_IMPL[_KEY + "="] = _IMPL


@decorators.yes_if_nothing_inferred
def const_infer_binary_op(
    self: nodes.Const,
    opnode: nodes.AugAssign | nodes.BinOp,
    operator: str,
    other: InferenceResult,
    context: InferenceContext,
    _: SuccessfulInferenceResult,
) -> Generator[ConstFactoryResult | util.UninferableBase, None, None]:
    not_implemented = nodes.Const(NotImplemented)
    if isinstance(other, nodes.Const):
        if (
            operator == "**"
            and isinstance(self.value, (int, float))
            and isinstance(other.value, (int, float))
            and (self.value > 1e5 or other.value > 1e5)
        ):
            yield not_implemented
            return
        try:
            impl = BIN_OP_IMPL[operator]
            try:
                yield nodes.const_factory(impl(self.value, other.value))
            except TypeError:
                # ArithmeticError is not enough: float >> float is a TypeError
                yield not_implemented
            except Exception:  # pylint: disable=broad-except
                yield util.Uninferable
        except TypeError:
            yield not_implemented
    elif isinstance(self.value, str) and operator == "%":
        # TODO(cpopa): implement string interpolation later on.
        yield util.Uninferable
    else:
        yield not_implemented


def _multiply_seq_by_int(
    self: _TupleListNodeT,
    opnode: nodes.AugAssign | nodes.BinOp,
    value: int,
    context: InferenceContext,
) -> _TupleListNodeT:
    node = self.__class__(parent=opnode)
    if value > 1e8:
        node.elts = [util.Uninferable]
        return node
    filtered_elts = (
        util.safe_infer(elt, context) or util.Uninferable
        for elt in self.elts
        if not isinstance(elt, util.UninferableBase)
    )
    node.elts = list(filtered_elts) * value
    return node


def _filter_uninferable_nodes(
    elts: Sequence[InferenceResult], context: InferenceContext
) -> Iterator[SuccessfulInferenceResult]:
    for elt in elts:
        if isinstance(elt, util.UninferableBase):
            yield nodes.Unknown()
        else:
            for inferred in elt.infer(context):
                if not isinstance(inferred, util.UninferableBase):
                    yield inferred
                else:
                    yield nodes.Unknown()


@decorators.yes_if_nothing_inferred
def tl_infer_binary_op(
    self: _TupleListNodeT,
    opnode: nodes.AugAssign | nodes.BinOp,
    operator: str,
    other: InferenceResult,
    context: InferenceContext,
    method: SuccessfulInferenceResult,
) -> 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
    or list. This refers to the left-hand side of the operation, so:
    'tuple() + 1' or '[] + A()'
    """
    from astroid import helpers  # pylint: disable=import-outside-toplevel

    # For tuples and list the boundnode is no longer the tuple or list instance
    context.boundnode = None
    not_implemented = nodes.Const(NotImplemented)
    if isinstance(other, self.__class__) and operator == "+":
        node = self.__class__(parent=opnode)
        node.elts = list(
            itertools.chain(
                _filter_uninferable_nodes(self.elts, context),
                _filter_uninferable_nodes(other.elts, context),
            )
        )
        yield node
    elif isinstance(other, nodes.Const) and operator == "*":
        if not isinstance(other.value, int):
            yield not_implemented
            return
        yield _multiply_seq_by_int(self, opnode, other.value, context)
    elif isinstance(other, bases.Instance) and operator == "*":
        # Verify if the instance supports __index__.
        as_index = helpers.class_instance_as_index(other)
        if not as_index:
            yield util.Uninferable
        elif not isinstance(as_index.value, int):  # pragma: no cover
            # already checked by class_instance_as_index() but faster than casting
            raise AssertionError("Please open a bug report.")
        else:
            yield _multiply_seq_by_int(self, opnode, as_index.value, context)
    else:
        yield not_implemented


@decorators.yes_if_nothing_inferred
def instance_class_infer_binary_op(
    self: nodes.ClassDef,
    opnode: nodes.AugAssign | nodes.BinOp,
    operator: str,
    other: InferenceResult,
    context: InferenceContext,
    method: SuccessfulInferenceResult,
) -> Generator[InferenceResult, None, None]:
    return method.infer_call_result(self, context)


# assignment ##################################################################
# pylint: disable-next=pointless-string-statement
"""The assigned_stmts method is responsible to return the assigned statement
(e.g. not inferred) according to the assignment type.

The `assign_path` argument is used to record the lhs path of the original node.
For instance if we want assigned statements for 'c' in 'a, (b,c)', assign_path
will be [1, 1] once arrived to the Assign node.

The `context` argument is the current inference context which should be given
to any intermediary inference necessary.
"""


def _resolve_looppart(parts, assign_path, context):
    """Recursive function to resolve multiple assignments on loops."""
    assign_path = assign_path[:]
    index = assign_path.pop(0)
    for part in parts:
        if isinstance(part, util.UninferableBase):
            continue
        if not hasattr(part, "itered"):
            continue
        try:
            itered = part.itered()
        except TypeError:
            continue
        try:
            if isinstance(itered[index], (nodes.Const, nodes.Name)):
                itered = [part]
        except IndexError:
            pass
        for stmt in itered:
            index_node = nodes.Const(index)
            try:
                assigned = stmt.getitem(index_node, context)
            except (AttributeError, AstroidTypeError, AstroidIndexError):
                continue
            if not assign_path:
                # we achieved to resolved the assignment path,
                # don't infer the last part
                yield assigned
            elif isinstance(assigned, util.UninferableBase):
                break
            else:
                # we are not yet on the last part of the path
                # search on each possibly inferred value
                try:
                    yield from _resolve_looppart(
                        assigned.infer(context), assign_path, context
                    )
                except InferenceError:
                    break


@decorators.raise_if_nothing_inferred
def for_assigned_stmts(
    self: nodes.For | nodes.Comprehension,
    node: node_classes.AssignedStmtsPossibleNode = None,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    if isinstance(self, nodes.AsyncFor) or getattr(self, "is_async", False):
        # Skip inferring of async code for now
        return {
            "node": self,
            "unknown": node,
            "assign_path": assign_path,
            "context": context,
        }
    if assign_path is None:
        for lst in self.iter.infer(context):
            if isinstance(lst, (nodes.Tuple, nodes.List)):
                yield from lst.elts
    else:
        yield from _resolve_looppart(self.iter.infer(context), assign_path, context)
    return {
        "node": self,
        "unknown": node,
        "assign_path": assign_path,
        "context": context,
    }


def sequence_assigned_stmts(
    self: nodes.Tuple | nodes.List,
    node: node_classes.AssignedStmtsPossibleNode = None,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    if assign_path is None:
        assign_path = []
    try:
        index = self.elts.index(node)  # type: ignore[arg-type]
    except ValueError as exc:
        raise InferenceError(
            "Tried to retrieve a node {node!r} which does not exist",
            node=self,
            assign_path=assign_path,
            context=context,
        ) from exc

    assign_path.insert(0, index)
    return self.parent.assigned_stmts(
        node=self, context=context, assign_path=assign_path
    )


def assend_assigned_stmts(
    self: nodes.AssignName | nodes.AssignAttr,
    node: node_classes.AssignedStmtsPossibleNode = None,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    return self.parent.assigned_stmts(node=self, context=context)


def _arguments_infer_argname(
    self, name: str | None, context: InferenceContext
) -> Generator[InferenceResult, None, None]:
    # arguments information may be missing, in which case we can't do anything
    # more
    from astroid import arguments  # pylint: disable=import-outside-toplevel

    if not self.arguments:
        yield util.Uninferable
        return

    args = [arg for arg in self.arguments if arg.name not in [self.vararg, self.kwarg]]
    functype = self.parent.type
    # first argument of instance/class method
    if (
        args
        and getattr(self.arguments[0], "name", None) == name
        and functype != "staticmethod"
    ):
        cls = self.parent.parent.scope()
        is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == "metaclass"
        # If this is a metaclass, then the first argument will always
        # be the class, not an instance.
        if context.boundnode and isinstance(context.boundnode, bases.Instance):
            cls = context.boundnode._proxied
        if is_metaclass or functype == "classmethod":
            yield cls
            return
        if functype == "method":
            yield cls.instantiate_class()
            return

    if context and context.callcontext:
        callee = context.callcontext.callee
        while hasattr(callee, "_proxied"):
            callee = callee._proxied
        if getattr(callee, "name", None) == self.parent.name:
            call_site = arguments.CallSite(context.callcontext, context.extra_context)
            yield from call_site.infer_argument(self.parent, name, context)
            return

    if name == self.vararg:
        vararg = nodes.const_factory(())
        vararg.parent = self
        if not args and self.parent.name == "__init__":
            cls = self.parent.parent.scope()
            vararg.elts = [cls.instantiate_class()]
        yield vararg
        return
    if name == self.kwarg:
        kwarg = nodes.const_factory({})
        kwarg.parent = self
        yield kwarg
        return
    # if there is a default value, yield it. And then yield Uninferable to reflect
    # we can't guess given argument value
    try:
        context = copy_context(context)
        yield from self.default_value(name).infer(context)
        yield util.Uninferable
    except NoDefault:
        yield util.Uninferable


def arguments_assigned_stmts(
    self: nodes.Arguments,
    node: node_classes.AssignedStmtsPossibleNode = None,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    from astroid import arguments  # pylint: disable=import-outside-toplevel

    try:
        node_name = node.name  # type: ignore[union-attr]
    except AttributeError:
        # Added to handle edge cases where node.name is not defined.
        # https://github.com/pylint-dev/astroid/pull/1644#discussion_r901545816
        node_name = None  # pragma: no cover

    if context and context.callcontext:
        callee = context.callcontext.callee
        while hasattr(callee, "_proxied"):
            callee = callee._proxied
    else:
        return _arguments_infer_argname(self, node_name, context)
    if node and getattr(callee, "name", None) == node.frame().name:
        # reset call context/name
        callcontext = context.callcontext
        context = copy_context(context)
        context.callcontext = None
        args = arguments.CallSite(callcontext, context=context)
        return args.infer_argument(self.parent, node_name, context)
    return _arguments_infer_argname(self, node_name, context)


@decorators.raise_if_nothing_inferred
def assign_assigned_stmts(
    self: nodes.AugAssign | nodes.Assign | nodes.AnnAssign | nodes.TypeAlias,
    node: node_classes.AssignedStmtsPossibleNode = None,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    if not assign_path:
        yield self.value
        return None
    yield from _resolve_assignment_parts(
        self.value.infer(context), assign_path, context
    )

    return {
        "node": self,
        "unknown": node,
        "assign_path": assign_path,
        "context": context,
    }


def assign_annassigned_stmts(
    self: nodes.AnnAssign,
    node: node_classes.AssignedStmtsPossibleNode = None,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    for inferred in assign_assigned_stmts(self, node, context, assign_path):
        if inferred is None:
            yield util.Uninferable
        else:
            yield inferred


def _resolve_assignment_parts(parts, assign_path, context):
    """Recursive function to resolve multiple assignments."""
    assign_path = assign_path[:]
    index = assign_path.pop(0)
    for part in parts:
        assigned = None
        if isinstance(part, nodes.Dict):
            # A dictionary in an iterating context
            try:
                assigned, _ = part.items[index]
            except IndexError:
                return

        elif hasattr(part, "getitem"):
            index_node = nodes.Const(index)
            try:
                assigned = part.getitem(index_node, context)
            except (AstroidTypeError, AstroidIndexError):
                return

        if not assigned:
            return

        if not assign_path:
            # we achieved to resolved the assignment path, don't infer the
            # last part
            yield assigned
        elif isinstance(assigned, util.UninferableBase):
            return
        else:
            # we are not yet on the last part of the path search on each
            # possibly inferred value
            try:
                yield from _resolve_assignment_parts(
                    assigned.infer(context), assign_path, context
                )
            except InferenceError:
                return


@decorators.raise_if_nothing_inferred
def excepthandler_assigned_stmts(
    self: nodes.ExceptHandler,
    node: node_classes.AssignedStmtsPossibleNode = None,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    from astroid import objects  # pylint: disable=import-outside-toplevel

    for assigned in node_classes.unpack_infer(self.type):
        if isinstance(assigned, nodes.ClassDef):
            assigned = objects.ExceptionInstance(assigned)

        yield assigned
    return {
        "node": self,
        "unknown": node,
        "assign_path": assign_path,
        "context": context,
    }


def _infer_context_manager(self, mgr, context):
    try:
        inferred = next(mgr.infer(context=context))
    except StopIteration as e:
        raise InferenceError(node=mgr) from e
    if isinstance(inferred, bases.Generator):
        # Check if it is decorated with contextlib.contextmanager.
        func = inferred.parent
        if not func.decorators:
            raise InferenceError(
                "No decorators found on inferred generator %s", node=func
            )

        for decorator_node in func.decorators.nodes:
            decorator = next(decorator_node.infer(context=context), None)
            if isinstance(decorator, nodes.FunctionDef):
                if decorator.qname() == _CONTEXTLIB_MGR:
                    break
        else:
            # It doesn't interest us.
            raise InferenceError(node=func)
        try:
            yield next(inferred.infer_yield_types())
        except StopIteration as e:
            raise InferenceError(node=func) from e

    elif isinstance(inferred, bases.Instance):
        try:
            enter = next(inferred.igetattr("__enter__", context=context))
        except (InferenceError, AttributeInferenceError, StopIteration) as exc:
            raise InferenceError(node=inferred) from exc
        if not isinstance(enter, bases.BoundMethod):
            raise InferenceError(node=enter)
        yield from enter.infer_call_result(self, context)
    else:
        raise InferenceError(node=mgr)


@decorators.raise_if_nothing_inferred
def with_assigned_stmts(
    self: nodes.With,
    node: node_classes.AssignedStmtsPossibleNode = None,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    """Infer names and other nodes from a *with* statement.

    This enables only inference for name binding in a *with* statement.
    For instance, in the following code, inferring `func` will return
    the `ContextManager` class, not whatever ``__enter__`` returns.
    We are doing this intentionally, because we consider that the context
    manager result is whatever __enter__ returns and what it is binded
    using the ``as`` keyword.

        class ContextManager(object):
            def __enter__(self):
                return 42
        with ContextManager() as f:
            pass

        # ContextManager().infer() will return ContextManager
        # f.infer() will return 42.

    Arguments:
        self: nodes.With
        node: The target of the assignment, `as (a, b)` in `with foo as (a, b)`.
        context: Inference context used for caching already inferred objects
        assign_path:
            A list of indices, where each index specifies what item to fetch from
            the inference results.
    """
    try:
        mgr = next(mgr for (mgr, vars) in self.items if vars == node)
    except StopIteration:
        return None
    if assign_path is None:
        yield from _infer_context_manager(self, mgr, context)
    else:
        for result in _infer_context_manager(self, mgr, context):
            # Walk the assign_path and get the item at the final index.
            obj = result
            for index in assign_path:
                if not hasattr(obj, "elts"):
                    raise InferenceError(
                        "Wrong type ({targets!r}) for {node!r} assignment",
                        node=self,
                        targets=node,
                        assign_path=assign_path,
                        context=context,
                    )
                try:
                    obj = obj.elts[index]
                except IndexError as exc:
                    raise InferenceError(
                        "Tried to infer a nonexistent target with index {index} "
                        "in {node!r}.",
                        node=self,
                        targets=node,
                        assign_path=assign_path,
                        context=context,
                    ) from exc
                except TypeError as exc:
                    raise InferenceError(
                        "Tried to unpack a non-iterable value in {node!r}.",
                        node=self,
                        targets=node,
                        assign_path=assign_path,
                        context=context,
                    ) from exc
            yield obj
    return {
        "node": self,
        "unknown": node,
        "assign_path": assign_path,
        "context": context,
    }


@decorators.raise_if_nothing_inferred
def named_expr_assigned_stmts(
    self: nodes.NamedExpr,
    node: node_classes.AssignedStmtsPossibleNode,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    """Infer names and other nodes from an assignment expression."""
    if self.target == node:
        yield from self.value.infer(context=context)
    else:
        raise InferenceError(
            "Cannot infer NamedExpr node {node!r}",
            node=self,
            assign_path=assign_path,
            context=context,
        )


@decorators.yes_if_nothing_inferred
def starred_assigned_stmts(  # noqa: C901
    self: nodes.Starred,
    node: node_classes.AssignedStmtsPossibleNode = None,
    context: InferenceContext | None = None,
    assign_path: list[int] | None = None,
) -> Any:
    """
    Arguments:
        self: nodes.Starred
        node: a node related to the current underlying Node.
        context: Inference context used for caching already inferred objects
        assign_path:
            A list of indices, where each index specifies what item to fetch from
            the inference results.
    """

    # pylint: disable=too-many-locals,too-many-statements
    def _determine_starred_iteration_lookups(
        starred: nodes.Starred, target: nodes.Tuple, lookups: list[tuple[int, int]]
    ) -> None:
        # Determine the lookups for the rhs of the iteration
        itered = target.itered()
        for index, element in enumerate(itered):
            if (
                isinstance(element, nodes.Starred)
                and element.value.name == starred.value.name
            ):
                lookups.append((index, len(itered)))
                break
            if isinstance(element, nodes.Tuple):
                lookups.append((index, len(element.itered())))
                _determine_starred_iteration_lookups(starred, element, lookups)

    stmt = self.statement()
    if not isinstance(stmt, (nodes.Assign, nodes.For)):
        raise InferenceError(
            "Statement {stmt!r} enclosing {node!r} must be an Assign or For node.",
            node=self,
            stmt=stmt,
            unknown=node,
            context=context,
        )

    if context is None:
        context = InferenceContext()

    if isinstance(stmt, nodes.Assign):
        value = stmt.value
        lhs = stmt.targets[0]
        if not isinstance(lhs, nodes.BaseContainer):
            yield util.Uninferable
            return

        if sum(1 for _ in lhs.nodes_of_class(nodes.Starred)) > 1:
            raise InferenceError(
                "Too many starred arguments in the assignment targets {lhs!r}.",
                node=self,
                targets=lhs,
                unknown=node,
                context=context,
            )

        try:
            rhs = next(value.infer(context))
        except (InferenceError, StopIteration):
            yield util.Uninferable
            return
        if isinstance(rhs, util.UninferableBase) or not hasattr(rhs, "itered"):
            yield util.Uninferable
            return

        try:
            elts = collections.deque(rhs.itered())  # type: ignore[union-attr]
        except TypeError:
            yield util.Uninferable
            return

        # Unpack iteratively the values from the rhs of the assignment,
        # until the find the starred node. What will remain will
        # be the list of values which the Starred node will represent
        # This is done in two steps, from left to right to remove
        # anything before the starred node and from right to left
        # to remove anything after the starred node.

        for index, left_node in enumerate(lhs.elts):
            if not isinstance(left_node, nodes.Starred):
                if not elts:
                    break
                elts.popleft()
                continue
            lhs_elts = collections.deque(reversed(lhs.elts[index:]))
            for right_node in lhs_elts:
                if not isinstance(right_node, nodes.Starred):
                    if not elts:
                        break
                    elts.pop()
                    continue

                # We're done unpacking.
                packed = nodes.List(
                    ctx=Context.Store,
                    parent=self,
                    lineno=lhs.lineno,
                    col_offset=lhs.col_offset,
                )
                packed.postinit(elts=list(elts))
                yield packed
                break

    if isinstance(stmt, nodes.For):
        try:
            inferred_iterable = next(stmt.iter.infer(context=context))
        except (InferenceError, StopIteration):
            yield util.Uninferable
            return
        if isinstance(inferred_iterable, util.UninferableBase) or not hasattr(
            inferred_iterable, "itered"
        ):
            yield util.Uninferable
            return
        try:
            itered = inferred_iterable.itered()  # type: ignore[union-attr]
        except TypeError:
            yield util.Uninferable
            return

        target = stmt.target

        if not isinstance(target, nodes.Tuple):
            raise InferenceError(
                "Could not make sense of this, the target must be a tuple",
                context=context,
            )

        lookups: list[tuple[int, int]] = []
        _determine_starred_iteration_lookups(self, target, lookups)
        if not lookups:
            raise InferenceError(
                "Could not make sense of this, needs at least a lookup", context=context
            )

        # Make the last lookup a slice, since that what we want for a Starred node
        last_element_index, last_element_length = lookups[-1]
        is_starred_last = last_element_index == (last_element_length - 1)

        lookup_slice = slice(
            last_element_index,
            None if is_starred_last else (last_element_length - last_element_index),
        )
        last_lookup = lookup_slice

        for element in itered:
            # We probably want to infer the potential values *for each* element in an
            # iterable, but we can't infer a list of all values, when only a list of
            # step values are expected:
            #
            # for a, *b in [...]:
            #   b
            #
            # *b* should now point to just the elements at that particular iteration step,
            # which astroid can't know about.

            found_element = None
            for index, lookup in enumerate(lookups):
                if not hasattr(element, "itered"):
                    break
                if index + 1 is len(lookups):
                    cur_lookup: slice | int = last_lookup
                else:
                    # Grab just the index, not the whole length
                    cur_lookup = lookup[0]
                try:
                    itered_inner_element = element.itered()
                    element = itered_inner_element[cur_lookup]
                except IndexError:
                    break
                except TypeError:
                    # Most likely the itered() call failed, cannot make sense of this
                    yield util.Uninferable
                    return
                else:
                    found_element = element

            unpacked = nodes.List(
                ctx=Context.Store,
                parent=self,
                lineno=self.lineno,
                col_offset=self.col_offset,
            )
            unpacked.postinit(elts=found_element or [])
            yield unpacked
            return

        yield util.Uninferable


@decorators.yes_if_nothing_inferred
def match_mapping_assigned_stmts(
    self: nodes.MatchMapping,
    node: nodes.AssignName,
    context: InferenceContext | None = None,
    assign_path: None = None,
) -> Generator[nodes.NodeNG, None, None]:
    """Return empty generator (return -> raises StopIteration) so inferred value
    is Uninferable.
    """
    return
    yield


@decorators.yes_if_nothing_inferred
def match_star_assigned_stmts(
    self: nodes.MatchStar,
    node: nodes.AssignName,
    context: InferenceContext | None = None,
    assign_path: None = None,
) -> Generator[nodes.NodeNG, None, None]:
    """Return empty generator (return -> raises StopIteration) so inferred value
    is Uninferable.
    """
    return
    yield


@decorators.yes_if_nothing_inferred
def match_as_assigned_stmts(
    self: nodes.MatchAs,
    node: nodes.AssignName,
    context: InferenceContext | None = None,
    assign_path: None = None,
) -> Generator[nodes.NodeNG, None, None]:
    """Infer MatchAs as the Match subject if it's the only MatchCase pattern
    else raise StopIteration to yield Uninferable.
    """
    if (
        isinstance(self.parent, nodes.MatchCase)
        and isinstance(self.parent.parent, nodes.Match)
        and self.pattern is None
    ):
        yield self.parent.parent.subject

back/venvmsg/Lib/site-packages/astroid/protocols.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""this module contains a set of functions to create astroid trees from scratch
(build_* functions) or from living object (object_build_* functions)
"""

from __future__ import annotations

import builtins
import inspect
import io
import logging
import os
import sys
import types
import warnings
from collections.abc import Iterable
from contextlib import redirect_stderr, redirect_stdout
from typing import Any, Union

from astroid import bases, nodes
from astroid.const import _EMPTY_OBJECT_MARKER, IS_PYPY
from astroid.manager import AstroidManager
from astroid.nodes import node_classes

logger = logging.getLogger(__name__)


_FunctionTypes = Union[
    types.FunctionType,
    types.MethodType,
    types.BuiltinFunctionType,
    types.WrapperDescriptorType,
    types.MethodDescriptorType,
    types.ClassMethodDescriptorType,
]

# the keys of CONST_CLS eg python builtin types
_CONSTANTS = tuple(node_classes.CONST_CLS)
TYPE_NONE = type(None)
TYPE_NOTIMPLEMENTED = type(NotImplemented)
TYPE_ELLIPSIS = type(...)


def _attach_local_node(parent, node, name: str) -> None:
    node.name = name  # needed by add_local_node
    parent.add_local_node(node)


def _add_dunder_class(func, member) -> None:
    """Add a __class__ member to the given func node, if we can determine it."""
    python_cls = member.__class__
    cls_name = getattr(python_cls, "__name__", None)
    if not cls_name:
        return
    cls_bases = [ancestor.__name__ for ancestor in python_cls.__bases__]
    ast_klass = build_class(cls_name, cls_bases, python_cls.__doc__)
    func.instance_attrs["__class__"] = [ast_klass]


def attach_dummy_node(node, name: str, runtime_object=_EMPTY_OBJECT_MARKER) -> None:
    """create a dummy node and register it in the locals of the given
    node with the specified name
    """
    enode = nodes.EmptyNode()
    enode.object = runtime_object
    _attach_local_node(node, enode, name)


def attach_const_node(node, name: str, value) -> None:
    """create a Const node and register it in the locals of the given
    node with the specified name
    """
    if name not in node.special_attributes:
        _attach_local_node(node, nodes.const_factory(value), name)


def attach_import_node(node, modname: str, membername: str) -> None:
    """create a ImportFrom node and register it in the locals of the given
    node with the specified name
    """
    from_node = nodes.ImportFrom(modname, [(membername, None)])
    _attach_local_node(node, from_node, membername)


def build_module(name: str, doc: str | None = None) -> nodes.Module:
    """create and initialize an astroid Module node"""
    node = nodes.Module(name, pure_python=False, package=False)
    node.postinit(
        body=[],
        doc_node=nodes.Const(value=doc) if doc else None,
    )
    return node


def build_class(
    name: str, basenames: Iterable[str] = (), doc: str | None = None
) -> nodes.ClassDef:
    """Create and initialize an astroid ClassDef node."""
    node = nodes.ClassDef(
        name,
        lineno=0,
        col_offset=0,
        end_lineno=0,
        end_col_offset=0,
        parent=nodes.Unknown(),
    )
    node.postinit(
        bases=[
            nodes.Name(
                name=base,
                lineno=0,
                col_offset=0,
                parent=node,
                end_lineno=None,
                end_col_offset=None,
            )
            for base in basenames
        ],
        body=[],
        decorators=None,
        doc_node=nodes.Const(value=doc) if doc else None,
    )
    return node


def build_function(
    name: str,
    args: list[str] | None = None,
    posonlyargs: list[str] | None = None,
    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
    func = nodes.FunctionDef(
        name,
        lineno=0,
        col_offset=0,
        parent=node_classes.Unknown(),
        end_col_offset=0,
        end_lineno=0,
    )
    argsnode = nodes.Arguments(parent=func, vararg=None, kwarg=None)

    # If args is None we don't have any information about the signature
    # (in contrast to when there are no arguments and args == []). We pass
    # this to the builder to indicate this.
    if args is not None:
        # We set the lineno and col_offset to 0 because we don't have any
        # information about the location of the function definition.
        arguments = [
            nodes.AssignName(
                name=arg,
                parent=argsnode,
                lineno=0,
                col_offset=0,
                end_lineno=None,
                end_col_offset=None,
            )
            for arg in args
        ]
    else:
        arguments = None

    default_nodes: list[nodes.NodeNG] | None
    if defaults is None:
        default_nodes = None
    else:
        default_nodes = []
        for default in defaults:
            default_node = nodes.const_factory(default)
            default_node.parent = argsnode
            default_nodes.append(default_node)

    kwonlydefault_nodes: list[nodes.NodeNG | None] | None
    if kwonlydefaults is None:
        kwonlydefault_nodes = None
    else:
        kwonlydefault_nodes = []
        for kwonlydefault in kwonlydefaults:
            kwonlydefault_node = nodes.const_factory(kwonlydefault)
            kwonlydefault_node.parent = argsnode
            kwonlydefault_nodes.append(kwonlydefault_node)

    # We set the lineno and col_offset to 0 because we don't have any
    # information about the location of the kwonly and posonlyargs.
    argsnode.postinit(
        args=arguments,
        defaults=default_nodes,
        kwonlyargs=[
            nodes.AssignName(
                name=arg,
                parent=argsnode,
                lineno=0,
                col_offset=0,
                end_lineno=None,
                end_col_offset=None,
            )
            for arg in kwonlyargs or ()
        ],
        kw_defaults=kwonlydefault_nodes,
        annotations=[],
        posonlyargs=[
            nodes.AssignName(
                name=arg,
                parent=argsnode,
                lineno=0,
                col_offset=0,
                end_lineno=None,
                end_col_offset=None,
            )
            for arg in posonlyargs or ()
        ],
        kwonlyargs_annotations=[],
        posonlyargs_annotations=[],
    )
    func.postinit(
        args=argsnode,
        body=[],
        doc_node=nodes.Const(value=doc) if doc else None,
    )
    if args:
        register_arguments(func)
    return func


def build_from_import(fromname: str, names: list[str]) -> nodes.ImportFrom:
    """create and initialize an astroid ImportFrom import statement"""
    return nodes.ImportFrom(fromname, [(name, None) for name in names])


def register_arguments(func: nodes.FunctionDef, args: list | None = None) -> None:
    """add given arguments to local

    args is a list that may contains nested lists
    (i.e. def func(a, (b, c, d)): ...)
    """
    # If no args are passed in, get the args from the function.
    if args is None:
        if func.args.vararg:
            func.set_local(func.args.vararg, func.args)
        if func.args.kwarg:
            func.set_local(func.args.kwarg, func.args)
        args = func.args.args
        # If the function has no args, there is nothing left to do.
        if args is None:
            return
    for arg in args:
        if isinstance(arg, nodes.AssignName):
            func.set_local(arg.name, arg)
        else:
            register_arguments(func, arg.elts)


def object_build_class(
    node: nodes.Module | nodes.ClassDef, member: type, localname: str
) -> nodes.ClassDef:
    """create astroid for a living class object"""
    basenames = [base.__name__ for base in member.__bases__]
    return _base_class_object_build(node, member, basenames, localname=localname)


def _get_args_info_from_callable(
    member: _FunctionTypes,
) -> tuple[list[str], list[str], list[Any], list[str], list[Any]]:
    """Returns args, posonlyargs, defaults, kwonlyargs.

    :note: currently ignores the return annotation.
    """
    signature = inspect.signature(member)
    args: list[str] = []
    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:
            posonlyargs.append(param_name)
        elif param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
            args.append(param_name)
        elif param.kind == inspect.Parameter.VAR_POSITIONAL:
            args.append(param_name)
        elif param.kind == inspect.Parameter.VAR_KEYWORD:
            args.append(param_name)
        elif param.kind == inspect.Parameter.KEYWORD_ONLY:
            kwonlyargs.append(param_name)
            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, 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,
        kwonly_defaults,
    ) = _get_args_info_from_callable(member)

    func = build_function(
        getattr(member, "__name__", None) or localname,
        args,
        posonlyargs,
        defaults,
        member.__doc__,
        kwonlyargs=kwonlyargs,
        kwonlydefaults=kwonly_defaults,
    )

    node.add_local_node(func, localname)


def object_build_datadescriptor(
    node: nodes.Module | nodes.ClassDef, member: type, name: str
) -> nodes.ClassDef:
    """create astroid for a living data descriptor object"""
    return _base_class_object_build(node, member, [], name)


def object_build_methoddescriptor(
    node: nodes.Module | nodes.ClassDef,
    member: _FunctionTypes,
    localname: str,
) -> None:
    """create astroid for a living method descriptor object"""
    # FIXME get arguments ?
    func = build_function(
        getattr(member, "__name__", None) or localname, doc=member.__doc__
    )
    node.add_local_node(func, localname)
    _add_dunder_class(func, member)


def _base_class_object_build(
    node: nodes.Module | nodes.ClassDef,
    member: type,
    basenames: list[str],
    name: str | None = None,
    localname: str | None = None,
) -> nodes.ClassDef:
    """create astroid for a living class object, with a given set of base names
    (e.g. ancestors)
    """
    class_name = name or getattr(member, "__name__", None) or localname
    assert isinstance(class_name, str)
    klass = build_class(
        class_name,
        basenames,
        member.__doc__,
    )
    klass._newstyle = isinstance(member, type)
    node.add_local_node(klass, localname)
    try:
        # limit the instantiation trick since it's too dangerous
        # (such as infinite test execution...)
        # this at least resolves common case such as Exception.args,
        # OSError.errno
        if issubclass(member, Exception):
            instdict = member().__dict__
        else:
            raise TypeError
    except TypeError:
        pass
    else:
        for item_name, obj in instdict.items():
            valnode = nodes.EmptyNode()
            valnode.object = obj
            valnode.parent = klass
            valnode.lineno = 1
            klass.instance_attrs[item_name] = [valnode]
    return klass


def _build_from_function(
    node: nodes.Module | nodes.ClassDef,
    name: str,
    member: _FunctionTypes,
    module: types.ModuleType,
) -> None:
    # verify this is not an imported function
    try:
        code = member.__code__  # type: ignore[union-attr]
    except AttributeError:
        # Some implementations don't provide the code object,
        # such as Jython.
        code = None
    filename = getattr(code, "co_filename", None)
    if filename is None:
        assert isinstance(member, object)
        object_build_methoddescriptor(node, member, name)
    elif filename != getattr(module, "__file__", None):
        attach_dummy_node(node, name, member)
    else:
        object_build_function(node, member, name)


def _safe_has_attribute(obj, member: str) -> bool:
    """Required because unexpected RunTimeError can be raised.

    See https://github.com/pylint-dev/astroid/issues/1958
    """
    try:
        return hasattr(obj, member)
    except Exception:  # pylint: disable=broad-except
        return False


class InspectBuilder:
    """class for building nodes from living object

    this is actually a really minimal representation, including only Module,
    FunctionDef and ClassDef nodes and some others as guessed.
    """

    bootstrapped: bool = False

    def __init__(self, manager_instance: AstroidManager | None = None) -> None:
        self._manager = manager_instance or AstroidManager()
        self._done: dict[types.ModuleType | type, nodes.Module | nodes.ClassDef] = {}
        self._module: types.ModuleType

    def inspect_build(
        self,
        module: types.ModuleType,
        modname: str | None = None,
        path: str | None = None,
    ) -> nodes.Module:
        """build astroid from a living module (i.e. using inspect)
        this is used when there is no python source code available (either
        because it's a built-in module or because the .py is not available)
        """
        self._module = module
        if modname is None:
            modname = module.__name__
        try:
            node = build_module(modname, module.__doc__)
        except AttributeError:
            # in jython, java modules have no __doc__ (see #109562)
            node = build_module(modname)
        if path is None:
            node.path = node.file = path
        else:
            node.path = [os.path.abspath(path)]
            node.file = node.path[0]
        node.name = modname
        self._manager.cache_module(node)
        node.package = hasattr(module, "__path__")
        self._done = {}
        self.object_build(node, module)
        return node

    def object_build(
        self, node: nodes.Module | nodes.ClassDef, obj: types.ModuleType | type
    ) -> None:
        """recursive method which create a partial ast from real objects
        (only function, class, and method are handled)
        """
        if obj in self._done:
            return None
        self._done[obj] = node
        for name in dir(obj):
            # inspect.ismethod() and inspect.isbuiltin() in PyPy return
            # the opposite of what they do in CPython for __class_getitem__.
            pypy__class_getitem__ = IS_PYPY and name == "__class_getitem__"
            try:
                with warnings.catch_warnings():
                    warnings.simplefilter("ignore")
                    member = getattr(obj, name)
            except AttributeError:
                # damned ExtensionClass.Base, I know you're there !
                attach_dummy_node(node, name)
                continue
            if inspect.ismethod(member) and not pypy__class_getitem__:
                member = member.__func__
            if inspect.isfunction(member):
                _build_from_function(node, name, member, self._module)
            elif inspect.isbuiltin(member) or pypy__class_getitem__:
                if self.imported_member(node, member, name):
                    continue
                object_build_methoddescriptor(node, member, name)
            elif inspect.isclass(member):
                if self.imported_member(node, member, name):
                    continue
                if member in self._done:
                    class_node = self._done[member]
                    assert isinstance(class_node, nodes.ClassDef)
                    if class_node not in node.locals.get(name, ()):
                        node.add_local_node(class_node, name)
                else:
                    class_node = object_build_class(node, member, name)
                    # recursion
                    self.object_build(class_node, member)
                if name == "__class__" and class_node.parent is None:
                    class_node.parent = self._done[self._module]
            elif inspect.ismethoddescriptor(member):
                object_build_methoddescriptor(node, member, name)
            elif inspect.isdatadescriptor(member):
                object_build_datadescriptor(node, member, name)
            elif isinstance(member, _CONSTANTS):
                attach_const_node(node, name, member)
            elif inspect.isroutine(member):
                # This should be called for Jython, where some builtin
                # methods aren't caught by isbuiltin branch.
                _build_from_function(node, name, member, self._module)
            elif _safe_has_attribute(member, "__all__"):
                module = build_module(name)
                _attach_local_node(node, module, name)
                # recursion
                self.object_build(module, member)
            else:
                # create an empty node so that the name is actually defined
                attach_dummy_node(node, name, member)
        return None

    def imported_member(self, node, member, name: str) -> bool:
        """verify this is not an imported class or handle it"""
        # /!\ some classes like ExtensionClass doesn't have a __module__
        # attribute ! Also, this may trigger an exception on badly built module
        # (see http://www.logilab.org/ticket/57299 for instance)
        try:
            modname = getattr(member, "__module__", None)
        except TypeError:
            modname = None
        if modname is None:
            if name in {"__new__", "__subclasshook__"}:
                # Python 2.5.1 (r251:54863, Sep  1 2010, 22:03:14)
                # >>> print object.__new__.__module__
                # None
                modname = builtins.__name__
            else:
                attach_dummy_node(node, name, member)
                return True

        # On PyPy during bootstrapping we infer _io while _module is
        # builtins. In CPython _io names itself io, see http://bugs.python.org/issue18602
        # Therefore, this basically checks whether we are not in PyPy.
        if modname == "_io" and not self._module.__name__ == "builtins":
            return False

        real_name = {"gtk": "gtk_gtk"}.get(modname, modname)

        if real_name != self._module.__name__:
            # check if it sounds valid and then add an import node, else use a
            # dummy node
            try:
                with redirect_stderr(io.StringIO()) as stderr, redirect_stdout(
                    io.StringIO()
                ) as stdout:
                    getattr(sys.modules[modname], name)
                    stderr_value = stderr.getvalue()
                    if stderr_value:
                        logger.error(
                            "Captured stderr while getting %s from %s:\n%s",
                            name,
                            sys.modules[modname],
                            stderr_value,
                        )
                    stdout_value = stdout.getvalue()
                    if stdout_value:
                        logger.info(
                            "Captured stdout while getting %s from %s:\n%s",
                            name,
                            sys.modules[modname],
                            stdout_value,
                        )
            except (KeyError, AttributeError):
                attach_dummy_node(node, name, member)
            else:
                attach_import_node(node, modname, name)
            return True
        return False


# astroid bootstrapping ######################################################

_CONST_PROXY: dict[type, nodes.ClassDef] = {}


def _set_proxied(const) -> nodes.ClassDef:
    # TODO : find a nicer way to handle this situation;
    return _CONST_PROXY[const.value.__class__]


def _astroid_bootstrapping() -> None:
    """astroid bootstrapping the builtins module"""
    # this boot strapping is necessary since we need the Const nodes to
    # inspect_build builtins, and then we can proxy Const
    builder = InspectBuilder()
    astroid_builtin = builder.inspect_build(builtins)

    for cls, node_cls in node_classes.CONST_CLS.items():
        if cls is TYPE_NONE:
            proxy = build_class("NoneType")
            proxy.parent = astroid_builtin
        elif cls is TYPE_NOTIMPLEMENTED:
            proxy = build_class("NotImplementedType")
            proxy.parent = astroid_builtin
        elif cls is TYPE_ELLIPSIS:
            proxy = build_class("Ellipsis")
            proxy.parent = astroid_builtin
        else:
            proxy = astroid_builtin.getattr(cls.__name__)[0]
            assert isinstance(proxy, nodes.ClassDef)
        if cls in (dict, list, set, tuple):
            node_cls._proxied = proxy
        else:
            _CONST_PROXY[cls] = proxy

    # Set the builtin module as parent for some builtins.
    nodes.Const._proxied = property(_set_proxied)

    _GeneratorType = nodes.ClassDef(
        types.GeneratorType.__name__,
        lineno=0,
        col_offset=0,
        end_lineno=0,
        end_col_offset=0,
        parent=nodes.Unknown(),
    )
    _GeneratorType.parent = astroid_builtin
    generator_doc_node = (
        nodes.Const(value=types.GeneratorType.__doc__)
        if types.GeneratorType.__doc__
        else None
    )
    _GeneratorType.postinit(
        bases=[],
        body=[],
        decorators=None,
        doc_node=generator_doc_node,
    )
    bases.Generator._proxied = _GeneratorType
    builder.object_build(bases.Generator._proxied, types.GeneratorType)

    if hasattr(types, "AsyncGeneratorType"):
        _AsyncGeneratorType = nodes.ClassDef(
            types.AsyncGeneratorType.__name__,
            lineno=0,
            col_offset=0,
            end_lineno=0,
            end_col_offset=0,
            parent=nodes.Unknown(),
        )
        _AsyncGeneratorType.parent = astroid_builtin
        async_generator_doc_node = (
            nodes.Const(value=types.AsyncGeneratorType.__doc__)
            if types.AsyncGeneratorType.__doc__
            else None
        )
        _AsyncGeneratorType.postinit(
            bases=[],
            body=[],
            decorators=None,
            doc_node=async_generator_doc_node,
        )
        bases.AsyncGenerator._proxied = _AsyncGeneratorType
        builder.object_build(bases.AsyncGenerator._proxied, types.AsyncGeneratorType)

    if hasattr(types, "UnionType"):
        _UnionTypeType = nodes.ClassDef(
            types.UnionType.__name__,
            lineno=0,
            col_offset=0,
            end_lineno=0,
            end_col_offset=0,
            parent=nodes.Unknown(),
        )
        _UnionTypeType.parent = astroid_builtin
        union_type_doc_node = (
            nodes.Const(value=types.UnionType.__doc__)
            if types.UnionType.__doc__
            else None
        )
        _UnionTypeType.postinit(
            bases=[],
            body=[],
            decorators=None,
            doc_node=union_type_doc_node,
        )
        bases.UnionType._proxied = _UnionTypeType
        builder.object_build(bases.UnionType._proxied, types.UnionType)

    builtin_types = (
        types.GetSetDescriptorType,
        types.GeneratorType,
        types.MemberDescriptorType,
        TYPE_NONE,
        TYPE_NOTIMPLEMENTED,
        types.FunctionType,
        types.MethodType,
        types.BuiltinFunctionType,
        types.ModuleType,
        types.TracebackType,
    )
    for _type in builtin_types:
        if _type.__name__ not in astroid_builtin:
            klass = nodes.ClassDef(
                _type.__name__,
                lineno=0,
                col_offset=0,
                end_lineno=0,
                end_col_offset=0,
                parent=nodes.Unknown(),
            )
            klass.parent = astroid_builtin
            klass.postinit(
                bases=[],
                body=[],
                decorators=None,
                doc_node=nodes.Const(value=_type.__doc__) if _type.__doc__ else None,
            )
            builder.object_build(klass, _type)
            astroid_builtin[_type.__name__] = klass

    InspectBuilder.bootstrapped = True

    # pylint: disable-next=import-outside-toplevel
    from astroid.brain.brain_builtin_inference import on_bootstrap

    # Instantiates an AstroidBuilder(), which is where
    # InspectBuilder.bootstrapped is checked, so place after bootstrapped=True.
    on_bootstrap()

back/venvmsg/Lib/site-packages/astroid/raw_building.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""This module contains utilities for rebuilding an _ast tree in
order to get a single Astroid representation.
"""

from __future__ import annotations

import ast
import sys
import token
from collections.abc import Callable, Generator
from io import StringIO
from tokenize import TokenInfo, generate_tokens
from typing import TYPE_CHECKING, Final, TypeVar, Union, cast, overload

from astroid import nodes
from astroid._ast import ParserModule, get_parser_module, parse_function_type_comment
from astroid.const import IS_PYPY, PY38, PY39_PLUS, PY312_PLUS, Context
from astroid.manager import AstroidManager
from astroid.nodes import NodeNG
from astroid.nodes.node_classes import AssignName
from astroid.nodes.utils import Position
from astroid.typing import InferenceResult

REDIRECT: Final[dict[str, str]] = {
    "arguments": "Arguments",
    "comprehension": "Comprehension",
    "ListCompFor": "Comprehension",
    "GenExprFor": "Comprehension",
    "excepthandler": "ExceptHandler",
    "keyword": "Keyword",
    "match_case": "MatchCase",
}


T_Doc = TypeVar(
    "T_Doc",
    "ast.Module",
    "ast.ClassDef",
    Union["ast.FunctionDef", "ast.AsyncFunctionDef"],
)
_FunctionT = TypeVar("_FunctionT", nodes.FunctionDef, nodes.AsyncFunctionDef)
_ForT = TypeVar("_ForT", nodes.For, nodes.AsyncFor)
_WithT = TypeVar("_WithT", nodes.With, nodes.AsyncWith)
NodesWithDocsType = Union[nodes.Module, nodes.ClassDef, nodes.FunctionDef]


# noinspection PyMethodMayBeStatic
class TreeRebuilder:
    """Rebuilds the _ast tree to become an Astroid tree."""

    def __init__(
        self,
        manager: AstroidManager,
        parser_module: ParserModule | None = None,
        data: str | None = None,
    ) -> None:
        self._manager = manager
        self._data = data.split("\n") if data else None
        self._global_names: list[dict[str, list[nodes.Global]]] = []
        self._import_from_nodes: list[nodes.ImportFrom] = []
        self._delayed_assattr: list[nodes.AssignAttr] = []
        self._visit_meths: dict[type[ast.AST], Callable[[ast.AST, NodeNG], NodeNG]] = {}

        if parser_module is None:
            self._parser_module = get_parser_module()
        else:
            self._parser_module = parser_module

    def _get_doc(self, node: T_Doc) -> tuple[T_Doc, ast.Constant | ast.Str | None]:
        """Return the doc ast node."""
        try:
            if node.body and isinstance(node.body[0], ast.Expr):
                first_value = node.body[0].value
                if isinstance(first_value, ast.Constant) and isinstance(
                    first_value.value, str
                ):
                    doc_ast_node = first_value
                    node.body = node.body[1:]
                    # The ast parser of python < 3.8 sets col_offset of multi-line strings to -1
                    # as it is unable to determine the value correctly. We reset this to None.
                    if doc_ast_node.col_offset == -1:
                        doc_ast_node.col_offset = None
                    return node, doc_ast_node
        except IndexError:
            pass  # ast built from scratch
        return node, None

    def _get_context(
        self,
        node: (
            ast.Attribute
            | ast.List
            | ast.Name
            | ast.Subscript
            | ast.Starred
            | ast.Tuple
        ),
    ) -> Context:
        return self._parser_module.context_classes.get(type(node.ctx), Context.Load)

    def _get_position_info(
        self,
        node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
        parent: nodes.ClassDef | nodes.FunctionDef | nodes.AsyncFunctionDef,
    ) -> Position | None:
        """Return position information for ClassDef and FunctionDef nodes.

        In contrast to AST positions, these only include the actual keyword(s)
        and the class / function name.

        >>> @decorator
        >>> async def some_func(var: int) -> None:
        >>> ^^^^^^^^^^^^^^^^^^^
        """
        if not self._data:
            return None
        end_lineno = node.end_lineno
        if node.body:
            end_lineno = node.body[0].lineno
        # pylint: disable-next=unsubscriptable-object
        data = "\n".join(self._data[node.lineno - 1 : end_lineno])

        start_token: TokenInfo | None = None
        keyword_tokens: tuple[int, ...] = (token.NAME,)
        if isinstance(parent, nodes.AsyncFunctionDef):
            search_token = "async"
        elif isinstance(parent, nodes.FunctionDef):
            search_token = "def"
        else:
            search_token = "class"

        for t in generate_tokens(StringIO(data).readline):
            if (
                start_token is not None
                and t.type == token.NAME
                and t.string == node.name
            ):
                break
            if t.type in keyword_tokens:
                if t.string == search_token:
                    start_token = t
                    continue
                if t.string in {"def"}:
                    continue
            start_token = None
        else:
            return None

        return Position(
            lineno=node.lineno + start_token.start[0] - 1,
            col_offset=start_token.start[1],
            end_lineno=node.lineno + t.end[0] - 1,
            end_col_offset=t.end[1],
        )

    def _reset_end_lineno(self, newnode: nodes.NodeNG) -> None:
        """Reset end_lineno and end_col_offset attributes for PyPy 3.8.

        For some nodes, these are either set to -1 or only partially assigned.
        To keep consistency across astroid and pylint, reset all.

        This has been fixed in PyPy 3.9.
        For reference, an (incomplete) list of nodes with issues:
            - ClassDef          - For
            - FunctionDef       - While
            - Call              - If
            - Decorators        - Try
            - With              - Assign
        """
        newnode.end_lineno = None
        newnode.end_col_offset = None
        for child_node in newnode.get_children():
            self._reset_end_lineno(child_node)

    def visit_module(
        self, node: ast.Module, modname: str, modpath: str, package: bool
    ) -> nodes.Module:
        """Visit a Module node by returning a fresh instance of it.

        Note: Method not called by 'visit'
        """
        node, doc_ast_node = self._get_doc(node)
        newnode = nodes.Module(
            name=modname,
            file=modpath,
            path=[modpath],
            package=package,
        )
        newnode.postinit(
            [self.visit(child, newnode) for child in node.body],
            doc_node=self.visit(doc_ast_node, newnode),
        )
        if IS_PYPY and PY38:
            self._reset_end_lineno(newnode)
        return newnode

    if TYPE_CHECKING:  # noqa: C901

        @overload
        def visit(self, node: ast.arg, parent: NodeNG) -> nodes.AssignName:
            ...

        @overload
        def visit(self, node: ast.arguments, parent: NodeNG) -> nodes.Arguments:
            ...

        @overload
        def visit(self, node: ast.Assert, parent: NodeNG) -> nodes.Assert:
            ...

        @overload
        def visit(
            self, node: ast.AsyncFunctionDef, parent: NodeNG
        ) -> nodes.AsyncFunctionDef:
            ...

        @overload
        def visit(self, node: ast.AsyncFor, parent: NodeNG) -> nodes.AsyncFor:
            ...

        @overload
        def visit(self, node: ast.Await, parent: NodeNG) -> nodes.Await:
            ...

        @overload
        def visit(self, node: ast.AsyncWith, parent: NodeNG) -> nodes.AsyncWith:
            ...

        @overload
        def visit(self, node: ast.Assign, parent: NodeNG) -> nodes.Assign:
            ...

        @overload
        def visit(self, node: ast.AnnAssign, parent: NodeNG) -> nodes.AnnAssign:
            ...

        @overload
        def visit(self, node: ast.AugAssign, parent: NodeNG) -> nodes.AugAssign:
            ...

        @overload
        def visit(self, node: ast.BinOp, parent: NodeNG) -> nodes.BinOp:
            ...

        @overload
        def visit(self, node: ast.BoolOp, parent: NodeNG) -> nodes.BoolOp:
            ...

        @overload
        def visit(self, node: ast.Break, parent: NodeNG) -> nodes.Break:
            ...

        @overload
        def visit(self, node: ast.Call, parent: NodeNG) -> nodes.Call:
            ...

        @overload
        def visit(self, node: ast.ClassDef, parent: NodeNG) -> nodes.ClassDef:
            ...

        @overload
        def visit(self, node: ast.Continue, parent: NodeNG) -> nodes.Continue:
            ...

        @overload
        def visit(self, node: ast.Compare, parent: NodeNG) -> nodes.Compare:
            ...

        @overload
        def visit(self, node: ast.comprehension, parent: NodeNG) -> nodes.Comprehension:
            ...

        @overload
        def visit(self, node: ast.Delete, parent: NodeNG) -> nodes.Delete:
            ...

        @overload
        def visit(self, node: ast.Dict, parent: NodeNG) -> nodes.Dict:
            ...

        @overload
        def visit(self, node: ast.DictComp, parent: NodeNG) -> nodes.DictComp:
            ...

        @overload
        def visit(self, node: ast.Expr, parent: NodeNG) -> nodes.Expr:
            ...

        @overload
        def visit(self, node: ast.ExceptHandler, parent: NodeNG) -> nodes.ExceptHandler:
            ...

        @overload
        def visit(self, node: ast.For, parent: NodeNG) -> nodes.For:
            ...

        @overload
        def visit(self, node: ast.ImportFrom, parent: NodeNG) -> nodes.ImportFrom:
            ...

        @overload
        def visit(self, node: ast.FunctionDef, parent: NodeNG) -> nodes.FunctionDef:
            ...

        @overload
        def visit(self, node: ast.GeneratorExp, parent: NodeNG) -> nodes.GeneratorExp:
            ...

        @overload
        def visit(self, node: ast.Attribute, parent: NodeNG) -> nodes.Attribute:
            ...

        @overload
        def visit(self, node: ast.Global, parent: NodeNG) -> nodes.Global:
            ...

        @overload
        def visit(self, node: ast.If, parent: NodeNG) -> nodes.If:
            ...

        @overload
        def visit(self, node: ast.IfExp, parent: NodeNG) -> nodes.IfExp:
            ...

        @overload
        def visit(self, node: ast.Import, parent: NodeNG) -> nodes.Import:
            ...

        @overload
        def visit(self, node: ast.JoinedStr, parent: NodeNG) -> nodes.JoinedStr:
            ...

        @overload
        def visit(
            self, node: ast.FormattedValue, parent: NodeNG
        ) -> nodes.FormattedValue:
            ...

        @overload
        def visit(self, node: ast.NamedExpr, parent: NodeNG) -> nodes.NamedExpr:
            ...

        if sys.version_info < (3, 9):
            # Not used in Python 3.9+
            @overload
            def visit(self, node: ast.ExtSlice, parent: nodes.Subscript) -> nodes.Tuple:
                ...

            @overload
            def visit(self, node: ast.Index, parent: nodes.Subscript) -> NodeNG:
                ...

        @overload
        def visit(self, node: ast.keyword, parent: NodeNG) -> nodes.Keyword:
            ...

        @overload
        def visit(self, node: ast.Lambda, parent: NodeNG) -> nodes.Lambda:
            ...

        @overload
        def visit(self, node: ast.List, parent: NodeNG) -> nodes.List:
            ...

        @overload
        def visit(self, node: ast.ListComp, parent: NodeNG) -> nodes.ListComp:
            ...

        @overload
        def visit(
            self, node: ast.Name, parent: NodeNG
        ) -> nodes.Name | nodes.Const | nodes.AssignName | nodes.DelName:
            ...

        @overload
        def visit(self, node: ast.Nonlocal, parent: NodeNG) -> nodes.Nonlocal:
            ...

        @overload
        def visit(self, node: ast.Constant, parent: NodeNG) -> nodes.Const:
            ...

        if sys.version_info >= (3, 12):

            @overload
            def visit(self, node: ast.ParamSpec, parent: NodeNG) -> nodes.ParamSpec:
                ...

        @overload
        def visit(self, node: ast.Pass, parent: NodeNG) -> nodes.Pass:
            ...

        @overload
        def visit(self, node: ast.Raise, parent: NodeNG) -> nodes.Raise:
            ...

        @overload
        def visit(self, node: ast.Return, parent: NodeNG) -> nodes.Return:
            ...

        @overload
        def visit(self, node: ast.Set, parent: NodeNG) -> nodes.Set:
            ...

        @overload
        def visit(self, node: ast.SetComp, parent: NodeNG) -> nodes.SetComp:
            ...

        @overload
        def visit(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice:
            ...

        @overload
        def visit(self, node: ast.Subscript, parent: NodeNG) -> nodes.Subscript:
            ...

        @overload
        def visit(self, node: ast.Starred, parent: NodeNG) -> nodes.Starred:
            ...

        @overload
        def visit(self, node: ast.Try, parent: NodeNG) -> nodes.Try:
            ...

        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:
            ...

        if sys.version_info >= (3, 12):

            @overload
            def visit(self, node: ast.TypeAlias, parent: NodeNG) -> nodes.TypeAlias:
                ...

            @overload
            def visit(self, node: ast.TypeVar, parent: NodeNG) -> nodes.TypeVar:
                ...

            @overload
            def visit(
                self, node: ast.TypeVarTuple, parent: NodeNG
            ) -> nodes.TypeVarTuple:
                ...

        @overload
        def visit(self, node: ast.UnaryOp, parent: NodeNG) -> nodes.UnaryOp:
            ...

        @overload
        def visit(self, node: ast.While, parent: NodeNG) -> nodes.While:
            ...

        @overload
        def visit(self, node: ast.With, parent: NodeNG) -> nodes.With:
            ...

        @overload
        def visit(self, node: ast.Yield, parent: NodeNG) -> nodes.Yield:
            ...

        @overload
        def visit(self, node: ast.YieldFrom, parent: NodeNG) -> nodes.YieldFrom:
            ...

        if sys.version_info >= (3, 10):

            @overload
            def visit(self, node: ast.Match, parent: NodeNG) -> nodes.Match:
                ...

            @overload
            def visit(self, node: ast.match_case, parent: NodeNG) -> nodes.MatchCase:
                ...

            @overload
            def visit(self, node: ast.MatchValue, parent: NodeNG) -> nodes.MatchValue:
                ...

            @overload
            def visit(
                self, node: ast.MatchSingleton, parent: NodeNG
            ) -> nodes.MatchSingleton:
                ...

            @overload
            def visit(
                self, node: ast.MatchSequence, parent: NodeNG
            ) -> nodes.MatchSequence:
                ...

            @overload
            def visit(
                self, node: ast.MatchMapping, parent: NodeNG
            ) -> nodes.MatchMapping:
                ...

            @overload
            def visit(self, node: ast.MatchClass, parent: NodeNG) -> nodes.MatchClass:
                ...

            @overload
            def visit(self, node: ast.MatchStar, parent: NodeNG) -> nodes.MatchStar:
                ...

            @overload
            def visit(self, node: ast.MatchAs, parent: NodeNG) -> nodes.MatchAs:
                ...

            @overload
            def visit(self, node: ast.MatchOr, parent: NodeNG) -> nodes.MatchOr:
                ...

            @overload
            def visit(self, node: ast.pattern, parent: NodeNG) -> nodes.Pattern:
                ...

        @overload
        def visit(self, node: ast.AST, parent: NodeNG) -> NodeNG:
            ...

        @overload
        def visit(self, node: None, parent: NodeNG) -> None:
            ...

    def visit(self, node: ast.AST | None, parent: NodeNG) -> NodeNG | None:
        if node is None:
            return None
        cls = node.__class__
        if cls in self._visit_meths:
            visit_method = self._visit_meths[cls]
        else:
            cls_name = cls.__name__
            visit_name = "visit_" + REDIRECT.get(cls_name, cls_name).lower()
            visit_method = getattr(self, visit_name)
            self._visit_meths[cls] = visit_method
        return visit_method(node, parent)

    def _save_assignment(self, node: nodes.AssignName | nodes.DelName) -> None:
        """Save assignment situation since node.parent is not available yet."""
        if self._global_names and node.name in self._global_names[-1]:
            node.root().set_local(node.name, node)
        else:
            assert node.parent
            assert node.name
            node.parent.set_local(node.name, node)

    def visit_arg(self, node: ast.arg, parent: NodeNG) -> nodes.AssignName:
        """Visit an arg node by returning a fresh AssName instance."""
        return self.visit_assignname(node, parent, node.arg)

    def visit_arguments(self, node: ast.arguments, parent: NodeNG) -> nodes.Arguments:
        """Visit an Arguments node by returning a fresh instance of it."""
        vararg: str | None = None
        kwarg: str | None = None
        vararg_node = node.vararg
        kwarg_node = node.kwarg

        newnode = nodes.Arguments(
            node.vararg.arg if node.vararg else None,
            node.kwarg.arg if node.kwarg else None,
            parent,
            AssignName(
                vararg_node.arg,
                vararg_node.lineno,
                vararg_node.col_offset,
                parent,
                end_lineno=vararg_node.end_lineno,
                end_col_offset=vararg_node.end_col_offset,
            )
            if vararg_node
            else None,
            AssignName(
                kwarg_node.arg,
                kwarg_node.lineno,
                kwarg_node.col_offset,
                parent,
                end_lineno=kwarg_node.end_lineno,
                end_col_offset=kwarg_node.end_col_offset,
            )
            if kwarg_node
            else None,
        )
        args = [self.visit(child, newnode) for child in node.args]
        defaults = [self.visit(child, newnode) for child in node.defaults]
        varargannotation: NodeNG | None = None
        kwargannotation: NodeNG | None = None
        if node.vararg:
            vararg = node.vararg.arg
            varargannotation = self.visit(node.vararg.annotation, newnode)
        if node.kwarg:
            kwarg = node.kwarg.arg
            kwargannotation = self.visit(node.kwarg.annotation, newnode)

        if PY38:
            # In Python 3.8 'end_lineno' and 'end_col_offset'
            # for 'kwonlyargs' don't include the annotation.
            for arg in node.kwonlyargs:
                if arg.annotation is not None:
                    arg.end_lineno = arg.annotation.end_lineno
                    arg.end_col_offset = arg.annotation.end_col_offset

        kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs]
        kw_defaults = [self.visit(child, newnode) for child in node.kw_defaults]
        annotations = [self.visit(arg.annotation, newnode) for arg in node.args]
        kwonlyargs_annotations = [
            self.visit(arg.annotation, newnode) for arg in node.kwonlyargs
        ]

        posonlyargs = [self.visit(child, newnode) for child in node.posonlyargs]
        posonlyargs_annotations = [
            self.visit(arg.annotation, newnode) for arg in node.posonlyargs
        ]
        type_comment_args = [
            self.check_type_comment(child, parent=newnode) for child in node.args
        ]
        type_comment_kwonlyargs = [
            self.check_type_comment(child, parent=newnode) for child in node.kwonlyargs
        ]
        type_comment_posonlyargs = [
            self.check_type_comment(child, parent=newnode) for child in node.posonlyargs
        ]

        newnode.postinit(
            args=args,
            defaults=defaults,
            kwonlyargs=kwonlyargs,
            posonlyargs=posonlyargs,
            kw_defaults=kw_defaults,
            annotations=annotations,
            kwonlyargs_annotations=kwonlyargs_annotations,
            posonlyargs_annotations=posonlyargs_annotations,
            varargannotation=varargannotation,
            kwargannotation=kwargannotation,
            type_comment_args=type_comment_args,
            type_comment_kwonlyargs=type_comment_kwonlyargs,
            type_comment_posonlyargs=type_comment_posonlyargs,
        )
        # save argument names in locals:
        assert newnode.parent
        if vararg:
            newnode.parent.set_local(vararg, newnode)
        if kwarg:
            newnode.parent.set_local(kwarg, newnode)
        return newnode

    def visit_assert(self, node: ast.Assert, parent: NodeNG) -> nodes.Assert:
        """Visit a Assert node by returning a fresh instance of it."""
        newnode = nodes.Assert(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        msg: NodeNG | None = None
        if node.msg:
            msg = self.visit(node.msg, newnode)
        newnode.postinit(self.visit(node.test, newnode), msg)
        return newnode

    def check_type_comment(
        self,
        node: (
            ast.Assign | ast.arg | ast.For | ast.AsyncFor | ast.With | ast.AsyncWith
        ),
        parent: (
            nodes.Assign
            | nodes.Arguments
            | nodes.For
            | nodes.AsyncFor
            | nodes.With
            | nodes.AsyncWith
        ),
    ) -> NodeNG | None:
        if not node.type_comment:
            return None

        try:
            type_comment_ast = self._parser_module.parse(node.type_comment)
        except SyntaxError:
            # Invalid type comment, just skip it.
            return None

        # For '# type: # any comment' ast.parse returns a Module node,
        # without any nodes in the body.
        if not type_comment_ast.body:
            return None

        type_object = self.visit(type_comment_ast.body[0], parent=parent)
        if not isinstance(type_object, nodes.Expr):
            return None

        return type_object.value

    def check_function_type_comment(
        self, node: ast.FunctionDef | ast.AsyncFunctionDef, parent: NodeNG
    ) -> tuple[NodeNG | None, list[NodeNG]] | None:
        if not node.type_comment:
            return None

        try:
            type_comment_ast = parse_function_type_comment(node.type_comment)
        except SyntaxError:
            # Invalid type comment, just skip it.
            return None

        if not type_comment_ast:
            return None

        returns: NodeNG | None = None
        argtypes: list[NodeNG] = [
            self.visit(elem, parent) for elem in (type_comment_ast.argtypes or [])
        ]
        if type_comment_ast.returns:
            returns = self.visit(type_comment_ast.returns, parent)

        return returns, argtypes

    def visit_asyncfunctiondef(
        self, node: ast.AsyncFunctionDef, parent: NodeNG
    ) -> nodes.AsyncFunctionDef:
        return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent)

    def visit_asyncfor(self, node: ast.AsyncFor, parent: NodeNG) -> nodes.AsyncFor:
        return self._visit_for(nodes.AsyncFor, node, parent)

    def visit_await(self, node: ast.Await, parent: NodeNG) -> nodes.Await:
        newnode = nodes.Await(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(value=self.visit(node.value, newnode))
        return newnode

    def visit_asyncwith(self, node: ast.AsyncWith, parent: NodeNG) -> nodes.AsyncWith:
        return self._visit_with(nodes.AsyncWith, node, parent)

    def visit_assign(self, node: ast.Assign, parent: NodeNG) -> nodes.Assign:
        """Visit a Assign node by returning a fresh instance of it."""
        newnode = nodes.Assign(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        type_annotation = self.check_type_comment(node, parent=newnode)
        newnode.postinit(
            targets=[self.visit(child, newnode) for child in node.targets],
            value=self.visit(node.value, newnode),
            type_annotation=type_annotation,
        )
        return newnode

    def visit_annassign(self, node: ast.AnnAssign, parent: NodeNG) -> nodes.AnnAssign:
        """Visit an AnnAssign node by returning a fresh instance of it."""
        newnode = nodes.AnnAssign(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            target=self.visit(node.target, newnode),
            annotation=self.visit(node.annotation, newnode),
            simple=node.simple,
            value=self.visit(node.value, newnode),
        )
        return newnode

    @overload
    def visit_assignname(
        self, node: ast.AST, parent: NodeNG, node_name: str
    ) -> nodes.AssignName:
        ...

    @overload
    def visit_assignname(self, node: ast.AST, parent: NodeNG, node_name: None) -> None:
        ...

    def visit_assignname(
        self, node: ast.AST, parent: NodeNG, node_name: str | None
    ) -> nodes.AssignName | None:
        """Visit a node and return a AssignName node.

        Note: Method not called by 'visit'
        """
        if node_name is None:
            return None
        newnode = nodes.AssignName(
            name=node_name,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        self._save_assignment(newnode)
        return newnode

    def visit_augassign(self, node: ast.AugAssign, parent: NodeNG) -> nodes.AugAssign:
        """Visit a AugAssign node by returning a fresh instance of it."""
        newnode = nodes.AugAssign(
            op=self._parser_module.bin_op_classes[type(node.op)] + "=",
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.target, newnode), self.visit(node.value, newnode)
        )
        return newnode

    def visit_binop(self, node: ast.BinOp, parent: NodeNG) -> nodes.BinOp:
        """Visit a BinOp node by returning a fresh instance of it."""
        newnode = nodes.BinOp(
            op=self._parser_module.bin_op_classes[type(node.op)],
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.left, newnode), self.visit(node.right, newnode)
        )
        return newnode

    def visit_boolop(self, node: ast.BoolOp, parent: NodeNG) -> nodes.BoolOp:
        """Visit a BoolOp node by returning a fresh instance of it."""
        newnode = nodes.BoolOp(
            op=self._parser_module.bool_op_classes[type(node.op)],
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit([self.visit(child, newnode) for child in node.values])
        return newnode

    def visit_break(self, node: ast.Break, parent: NodeNG) -> nodes.Break:
        """Visit a Break node by returning a fresh instance of it."""
        return nodes.Break(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )

    def visit_call(self, node: ast.Call, parent: NodeNG) -> nodes.Call:
        """Visit a CallFunc node by returning a fresh instance of it."""
        newnode = nodes.Call(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            func=self.visit(node.func, newnode),
            args=[self.visit(child, newnode) for child in node.args],
            keywords=[self.visit(child, newnode) for child in node.keywords],
        )
        return newnode

    def visit_classdef(
        self, node: ast.ClassDef, parent: NodeNG, newstyle: bool = True
    ) -> nodes.ClassDef:
        """Visit a ClassDef node to become astroid."""
        node, doc_ast_node = self._get_doc(node)
        newnode = nodes.ClassDef(
            name=node.name,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        metaclass = None
        for keyword in node.keywords:
            if keyword.arg == "metaclass":
                metaclass = self.visit(keyword, newnode).value
                break
        decorators = self.visit_decorators(node, newnode)
        newnode.postinit(
            [self.visit(child, newnode) for child in node.bases],
            [self.visit(child, newnode) for child in node.body],
            decorators,
            newstyle,
            metaclass,
            [
                self.visit(kwd, newnode)
                for kwd in node.keywords
                if kwd.arg != "metaclass"
            ],
            position=self._get_position_info(node, newnode),
            doc_node=self.visit(doc_ast_node, newnode),
            type_params=[self.visit(param, newnode) for param in node.type_params]
            if PY312_PLUS
            else [],
        )
        return newnode

    def visit_continue(self, node: ast.Continue, parent: NodeNG) -> nodes.Continue:
        """Visit a Continue node by returning a fresh instance of it."""
        return nodes.Continue(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )

    def visit_compare(self, node: ast.Compare, parent: NodeNG) -> nodes.Compare:
        """Visit a Compare node by returning a fresh instance of it."""
        newnode = nodes.Compare(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.left, newnode),
            [
                (
                    self._parser_module.cmp_op_classes[op.__class__],
                    self.visit(expr, newnode),
                )
                for (op, expr) in zip(node.ops, node.comparators)
            ],
        )
        return newnode

    def visit_comprehension(
        self, node: ast.comprehension, parent: NodeNG
    ) -> nodes.Comprehension:
        """Visit a Comprehension node by returning a fresh instance of it."""
        newnode = nodes.Comprehension(
            parent=parent,
            # Comprehension nodes don't have these attributes
            # see https://docs.python.org/3/library/ast.html#abstract-grammar
            lineno=None,
            col_offset=None,
            end_lineno=None,
            end_col_offset=None,
        )
        newnode.postinit(
            self.visit(node.target, newnode),
            self.visit(node.iter, newnode),
            [self.visit(child, newnode) for child in node.ifs],
            bool(node.is_async),
        )
        return newnode

    def visit_decorators(
        self,
        node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
        parent: NodeNG,
    ) -> nodes.Decorators | None:
        """Visit a Decorators node by returning a fresh instance of it.

        Note: Method not called by 'visit'
        """
        if not node.decorator_list:
            return None
        # /!\ node is actually an _ast.FunctionDef node while
        # parent is an astroid.nodes.FunctionDef node

        # Set the line number of the first decorator for Python 3.8+.
        lineno = node.decorator_list[0].lineno
        end_lineno = node.decorator_list[-1].end_lineno
        end_col_offset = node.decorator_list[-1].end_col_offset

        newnode = nodes.Decorators(
            lineno=lineno,
            col_offset=node.col_offset,
            end_lineno=end_lineno,
            end_col_offset=end_col_offset,
            parent=parent,
        )
        newnode.postinit([self.visit(child, newnode) for child in node.decorator_list])
        return newnode

    def visit_delete(self, node: ast.Delete, parent: NodeNG) -> nodes.Delete:
        """Visit a Delete node by returning a fresh instance of it."""
        newnode = nodes.Delete(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit([self.visit(child, newnode) for child in node.targets])
        return newnode

    def _visit_dict_items(
        self, node: ast.Dict, parent: NodeNG, newnode: nodes.Dict
    ) -> Generator[tuple[NodeNG, NodeNG], None, None]:
        for key, value in zip(node.keys, node.values):
            rebuilt_key: NodeNG
            rebuilt_value = self.visit(value, newnode)
            if not key:
                # Extended unpacking
                rebuilt_key = nodes.DictUnpack(
                    lineno=rebuilt_value.lineno,
                    col_offset=rebuilt_value.col_offset,
                    end_lineno=rebuilt_value.end_lineno,
                    end_col_offset=rebuilt_value.end_col_offset,
                    parent=parent,
                )
            else:
                rebuilt_key = self.visit(key, newnode)
            yield rebuilt_key, rebuilt_value

    def visit_dict(self, node: ast.Dict, parent: NodeNG) -> nodes.Dict:
        """Visit a Dict node by returning a fresh instance of it."""
        newnode = nodes.Dict(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        items: list[tuple[InferenceResult, InferenceResult]] = list(
            self._visit_dict_items(node, parent, newnode)
        )
        newnode.postinit(items)
        return newnode

    def visit_dictcomp(self, node: ast.DictComp, parent: NodeNG) -> nodes.DictComp:
        """Visit a DictComp node by returning a fresh instance of it."""
        newnode = nodes.DictComp(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.key, newnode),
            self.visit(node.value, newnode),
            [self.visit(child, newnode) for child in node.generators],
        )
        return newnode

    def visit_expr(self, node: ast.Expr, parent: NodeNG) -> nodes.Expr:
        """Visit a Expr node by returning a fresh instance of it."""
        newnode = nodes.Expr(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(self.visit(node.value, newnode))
        return newnode

    def visit_excepthandler(
        self, node: ast.ExceptHandler, parent: NodeNG
    ) -> nodes.ExceptHandler:
        """Visit an ExceptHandler node by returning a fresh instance of it."""
        newnode = nodes.ExceptHandler(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.type, newnode),
            self.visit_assignname(node, newnode, node.name),
            [self.visit(child, newnode) for child in node.body],
        )
        return newnode

    @overload
    def _visit_for(
        self, cls: type[nodes.For], node: ast.For, parent: NodeNG
    ) -> nodes.For:
        ...

    @overload
    def _visit_for(
        self, cls: type[nodes.AsyncFor], node: ast.AsyncFor, parent: NodeNG
    ) -> nodes.AsyncFor:
        ...

    def _visit_for(
        self, cls: type[_ForT], node: ast.For | ast.AsyncFor, parent: NodeNG
    ) -> _ForT:
        """Visit a For node by returning a fresh instance of it."""
        col_offset = node.col_offset
        if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncFor) and self._data:
            # pylint: disable-next=unsubscriptable-object
            col_offset = self._data[node.lineno - 1].index("async")

        newnode = cls(
            lineno=node.lineno,
            col_offset=col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        type_annotation = self.check_type_comment(node, parent=newnode)
        newnode.postinit(
            target=self.visit(node.target, newnode),
            iter=self.visit(node.iter, newnode),
            body=[self.visit(child, newnode) for child in node.body],
            orelse=[self.visit(child, newnode) for child in node.orelse],
            type_annotation=type_annotation,
        )
        return newnode

    def visit_for(self, node: ast.For, parent: NodeNG) -> nodes.For:
        return self._visit_for(nodes.For, node, parent)

    def visit_importfrom(
        self, node: ast.ImportFrom, parent: NodeNG
    ) -> nodes.ImportFrom:
        """Visit an ImportFrom node by returning a fresh instance of it."""
        names = [(alias.name, alias.asname) for alias in node.names]
        newnode = nodes.ImportFrom(
            fromname=node.module or "",
            names=names,
            level=node.level or None,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        # store From names to add them to locals after building
        self._import_from_nodes.append(newnode)
        return newnode

    @overload
    def _visit_functiondef(
        self, cls: type[nodes.FunctionDef], node: ast.FunctionDef, parent: NodeNG
    ) -> nodes.FunctionDef:
        ...

    @overload
    def _visit_functiondef(
        self,
        cls: type[nodes.AsyncFunctionDef],
        node: ast.AsyncFunctionDef,
        parent: NodeNG,
    ) -> nodes.AsyncFunctionDef:
        ...

    def _visit_functiondef(
        self,
        cls: type[_FunctionT],
        node: ast.FunctionDef | ast.AsyncFunctionDef,
        parent: NodeNG,
    ) -> _FunctionT:
        """Visit an FunctionDef node to become astroid."""
        self._global_names.append({})
        node, doc_ast_node = self._get_doc(node)

        lineno = node.lineno
        if node.decorator_list:
            # Python 3.8 sets the line number of a decorated function
            # to be the actual line number of the function, but the
            # previous versions expected the decorator's line number instead.
            # We reset the function's line number to that of the
            # first decorator to maintain backward compatibility.
            # It's not ideal but this discrepancy was baked into
            # the framework for *years*.
            lineno = node.decorator_list[0].lineno

        newnode = cls(
            name=node.name,
            lineno=lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        decorators = self.visit_decorators(node, newnode)
        returns: NodeNG | None
        if node.returns:
            returns = self.visit(node.returns, newnode)
        else:
            returns = None

        type_comment_args = type_comment_returns = None
        type_comment_annotation = self.check_function_type_comment(node, newnode)
        if type_comment_annotation:
            type_comment_returns, type_comment_args = type_comment_annotation
        newnode.postinit(
            args=self.visit(node.args, newnode),
            body=[self.visit(child, newnode) for child in node.body],
            decorators=decorators,
            returns=returns,
            type_comment_returns=type_comment_returns,
            type_comment_args=type_comment_args,
            position=self._get_position_info(node, newnode),
            doc_node=self.visit(doc_ast_node, newnode),
            type_params=[self.visit(param, newnode) for param in node.type_params]
            if PY312_PLUS
            else [],
        )
        self._global_names.pop()
        return newnode

    def visit_functiondef(
        self, node: ast.FunctionDef, parent: NodeNG
    ) -> nodes.FunctionDef:
        return self._visit_functiondef(nodes.FunctionDef, node, parent)

    def visit_generatorexp(
        self, node: ast.GeneratorExp, parent: NodeNG
    ) -> nodes.GeneratorExp:
        """Visit a GeneratorExp node by returning a fresh instance of it."""
        newnode = nodes.GeneratorExp(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.elt, newnode),
            [self.visit(child, newnode) for child in node.generators],
        )
        return newnode

    def visit_attribute(
        self, node: ast.Attribute, parent: NodeNG
    ) -> nodes.Attribute | nodes.AssignAttr | nodes.DelAttr:
        """Visit an Attribute node by returning a fresh instance of it."""
        context = self._get_context(node)
        newnode: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr
        if context == Context.Del:
            # FIXME : maybe we should reintroduce and visit_delattr ?
            # for instance, deactivating assign_ctx
            newnode = nodes.DelAttr(
                attrname=node.attr,
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
        elif context == Context.Store:
            newnode = nodes.AssignAttr(
                attrname=node.attr,
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
            # Prohibit a local save if we are in an ExceptHandler.
            if not isinstance(parent, nodes.ExceptHandler):
                # mypy doesn't recognize that newnode has to be AssignAttr because it
                # doesn't support ParamSpec
                # See https://github.com/python/mypy/issues/8645
                self._delayed_assattr.append(newnode)  # type: ignore[arg-type]
        else:
            newnode = nodes.Attribute(
                attrname=node.attr,
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
        newnode.postinit(self.visit(node.value, newnode))
        return newnode

    def visit_global(self, node: ast.Global, parent: NodeNG) -> nodes.Global:
        """Visit a Global node to become astroid."""
        newnode = nodes.Global(
            names=node.names,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        if self._global_names:  # global at the module level, no effect
            for name in node.names:
                self._global_names[-1].setdefault(name, []).append(newnode)
        return newnode

    def visit_if(self, node: ast.If, parent: NodeNG) -> nodes.If:
        """Visit an If node by returning a fresh instance of it."""
        newnode = nodes.If(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.test, newnode),
            [self.visit(child, newnode) for child in node.body],
            [self.visit(child, newnode) for child in node.orelse],
        )
        return newnode

    def visit_ifexp(self, node: ast.IfExp, parent: NodeNG) -> nodes.IfExp:
        """Visit a IfExp node by returning a fresh instance of it."""
        newnode = nodes.IfExp(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.test, newnode),
            self.visit(node.body, newnode),
            self.visit(node.orelse, newnode),
        )
        return newnode

    def visit_import(self, node: ast.Import, parent: NodeNG) -> nodes.Import:
        """Visit a Import node by returning a fresh instance of it."""
        names = [(alias.name, alias.asname) for alias in node.names]
        newnode = nodes.Import(
            names=names,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        # save import names in parent's locals:
        for name, asname in newnode.names:
            name = asname or name
            parent.set_local(name.split(".")[0], newnode)
        return newnode

    def visit_joinedstr(self, node: ast.JoinedStr, parent: NodeNG) -> nodes.JoinedStr:
        newnode = nodes.JoinedStr(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit([self.visit(child, newnode) for child in node.values])
        return newnode

    def visit_formattedvalue(
        self, node: ast.FormattedValue, parent: NodeNG
    ) -> nodes.FormattedValue:
        newnode = nodes.FormattedValue(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            value=self.visit(node.value, newnode),
            conversion=node.conversion,
            format_spec=self.visit(node.format_spec, newnode),
        )
        return newnode

    def visit_namedexpr(self, node: ast.NamedExpr, parent: NodeNG) -> nodes.NamedExpr:
        newnode = nodes.NamedExpr(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.target, newnode), self.visit(node.value, newnode)
        )
        return newnode

    if sys.version_info < (3, 9):
        # Not used in Python 3.9+.
        def visit_extslice(
            self, node: ast.ExtSlice, parent: nodes.Subscript
        ) -> nodes.Tuple:
            """Visit an ExtSlice node by returning a fresh instance of Tuple."""
            # ExtSlice doesn't have lineno or col_offset information
            newnode = nodes.Tuple(ctx=Context.Load, parent=parent)
            newnode.postinit([self.visit(dim, newnode) for dim in node.dims])
            return newnode

        def visit_index(self, node: ast.Index, parent: nodes.Subscript) -> NodeNG:
            """Visit a Index node by returning a fresh instance of NodeNG."""
            return self.visit(node.value, parent)

    def visit_keyword(self, node: ast.keyword, parent: NodeNG) -> nodes.Keyword:
        """Visit a Keyword node by returning a fresh instance of it."""
        newnode = nodes.Keyword(
            arg=node.arg,
            # position attributes added in 3.9
            lineno=getattr(node, "lineno", None),
            col_offset=getattr(node, "col_offset", None),
            end_lineno=getattr(node, "end_lineno", None),
            end_col_offset=getattr(node, "end_col_offset", None),
            parent=parent,
        )
        newnode.postinit(self.visit(node.value, newnode))
        return newnode

    def visit_lambda(self, node: ast.Lambda, parent: NodeNG) -> nodes.Lambda:
        """Visit a Lambda node by returning a fresh instance of it."""
        newnode = nodes.Lambda(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(self.visit(node.args, newnode), self.visit(node.body, newnode))
        return newnode

    def visit_list(self, node: ast.List, parent: NodeNG) -> nodes.List:
        """Visit a List node by returning a fresh instance of it."""
        context = self._get_context(node)
        newnode = nodes.List(
            ctx=context,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit([self.visit(child, newnode) for child in node.elts])
        return newnode

    def visit_listcomp(self, node: ast.ListComp, parent: NodeNG) -> nodes.ListComp:
        """Visit a ListComp node by returning a fresh instance of it."""
        newnode = nodes.ListComp(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.elt, newnode),
            [self.visit(child, newnode) for child in node.generators],
        )
        return newnode

    def visit_name(
        self, node: ast.Name, parent: NodeNG
    ) -> nodes.Name | nodes.AssignName | nodes.DelName:
        """Visit a Name node by returning a fresh instance of it."""
        context = self._get_context(node)
        newnode: nodes.Name | nodes.AssignName | nodes.DelName
        if context == Context.Del:
            newnode = nodes.DelName(
                name=node.id,
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
        elif context == Context.Store:
            newnode = nodes.AssignName(
                name=node.id,
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
        else:
            newnode = nodes.Name(
                name=node.id,
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
        # XXX REMOVE me :
        if context in (Context.Del, Context.Store):  # 'Aug' ??
            newnode = cast(Union[nodes.AssignName, nodes.DelName], newnode)
            self._save_assignment(newnode)
        return newnode

    def visit_nonlocal(self, node: ast.Nonlocal, parent: NodeNG) -> nodes.Nonlocal:
        """Visit a Nonlocal node and return a new instance of it."""
        return nodes.Nonlocal(
            names=node.names,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )

    def visit_constant(self, node: ast.Constant, parent: NodeNG) -> nodes.Const:
        """Visit a Constant node by returning a fresh instance of Const."""
        return nodes.Const(
            value=node.value,
            kind=node.kind,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )

    def visit_paramspec(self, node: ast.ParamSpec, parent: NodeNG) -> nodes.ParamSpec:
        """Visit a ParamSpec node by returning a fresh instance of it."""
        newnode = nodes.ParamSpec(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        # Add AssignName node for 'node.name'
        # https://bugs.python.org/issue43994
        newnode.postinit(name=self.visit_assignname(node, newnode, node.name))
        return newnode

    def visit_pass(self, node: ast.Pass, parent: NodeNG) -> nodes.Pass:
        """Visit a Pass node by returning a fresh instance of it."""
        return nodes.Pass(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )

    def visit_raise(self, node: ast.Raise, parent: NodeNG) -> nodes.Raise:
        """Visit a Raise node by returning a fresh instance of it."""
        newnode = nodes.Raise(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        # no traceback; anyway it is not used in Pylint
        newnode.postinit(
            exc=self.visit(node.exc, newnode),
            cause=self.visit(node.cause, newnode),
        )
        return newnode

    def visit_return(self, node: ast.Return, parent: NodeNG) -> nodes.Return:
        """Visit a Return node by returning a fresh instance of it."""
        newnode = nodes.Return(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(self.visit(node.value, newnode))
        return newnode

    def visit_set(self, node: ast.Set, parent: NodeNG) -> nodes.Set:
        """Visit a Set node by returning a fresh instance of it."""
        newnode = nodes.Set(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit([self.visit(child, newnode) for child in node.elts])
        return newnode

    def visit_setcomp(self, node: ast.SetComp, parent: NodeNG) -> nodes.SetComp:
        """Visit a SetComp node by returning a fresh instance of it."""
        newnode = nodes.SetComp(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.elt, newnode),
            [self.visit(child, newnode) for child in node.generators],
        )
        return newnode

    def visit_slice(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice:
        """Visit a Slice node by returning a fresh instance of it."""
        newnode = nodes.Slice(
            # position attributes added in 3.9
            lineno=getattr(node, "lineno", None),
            col_offset=getattr(node, "col_offset", None),
            end_lineno=getattr(node, "end_lineno", None),
            end_col_offset=getattr(node, "end_col_offset", None),
            parent=parent,
        )
        newnode.postinit(
            lower=self.visit(node.lower, newnode),
            upper=self.visit(node.upper, newnode),
            step=self.visit(node.step, newnode),
        )
        return newnode

    def visit_subscript(self, node: ast.Subscript, parent: NodeNG) -> nodes.Subscript:
        """Visit a Subscript node by returning a fresh instance of it."""
        context = self._get_context(node)
        newnode = nodes.Subscript(
            ctx=context,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.value, newnode), self.visit(node.slice, newnode)
        )
        return newnode

    def visit_starred(self, node: ast.Starred, parent: NodeNG) -> nodes.Starred:
        """Visit a Starred node and return a new instance of it."""
        context = self._get_context(node)
        newnode = nodes.Starred(
            ctx=context,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(self.visit(node.value, newnode))
        return newnode

    def visit_try(self, node: ast.Try, parent: NodeNG) -> nodes.Try:
        """Visit a Try node by returning a fresh instance of it"""
        newnode = nodes.Try(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            body=[self.visit(child, newnode) for child in node.body],
            handlers=[self.visit(child, newnode) for child in node.handlers],
            orelse=[self.visit(child, newnode) for child in node.orelse],
            finalbody=[self.visit(child, newnode) for child in node.finalbody],
        )
        return newnode

    def visit_trystar(self, node: ast.TryStar, parent: NodeNG) -> nodes.TryStar:
        newnode = nodes.TryStar(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            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)
        newnode = nodes.Tuple(
            ctx=context,
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit([self.visit(child, newnode) for child in node.elts])
        return newnode

    def visit_typealias(self, node: ast.TypeAlias, parent: NodeNG) -> nodes.TypeAlias:
        """Visit a TypeAlias node by returning a fresh instance of it."""
        newnode = nodes.TypeAlias(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            name=self.visit(node.name, newnode),
            type_params=[self.visit(p, newnode) for p in node.type_params],
            value=self.visit(node.value, newnode),
        )
        return newnode

    def visit_typevar(self, node: ast.TypeVar, parent: NodeNG) -> nodes.TypeVar:
        """Visit a TypeVar node by returning a fresh instance of it."""
        newnode = nodes.TypeVar(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        # Add AssignName node for 'node.name'
        # https://bugs.python.org/issue43994
        newnode.postinit(
            name=self.visit_assignname(node, newnode, node.name),
            bound=self.visit(node.bound, newnode),
        )
        return newnode

    def visit_typevartuple(
        self, node: ast.TypeVarTuple, parent: NodeNG
    ) -> nodes.TypeVarTuple:
        """Visit a TypeVarTuple node by returning a fresh instance of it."""
        newnode = nodes.TypeVarTuple(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        # Add AssignName node for 'node.name'
        # https://bugs.python.org/issue43994
        newnode.postinit(name=self.visit_assignname(node, newnode, node.name))
        return newnode

    def visit_unaryop(self, node: ast.UnaryOp, parent: NodeNG) -> nodes.UnaryOp:
        """Visit a UnaryOp node by returning a fresh instance of it."""
        newnode = nodes.UnaryOp(
            op=self._parser_module.unary_op_classes[node.op.__class__],
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(self.visit(node.operand, newnode))
        return newnode

    def visit_while(self, node: ast.While, parent: NodeNG) -> nodes.While:
        """Visit a While node by returning a fresh instance of it."""
        newnode = nodes.While(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(
            self.visit(node.test, newnode),
            [self.visit(child, newnode) for child in node.body],
            [self.visit(child, newnode) for child in node.orelse],
        )
        return newnode

    @overload
    def _visit_with(
        self, cls: type[nodes.With], node: ast.With, parent: NodeNG
    ) -> nodes.With:
        ...

    @overload
    def _visit_with(
        self, cls: type[nodes.AsyncWith], node: ast.AsyncWith, parent: NodeNG
    ) -> nodes.AsyncWith:
        ...

    def _visit_with(
        self,
        cls: type[_WithT],
        node: ast.With | ast.AsyncWith,
        parent: NodeNG,
    ) -> _WithT:
        col_offset = node.col_offset
        if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncWith) and self._data:
            # pylint: disable-next=unsubscriptable-object
            col_offset = self._data[node.lineno - 1].index("async")

        newnode = cls(
            lineno=node.lineno,
            col_offset=col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )

        def visit_child(child: ast.withitem) -> tuple[NodeNG, NodeNG | None]:
            expr = self.visit(child.context_expr, newnode)
            var = self.visit(child.optional_vars, newnode)
            return expr, var

        type_annotation = self.check_type_comment(node, parent=newnode)
        newnode.postinit(
            items=[visit_child(child) for child in node.items],
            body=[self.visit(child, newnode) for child in node.body],
            type_annotation=type_annotation,
        )
        return newnode

    def visit_with(self, node: ast.With, parent: NodeNG) -> NodeNG:
        return self._visit_with(nodes.With, node, parent)

    def visit_yield(self, node: ast.Yield, parent: NodeNG) -> NodeNG:
        """Visit a Yield node by returning a fresh instance of it."""
        newnode = nodes.Yield(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(self.visit(node.value, newnode))
        return newnode

    def visit_yieldfrom(self, node: ast.YieldFrom, parent: NodeNG) -> NodeNG:
        newnode = nodes.YieldFrom(
            lineno=node.lineno,
            col_offset=node.col_offset,
            end_lineno=node.end_lineno,
            end_col_offset=node.end_col_offset,
            parent=parent,
        )
        newnode.postinit(self.visit(node.value, newnode))
        return newnode

    if sys.version_info >= (3, 10):

        def visit_match(self, node: ast.Match, parent: NodeNG) -> nodes.Match:
            newnode = nodes.Match(
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
            newnode.postinit(
                subject=self.visit(node.subject, newnode),
                cases=[self.visit(case, newnode) for case in node.cases],
            )
            return newnode

        def visit_matchcase(
            self, node: ast.match_case, parent: NodeNG
        ) -> nodes.MatchCase:
            newnode = nodes.MatchCase(parent=parent)
            newnode.postinit(
                pattern=self.visit(node.pattern, newnode),
                guard=self.visit(node.guard, newnode),
                body=[self.visit(child, newnode) for child in node.body],
            )
            return newnode

        def visit_matchvalue(
            self, node: ast.MatchValue, parent: NodeNG
        ) -> nodes.MatchValue:
            newnode = nodes.MatchValue(
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
            newnode.postinit(value=self.visit(node.value, newnode))
            return newnode

        def visit_matchsingleton(
            self, node: ast.MatchSingleton, parent: NodeNG
        ) -> nodes.MatchSingleton:
            return nodes.MatchSingleton(
                value=node.value,
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )

        def visit_matchsequence(
            self, node: ast.MatchSequence, parent: NodeNG
        ) -> nodes.MatchSequence:
            newnode = nodes.MatchSequence(
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
            newnode.postinit(
                patterns=[self.visit(pattern, newnode) for pattern in node.patterns]
            )
            return newnode

        def visit_matchmapping(
            self, node: ast.MatchMapping, parent: NodeNG
        ) -> nodes.MatchMapping:
            newnode = nodes.MatchMapping(
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
            # Add AssignName node for 'node.name'
            # https://bugs.python.org/issue43994
            newnode.postinit(
                keys=[self.visit(child, newnode) for child in node.keys],
                patterns=[self.visit(pattern, newnode) for pattern in node.patterns],
                rest=self.visit_assignname(node, newnode, node.rest),
            )
            return newnode

        def visit_matchclass(
            self, node: ast.MatchClass, parent: NodeNG
        ) -> nodes.MatchClass:
            newnode = nodes.MatchClass(
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
            newnode.postinit(
                cls=self.visit(node.cls, newnode),
                patterns=[self.visit(pattern, newnode) for pattern in node.patterns],
                kwd_attrs=node.kwd_attrs,
                kwd_patterns=[
                    self.visit(pattern, newnode) for pattern in node.kwd_patterns
                ],
            )
            return newnode

        def visit_matchstar(
            self, node: ast.MatchStar, parent: NodeNG
        ) -> nodes.MatchStar:
            newnode = nodes.MatchStar(
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
            # Add AssignName node for 'node.name'
            # https://bugs.python.org/issue43994
            newnode.postinit(name=self.visit_assignname(node, newnode, node.name))
            return newnode

        def visit_matchas(self, node: ast.MatchAs, parent: NodeNG) -> nodes.MatchAs:
            newnode = nodes.MatchAs(
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
            # Add AssignName node for 'node.name'
            # https://bugs.python.org/issue43994
            newnode.postinit(
                pattern=self.visit(node.pattern, newnode),
                name=self.visit_assignname(node, newnode, node.name),
            )
            return newnode

        def visit_matchor(self, node: ast.MatchOr, parent: NodeNG) -> nodes.MatchOr:
            newnode = nodes.MatchOr(
                lineno=node.lineno,
                col_offset=node.col_offset,
                end_lineno=node.end_lineno,
                end_col_offset=node.end_col_offset,
                parent=parent,
            )
            newnode.postinit(
                patterns=[self.visit(pattern, newnode) for pattern in node.patterns]
            )
            return newnode

back/venvmsg/Lib/site-packages/astroid/rebuilder.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

"""Utility functions for test code that uses astroid ASTs as input."""

from __future__ import annotations

import contextlib
import functools
import sys
import warnings
from collections.abc import Callable

import pytest

from astroid import manager, nodes, transforms


def require_version(minver: str = "0.0.0", maxver: str = "4.0.0") -> Callable:
    """Compare version of python interpreter to the given one and skips the test if older."""

    def parse(python_version: str) -> tuple[int, ...]:
        try:
            return tuple(int(v) for v in python_version.split("."))
        except ValueError as e:
            msg = f"{python_version} is not a correct version : should be X.Y[.Z]."
            raise ValueError(msg) from e

    min_version = parse(minver)
    max_version = parse(maxver)

    def check_require_version(f):
        current: tuple[int, int, int] = sys.version_info[:3]
        if min_version < current <= max_version:
            return f

        version: str = ".".join(str(v) for v in sys.version_info)

        @functools.wraps(f)
        def new_f(*args, **kwargs):
            if current <= min_version:
                pytest.skip(f"Needs Python > {minver}. Current version is {version}.")
            elif current > max_version:
                pytest.skip(f"Needs Python <= {maxver}. Current version is {version}.")

        return new_f

    return check_require_version


def get_name_node(start_from, name, index=0):
    return [n for n in start_from.nodes_of_class(nodes.Name) if n.name == name][index]


@contextlib.contextmanager
def enable_warning(warning):
    warnings.simplefilter("always", warning)
    try:
        yield
    finally:
        # Reset it to default value, so it will take
        # into account the values from the -W flag.
        warnings.simplefilter("default", warning)


def brainless_manager():
    m = manager.AstroidManager()
    # avoid caching into the AstroidManager borg since we get problems
    # with other tests :
    m.__dict__ = {}
    m._failed_import_hooks = []
    m.astroid_cache = {}
    m._mod_file_cache = {}
    m._transform = transforms.TransformVisitor()
    m.extension_package_whitelist = set()
    return m

back/venvmsg/Lib/site-packages/astroid/test_utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from collections import defaultdict
from collections.abc import Callable
from typing import TYPE_CHECKING, List, Optional, Tuple, TypeVar, Union, cast, overload

from astroid.context import _invalidate_cache
from astroid.typing import SuccessfulInferenceResult, TransformFn

if TYPE_CHECKING:
    from astroid import nodes

    _SuccessfulInferenceResultT = TypeVar(
        "_SuccessfulInferenceResultT", bound=SuccessfulInferenceResult
    )
    _Predicate = Optional[Callable[[_SuccessfulInferenceResultT], bool]]

_Vistables = Union[
    "nodes.NodeNG", List["nodes.NodeNG"], Tuple["nodes.NodeNG", ...], str, None
]
_VisitReturns = Union[
    SuccessfulInferenceResult,
    List[SuccessfulInferenceResult],
    Tuple[SuccessfulInferenceResult, ...],
    str,
    None,
]


class TransformVisitor:
    """A visitor for handling transforms.

    The standard approach of using it is to call
    :meth:`~visit` with an *astroid* module and the class
    will take care of the rest, walking the tree and running the
    transforms for each encountered node.

    Based on its usage in AstroidManager.brain, it should not be reinstantiated.
    """

    def __init__(self) -> None:
        # The typing here is incorrect, but it's the best we can do
        # Refer to register_transform and unregister_transform for the correct types
        self.transforms: defaultdict[
            type[SuccessfulInferenceResult],
            list[
                tuple[
                    TransformFn[SuccessfulInferenceResult],
                    _Predicate[SuccessfulInferenceResult],
                ]
            ],
        ] = defaultdict(list)

    def _transform(self, node: SuccessfulInferenceResult) -> SuccessfulInferenceResult:
        """Call matching transforms for the given node if any and return the
        transformed node.
        """
        cls = node.__class__

        for transform_func, predicate in self.transforms[cls]:
            if predicate is None or predicate(node):
                ret = transform_func(node)
                # if the transformation function returns something, it's
                # expected to be a replacement for the node
                if ret is not None:
                    _invalidate_cache()
                    node = ret
                if ret.__class__ != cls:
                    # Can no longer apply the rest of the transforms.
                    break
        return node

    def _visit(self, node: nodes.NodeNG) -> SuccessfulInferenceResult:
        for name in node._astroid_fields:
            value = getattr(node, name)
            value = cast(_Vistables, value)
            visited = self._visit_generic(value)
            if visited != value:
                setattr(node, name, visited)
        return self._transform(node)

    @overload
    def _visit_generic(self, node: None) -> None:
        ...

    @overload
    def _visit_generic(self, node: str) -> str:
        ...

    @overload
    def _visit_generic(
        self, node: list[nodes.NodeNG]
    ) -> list[SuccessfulInferenceResult]:
        ...

    @overload
    def _visit_generic(
        self, node: tuple[nodes.NodeNG, ...]
    ) -> tuple[SuccessfulInferenceResult, ...]:
        ...

    @overload
    def _visit_generic(self, node: nodes.NodeNG) -> SuccessfulInferenceResult:
        ...

    def _visit_generic(self, node: _Vistables) -> _VisitReturns:
        if isinstance(node, list):
            return [self._visit_generic(child) for child in node]
        if isinstance(node, tuple):
            return tuple(self._visit_generic(child) for child in node)
        if not node or isinstance(node, str):
            return node

        return self._visit(node)

    def register_transform(
        self,
        node_class: type[_SuccessfulInferenceResultT],
        transform: TransformFn[_SuccessfulInferenceResultT],
        predicate: _Predicate[_SuccessfulInferenceResultT] | None = None,
    ) -> None:
        """Register `transform(node)` function to be applied on the given node.

        The transform will only be applied if `predicate` is None or returns true
        when called with the node as argument.

        The transform function may return a value which is then used to
        substitute the original node in the tree.
        """
        self.transforms[node_class].append((transform, predicate))  # type: ignore[index, arg-type]

    def unregister_transform(
        self,
        node_class: type[_SuccessfulInferenceResultT],
        transform: TransformFn[_SuccessfulInferenceResultT],
        predicate: _Predicate[_SuccessfulInferenceResultT] | None = None,
    ) -> None:
        """Unregister the given transform."""
        self.transforms[node_class].remove((transform, predicate))  # type: ignore[index, arg-type]

    def visit(self, node: nodes.NodeNG) -> SuccessfulInferenceResult:
        """Walk the given astroid *tree* and transform each encountered node.

        Only the nodes which have transforms registered will actually
        be replaced or changed.
        """
        return self._visit(node)

back/venvmsg/Lib/site-packages/astroid/transforms.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import (
    TYPE_CHECKING,
    Any,
    Callable,
    Generator,
    Generic,
    Protocol,
    TypedDict,
    TypeVar,
    Union,
)

if TYPE_CHECKING:
    from collections.abc import Iterator

    from astroid import bases, exceptions, nodes, transforms, util
    from astroid.context import InferenceContext
    from astroid.interpreter._import import spec


class InferenceErrorInfo(TypedDict):
    """Store additional Inference error information
    raised with StopIteration exception.
    """

    node: nodes.NodeNG
    context: InferenceContext | None


class AstroidManagerBrain(TypedDict):
    """Dictionary to store relevant information for a AstroidManager class."""

    astroid_cache: dict[str, nodes.Module]
    _mod_file_cache: dict[
        tuple[str, str | None], spec.ModuleSpec | exceptions.AstroidImportError
    ]
    _failed_import_hooks: list[Callable[[str], nodes.Module]]
    always_load_extensions: bool
    optimize_ast: bool
    max_inferable_values: int
    extension_package_whitelist: set[str]
    _transform: transforms.TransformVisitor


InferenceResult = Union["nodes.NodeNG", "util.UninferableBase", "bases.Proxy"]
SuccessfulInferenceResult = Union["nodes.NodeNG", "bases.Proxy"]
_SuccessfulInferenceResultT = TypeVar(
    "_SuccessfulInferenceResultT", bound=SuccessfulInferenceResult
)
_SuccessfulInferenceResultT_contra = TypeVar(
    "_SuccessfulInferenceResultT_contra",
    bound=SuccessfulInferenceResult,
    contravariant=True,
)

ConstFactoryResult = Union[
    "nodes.List",
    "nodes.Set",
    "nodes.Tuple",
    "nodes.Dict",
    "nodes.Const",
    "nodes.EmptyNode",
]

InferBinaryOp = Callable[
    [
        _SuccessfulInferenceResultT,
        Union["nodes.AugAssign", "nodes.BinOp"],
        str,
        InferenceResult,
        "InferenceContext",
        SuccessfulInferenceResult,
    ],
    Generator[InferenceResult, None, None],
]


class InferFn(Protocol, Generic[_SuccessfulInferenceResultT_contra]):
    def __call__(
        self,
        node: _SuccessfulInferenceResultT_contra,
        context: InferenceContext | None = None,
        **kwargs: Any,
    ) -> Iterator[InferenceResult]:
        ...  # pragma: no cover


class TransformFn(Protocol, Generic[_SuccessfulInferenceResultT]):
    def __call__(
        self,
        node: _SuccessfulInferenceResultT,
        infer_function: InferFn[_SuccessfulInferenceResultT] = ...,
    ) -> _SuccessfulInferenceResultT | None:
        ...  # pragma: no cover

back/venvmsg/Lib/site-packages/astroid/typing.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt


from __future__ import annotations

import warnings
from typing import TYPE_CHECKING, Any, Final, Literal

from astroid.exceptions import InferenceError

if TYPE_CHECKING:
    from astroid import bases, nodes
    from astroid.context import InferenceContext
    from astroid.typing import InferenceResult


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) -> Literal["Uninferable"]:
        return "Uninferable"

    __str__ = __repr__

    def __getattribute__(self, name: str) -> Any:
        if name == "next":
            raise AttributeError("next method should not be called")
        if name.startswith("__") and name.endswith("__"):
            return object.__getattribute__(self, name)
        if name == "accept":
            return object.__getattribute__(self, name)
        return self

    def __call__(self, *args: Any, **kwargs: Any) -> UninferableBase:
        return self

    def __bool__(self) -> Literal[False]:
        return False

    __nonzero__ = __bool__

    def accept(self, visitor):
        return visitor.visit_uninferable(self)


Uninferable: Final = UninferableBase()


class BadOperationMessage:
    """Object which describes a TypeError occurred somewhere in the inference chain.

    This is not an exception, but a container object which holds the types and
    the error which occurred.
    """


class BadUnaryOperationMessage(BadOperationMessage):
    """Object which describes operational failures on UnaryOps."""

    def __init__(self, operand, op, error):
        self.operand = operand
        self.op = op
        self.error = error

    @property
    def _object_type_helper(self):
        from astroid import helpers  # pylint: disable=import-outside-toplevel

        return helpers.object_type

    def _object_type(self, obj):
        objtype = self._object_type_helper(obj)
        if isinstance(objtype, UninferableBase):
            return None

        return objtype

    def __str__(self) -> str:
        if hasattr(self.operand, "name"):
            operand_type = self.operand.name
        else:
            object_type = self._object_type(self.operand)
            if hasattr(object_type, "name"):
                operand_type = object_type.name
            else:
                # Just fallback to as_string
                operand_type = object_type.as_string()

        msg = "bad operand type for unary {}: {}"
        return msg.format(self.op, operand_type)


class BadBinaryOperationMessage(BadOperationMessage):
    """Object which describes type errors for BinOps."""

    def __init__(self, left_type, op, right_type):
        self.left_type = left_type
        self.right_type = right_type
        self.op = op

    def __str__(self) -> str:
        msg = "unsupported operand type(s) for {}: {!r} and {!r}"
        return msg.format(self.op, self.left_type.name, self.right_type.name)


def _instancecheck(cls, other) -> bool:
    wrapped = cls.__wrapped__
    other_cls = other.__class__
    is_instance_of = wrapped is other_cls or issubclass(other_cls, wrapped)
    warnings.warn(
        "%r is deprecated and slated for removal in astroid "
        "2.0, use %r instead" % (cls.__class__.__name__, wrapped.__name__),
        PendingDeprecationWarning,
        stacklevel=2,
    )
    return is_instance_of


def check_warnings_filter() -> bool:
    """Return True if any other than the default DeprecationWarning filter is enabled.

    https://docs.python.org/3/library/warnings.html#default-warning-filter
    """
    return any(
        issubclass(DeprecationWarning, filter[2])
        and filter[0] != "ignore"
        and filter[3] != "__main__"
        for filter in warnings.filters
    )


def safe_infer(
    node: nodes.NodeNG | bases.Proxy | UninferableBase,
    context: InferenceContext | None = None,
) -> InferenceResult | None:
    """Return the inferred value for the given node.

    Return None if inference failed or if there is some ambiguity (more than
    one node has been inferred).
    """
    if isinstance(node, UninferableBase):
        return node
    try:
        inferit = node.infer(context=context)
        value = next(inferit)
    except (InferenceError, StopIteration):
        return None
    try:
        next(inferit)
        return None  # None if there is ambiguity on the inferred node
    except InferenceError:
        return None  # there is some kind of ambiguity
    except StopIteration:
        return value

back/venvmsg/Lib/site-packages/astroid/util.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8

try:
    # this works on Python < 3.12
    from imp import *

except ImportError:
    # this is a limited emulation for Python >= 3.12.
    # Note that this is used only for tests or for the old ffi.verify().
    # This is copied from the source code of Python 3.11.

    from _imp import (acquire_lock, release_lock,
                      is_builtin, is_frozen)

    from importlib._bootstrap import _load

    from importlib import machinery
    import os
    import sys
    import tokenize

    SEARCH_ERROR = 0
    PY_SOURCE = 1
    PY_COMPILED = 2
    C_EXTENSION = 3
    PY_RESOURCE = 4
    PKG_DIRECTORY = 5
    C_BUILTIN = 6
    PY_FROZEN = 7
    PY_CODERESOURCE = 8
    IMP_HOOK = 9

    def get_suffixes():
        extensions = [(s, 'rb', C_EXTENSION)
                      for s in machinery.EXTENSION_SUFFIXES]
        source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES]
        bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES]
        return extensions + source + bytecode

    def find_module(name, path=None):
        if not isinstance(name, str):
            raise TypeError("'name' must be a str, not {}".format(type(name)))
        elif not isinstance(path, (type(None), list)):
            # Backwards-compatibility
            raise RuntimeError("'path' must be None or a list, "
                               "not {}".format(type(path)))

        if path is None:
            if is_builtin(name):
                return None, None, ('', '', C_BUILTIN)
            elif is_frozen(name):
                return None, None, ('', '', PY_FROZEN)
            else:
                path = sys.path

        for entry in path:
            package_directory = os.path.join(entry, name)
            for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]:
                package_file_name = '__init__' + suffix
                file_path = os.path.join(package_directory, package_file_name)
                if os.path.isfile(file_path):
                    return None, package_directory, ('', '', PKG_DIRECTORY)
            for suffix, mode, type_ in get_suffixes():
                file_name = name + suffix
                file_path = os.path.join(entry, file_name)
                if os.path.isfile(file_path):
                    break
            else:
                continue
            break  # Break out of outer loop when breaking out of inner loop.
        else:
            raise ImportError(name, name=name)

        encoding = None
        if 'b' not in mode:
            with open(file_path, 'rb') as file:
                encoding = tokenize.detect_encoding(file.readline)[0]
        file = open(file_path, mode, encoding=encoding)
        return file, file_path, (suffix, mode, type_)

    def load_dynamic(name, path, file=None):
        loader = machinery.ExtensionFileLoader(name, path)
        spec = machinery.ModuleSpec(name=name, loader=loader, origin=path)
        return _load(spec)

back/venvmsg/Lib/site-packages/cffi/_imp_emulation.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""
Temporary shim module to indirect the bits of distutils we need from setuptools/distutils while providing useful
error messages beyond `No module named 'distutils' on Python >= 3.12, or when setuptools' vendored distutils is broken.

This is a compromise to avoid a hard-dep on setuptools for Python >= 3.12, since many users don't need runtime compilation support from CFFI.
"""
import sys

try:
    # import setuptools first; this is the most robust way to ensure its embedded distutils is available
    # (the .pth shim should usually work, but this is even more robust)
    import setuptools
except Exception as ex:
    if sys.version_info >= (3, 12):
        # Python 3.12 has no built-in distutils to fall back on, so any import problem is fatal
        raise Exception("This CFFI feature requires setuptools on Python >= 3.12. The setuptools module is missing or non-functional.") from ex

    # silently ignore on older Pythons (support fallback to stdlib distutils where available)
else:
    del setuptools

try:
    # bring in just the bits of distutils we need, whether they really came from setuptools or stdlib-embedded distutils
    from distutils import log, sysconfig
    from distutils.ccompiler import CCompiler
    from distutils.command.build_ext import build_ext
    from distutils.core import Distribution, Extension
    from distutils.dir_util import mkpath
    from distutils.errors import DistutilsSetupError, CompileError, LinkError
    from distutils.log import set_threshold, set_verbosity

    if sys.platform == 'win32':
        from distutils.msvc9compiler import MSVCCompiler
except Exception as ex:
    if sys.version_info >= (3, 12):
        raise Exception("This CFFI feature requires setuptools on Python >= 3.12. Please install the setuptools package.") from ex

    # anything older, just let the underlying distutils import error fly
    raise Exception("This CFFI feature requires distutils. Please install the distutils or setuptools package.") from ex

del sys

back/venvmsg/Lib/site-packages/cffi/_shimmed_dist_utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import sys, types
from .lock import allocate_lock
from .error import CDefError
from . import model

try:
    callable
except NameError:
    # Python 3.1
    from collections import Callable
    callable = lambda x: isinstance(x, Callable)

try:
    basestring
except NameError:
    # Python 3.x
    basestring = str

_unspecified = object()



class FFI(object):
    r'''
    The main top-level class that you instantiate once, or once per module.

    Example usage:

        ffi = FFI()
        ffi.cdef("""
            int printf(const char *, ...);
        """)

        C = ffi.dlopen(None)   # standard library
        -or-
        C = ffi.verify()  # use a C compiler: verify the decl above is right

        C.printf("hello, %s!\n", ffi.new("char[]", "world"))
    '''

    def __init__(self, backend=None):
        """Create an FFI instance.  The 'backend' argument is used to
        select a non-default backend, mostly for tests.
        """
        if backend is None:
            # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with
            # _cffi_backend.so compiled.
            import _cffi_backend as backend
            from . import __version__
            if backend.__version__ != __version__:
                # bad version!  Try to be as explicit as possible.
                if hasattr(backend, '__file__'):
                    # CPython
                    raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r.  When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r.  The two versions should be equal; check your installation." % (
                        __version__, __file__,
                        backend.__version__, backend.__file__))
                else:
                    # PyPy
                    raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r.  This interpreter comes with a built-in '_cffi_backend' module, which is version %s.  The two versions should be equal; check your installation." % (
                        __version__, __file__, backend.__version__))
            # (If you insist you can also try to pass the option
            # 'backend=backend_ctypes.CTypesBackend()', but don't
            # rely on it!  It's probably not going to work well.)

        from . import cparser
        self._backend = backend
        self._lock = allocate_lock()
        self._parser = cparser.Parser()
        self._cached_btypes = {}
        self._parsed_types = types.ModuleType('parsed_types').__dict__
        self._new_types = types.ModuleType('new_types').__dict__
        self._function_caches = []
        self._libraries = []
        self._cdefsources = []
        self._included_ffis = []
        self._windows_unicode = None
        self._init_once_cache = {}
        self._cdef_version = None
        self._embedding = None
        self._typecache = model.get_typecache(backend)
        if hasattr(backend, 'set_ffi'):
            backend.set_ffi(self)
        for name in list(backend.__dict__):
            if name.startswith('RTLD_'):
                setattr(self, name, getattr(backend, name))
        #
        with self._lock:
            self.BVoidP = self._get_cached_btype(model.voidp_type)
            self.BCharA = self._get_cached_btype(model.char_array_type)
        if isinstance(backend, types.ModuleType):
            # _cffi_backend: attach these constants to the class
            if not hasattr(FFI, 'NULL'):
                FFI.NULL = self.cast(self.BVoidP, 0)
                FFI.CData, FFI.CType = backend._get_types()
        else:
            # ctypes backend: attach these constants to the instance
            self.NULL = self.cast(self.BVoidP, 0)
            self.CData, self.CType = backend._get_types()
        self.buffer = backend.buffer

    def cdef(self, csource, override=False, packed=False, pack=None):
        """Parse the given C source.  This registers all declared functions,
        types, and global variables.  The functions and global variables can
        then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'.
        The types can be used in 'ffi.new()' and other functions.
        If 'packed' is specified as True, all structs declared inside this
        cdef are packed, i.e. laid out without any field alignment at all.
        Alternatively, 'pack' can be a small integer, and requests for
        alignment greater than that are ignored (pack=1 is equivalent to
        packed=True).
        """
        self._cdef(csource, override=override, packed=packed, pack=pack)

    def embedding_api(self, csource, packed=False, pack=None):
        self._cdef(csource, packed=packed, pack=pack, dllexport=True)
        if self._embedding is None:
            self._embedding = ''

    def _cdef(self, csource, override=False, **options):
        if not isinstance(csource, str):    # unicode, on Python 2
            if not isinstance(csource, basestring):
                raise TypeError("cdef() argument must be a string")
            csource = csource.encode('ascii')
        with self._lock:
            self._cdef_version = object()
            self._parser.parse(csource, override=override, **options)
            self._cdefsources.append(csource)
            if override:
                for cache in self._function_caches:
                    cache.clear()
            finishlist = self._parser._recomplete
            if finishlist:
                self._parser._recomplete = []
                for tp in finishlist:
                    tp.finish_backend_type(self, finishlist)

    def dlopen(self, name, flags=0):
        """Load and return a dynamic library identified by 'name'.
        The standard C library can be loaded by passing None.
        Note that functions and types declared by 'ffi.cdef()' are not
        linked to a particular library, just like C headers; in the
        library we only look for the actual (untyped) symbols.
        """
        if not (isinstance(name, basestring) or
                name is None or
                isinstance(name, self.CData)):
            raise TypeError("dlopen(name): name must be a file name, None, "
                            "or an already-opened 'void *' handle")
        with self._lock:
            lib, function_cache = _make_ffi_library(self, name, flags)
            self._function_caches.append(function_cache)
            self._libraries.append(lib)
        return lib

    def dlclose(self, lib):
        """Close a library obtained with ffi.dlopen().  After this call,
        access to functions or variables from the library will fail
        (possibly with a segmentation fault).
        """
        type(lib).__cffi_close__(lib)

    def _typeof_locked(self, cdecl):
        # call me with the lock!
        key = cdecl
        if key in self._parsed_types:
            return self._parsed_types[key]
        #
        if not isinstance(cdecl, str):    # unicode, on Python 2
            cdecl = cdecl.encode('ascii')
        #
        type = self._parser.parse_type(cdecl)
        really_a_function_type = type.is_raw_function
        if really_a_function_type:
            type = type.as_function_pointer()
        btype = self._get_cached_btype(type)
        result = btype, really_a_function_type
        self._parsed_types[key] = result
        return result

    def _typeof(self, cdecl, consider_function_as_funcptr=False):
        # string -> ctype object
        try:
            result = self._parsed_types[cdecl]
        except KeyError:
            with self._lock:
                result = self._typeof_locked(cdecl)
        #
        btype, really_a_function_type = result
        if really_a_function_type and not consider_function_as_funcptr:
            raise CDefError("the type %r is a function type, not a "
                            "pointer-to-function type" % (cdecl,))
        return btype

    def typeof(self, cdecl):
        """Parse the C type given as a string and return the
        corresponding <ctype> object.
        It can also be used on 'cdata' instance to get its C type.
        """
        if isinstance(cdecl, basestring):
            return self._typeof(cdecl)
        if isinstance(cdecl, self.CData):
            return self._backend.typeof(cdecl)
        if isinstance(cdecl, types.BuiltinFunctionType):
            res = _builtin_function_type(cdecl)
            if res is not None:
                return res
        if (isinstance(cdecl, types.FunctionType)
                and hasattr(cdecl, '_cffi_base_type')):
            with self._lock:
                return self._get_cached_btype(cdecl._cffi_base_type)
        raise TypeError(type(cdecl))

    def sizeof(self, cdecl):
        """Return the size in bytes of the argument.  It can be a
        string naming a C type, or a 'cdata' instance.
        """
        if isinstance(cdecl, basestring):
            BType = self._typeof(cdecl)
            return self._backend.sizeof(BType)
        else:
            return self._backend.sizeof(cdecl)

    def alignof(self, cdecl):
        """Return the natural alignment size in bytes of the C type
        given as a string.
        """
        if isinstance(cdecl, basestring):
            cdecl = self._typeof(cdecl)
        return self._backend.alignof(cdecl)

    def offsetof(self, cdecl, *fields_or_indexes):
        """Return the offset of the named field inside the given
        structure or array, which must be given as a C type name.
        You can give several field names in case of nested structures.
        You can also give numeric values which correspond to array
        items, in case of an array type.
        """
        if isinstance(cdecl, basestring):
            cdecl = self._typeof(cdecl)
        return self._typeoffsetof(cdecl, *fields_or_indexes)[1]

    def new(self, cdecl, init=None):
        """Allocate an instance according to the specified C type and
        return a pointer to it.  The specified C type must be either a
        pointer or an array: ``new('X *')`` allocates an X and returns
        a pointer to it, whereas ``new('X[n]')`` allocates an array of
        n X'es and returns an array referencing it (which works
        mostly like a pointer, like in C).  You can also use
        ``new('X[]', n)`` to allocate an array of a non-constant
        length n.

        The memory is initialized following the rules of declaring a
        global variable in C: by default it is zero-initialized, but
        an explicit initializer can be given which can be used to
        fill all or part of the memory.

        When the returned <cdata> object goes out of scope, the memory
        is freed.  In other words the returned <cdata> object has
        ownership of the value of type 'cdecl' that it points to.  This
        means that the raw data can be used as long as this object is
        kept alive, but must not be used for a longer time.  Be careful
        about that when copying the pointer to the memory somewhere
        else, e.g. into another structure.
        """
        if isinstance(cdecl, basestring):
            cdecl = self._typeof(cdecl)
        return self._backend.newp(cdecl, init)

    def new_allocator(self, alloc=None, free=None,
                      should_clear_after_alloc=True):
        """Return a new allocator, i.e. a function that behaves like ffi.new()
        but uses the provided low-level 'alloc' and 'free' functions.

        'alloc' is called with the size as argument.  If it returns NULL, a
        MemoryError is raised.  'free' is called with the result of 'alloc'
        as argument.  Both can be either Python function or directly C
        functions.  If 'free' is None, then no free function is called.
        If both 'alloc' and 'free' are None, the default is used.

        If 'should_clear_after_alloc' is set to False, then the memory
        returned by 'alloc' is assumed to be already cleared (or you are
        fine with garbage); otherwise CFFI will clear it.
        """
        compiled_ffi = self._backend.FFI()
        allocator = compiled_ffi.new_allocator(alloc, free,
                                               should_clear_after_alloc)
        def allocate(cdecl, init=None):
            if isinstance(cdecl, basestring):
                cdecl = self._typeof(cdecl)
            return allocator(cdecl, init)
        return allocate

    def cast(self, cdecl, source):
        """Similar to a C cast: returns an instance of the named C
        type initialized with the given 'source'.  The source is
        casted between integers or pointers of any type.
        """
        if isinstance(cdecl, basestring):
            cdecl = self._typeof(cdecl)
        return self._backend.cast(cdecl, source)

    def string(self, cdata, maxlen=-1):
        """Return a Python string (or unicode string) from the 'cdata'.
        If 'cdata' is a pointer or array of characters or bytes, returns
        the null-terminated string.  The returned string extends until
        the first null character, or at most 'maxlen' characters.  If
        'cdata' is an array then 'maxlen' defaults to its length.

        If 'cdata' is a pointer or array of wchar_t, returns a unicode
        string following the same rules.

        If 'cdata' is a single character or byte or a wchar_t, returns
        it as a string or unicode string.

        If 'cdata' is an enum, returns the value of the enumerator as a
        string, or 'NUMBER' if the value is out of range.
        """
        return self._backend.string(cdata, maxlen)

    def unpack(self, cdata, length):
        """Unpack an array of C data of the given length,
        returning a Python string/unicode/list.

        If 'cdata' is a pointer to 'char', returns a byte string.
        It does not stop at the first null.  This is equivalent to:
        ffi.buffer(cdata, length)[:]

        If 'cdata' is a pointer to 'wchar_t', returns a unicode string.
        'length' is measured in wchar_t's; it is not the size in bytes.

        If 'cdata' is a pointer to anything else, returns a list of
        'length' items.  This is a faster equivalent to:
        [cdata[i] for i in range(length)]
        """
        return self._backend.unpack(cdata, length)

   #def buffer(self, cdata, size=-1):
   #    """Return a read-write buffer object that references the raw C data
   #    pointed to by the given 'cdata'.  The 'cdata' must be a pointer or
   #    an array.  Can be passed to functions expecting a buffer, or directly
   #    manipulated with:
   #
   #        buf[:]          get a copy of it in a regular string, or
   #        buf[idx]        as a single character
   #        buf[:] = ...
   #        buf[idx] = ...  change the content
   #    """
   #    note that 'buffer' is a type, set on this instance by __init__

    def from_buffer(self, cdecl, python_buffer=_unspecified,
                    require_writable=False):
        """Return a cdata of the given type pointing to the data of the
        given Python object, which must support the buffer interface.
        Note that this is not meant to be used on the built-in types
        str or unicode (you can build 'char[]' arrays explicitly)
        but only on objects containing large quantities of raw data
        in some other format, like 'array.array' or numpy arrays.

        The first argument is optional and default to 'char[]'.
        """
        if python_buffer is _unspecified:
            cdecl, python_buffer = self.BCharA, cdecl
        elif isinstance(cdecl, basestring):
            cdecl = self._typeof(cdecl)
        return self._backend.from_buffer(cdecl, python_buffer,
                                         require_writable)

    def memmove(self, dest, src, n):
        """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.

        Like the C function memmove(), the memory areas may overlap;
        apart from that it behaves like the C function memcpy().

        'src' can be any cdata ptr or array, or any Python buffer object.
        'dest' can be any cdata ptr or array, or a writable Python buffer
        object.  The size to copy, 'n', is always measured in bytes.

        Unlike other methods, this one supports all Python buffer including
        byte strings and bytearrays---but it still does not support
        non-contiguous buffers.
        """
        return self._backend.memmove(dest, src, n)

    def callback(self, cdecl, python_callable=None, error=None, onerror=None):
        """Return a callback object or a decorator making such a
        callback object.  'cdecl' must name a C function pointer type.
        The callback invokes the specified 'python_callable' (which may
        be provided either directly or via a decorator).  Important: the
        callback object must be manually kept alive for as long as the
        callback may be invoked from the C level.
        """
        def callback_decorator_wrap(python_callable):
            if not callable(python_callable):
                raise TypeError("the 'python_callable' argument "
                                "is not callable")
            return self._backend.callback(cdecl, python_callable,
                                          error, onerror)
        if isinstance(cdecl, basestring):
            cdecl = self._typeof(cdecl, consider_function_as_funcptr=True)
        if python_callable is None:
            return callback_decorator_wrap                # decorator mode
        else:
            return callback_decorator_wrap(python_callable)  # direct mode

    def getctype(self, cdecl, replace_with=''):
        """Return a string giving the C type 'cdecl', which may be itself
        a string or a <ctype> object.  If 'replace_with' is given, it gives
        extra text to append (or insert for more complicated C types), like
        a variable name, or '*' to get actually the C type 'pointer-to-cdecl'.
        """
        if isinstance(cdecl, basestring):
            cdecl = self._typeof(cdecl)
        replace_with = replace_with.strip()
        if (replace_with.startswith('*')
                and '&[' in self._backend.getcname(cdecl, '&')):
            replace_with = '(%s)' % replace_with
        elif replace_with and not replace_with[0] in '[(':
            replace_with = ' ' + replace_with
        return self._backend.getcname(cdecl, replace_with)

    def gc(self, cdata, destructor, size=0):
        """Return a new cdata object that points to the same
        data.  Later, when this new cdata object is garbage-collected,
        'destructor(old_cdata_object)' will be called.

        The optional 'size' gives an estimate of the size, used to
        trigger the garbage collection more eagerly.  So far only used
        on PyPy.  It tells the GC that the returned object keeps alive
        roughly 'size' bytes of external memory.
        """
        return self._backend.gcp(cdata, destructor, size)

    def _get_cached_btype(self, type):
        assert self._lock.acquire(False) is False
        # call me with the lock!
        try:
            BType = self._cached_btypes[type]
        except KeyError:
            finishlist = []
            BType = type.get_cached_btype(self, finishlist)
            for type in finishlist:
                type.finish_backend_type(self, finishlist)
        return BType

    def verify(self, source='', tmpdir=None, **kwargs):
        """Verify that the current ffi signatures compile on this
        machine, and return a dynamic library object.  The dynamic
        library can be used to call functions and access global
        variables declared in this 'ffi'.  The library is compiled
        by the C compiler: it gives you C-level API compatibility
        (including calling macros).  This is unlike 'ffi.dlopen()',
        which requires binary compatibility in the signatures.
        """
        from .verifier import Verifier, _caller_dir_pycache
        #
        # If set_unicode(True) was called, insert the UNICODE and
        # _UNICODE macro declarations
        if self._windows_unicode:
            self._apply_windows_unicode(kwargs)
        #
        # Set the tmpdir here, and not in Verifier.__init__: it picks
        # up the caller's directory, which we want to be the caller of
        # ffi.verify(), as opposed to the caller of Veritier().
        tmpdir = tmpdir or _caller_dir_pycache()
        #
        # Make a Verifier() and use it to load the library.
        self.verifier = Verifier(self, source, tmpdir, **kwargs)
        lib = self.verifier.load_library()
        #
        # Save the loaded library for keep-alive purposes, even
        # if the caller doesn't keep it alive itself (it should).
        self._libraries.append(lib)
        return lib

    def _get_errno(self):
        return self._backend.get_errno()
    def _set_errno(self, errno):
        self._backend.set_errno(errno)
    errno = property(_get_errno, _set_errno, None,
                     "the value of 'errno' from/to the C calls")

    def getwinerror(self, code=-1):
        return self._backend.getwinerror(code)

    def _pointer_to(self, ctype):
        with self._lock:
            return model.pointer_cache(self, ctype)

    def addressof(self, cdata, *fields_or_indexes):
        """Return the address of a <cdata 'struct-or-union'>.
        If 'fields_or_indexes' are given, returns the address of that
        field or array item in the structure or array, recursively in
        case of nested structures.
        """
        try:
            ctype = self._backend.typeof(cdata)
        except TypeError:
            if '__addressof__' in type(cdata).__dict__:
                return type(cdata).__addressof__(cdata, *fields_or_indexes)
            raise
        if fields_or_indexes:
            ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes)
        else:
            if ctype.kind == "pointer":
                raise TypeError("addressof(pointer)")
            offset = 0
        ctypeptr = self._pointer_to(ctype)
        return self._backend.rawaddressof(ctypeptr, cdata, offset)

    def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes):
        ctype, offset = self._backend.typeoffsetof(ctype, field_or_index)
        for field1 in fields_or_indexes:
            ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1)
            offset += offset1
        return ctype, offset

    def include(self, ffi_to_include):
        """Includes the typedefs, structs, unions and enums defined
        in another FFI instance.  Usage is similar to a #include in C,
        where a part of the program might include types defined in
        another part for its own usage.  Note that the include()
        method has no effect on functions, constants and global
        variables, which must anyway be accessed directly from the
        lib object returned by the original FFI instance.
        """
        if not isinstance(ffi_to_include, FFI):
            raise TypeError("ffi.include() expects an argument that is also of"
                            " type cffi.FFI, not %r" % (
                                type(ffi_to_include).__name__,))
        if ffi_to_include is self:
            raise ValueError("self.include(self)")
        with ffi_to_include._lock:
            with self._lock:
                self._parser.include(ffi_to_include._parser)
                self._cdefsources.append('[')
                self._cdefsources.extend(ffi_to_include._cdefsources)
                self._cdefsources.append(']')
                self._included_ffis.append(ffi_to_include)

    def new_handle(self, x):
        return self._backend.newp_handle(self.BVoidP, x)

    def from_handle(self, x):
        return self._backend.from_handle(x)

    def release(self, x):
        self._backend.release(x)

    def set_unicode(self, enabled_flag):
        """Windows: if 'enabled_flag' is True, enable the UNICODE and
        _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR
        to be (pointers to) wchar_t.  If 'enabled_flag' is False,
        declare these types to be (pointers to) plain 8-bit characters.
        This is mostly for backward compatibility; you usually want True.
        """
        if self._windows_unicode is not None:
            raise ValueError("set_unicode() can only be called once")
        enabled_flag = bool(enabled_flag)
        if enabled_flag:
            self.cdef("typedef wchar_t TBYTE;"
                      "typedef wchar_t TCHAR;"
                      "typedef const wchar_t *LPCTSTR;"
                      "typedef const wchar_t *PCTSTR;"
                      "typedef wchar_t *LPTSTR;"
                      "typedef wchar_t *PTSTR;"
                      "typedef TBYTE *PTBYTE;"
                      "typedef TCHAR *PTCHAR;")
        else:
            self.cdef("typedef char TBYTE;"
                      "typedef char TCHAR;"
                      "typedef const char *LPCTSTR;"
                      "typedef const char *PCTSTR;"
                      "typedef char *LPTSTR;"
                      "typedef char *PTSTR;"
                      "typedef TBYTE *PTBYTE;"
                      "typedef TCHAR *PTCHAR;")
        self._windows_unicode = enabled_flag

    def _apply_windows_unicode(self, kwds):
        defmacros = kwds.get('define_macros', ())
        if not isinstance(defmacros, (list, tuple)):
            raise TypeError("'define_macros' must be a list or tuple")
        defmacros = list(defmacros) + [('UNICODE', '1'),
                                       ('_UNICODE', '1')]
        kwds['define_macros'] = defmacros

    def _apply_embedding_fix(self, kwds):
        # must include an argument like "-lpython2.7" for the compiler
        def ensure(key, value):
            lst = kwds.setdefault(key, [])
            if value not in lst:
                lst.append(value)
        #
        if '__pypy__' in sys.builtin_module_names:
            import os
            if sys.platform == "win32":
                # we need 'libpypy-c.lib'.  Current distributions of
                # pypy (>= 4.1) contain it as 'libs/python27.lib'.
                pythonlib = "python{0[0]}{0[1]}".format(sys.version_info)
                if hasattr(sys, 'prefix'):
                    ensure('library_dirs', os.path.join(sys.prefix, 'libs'))
            else:
                # we need 'libpypy-c.{so,dylib}', which should be by
                # default located in 'sys.prefix/bin' for installed
                # systems.
                if sys.version_info < (3,):
                    pythonlib = "pypy-c"
                else:
                    pythonlib = "pypy3-c"
                if hasattr(sys, 'prefix'):
                    ensure('library_dirs', os.path.join(sys.prefix, 'bin'))
            # On uninstalled pypy's, the libpypy-c is typically found in
            # .../pypy/goal/.
            if hasattr(sys, 'prefix'):
                ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal'))
        else:
            if sys.platform == "win32":
                template = "python%d%d"
                if hasattr(sys, 'gettotalrefcount'):
                    template += '_d'
            else:
                try:
                    import sysconfig
                except ImportError:    # 2.6
                    from cffi._shimmed_dist_utils import sysconfig
                template = "python%d.%d"
                if sysconfig.get_config_var('DEBUG_EXT'):
                    template += sysconfig.get_config_var('DEBUG_EXT')
            pythonlib = (template %
                    (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
            if hasattr(sys, 'abiflags'):
                pythonlib += sys.abiflags
        ensure('libraries', pythonlib)
        if sys.platform == "win32":
            ensure('extra_link_args', '/MANIFEST')

    def set_source(self, module_name, source, source_extension='.c', **kwds):
        import os
        if hasattr(self, '_assigned_source'):
            raise ValueError("set_source() cannot be called several times "
                             "per ffi object")
        if not isinstance(module_name, basestring):
            raise TypeError("'module_name' must be a string")
        if os.sep in module_name or (os.altsep and os.altsep in module_name):
            raise ValueError("'module_name' must not contain '/': use a dotted "
                             "name to make a 'package.module' location")
        self._assigned_source = (str(module_name), source,
                                 source_extension, kwds)

    def set_source_pkgconfig(self, module_name, pkgconfig_libs, source,
                             source_extension='.c', **kwds):
        from . import pkgconfig
        if not isinstance(pkgconfig_libs, list):
            raise TypeError("the pkgconfig_libs argument must be a list "
                            "of package names")
        kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs)
        pkgconfig.merge_flags(kwds, kwds2)
        self.set_source(module_name, source, source_extension, **kwds)

    def distutils_extension(self, tmpdir='build', verbose=True):
        from cffi._shimmed_dist_utils import mkpath
        from .recompiler import recompile
        #
        if not hasattr(self, '_assigned_source'):
            if hasattr(self, 'verifier'):     # fallback, 'tmpdir' ignored
                return self.verifier.get_extension()
            raise ValueError("set_source() must be called before"
                             " distutils_extension()")
        module_name, source, source_extension, kwds = self._assigned_source
        if source is None:
            raise TypeError("distutils_extension() is only for C extension "
                            "modules, not for dlopen()-style pure Python "
                            "modules")
        mkpath(tmpdir)
        ext, updated = recompile(self, module_name,
                                 source, tmpdir=tmpdir, extradir=tmpdir,
                                 source_extension=source_extension,
                                 call_c_compiler=False, **kwds)
        if verbose:
            if updated:
                sys.stderr.write("regenerated: %r\n" % (ext.sources[0],))
            else:
                sys.stderr.write("not modified: %r\n" % (ext.sources[0],))
        return ext

    def emit_c_code(self, filename):
        from .recompiler import recompile
        #
        if not hasattr(self, '_assigned_source'):
            raise ValueError("set_source() must be called before emit_c_code()")
        module_name, source, source_extension, kwds = self._assigned_source
        if source is None:
            raise TypeError("emit_c_code() is only for C extension modules, "
                            "not for dlopen()-style pure Python modules")
        recompile(self, module_name, source,
                  c_file=filename, call_c_compiler=False, **kwds)

    def emit_python_code(self, filename):
        from .recompiler import recompile
        #
        if not hasattr(self, '_assigned_source'):
            raise ValueError("set_source() must be called before emit_c_code()")
        module_name, source, source_extension, kwds = self._assigned_source
        if source is not None:
            raise TypeError("emit_python_code() is only for dlopen()-style "
                            "pure Python modules, not for C extension modules")
        recompile(self, module_name, source,
                  c_file=filename, call_c_compiler=False, **kwds)

    def compile(self, tmpdir='.', verbose=0, target=None, debug=None):
        """The 'target' argument gives the final file name of the
        compiled DLL.  Use '*' to force distutils' choice, suitable for
        regular CPython C API modules.  Use a file name ending in '.*'
        to ask for the system's default extension for dynamic libraries
        (.so/.dll/.dylib).

        The default is '*' when building a non-embedded C API extension,
        and (module_name + '.*') when building an embedded library.
        """
        from .recompiler import recompile
        #
        if not hasattr(self, '_assigned_source'):
            raise ValueError("set_source() must be called before compile()")
        module_name, source, source_extension, kwds = self._assigned_source
        return recompile(self, module_name, source, tmpdir=tmpdir,
                         target=target, source_extension=source_extension,
                         compiler_verbose=verbose, debug=debug, **kwds)

    def init_once(self, func, tag):
        # Read _init_once_cache[tag], which is either (False, lock) if
        # we're calling the function now in some thread, or (True, result).
        # Don't call setdefault() in most cases, to avoid allocating and
        # immediately freeing a lock; but still use setdefaut() to avoid
        # races.
        try:
            x = self._init_once_cache[tag]
        except KeyError:
            x = self._init_once_cache.setdefault(tag, (False, allocate_lock()))
        # Common case: we got (True, result), so we return the result.
        if x[0]:
            return x[1]
        # Else, it's a lock.  Acquire it to serialize the following tests.
        with x[1]:
            # Read again from _init_once_cache the current status.
            x = self._init_once_cache[tag]
            if x[0]:
                return x[1]
            # Call the function and store the result back.
            result = func()
            self._init_once_cache[tag] = (True, result)
        return result

    def embedding_init_code(self, pysource):
        if self._embedding:
            raise ValueError("embedding_init_code() can only be called once")
        # fix 'pysource' before it gets dumped into the C file:
        # - remove empty lines at the beginning, so it starts at "line 1"
        # - dedent, if all non-empty lines are indented
        # - check for SyntaxErrors
        import re
        match = re.match(r'\s*\n', pysource)
        if match:
            pysource = pysource[match.end():]
        lines = pysource.splitlines() or ['']
        prefix = re.match(r'\s*', lines[0]).group()
        for i in range(1, len(lines)):
            line = lines[i]
            if line.rstrip():
                while not line.startswith(prefix):
                    prefix = prefix[:-1]
        i = len(prefix)
        lines = [line[i:]+'\n' for line in lines]
        pysource = ''.join(lines)
        #
        compile(pysource, "cffi_init", "exec")
        #
        self._embedding = pysource

    def def_extern(self, *args, **kwds):
        raise ValueError("ffi.def_extern() is only available on API-mode FFI "
                         "objects")

    def list_types(self):
        """Returns the user type names known to this FFI instance.
        This returns a tuple containing three lists of names:
        (typedef_names, names_of_structs, names_of_unions)
        """
        typedefs = []
        structs = []
        unions = []
        for key in self._parser._declarations:
            if key.startswith('typedef '):
                typedefs.append(key[8:])
            elif key.startswith('struct '):
                structs.append(key[7:])
            elif key.startswith('union '):
                unions.append(key[6:])
        typedefs.sort()
        structs.sort()
        unions.sort()
        return (typedefs, structs, unions)


def _load_backend_lib(backend, name, flags):
    import os
    if not isinstance(name, basestring):
        if sys.platform != "win32" or name is not None:
            return backend.load_library(name, flags)
        name = "c"    # Windows: load_library(None) fails, but this works
                      # on Python 2 (backward compatibility hack only)
    first_error = None
    if '.' in name or '/' in name or os.sep in name:
        try:
            return backend.load_library(name, flags)
        except OSError as e:
            first_error = e
    import ctypes.util
    path = ctypes.util.find_library(name)
    if path is None:
        if name == "c" and sys.platform == "win32" and sys.version_info >= (3,):
            raise OSError("dlopen(None) cannot work on Windows for Python 3 "
                          "(see http://bugs.python.org/issue23606)")
        msg = ("ctypes.util.find_library() did not manage "
               "to locate a library called %r" % (name,))
        if first_error is not None:
            msg = "%s.  Additionally, %s" % (first_error, msg)
        raise OSError(msg)
    return backend.load_library(path, flags)

def _make_ffi_library(ffi, libname, flags):
    backend = ffi._backend
    backendlib = _load_backend_lib(backend, libname, flags)
    #
    def accessor_function(name):
        key = 'function ' + name
        tp, _ = ffi._parser._declarations[key]
        BType = ffi._get_cached_btype(tp)
        value = backendlib.load_function(BType, name)
        library.__dict__[name] = value
    #
    def accessor_variable(name):
        key = 'variable ' + name
        tp, _ = ffi._parser._declarations[key]
        BType = ffi._get_cached_btype(tp)
        read_variable = backendlib.read_variable
        write_variable = backendlib.write_variable
        setattr(FFILibrary, name, property(
            lambda self: read_variable(BType, name),
            lambda self, value: write_variable(BType, name, value)))
    #
    def addressof_var(name):
        try:
            return addr_variables[name]
        except KeyError:
            with ffi._lock:
                if name not in addr_variables:
                    key = 'variable ' + name
                    tp, _ = ffi._parser._declarations[key]
                    BType = ffi._get_cached_btype(tp)
                    if BType.kind != 'array':
                        BType = model.pointer_cache(ffi, BType)
                    p = backendlib.load_function(BType, name)
                    addr_variables[name] = p
            return addr_variables[name]
    #
    def accessor_constant(name):
        raise NotImplementedError("non-integer constant '%s' cannot be "
                                  "accessed from a dlopen() library" % (name,))
    #
    def accessor_int_constant(name):
        library.__dict__[name] = ffi._parser._int_constants[name]
    #
    accessors = {}
    accessors_version = [False]
    addr_variables = {}
    #
    def update_accessors():
        if accessors_version[0] is ffi._cdef_version:
            return
        #
        for key, (tp, _) in ffi._parser._declarations.items():
            if not isinstance(tp, model.EnumType):
                tag, name = key.split(' ', 1)
                if tag == 'function':
                    accessors[name] = accessor_function
                elif tag == 'variable':
                    accessors[name] = accessor_variable
                elif tag == 'constant':
                    accessors[name] = accessor_constant
            else:
                for i, enumname in enumerate(tp.enumerators):
                    def accessor_enum(name, tp=tp, i=i):
                        tp.check_not_partial()
                        library.__dict__[name] = tp.enumvalues[i]
                    accessors[enumname] = accessor_enum
        for name in ffi._parser._int_constants:
            accessors.setdefault(name, accessor_int_constant)
        accessors_version[0] = ffi._cdef_version
    #
    def make_accessor(name):
        with ffi._lock:
            if name in library.__dict__ or name in FFILibrary.__dict__:
                return    # added by another thread while waiting for the lock
            if name not in accessors:
                update_accessors()
                if name not in accessors:
                    raise AttributeError(name)
            accessors[name](name)
    #
    class FFILibrary(object):
        def __getattr__(self, name):
            make_accessor(name)
            return getattr(self, name)
        def __setattr__(self, name, value):
            try:
                property = getattr(self.__class__, name)
            except AttributeError:
                make_accessor(name)
                setattr(self, name, value)
            else:
                property.__set__(self, value)
        def __dir__(self):
            with ffi._lock:
                update_accessors()
                return accessors.keys()
        def __addressof__(self, name):
            if name in library.__dict__:
                return library.__dict__[name]
            if name in FFILibrary.__dict__:
                return addressof_var(name)
            make_accessor(name)
            if name in library.__dict__:
                return library.__dict__[name]
            if name in FFILibrary.__dict__:
                return addressof_var(name)
            raise AttributeError("cffi library has no function or "
                                 "global variable named '%s'" % (name,))
        def __cffi_close__(self):
            backendlib.close_lib()
            self.__dict__.clear()
    #
    if isinstance(libname, basestring):
        try:
            if not isinstance(libname, str):    # unicode, on Python 2
                libname = libname.encode('utf-8')
            FFILibrary.__name__ = 'FFILibrary_%s' % libname
        except UnicodeError:
            pass
    library = FFILibrary()
    return library, library.__dict__

def _builtin_function_type(func):
    # a hack to make at least ffi.typeof(builtin_function) work,
    # if the builtin function was obtained by 'vengine_cpy'.
    import sys
    try:
        module = sys.modules[func.__module__]
        ffi = module._cffi_original_ffi
        types_of_builtin_funcs = module._cffi_types_of_builtin_funcs
        tp = types_of_builtin_funcs[func]
    except (KeyError, AttributeError, TypeError):
        return None
    else:
        with ffi._lock:
            return ffi._get_cached_btype(tp)

back/venvmsg/Lib/site-packages/cffi/api.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import sys, os
from .error import VerificationError


LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs',
                      'extra_objects', 'depends']

def get_extension(srcfilename, modname, sources=(), **kwds):
    from cffi._shimmed_dist_utils import Extension
    allsources = [srcfilename]
    for src in sources:
        allsources.append(os.path.normpath(src))
    return Extension(name=modname, sources=allsources, **kwds)

def compile(tmpdir, ext, compiler_verbose=0, debug=None):
    """Compile a C extension module using distutils."""

    saved_environ = os.environ.copy()
    try:
        outputfilename = _build(tmpdir, ext, compiler_verbose, debug)
        outputfilename = os.path.abspath(outputfilename)
    finally:
        # workaround for a distutils bugs where some env vars can
        # become longer and longer every time it is used
        for key, value in saved_environ.items():
            if os.environ.get(key) != value:
                os.environ[key] = value
    return outputfilename

def _build(tmpdir, ext, compiler_verbose=0, debug=None):
    # XXX compact but horrible :-(
    from cffi._shimmed_dist_utils import Distribution, CompileError, LinkError, set_threshold, set_verbosity

    dist = Distribution({'ext_modules': [ext]})
    dist.parse_config_files()
    options = dist.get_option_dict('build_ext')
    if debug is None:
        debug = sys.flags.debug
    options['debug'] = ('ffiplatform', debug)
    options['force'] = ('ffiplatform', True)
    options['build_lib'] = ('ffiplatform', tmpdir)
    options['build_temp'] = ('ffiplatform', tmpdir)
    #
    try:
        old_level = set_threshold(0) or 0
        try:
            set_verbosity(compiler_verbose)
            dist.run_command('build_ext')
            cmd_obj = dist.get_command_obj('build_ext')
            [soname] = cmd_obj.get_outputs()
        finally:
            set_threshold(old_level)
    except (CompileError, LinkError) as e:
        raise VerificationError('%s: %s' % (e.__class__.__name__, e))
    #
    return soname

try:
    from os.path import samefile
except ImportError:
    def samefile(f1, f2):
        return os.path.abspath(f1) == os.path.abspath(f2)

def maybe_relative_path(path):
    if not os.path.isabs(path):
        return path      # already relative
    dir = path
    names = []
    while True:
        prevdir = dir
        dir, name = os.path.split(prevdir)
        if dir == prevdir or not dir:
            return path     # failed to make it relative
        names.append(name)
        try:
            if samefile(dir, os.curdir):
                names.reverse()
                return os.path.join(*names)
        except OSError:
            pass

# ____________________________________________________________

try:
    int_or_long = (int, long)
    import cStringIO
except NameError:
    int_or_long = int      # Python 3
    import io as cStringIO

def _flatten(x, f):
    if isinstance(x, str):
        f.write('%ds%s' % (len(x), x))
    elif isinstance(x, dict):
        keys = sorted(x.keys())
        f.write('%dd' % len(keys))
        for key in keys:
            _flatten(key, f)
            _flatten(x[key], f)
    elif isinstance(x, (list, tuple)):
        f.write('%dl' % len(x))
        for value in x:
            _flatten(value, f)
    elif isinstance(x, int_or_long):
        f.write('%di' % (x,))
    else:
        raise TypeError(
            "the keywords to verify() contains unsupported object %r" % (x,))

def flatten(x):
    f = cStringIO.StringIO()
    _flatten(x, f)
    return f.getvalue()

back/venvmsg/Lib/site-packages/cffi/ffiplatform.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import types
import weakref

from .lock import allocate_lock
from .error import CDefError, VerificationError, VerificationMissing

# type qualifiers
Q_CONST    = 0x01
Q_RESTRICT = 0x02
Q_VOLATILE = 0x04

def qualify(quals, replace_with):
    if quals & Q_CONST:
        replace_with = ' const ' + replace_with.lstrip()
    if quals & Q_VOLATILE:
        replace_with = ' volatile ' + replace_with.lstrip()
    if quals & Q_RESTRICT:
        # It seems that __restrict is supported by gcc and msvc.
        # If you hit some different compiler, add a #define in
        # _cffi_include.h for it (and in its copies, documented there)
        replace_with = ' __restrict ' + replace_with.lstrip()
    return replace_with


class BaseTypeByIdentity(object):
    is_array_type = False
    is_raw_function = False

    def get_c_name(self, replace_with='', context='a C file', quals=0):
        result = self.c_name_with_marker
        assert result.count('&') == 1
        # some logic duplication with ffi.getctype()... :-(
        replace_with = replace_with.strip()
        if replace_with:
            if replace_with.startswith('*') and '&[' in result:
                replace_with = '(%s)' % replace_with
            elif not replace_with[0] in '[(':
                replace_with = ' ' + replace_with
        replace_with = qualify(quals, replace_with)
        result = result.replace('&', replace_with)
        if '$' in result:
            raise VerificationError(
                "cannot generate '%s' in %s: unknown type name"
                % (self._get_c_name(), context))
        return result

    def _get_c_name(self):
        return self.c_name_with_marker.replace('&', '')

    def has_c_name(self):
        return '$' not in self._get_c_name()

    def is_integer_type(self):
        return False

    def get_cached_btype(self, ffi, finishlist, can_delay=False):
        try:
            BType = ffi._cached_btypes[self]
        except KeyError:
            BType = self.build_backend_type(ffi, finishlist)
            BType2 = ffi._cached_btypes.setdefault(self, BType)
            assert BType2 is BType
        return BType

    def __repr__(self):
        return '<%s>' % (self._get_c_name(),)

    def _get_items(self):
        return [(name, getattr(self, name)) for name in self._attrs_]


class BaseType(BaseTypeByIdentity):

    def __eq__(self, other):
        return (self.__class__ == other.__class__ and
                self._get_items() == other._get_items())

    def __ne__(self, other):
        return not self == other

    def __hash__(self):
        return hash((self.__class__, tuple(self._get_items())))


class VoidType(BaseType):
    _attrs_ = ()

    def __init__(self):
        self.c_name_with_marker = 'void&'

    def build_backend_type(self, ffi, finishlist):
        return global_cache(self, ffi, 'new_void_type')

void_type = VoidType()


class BasePrimitiveType(BaseType):
    def is_complex_type(self):
        return False


class PrimitiveType(BasePrimitiveType):
    _attrs_ = ('name',)

    ALL_PRIMITIVE_TYPES = {
        'char':               'c',
        'short':              'i',
        'int':                'i',
        'long':               'i',
        'long long':          'i',
        'signed char':        'i',
        'unsigned char':      'i',
        'unsigned short':     'i',
        'unsigned int':       'i',
        'unsigned long':      'i',
        'unsigned long long': 'i',
        'float':              'f',
        'double':             'f',
        'long double':        'f',
        'float _Complex':     'j',
        'double _Complex':    'j',
        '_Bool':              'i',
        # the following types are not primitive in the C sense
        'wchar_t':            'c',
        'char16_t':           'c',
        'char32_t':           'c',
        'int8_t':             'i',
        'uint8_t':            'i',
        'int16_t':            'i',
        'uint16_t':           'i',
        'int32_t':            'i',
        'uint32_t':           'i',
        'int64_t':            'i',
        'uint64_t':           'i',
        'int_least8_t':       'i',
        'uint_least8_t':      'i',
        'int_least16_t':      'i',
        'uint_least16_t':     'i',
        'int_least32_t':      'i',
        'uint_least32_t':     'i',
        'int_least64_t':      'i',
        'uint_least64_t':     'i',
        'int_fast8_t':        'i',
        'uint_fast8_t':       'i',
        'int_fast16_t':       'i',
        'uint_fast16_t':      'i',
        'int_fast32_t':       'i',
        'uint_fast32_t':      'i',
        'int_fast64_t':       'i',
        'uint_fast64_t':      'i',
        'intptr_t':           'i',
        'uintptr_t':          'i',
        'intmax_t':           'i',
        'uintmax_t':          'i',
        'ptrdiff_t':          'i',
        'size_t':             'i',
        'ssize_t':            'i',
        }

    def __init__(self, name):
        assert name in self.ALL_PRIMITIVE_TYPES
        self.name = name
        self.c_name_with_marker = name + '&'

    def is_char_type(self):
        return self.ALL_PRIMITIVE_TYPES[self.name] == 'c'
    def is_integer_type(self):
        return self.ALL_PRIMITIVE_TYPES[self.name] == 'i'
    def is_float_type(self):
        return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'
    def is_complex_type(self):
        return self.ALL_PRIMITIVE_TYPES[self.name] == 'j'

    def build_backend_type(self, ffi, finishlist):
        return global_cache(self, ffi, 'new_primitive_type', self.name)


class UnknownIntegerType(BasePrimitiveType):
    _attrs_ = ('name',)

    def __init__(self, name):
        self.name = name
        self.c_name_with_marker = name + '&'

    def is_integer_type(self):
        return True

    def build_backend_type(self, ffi, finishlist):
        raise NotImplementedError("integer type '%s' can only be used after "
                                  "compilation" % self.name)

class UnknownFloatType(BasePrimitiveType):
    _attrs_ = ('name', )

    def __init__(self, name):
        self.name = name
        self.c_name_with_marker = name + '&'

    def build_backend_type(self, ffi, finishlist):
        raise NotImplementedError("float type '%s' can only be used after "
                                  "compilation" % self.name)


class BaseFunctionType(BaseType):
    _attrs_ = ('args', 'result', 'ellipsis', 'abi')

    def __init__(self, args, result, ellipsis, abi=None):
        self.args = args
        self.result = result
        self.ellipsis = ellipsis
        self.abi = abi
        #
        reprargs = [arg._get_c_name() for arg in self.args]
        if self.ellipsis:
            reprargs.append('...')
        reprargs = reprargs or ['void']
        replace_with = self._base_pattern % (', '.join(reprargs),)
        if abi is not None:
            replace_with = replace_with[:1] + abi + ' ' + replace_with[1:]
        self.c_name_with_marker = (
            self.result.c_name_with_marker.replace('&', replace_with))


class RawFunctionType(BaseFunctionType):
    # Corresponds to a C type like 'int(int)', which is the C type of
    # a function, but not a pointer-to-function.  The backend has no
    # notion of such a type; it's used temporarily by parsing.
    _base_pattern = '(&)(%s)'
    is_raw_function = True

    def build_backend_type(self, ffi, finishlist):
        raise CDefError("cannot render the type %r: it is a function "
                        "type, not a pointer-to-function type" % (self,))

    def as_function_pointer(self):
        return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi)


class FunctionPtrType(BaseFunctionType):
    _base_pattern = '(*&)(%s)'

    def build_backend_type(self, ffi, finishlist):
        result = self.result.get_cached_btype(ffi, finishlist)
        args = []
        for tp in self.args:
            args.append(tp.get_cached_btype(ffi, finishlist))
        abi_args = ()
        if self.abi == "__stdcall":
            if not self.ellipsis:    # __stdcall ignored for variadic funcs
                try:
                    abi_args = (ffi._backend.FFI_STDCALL,)
                except AttributeError:
                    pass
        return global_cache(self, ffi, 'new_function_type',
                            tuple(args), result, self.ellipsis, *abi_args)

    def as_raw_function(self):
        return RawFunctionType(self.args, self.result, self.ellipsis, self.abi)


class PointerType(BaseType):
    _attrs_ = ('totype', 'quals')

    def __init__(self, totype, quals=0):
        self.totype = totype
        self.quals = quals
        extra = " *&"
        if totype.is_array_type:
            extra = "(%s)" % (extra.lstrip(),)
        extra = qualify(quals, extra)
        self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra)

    def build_backend_type(self, ffi, finishlist):
        BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True)
        return global_cache(self, ffi, 'new_pointer_type', BItem)

voidp_type = PointerType(void_type)

def ConstPointerType(totype):
    return PointerType(totype, Q_CONST)

const_voidp_type = ConstPointerType(void_type)


class NamedPointerType(PointerType):
    _attrs_ = ('totype', 'name')

    def __init__(self, totype, name, quals=0):
        PointerType.__init__(self, totype, quals)
        self.name = name
        self.c_name_with_marker = name + '&'


class ArrayType(BaseType):
    _attrs_ = ('item', 'length')
    is_array_type = True

    def __init__(self, item, length):
        self.item = item
        self.length = length
        #
        if length is None:
            brackets = '&[]'
        elif length == '...':
            brackets = '&[/*...*/]'
        else:
            brackets = '&[%s]' % length
        self.c_name_with_marker = (
            self.item.c_name_with_marker.replace('&', brackets))

    def length_is_unknown(self):
        return isinstance(self.length, str)

    def resolve_length(self, newlength):
        return ArrayType(self.item, newlength)

    def build_backend_type(self, ffi, finishlist):
        if self.length_is_unknown():
            raise CDefError("cannot render the type %r: unknown length" %
                            (self,))
        self.item.get_cached_btype(ffi, finishlist)   # force the item BType
        BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist)
        return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length)

char_array_type = ArrayType(PrimitiveType('char'), None)


class StructOrUnionOrEnum(BaseTypeByIdentity):
    _attrs_ = ('name',)
    forcename = None

    def build_c_name_with_marker(self):
        name = self.forcename or '%s %s' % (self.kind, self.name)
        self.c_name_with_marker = name + '&'

    def force_the_name(self, forcename):
        self.forcename = forcename
        self.build_c_name_with_marker()

    def get_official_name(self):
        assert self.c_name_with_marker.endswith('&')
        return self.c_name_with_marker[:-1]


class StructOrUnion(StructOrUnionOrEnum):
    fixedlayout = None
    completed = 0
    partial = False
    packed = 0

    def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None):
        self.name = name
        self.fldnames = fldnames
        self.fldtypes = fldtypes
        self.fldbitsize = fldbitsize
        self.fldquals = fldquals
        self.build_c_name_with_marker()

    def anonymous_struct_fields(self):
        if self.fldtypes is not None:
            for name, type in zip(self.fldnames, self.fldtypes):
                if name == '' and isinstance(type, StructOrUnion):
                    yield type

    def enumfields(self, expand_anonymous_struct_union=True):
        fldquals = self.fldquals
        if fldquals is None:
            fldquals = (0,) * len(self.fldnames)
        for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes,
                                              self.fldbitsize, fldquals):
            if (name == '' and isinstance(type, StructOrUnion)
                    and expand_anonymous_struct_union):
                # nested anonymous struct/union
                for result in type.enumfields():
                    yield result
            else:
                yield (name, type, bitsize, quals)

    def force_flatten(self):
        # force the struct or union to have a declaration that lists
        # directly all fields returned by enumfields(), flattening
        # nested anonymous structs/unions.
        names = []
        types = []
        bitsizes = []
        fldquals = []
        for name, type, bitsize, quals in self.enumfields():
            names.append(name)
            types.append(type)
            bitsizes.append(bitsize)
            fldquals.append(quals)
        self.fldnames = tuple(names)
        self.fldtypes = tuple(types)
        self.fldbitsize = tuple(bitsizes)
        self.fldquals = tuple(fldquals)

    def get_cached_btype(self, ffi, finishlist, can_delay=False):
        BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist,
                                                     can_delay)
        if not can_delay:
            self.finish_backend_type(ffi, finishlist)
        return BType

    def finish_backend_type(self, ffi, finishlist):
        if self.completed:
            if self.completed != 2:
                raise NotImplementedError("recursive structure declaration "
                                          "for '%s'" % (self.name,))
            return
        BType = ffi._cached_btypes[self]
        #
        self.completed = 1
        #
        if self.fldtypes is None:
            pass    # not completing it: it's an opaque struct
            #
        elif self.fixedlayout is None:
            fldtypes = [tp.get_cached_btype(ffi, finishlist)
                        for tp in self.fldtypes]
            lst = list(zip(self.fldnames, fldtypes, self.fldbitsize))
            extra_flags = ()
            if self.packed:
                if self.packed == 1:
                    extra_flags = (8,)    # SF_PACKED
                else:
                    extra_flags = (0, self.packed)
            ffi._backend.complete_struct_or_union(BType, lst, self,
                                                  -1, -1, *extra_flags)
            #
        else:
            fldtypes = []
            fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout
            for i in range(len(self.fldnames)):
                fsize = fieldsize[i]
                ftype = self.fldtypes[i]
                #
                if isinstance(ftype, ArrayType) and ftype.length_is_unknown():
                    # fix the length to match the total size
                    BItemType = ftype.item.get_cached_btype(ffi, finishlist)
                    nlen, nrest = divmod(fsize, ffi.sizeof(BItemType))
                    if nrest != 0:
                        self._verification_error(
                            "field '%s.%s' has a bogus size?" % (
                            self.name, self.fldnames[i] or '{}'))
                    ftype = ftype.resolve_length(nlen)
                    self.fldtypes = (self.fldtypes[:i] + (ftype,) +
                                     self.fldtypes[i+1:])
                #
                BFieldType = ftype.get_cached_btype(ffi, finishlist)
                if isinstance(ftype, ArrayType) and ftype.length is None:
                    assert fsize == 0
                else:
                    bitemsize = ffi.sizeof(BFieldType)
                    if bitemsize != fsize:
                        self._verification_error(
                            "field '%s.%s' is declared as %d bytes, but is "
                            "really %d bytes" % (self.name,
                                                 self.fldnames[i] or '{}',
                                                 bitemsize, fsize))
                fldtypes.append(BFieldType)
            #
            lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs))
            ffi._backend.complete_struct_or_union(BType, lst, self,
                                                  totalsize, totalalignment)
        self.completed = 2

    def _verification_error(self, msg):
        raise VerificationError(msg)

    def check_not_partial(self):
        if self.partial and self.fixedlayout is None:
            raise VerificationMissing(self._get_c_name())

    def build_backend_type(self, ffi, finishlist):
        self.check_not_partial()
        finishlist.append(self)
        #
        return global_cache(self, ffi, 'new_%s_type' % self.kind,
                            self.get_official_name(), key=self)


class StructType(StructOrUnion):
    kind = 'struct'


class UnionType(StructOrUnion):
    kind = 'union'


class EnumType(StructOrUnionOrEnum):
    kind = 'enum'
    partial = False
    partial_resolved = False

    def __init__(self, name, enumerators, enumvalues, baseinttype=None):
        self.name = name
        self.enumerators = enumerators
        self.enumvalues = enumvalues
        self.baseinttype = baseinttype
        self.build_c_name_with_marker()

    def force_the_name(self, forcename):
        StructOrUnionOrEnum.force_the_name(self, forcename)
        if self.forcename is None:
            name = self.get_official_name()
            self.forcename = '$' + name.replace(' ', '_')

    def check_not_partial(self):
        if self.partial and not self.partial_resolved:
            raise VerificationMissing(self._get_c_name())

    def build_backend_type(self, ffi, finishlist):
        self.check_not_partial()
        base_btype = self.build_baseinttype(ffi, finishlist)
        return global_cache(self, ffi, 'new_enum_type',
                            self.get_official_name(),
                            self.enumerators, self.enumvalues,
                            base_btype, key=self)

    def build_baseinttype(self, ffi, finishlist):
        if self.baseinttype is not None:
            return self.baseinttype.get_cached_btype(ffi, finishlist)
        #
        if self.enumvalues:
            smallest_value = min(self.enumvalues)
            largest_value = max(self.enumvalues)
        else:
            import warnings
            try:
                # XXX!  The goal is to ensure that the warnings.warn()
                # will not suppress the warning.  We want to get it
                # several times if we reach this point several times.
                __warningregistry__.clear()
            except NameError:
                pass
            warnings.warn("%r has no values explicitly defined; "
                          "guessing that it is equivalent to 'unsigned int'"
                          % self._get_c_name())
            smallest_value = largest_value = 0
        if smallest_value < 0:   # needs a signed type
            sign = 1
            candidate1 = PrimitiveType("int")
            candidate2 = PrimitiveType("long")
        else:
            sign = 0
            candidate1 = PrimitiveType("unsigned int")
            candidate2 = PrimitiveType("unsigned long")
        btype1 = candidate1.get_cached_btype(ffi, finishlist)
        btype2 = candidate2.get_cached_btype(ffi, finishlist)
        size1 = ffi.sizeof(btype1)
        size2 = ffi.sizeof(btype2)
        if (smallest_value >= ((-1) << (8*size1-1)) and
            largest_value < (1 << (8*size1-sign))):
            return btype1
        if (smallest_value >= ((-1) << (8*size2-1)) and
            largest_value < (1 << (8*size2-sign))):
            return btype2
        raise CDefError("%s values don't all fit into either 'long' "
                        "or 'unsigned long'" % self._get_c_name())

def unknown_type(name, structname=None):
    if structname is None:
        structname = '$%s' % name
    tp = StructType(structname, None, None, None)
    tp.force_the_name(name)
    tp.origin = "unknown_type"
    return tp

def unknown_ptr_type(name, structname=None):
    if structname is None:
        structname = '$$%s' % name
    tp = StructType(structname, None, None, None)
    return NamedPointerType(tp, name)


global_lock = allocate_lock()
_typecache_cffi_backend = weakref.WeakValueDictionary()

def get_typecache(backend):
    # returns _typecache_cffi_backend if backend is the _cffi_backend
    # module, or type(backend).__typecache if backend is an instance of
    # CTypesBackend (or some FakeBackend class during tests)
    if isinstance(backend, types.ModuleType):
        return _typecache_cffi_backend
    with global_lock:
        if not hasattr(type(backend), '__typecache'):
            type(backend).__typecache = weakref.WeakValueDictionary()
        return type(backend).__typecache

def global_cache(srctype, ffi, funcname, *args, **kwds):
    key = kwds.pop('key', (funcname, args))
    assert not kwds
    try:
        return ffi._typecache[key]
    except KeyError:
        pass
    try:
        res = getattr(ffi._backend, funcname)(*args)
    except NotImplementedError as e:
        raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e))
    # note that setdefault() on WeakValueDictionary is not atomic
    # and contains a rare bug (http://bugs.python.org/issue19542);
    # we have to use a lock and do it ourselves
    cache = ffi._typecache
    with global_lock:
        res1 = cache.get(key)
        if res1 is None:
            cache[key] = res
            return res
        else:
            return res1

def pointer_cache(ffi, BType):
    return global_cache('?', ffi, 'new_pointer_type', BType)

def attach_exception_info(e, name):
    if e.args and type(e.args[0]) is str:
        e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:]

back/venvmsg/Lib/site-packages/cffi/model.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import os, sys, io
from . import ffiplatform, model
from .error import VerificationError
from .cffi_opcode import *

VERSION_BASE = 0x2601
VERSION_EMBEDDED = 0x2701
VERSION_CHAR16CHAR32 = 0x2801

USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or
                   sys.version_info >= (3, 5))


class GlobalExpr:
    def __init__(self, name, address, type_op, size=0, check_value=0):
        self.name = name
        self.address = address
        self.type_op = type_op
        self.size = size
        self.check_value = check_value

    def as_c_expr(self):
        return '  { "%s", (void *)%s, %s, (void *)%s },' % (
            self.name, self.address, self.type_op.as_c_expr(), self.size)

    def as_python_expr(self):
        return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name,
                               self.check_value)

class FieldExpr:
    def __init__(self, name, field_offset, field_size, fbitsize, field_type_op):
        self.name = name
        self.field_offset = field_offset
        self.field_size = field_size
        self.fbitsize = fbitsize
        self.field_type_op = field_type_op

    def as_c_expr(self):
        spaces = " " * len(self.name)
        return ('  { "%s", %s,\n' % (self.name, self.field_offset) +
                '     %s   %s,\n' % (spaces, self.field_size) +
                '     %s   %s },' % (spaces, self.field_type_op.as_c_expr()))

    def as_python_expr(self):
        raise NotImplementedError

    def as_field_python_expr(self):
        if self.field_type_op.op == OP_NOOP:
            size_expr = ''
        elif self.field_type_op.op == OP_BITFIELD:
            size_expr = format_four_bytes(self.fbitsize)
        else:
            raise NotImplementedError
        return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(),
                              size_expr,
                              self.name)

class StructUnionExpr:
    def __init__(self, name, type_index, flags, size, alignment, comment,
                 first_field_index, c_fields):
        self.name = name
        self.type_index = type_index
        self.flags = flags
        self.size = size
        self.alignment = alignment
        self.comment = comment
        self.first_field_index = first_field_index
        self.c_fields = c_fields

    def as_c_expr(self):
        return ('  { "%s", %d, %s,' % (self.name, self.type_index, self.flags)
                + '\n    %s, %s, ' % (self.size, self.alignment)
                + '%d, %d ' % (self.first_field_index, len(self.c_fields))
                + ('/* %s */ ' % self.comment if self.comment else '')
                + '},')

    def as_python_expr(self):
        flags = eval(self.flags, G_FLAGS)
        fields_expr = [c_field.as_field_python_expr()
                       for c_field in self.c_fields]
        return "(b'%s%s%s',%s)" % (
            format_four_bytes(self.type_index),
            format_four_bytes(flags),
            self.name,
            ','.join(fields_expr))

class EnumExpr:
    def __init__(self, name, type_index, size, signed, allenums):
        self.name = name
        self.type_index = type_index
        self.size = size
        self.signed = signed
        self.allenums = allenums

    def as_c_expr(self):
        return ('  { "%s", %d, _cffi_prim_int(%s, %s),\n'
                '    "%s" },' % (self.name, self.type_index,
                                 self.size, self.signed, self.allenums))

    def as_python_expr(self):
        prim_index = {
            (1, 0): PRIM_UINT8,  (1, 1):  PRIM_INT8,
            (2, 0): PRIM_UINT16, (2, 1):  PRIM_INT16,
            (4, 0): PRIM_UINT32, (4, 1):  PRIM_INT32,
            (8, 0): PRIM_UINT64, (8, 1):  PRIM_INT64,
            }[self.size, self.signed]
        return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index),
                                     format_four_bytes(prim_index),
                                     self.name, self.allenums)

class TypenameExpr:
    def __init__(self, name, type_index):
        self.name = name
        self.type_index = type_index

    def as_c_expr(self):
        return '  { "%s", %d },' % (self.name, self.type_index)

    def as_python_expr(self):
        return "b'%s%s'" % (format_four_bytes(self.type_index), self.name)


# ____________________________________________________________


class Recompiler:
    _num_externpy = 0

    def __init__(self, ffi, module_name, target_is_python=False):
        self.ffi = ffi
        self.module_name = module_name
        self.target_is_python = target_is_python
        self._version = VERSION_BASE

    def needs_version(self, ver):
        self._version = max(self._version, ver)

    def collect_type_table(self):
        self._typesdict = {}
        self._generate("collecttype")
        #
        all_decls = sorted(self._typesdict, key=str)
        #
        # prepare all FUNCTION bytecode sequences first
        self.cffi_types = []
        for tp in all_decls:
            if tp.is_raw_function:
                assert self._typesdict[tp] is None
                self._typesdict[tp] = len(self.cffi_types)
                self.cffi_types.append(tp)     # placeholder
                for tp1 in tp.args:
                    assert isinstance(tp1, (model.VoidType,
                                            model.BasePrimitiveType,
                                            model.PointerType,
                                            model.StructOrUnionOrEnum,
                                            model.FunctionPtrType))
                    if self._typesdict[tp1] is None:
                        self._typesdict[tp1] = len(self.cffi_types)
                    self.cffi_types.append(tp1)   # placeholder
                self.cffi_types.append('END')     # placeholder
        #
        # prepare all OTHER bytecode sequences
        for tp in all_decls:
            if not tp.is_raw_function and self._typesdict[tp] is None:
                self._typesdict[tp] = len(self.cffi_types)
                self.cffi_types.append(tp)        # placeholder
                if tp.is_array_type and tp.length is not None:
                    self.cffi_types.append('LEN') # placeholder
        assert None not in self._typesdict.values()
        #
        # collect all structs and unions and enums
        self._struct_unions = {}
        self._enums = {}
        for tp in all_decls:
            if isinstance(tp, model.StructOrUnion):
                self._struct_unions[tp] = None
            elif isinstance(tp, model.EnumType):
                self._enums[tp] = None
        for i, tp in enumerate(sorted(self._struct_unions,
                                      key=lambda tp: tp.name)):
            self._struct_unions[tp] = i
        for i, tp in enumerate(sorted(self._enums,
                                      key=lambda tp: tp.name)):
            self._enums[tp] = i
        #
        # emit all bytecode sequences now
        for tp in all_decls:
            method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__)
            method(tp, self._typesdict[tp])
        #
        # consistency check
        for op in self.cffi_types:
            assert isinstance(op, CffiOp)
        self.cffi_types = tuple(self.cffi_types)    # don't change any more

    def _enum_fields(self, tp):
        # When producing C, expand all anonymous struct/union fields.
        # That's necessary to have C code checking the offsets of the
        # individual fields contained in them.  When producing Python,
        # don't do it and instead write it like it is, with the
        # corresponding fields having an empty name.  Empty names are
        # recognized at runtime when we import the generated Python
        # file.
        expand_anonymous_struct_union = not self.target_is_python
        return tp.enumfields(expand_anonymous_struct_union)

    def _do_collect_type(self, tp):
        if not isinstance(tp, model.BaseTypeByIdentity):
            if isinstance(tp, tuple):
                for x in tp:
                    self._do_collect_type(x)
            return
        if tp not in self._typesdict:
            self._typesdict[tp] = None
            if isinstance(tp, model.FunctionPtrType):
                self._do_collect_type(tp.as_raw_function())
            elif isinstance(tp, model.StructOrUnion):
                if tp.fldtypes is not None and (
                        tp not in self.ffi._parser._included_declarations):
                    for name1, tp1, _, _ in self._enum_fields(tp):
                        self._do_collect_type(self._field_type(tp, name1, tp1))
            else:
                for _, x in tp._get_items():
                    self._do_collect_type(x)

    def _generate(self, step_name):
        lst = self.ffi._parser._declarations.items()
        for name, (tp, quals) in sorted(lst):
            kind, realname = name.split(' ', 1)
            try:
                method = getattr(self, '_generate_cpy_%s_%s' % (kind,
                                                                step_name))
            except AttributeError:
                raise VerificationError(
                    "not implemented in recompile(): %r" % name)
            try:
                self._current_quals = quals
                method(tp, realname)
            except Exception as e:
                model.attach_exception_info(e, name)
                raise

    # ----------

    ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]

    def collect_step_tables(self):
        # collect the declarations for '_cffi_globals', '_cffi_typenames', etc.
        self._lsts = {}
        for step_name in self.ALL_STEPS:
            self._lsts[step_name] = []
        self._seen_struct_unions = set()
        self._generate("ctx")
        self._add_missing_struct_unions()
        #
        for step_name in self.ALL_STEPS:
            lst = self._lsts[step_name]
            if step_name != "field":
                lst.sort(key=lambda entry: entry.name)
            self._lsts[step_name] = tuple(lst)    # don't change any more
        #
        # check for a possible internal inconsistency: _cffi_struct_unions
        # should have been generated with exactly self._struct_unions
        lst = self._lsts["struct_union"]
        for tp, i in self._struct_unions.items():
            assert i < len(lst)
            assert lst[i].name == tp.name
        assert len(lst) == len(self._struct_unions)
        # same with enums
        lst = self._lsts["enum"]
        for tp, i in self._enums.items():
            assert i < len(lst)
            assert lst[i].name == tp.name
        assert len(lst) == len(self._enums)

    # ----------

    def _prnt(self, what=''):
        self._f.write(what + '\n')

    def write_source_to_f(self, f, preamble):
        if self.target_is_python:
            assert preamble is None
            self.write_py_source_to_f(f)
        else:
            assert preamble is not None
            self.write_c_source_to_f(f, preamble)

    def _rel_readlines(self, filename):
        g = open(os.path.join(os.path.dirname(__file__), filename), 'r')
        lines = g.readlines()
        g.close()
        return lines

    def write_c_source_to_f(self, f, preamble):
        self._f = f
        prnt = self._prnt
        if self.ffi._embedding is not None:
            prnt('#define _CFFI_USE_EMBEDDING')
        if not USE_LIMITED_API:
            prnt('#define _CFFI_NO_LIMITED_API')
        #
        # first the '#include' (actually done by inlining the file's content)
        lines = self._rel_readlines('_cffi_include.h')
        i = lines.index('#include "parse_c_type.h"\n')
        lines[i:i+1] = self._rel_readlines('parse_c_type.h')
        prnt(''.join(lines))
        #
        # if we have ffi._embedding != None, we give it here as a macro
        # and include an extra file
        base_module_name = self.module_name.split('.')[-1]
        if self.ffi._embedding is not None:
            prnt('#define _CFFI_MODULE_NAME  "%s"' % (self.module_name,))
            prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {')
            self._print_string_literal_in_array(self.ffi._embedding)
            prnt('0 };')
            prnt('#ifdef PYPY_VERSION')
            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  _cffi_pypyinit_%s' % (
                base_module_name,))
            prnt('#elif PY_MAJOR_VERSION >= 3')
            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  PyInit_%s' % (
                base_module_name,))
            prnt('#else')
            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  init%s' % (
                base_module_name,))
            prnt('#endif')
            lines = self._rel_readlines('_embedding.h')
            i = lines.index('#include "_cffi_errors.h"\n')
            lines[i:i+1] = self._rel_readlines('_cffi_errors.h')
            prnt(''.join(lines))
            self.needs_version(VERSION_EMBEDDED)
        #
        # then paste the C source given by the user, verbatim.
        prnt('/************************************************************/')
        prnt()
        prnt(preamble)
        prnt()
        prnt('/************************************************************/')
        prnt()
        #
        # the declaration of '_cffi_types'
        prnt('static void *_cffi_types[] = {')
        typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
        for i, op in enumerate(self.cffi_types):
            comment = ''
            if i in typeindex2type:
                comment = ' // ' + typeindex2type[i]._get_c_name()
            prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment))
        if not self.cffi_types:
            prnt('  0')
        prnt('};')
        prnt()
        #
        # call generate_cpy_xxx_decl(), for every xxx found from
        # ffi._parser._declarations.  This generates all the functions.
        self._seen_constants = set()
        self._generate("decl")
        #
        # the declaration of '_cffi_globals' and '_cffi_typenames'
        nums = {}
        for step_name in self.ALL_STEPS:
            lst = self._lsts[step_name]
            nums[step_name] = len(lst)
            if nums[step_name] > 0:
                prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % (
                    step_name, step_name))
                for entry in lst:
                    prnt(entry.as_c_expr())
                prnt('};')
                prnt()
        #
        # the declaration of '_cffi_includes'
        if self.ffi._included_ffis:
            prnt('static const char * const _cffi_includes[] = {')
            for ffi_to_include in self.ffi._included_ffis:
                try:
                    included_module_name, included_source = (
                        ffi_to_include._assigned_source[:2])
                except AttributeError:
                    raise VerificationError(
                        "ffi object %r includes %r, but the latter has not "
                        "been prepared with set_source()" % (
                            self.ffi, ffi_to_include,))
                if included_source is None:
                    raise VerificationError(
                        "not implemented yet: ffi.include() of a Python-based "
                        "ffi inside a C-based ffi")
                prnt('  "%s",' % (included_module_name,))
            prnt('  NULL')
            prnt('};')
            prnt()
        #
        # the declaration of '_cffi_type_context'
        prnt('static const struct _cffi_type_context_s _cffi_type_context = {')
        prnt('  _cffi_types,')
        for step_name in self.ALL_STEPS:
            if nums[step_name] > 0:
                prnt('  _cffi_%ss,' % step_name)
            else:
                prnt('  NULL,  /* no %ss */' % step_name)
        for step_name in self.ALL_STEPS:
            if step_name != "field":
                prnt('  %d,  /* num_%ss */' % (nums[step_name], step_name))
        if self.ffi._included_ffis:
            prnt('  _cffi_includes,')
        else:
            prnt('  NULL,  /* no includes */')
        prnt('  %d,  /* num_types */' % (len(self.cffi_types),))
        flags = 0
        if self._num_externpy > 0 or self.ffi._embedding is not None:
            flags |= 1     # set to mean that we use extern "Python"
        prnt('  %d,  /* flags */' % flags)
        prnt('};')
        prnt()
        #
        # the init function
        prnt('#ifdef __GNUC__')
        prnt('#  pragma GCC visibility push(default)  /* for -fvisibility= */')
        prnt('#endif')
        prnt()
        prnt('#ifdef PYPY_VERSION')
        prnt('PyMODINIT_FUNC')
        prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,))
        prnt('{')
        if flags & 1:
            prnt('    if (((intptr_t)p[0]) >= 0x0A03) {')
            prnt('        _cffi_call_python_org = '
                 '(void(*)(struct _cffi_externpy_s *, char *))p[1];')
            prnt('    }')
        prnt('    p[0] = (const void *)0x%x;' % self._version)
        prnt('    p[1] = &_cffi_type_context;')
        prnt('#if PY_MAJOR_VERSION >= 3')
        prnt('    return NULL;')
        prnt('#endif')
        prnt('}')
        # on Windows, distutils insists on putting init_cffi_xyz in
        # 'export_symbols', so instead of fighting it, just give up and
        # give it one
        prnt('#  ifdef _MSC_VER')
        prnt('     PyMODINIT_FUNC')
        prnt('#  if PY_MAJOR_VERSION >= 3')
        prnt('     PyInit_%s(void) { return NULL; }' % (base_module_name,))
        prnt('#  else')
        prnt('     init%s(void) { }' % (base_module_name,))
        prnt('#  endif')
        prnt('#  endif')
        prnt('#elif PY_MAJOR_VERSION >= 3')
        prnt('PyMODINIT_FUNC')
        prnt('PyInit_%s(void)' % (base_module_name,))
        prnt('{')
        prnt('  return _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
            self.module_name, self._version))
        prnt('}')
        prnt('#else')
        prnt('PyMODINIT_FUNC')
        prnt('init%s(void)' % (base_module_name,))
        prnt('{')
        prnt('  _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
            self.module_name, self._version))
        prnt('}')
        prnt('#endif')
        prnt()
        prnt('#ifdef __GNUC__')
        prnt('#  pragma GCC visibility pop')
        prnt('#endif')
        self._version = None

    def _to_py(self, x):
        if isinstance(x, str):
            return "b'%s'" % (x,)
        if isinstance(x, (list, tuple)):
            rep = [self._to_py(item) for item in x]
            if len(rep) == 1:
                rep.append('')
            return "(%s)" % (','.join(rep),)
        return x.as_python_expr()  # Py2: unicode unexpected; Py3: bytes unexp.

    def write_py_source_to_f(self, f):
        self._f = f
        prnt = self._prnt
        #
        # header
        prnt("# auto-generated file")
        prnt("import _cffi_backend")
        #
        # the 'import' of the included ffis
        num_includes = len(self.ffi._included_ffis or ())
        for i in range(num_includes):
            ffi_to_include = self.ffi._included_ffis[i]
            try:
                included_module_name, included_source = (
                    ffi_to_include._assigned_source[:2])
            except AttributeError:
                raise VerificationError(
                    "ffi object %r includes %r, but the latter has not "
                    "been prepared with set_source()" % (
                        self.ffi, ffi_to_include,))
            if included_source is not None:
                raise VerificationError(
                    "not implemented yet: ffi.include() of a C-based "
                    "ffi inside a Python-based ffi")
            prnt('from %s import ffi as _ffi%d' % (included_module_name, i))
        prnt()
        prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,))
        prnt("    _version = 0x%x," % (self._version,))
        self._version = None
        #
        # the '_types' keyword argument
        self.cffi_types = tuple(self.cffi_types)    # don't change any more
        types_lst = [op.as_python_bytes() for op in self.cffi_types]
        prnt('    _types = %s,' % (self._to_py(''.join(types_lst)),))
        typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
        #
        # the keyword arguments from ALL_STEPS
        for step_name in self.ALL_STEPS:
            lst = self._lsts[step_name]
            if len(lst) > 0 and step_name != "field":
                prnt('    _%ss = %s,' % (step_name, self._to_py(lst)))
        #
        # the '_includes' keyword argument
        if num_includes > 0:
            prnt('    _includes = (%s,),' % (
                ', '.join(['_ffi%d' % i for i in range(num_includes)]),))
        #
        # the footer
        prnt(')')

    # ----------

    def _gettypenum(self, type):
        # a KeyError here is a bug.  please report it! :-)
        return self._typesdict[type]

    def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
        extraarg = ''
        if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type():
            if tp.is_integer_type() and tp.name != '_Bool':
                converter = '_cffi_to_c_int'
                extraarg = ', %s' % tp.name
            elif isinstance(tp, model.UnknownFloatType):
                # don't check with is_float_type(): it may be a 'long
                # double' here, and _cffi_to_c_double would loose precision
                converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)
            else:
                cname = tp.get_c_name('')
                converter = '(%s)_cffi_to_c_%s' % (cname,
                                                   tp.name.replace(' ', '_'))
                if cname in ('char16_t', 'char32_t'):
                    self.needs_version(VERSION_CHAR16CHAR32)
            errvalue = '-1'
        #
        elif isinstance(tp, model.PointerType):
            self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
                                                    tovar, errcode)
            return
        #
        elif (isinstance(tp, model.StructOrUnionOrEnum) or
              isinstance(tp, model.BasePrimitiveType)):
            # a struct (not a struct pointer) as a function argument;
            # or, a complex (the same code works)
            self._prnt('  if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
                      % (tovar, self._gettypenum(tp), fromvar))
            self._prnt('    %s;' % errcode)
            return
        #
        elif isinstance(tp, model.FunctionPtrType):
            converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
            extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
            errvalue = 'NULL'
        #
        else:
            raise NotImplementedError(tp)
        #
        self._prnt('  %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
        self._prnt('  if (%s == (%s)%s && PyErr_Occurred())' % (
            tovar, tp.get_c_name(''), errvalue))
        self._prnt('    %s;' % errcode)

    def _extra_local_variables(self, tp, localvars, freelines):
        if isinstance(tp, model.PointerType):
            localvars.add('Py_ssize_t datasize')
            localvars.add('struct _cffi_freeme_s *large_args_free = NULL')
            freelines.add('if (large_args_free != NULL)'
                          ' _cffi_free_array_arguments(large_args_free);')

    def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
        self._prnt('  datasize = _cffi_prepare_pointer_call_argument(')
        self._prnt('      _cffi_type(%d), %s, (char **)&%s);' % (
            self._gettypenum(tp), fromvar, tovar))
        self._prnt('  if (datasize != 0) {')
        self._prnt('    %s = ((size_t)datasize) <= 640 ? '
                   '(%s)alloca((size_t)datasize) : NULL;' % (
            tovar, tp.get_c_name('')))
        self._prnt('    if (_cffi_convert_array_argument(_cffi_type(%d), %s, '
                   '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))
        self._prnt('            datasize, &large_args_free) < 0)')
        self._prnt('      %s;' % errcode)
        self._prnt('  }')

    def _convert_expr_from_c(self, tp, var, context):
        if isinstance(tp, model.BasePrimitiveType):
            if tp.is_integer_type() and tp.name != '_Bool':
                return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
            elif isinstance(tp, model.UnknownFloatType):
                return '_cffi_from_c_double(%s)' % (var,)
            elif tp.name != 'long double' and not tp.is_complex_type():
                cname = tp.name.replace(' ', '_')
                if cname in ('char16_t', 'char32_t'):
                    self.needs_version(VERSION_CHAR16CHAR32)
                return '_cffi_from_c_%s(%s)' % (cname, var)
            else:
                return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
                    var, self._gettypenum(tp))
        elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
                var, self._gettypenum(tp))
        elif isinstance(tp, model.ArrayType):
            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
                var, self._gettypenum(model.PointerType(tp.item)))
        elif isinstance(tp, model.StructOrUnion):
            if tp.fldnames is None:
                raise TypeError("'%s' is used as %s, but is opaque" % (
                    tp._get_c_name(), context))
            return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
                var, self._gettypenum(tp))
        elif isinstance(tp, model.EnumType):
            return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
                var, self._gettypenum(tp))
        else:
            raise NotImplementedError(tp)

    # ----------
    # typedefs

    def _typedef_type(self, tp, name):
        return self._global_type(tp, "(*(%s *)0)" % (name,))

    def _generate_cpy_typedef_collecttype(self, tp, name):
        self._do_collect_type(self._typedef_type(tp, name))

    def _generate_cpy_typedef_decl(self, tp, name):
        pass

    def _typedef_ctx(self, tp, name):
        type_index = self._typesdict[tp]
        self._lsts["typename"].append(TypenameExpr(name, type_index))

    def _generate_cpy_typedef_ctx(self, tp, name):
        tp = self._typedef_type(tp, name)
        self._typedef_ctx(tp, name)
        if getattr(tp, "origin", None) == "unknown_type":
            self._struct_ctx(tp, tp.name, approxname=None)
        elif isinstance(tp, model.NamedPointerType):
            self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name,
                             named_ptr=tp)

    # ----------
    # function declarations

    def _generate_cpy_function_collecttype(self, tp, name):
        self._do_collect_type(tp.as_raw_function())
        if tp.ellipsis and not self.target_is_python:
            self._do_collect_type(tp)

    def _generate_cpy_function_decl(self, tp, name):
        assert not self.target_is_python
        assert isinstance(tp, model.FunctionPtrType)
        if tp.ellipsis:
            # cannot support vararg functions better than this: check for its
            # exact type (including the fixed arguments), and build it as a
            # constant function pointer (no CPython wrapper)
            self._generate_cpy_constant_decl(tp, name)
            return
        prnt = self._prnt
        numargs = len(tp.args)
        if numargs == 0:
            argname = 'noarg'
        elif numargs == 1:
            argname = 'arg0'
        else:
            argname = 'args'
        #
        # ------------------------------
        # the 'd' version of the function, only for addressof(lib, 'func')
        arguments = []
        call_arguments = []
        context = 'argument of %s' % name
        for i, type in enumerate(tp.args):
            arguments.append(type.get_c_name(' x%d' % i, context))
            call_arguments.append('x%d' % i)
        repr_arguments = ', '.join(arguments)
        repr_arguments = repr_arguments or 'void'
        if tp.abi:
            abi = tp.abi + ' '
        else:
            abi = ''
        name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments)
        prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
        prnt('{')
        call_arguments = ', '.join(call_arguments)
        result_code = 'return '
        if isinstance(tp.result, model.VoidType):
            result_code = ''
        prnt('  %s%s(%s);' % (result_code, name, call_arguments))
        prnt('}')
        #
        prnt('#ifndef PYPY_VERSION')        # ------------------------------
        #
        prnt('static PyObject *')
        prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
        prnt('{')
        #
        context = 'argument of %s' % name
        for i, type in enumerate(tp.args):
            arg = type.get_c_name(' x%d' % i, context)
            prnt('  %s;' % arg)
        #
        localvars = set()
        freelines = set()
        for type in tp.args:
            self._extra_local_variables(type, localvars, freelines)
        for decl in sorted(localvars):
            prnt('  %s;' % (decl,))
        #
        if not isinstance(tp.result, model.VoidType):
            result_code = 'result = '
            context = 'result of %s' % name
            result_decl = '  %s;' % tp.result.get_c_name(' result', context)
            prnt(result_decl)
            prnt('  PyObject *pyresult;')
        else:
            result_decl = None
            result_code = ''
        #
        if len(tp.args) > 1:
            rng = range(len(tp.args))
            for i in rng:
                prnt('  PyObject *arg%d;' % i)
            prnt()
            prnt('  if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % (
                name, len(rng), len(rng),
                ', '.join(['&arg%d' % i for i in rng])))
            prnt('    return NULL;')
        prnt()
        #
        for i, type in enumerate(tp.args):
            self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
                                       'return NULL')
            prnt()
        #
        prnt('  Py_BEGIN_ALLOW_THREADS')
        prnt('  _cffi_restore_errno();')
        call_arguments = ['x%d' % i for i in range(len(tp.args))]
        call_arguments = ', '.join(call_arguments)
        prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
        prnt('  _cffi_save_errno();')
        prnt('  Py_END_ALLOW_THREADS')
        prnt()
        #
        prnt('  (void)self; /* unused */')
        if numargs == 0:
            prnt('  (void)noarg; /* unused */')
        if result_code:
            prnt('  pyresult = %s;' %
                 self._convert_expr_from_c(tp.result, 'result', 'result type'))
            for freeline in freelines:
                prnt('  ' + freeline)
            prnt('  return pyresult;')
        else:
            for freeline in freelines:
                prnt('  ' + freeline)
            prnt('  Py_INCREF(Py_None);')
            prnt('  return Py_None;')
        prnt('}')
        #
        prnt('#else')        # ------------------------------
        #
        # the PyPy version: need to replace struct/union arguments with
        # pointers, and if the result is a struct/union, insert a first
        # arg that is a pointer to the result.  We also do that for
        # complex args and return type.
        def need_indirection(type):
            return (isinstance(type, model.StructOrUnion) or
                    (isinstance(type, model.PrimitiveType) and
                     type.is_complex_type()))
        difference = False
        arguments = []
        call_arguments = []
        context = 'argument of %s' % name
        for i, type in enumerate(tp.args):
            indirection = ''
            if need_indirection(type):
                indirection = '*'
                difference = True
            arg = type.get_c_name(' %sx%d' % (indirection, i), context)
            arguments.append(arg)
            call_arguments.append('%sx%d' % (indirection, i))
        tp_result = tp.result
        if need_indirection(tp_result):
            context = 'result of %s' % name
            arg = tp_result.get_c_name(' *result', context)
            arguments.insert(0, arg)
            tp_result = model.void_type
            result_decl = None
            result_code = '*result = '
            difference = True
        if difference:
            repr_arguments = ', '.join(arguments)
            repr_arguments = repr_arguments or 'void'
            name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name,
                                                       repr_arguments)
            prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
            prnt('{')
            if result_decl:
                prnt(result_decl)
            call_arguments = ', '.join(call_arguments)
            prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
            if result_decl:
                prnt('  return result;')
            prnt('}')
        else:
            prnt('#  define _cffi_f_%s _cffi_d_%s' % (name, name))
        #
        prnt('#endif')        # ------------------------------
        prnt()

    def _generate_cpy_function_ctx(self, tp, name):
        if tp.ellipsis and not self.target_is_python:
            self._generate_cpy_constant_ctx(tp, name)
            return
        type_index = self._typesdict[tp.as_raw_function()]
        numargs = len(tp.args)
        if self.target_is_python:
            meth_kind = OP_DLOPEN_FUNC
        elif numargs == 0:
            meth_kind = OP_CPYTHON_BLTN_N   # 'METH_NOARGS'
        elif numargs == 1:
            meth_kind = OP_CPYTHON_BLTN_O   # 'METH_O'
        else:
            meth_kind = OP_CPYTHON_BLTN_V   # 'METH_VARARGS'
        self._lsts["global"].append(
            GlobalExpr(name, '_cffi_f_%s' % name,
                       CffiOp(meth_kind, type_index),
                       size='_cffi_d_%s' % name))

    # ----------
    # named structs or unions

    def _field_type(self, tp_struct, field_name, tp_field):
        if isinstance(tp_field, model.ArrayType):
            actual_length = tp_field.length
            if actual_length == '...':
                ptr_struct_name = tp_struct.get_c_name('*')
                actual_length = '_cffi_array_len(((%s)0)->%s)' % (
                    ptr_struct_name, field_name)
            tp_item = self._field_type(tp_struct, '%s[0]' % field_name,
                                       tp_field.item)
            tp_field = model.ArrayType(tp_item, actual_length)
        return tp_field

    def _struct_collecttype(self, tp):
        self._do_collect_type(tp)
        if self.target_is_python:
            # also requires nested anon struct/unions in ABI mode, recursively
            for fldtype in tp.anonymous_struct_fields():
                self._struct_collecttype(fldtype)

    def _struct_decl(self, tp, cname, approxname):
        if tp.fldtypes is None:
            return
        prnt = self._prnt
        checkfuncname = '_cffi_checkfld_%s' % (approxname,)
        prnt('_CFFI_UNUSED_FN')
        prnt('static void %s(%s *p)' % (checkfuncname, cname))
        prnt('{')
        prnt('  /* only to generate compile-time warnings or errors */')
        prnt('  (void)p;')
        for fname, ftype, fbitsize, fqual in self._enum_fields(tp):
            try:
                if ftype.is_integer_type() or fbitsize >= 0:
                    # accept all integers, but complain on float or double
                    if fname != '':
                        prnt("  (void)((p->%s) | 0);  /* check that '%s.%s' is "
                             "an integer */" % (fname, cname, fname))
                    continue
                # only accept exactly the type declared, except that '[]'
                # is interpreted as a '*' and so will match any array length.
                # (It would also match '*', but that's harder to detect...)
                while (isinstance(ftype, model.ArrayType)
                       and (ftype.length is None or ftype.length == '...')):
                    ftype = ftype.item
                    fname = fname + '[0]'
                prnt('  { %s = &p->%s; (void)tmp; }' % (
                    ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
                    fname))
            except VerificationError as e:
                prnt('  /* %s */' % str(e))   # cannot verify it, ignore
        prnt('}')
        prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname))
        prnt()

    def _struct_ctx(self, tp, cname, approxname, named_ptr=None):
        type_index = self._typesdict[tp]
        reason_for_not_expanding = None
        flags = []
        if isinstance(tp, model.UnionType):
            flags.append("_CFFI_F_UNION")
        if tp.fldtypes is None:
            flags.append("_CFFI_F_OPAQUE")
            reason_for_not_expanding = "opaque"
        if (tp not in self.ffi._parser._included_declarations and
                (named_ptr is None or
                 named_ptr not in self.ffi._parser._included_declarations)):
            if tp.fldtypes is None:
                pass    # opaque
            elif tp.partial or any(tp.anonymous_struct_fields()):
                pass    # field layout obtained silently from the C compiler
            else:
                flags.append("_CFFI_F_CHECK_FIELDS")
            if tp.packed:
                if tp.packed > 1:
                    raise NotImplementedError(
                        "%r is declared with 'pack=%r'; only 0 or 1 are "
                        "supported in API mode (try to use \"...;\", which "
                        "does not require a 'pack' declaration)" %
                        (tp, tp.packed))
                flags.append("_CFFI_F_PACKED")
        else:
            flags.append("_CFFI_F_EXTERNAL")
            reason_for_not_expanding = "external"
        flags = '|'.join(flags) or '0'
        c_fields = []
        if reason_for_not_expanding is None:
            enumfields = list(self._enum_fields(tp))
            for fldname, fldtype, fbitsize, fqual in enumfields:
                fldtype = self._field_type(tp, fldname, fldtype)
                self._check_not_opaque(fldtype,
                                       "field '%s.%s'" % (tp.name, fldname))
                # cname is None for _add_missing_struct_unions() only
                op = OP_NOOP
                if fbitsize >= 0:
                    op = OP_BITFIELD
                    size = '%d /* bits */' % fbitsize
                elif cname is None or (
                        isinstance(fldtype, model.ArrayType) and
                        fldtype.length is None):
                    size = '(size_t)-1'
                else:
                    size = 'sizeof(((%s)0)->%s)' % (
                        tp.get_c_name('*') if named_ptr is None
                                           else named_ptr.name,
                        fldname)
                if cname is None or fbitsize >= 0:
                    offset = '(size_t)-1'
                elif named_ptr is not None:
                    offset = '((char *)&((%s)0)->%s) - (char *)0' % (
                        named_ptr.name, fldname)
                else:
                    offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname)
                c_fields.append(
                    FieldExpr(fldname, offset, size, fbitsize,
                              CffiOp(op, self._typesdict[fldtype])))
            first_field_index = len(self._lsts["field"])
            self._lsts["field"].extend(c_fields)
            #
            if cname is None:  # unknown name, for _add_missing_struct_unions
                size = '(size_t)-2'
                align = -2
                comment = "unnamed"
            else:
                if named_ptr is not None:
                    size = 'sizeof(*(%s)0)' % (named_ptr.name,)
                    align = '-1 /* unknown alignment */'
                else:
                    size = 'sizeof(%s)' % (cname,)
                    align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,)
                comment = None
        else:
            size = '(size_t)-1'
            align = -1
            first_field_index = -1
            comment = reason_for_not_expanding
        self._lsts["struct_union"].append(
            StructUnionExpr(tp.name, type_index, flags, size, align, comment,
                            first_field_index, c_fields))
        self._seen_struct_unions.add(tp)

    def _check_not_opaque(self, tp, location):
        while isinstance(tp, model.ArrayType):
            tp = tp.item
        if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None:
            raise TypeError(
                "%s is of an opaque type (not declared in cdef())" % location)

    def _add_missing_struct_unions(self):
        # not very nice, but some struct declarations might be missing
        # because they don't have any known C name.  Check that they are
        # not partial (we can't complete or verify them!) and emit them
        # anonymously.
        lst = list(self._struct_unions.items())
        lst.sort(key=lambda tp_order: tp_order[1])
        for tp, order in lst:
            if tp not in self._seen_struct_unions:
                if tp.partial:
                    raise NotImplementedError("internal inconsistency: %r is "
                                              "partial but was not seen at "
                                              "this point" % (tp,))
                if tp.name.startswith('$') and tp.name[1:].isdigit():
                    approxname = tp.name[1:]
                elif tp.name == '_IO_FILE' and tp.forcename == 'FILE':
                    approxname = 'FILE'
                    self._typedef_ctx(tp, 'FILE')
                else:
                    raise NotImplementedError("internal inconsistency: %r" %
                                              (tp,))
                self._struct_ctx(tp, None, approxname)

    def _generate_cpy_struct_collecttype(self, tp, name):
        self._struct_collecttype(tp)
    _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype

    def _struct_names(self, tp):
        cname = tp.get_c_name('')
        if ' ' in cname:
            return cname, cname.replace(' ', '_')
        else:
            return cname, '_' + cname

    def _generate_cpy_struct_decl(self, tp, name):
        self._struct_decl(tp, *self._struct_names(tp))
    _generate_cpy_union_decl = _generate_cpy_struct_decl

    def _generate_cpy_struct_ctx(self, tp, name):
        self._struct_ctx(tp, *self._struct_names(tp))
    _generate_cpy_union_ctx = _generate_cpy_struct_ctx

    # ----------
    # 'anonymous' declarations.  These are produced for anonymous structs
    # or unions; the 'name' is obtained by a typedef.

    def _generate_cpy_anonymous_collecttype(self, tp, name):
        if isinstance(tp, model.EnumType):
            self._generate_cpy_enum_collecttype(tp, name)
        else:
            self._struct_collecttype(tp)

    def _generate_cpy_anonymous_decl(self, tp, name):
        if isinstance(tp, model.EnumType):
            self._generate_cpy_enum_decl(tp)
        else:
            self._struct_decl(tp, name, 'typedef_' + name)

    def _generate_cpy_anonymous_ctx(self, tp, name):
        if isinstance(tp, model.EnumType):
            self._enum_ctx(tp, name)
        else:
            self._struct_ctx(tp, name, 'typedef_' + name)

    # ----------
    # constants, declared with "static const ..."

    def _generate_cpy_const(self, is_int, name, tp=None, category='const',
                            check_value=None):
        if (category, name) in self._seen_constants:
            raise VerificationError(
                "duplicate declaration of %s '%s'" % (category, name))
        self._seen_constants.add((category, name))
        #
        prnt = self._prnt
        funcname = '_cffi_%s_%s' % (category, name)
        if is_int:
            prnt('static int %s(unsigned long long *o)' % funcname)
            prnt('{')
            prnt('  int n = (%s) <= 0;' % (name,))
            prnt('  *o = (unsigned long long)((%s) | 0);'
                 '  /* check that %s is an integer */' % (name, name))
            if check_value is not None:
                if check_value > 0:
                    check_value = '%dU' % (check_value,)
                prnt('  if (!_cffi_check_int(*o, n, %s))' % (check_value,))
                prnt('    n |= 2;')
            prnt('  return n;')
            prnt('}')
        else:
            assert check_value is None
            prnt('static void %s(char *o)' % funcname)
            prnt('{')
            prnt('  *(%s)o = %s;' % (tp.get_c_name('*'), name))
            prnt('}')
        prnt()

    def _generate_cpy_constant_collecttype(self, tp, name):
        is_int = tp.is_integer_type()
        if not is_int or self.target_is_python:
            self._do_collect_type(tp)

    def _generate_cpy_constant_decl(self, tp, name):
        is_int = tp.is_integer_type()
        self._generate_cpy_const(is_int, name, tp)

    def _generate_cpy_constant_ctx(self, tp, name):
        if not self.target_is_python and tp.is_integer_type():
            type_op = CffiOp(OP_CONSTANT_INT, -1)
        else:
            if self.target_is_python:
                const_kind = OP_DLOPEN_CONST
            else:
                const_kind = OP_CONSTANT
            type_index = self._typesdict[tp]
            type_op = CffiOp(const_kind, type_index)
        self._lsts["global"].append(
            GlobalExpr(name, '_cffi_const_%s' % name, type_op))

    # ----------
    # enums

    def _generate_cpy_enum_collecttype(self, tp, name):
        self._do_collect_type(tp)

    def _generate_cpy_enum_decl(self, tp, name=None):
        for enumerator in tp.enumerators:
            self._generate_cpy_const(True, enumerator)

    def _enum_ctx(self, tp, cname):
        type_index = self._typesdict[tp]
        type_op = CffiOp(OP_ENUM, -1)
        if self.target_is_python:
            tp.check_not_partial()
        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
            self._lsts["global"].append(
                GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op,
                           check_value=enumvalue))
        #
        if cname is not None and '$' not in cname and not self.target_is_python:
            size = "sizeof(%s)" % cname
            signed = "((%s)-1) <= 0" % cname
        else:
            basetp = tp.build_baseinttype(self.ffi, [])
            size = self.ffi.sizeof(basetp)
            signed = int(int(self.ffi.cast(basetp, -1)) < 0)
        allenums = ",".join(tp.enumerators)
        self._lsts["enum"].append(
            EnumExpr(tp.name, type_index, size, signed, allenums))

    def _generate_cpy_enum_ctx(self, tp, name):
        self._enum_ctx(tp, tp._get_c_name())

    # ----------
    # macros: for now only for integers

    def _generate_cpy_macro_collecttype(self, tp, name):
        pass

    def _generate_cpy_macro_decl(self, tp, name):
        if tp == '...':
            check_value = None
        else:
            check_value = tp     # an integer
        self._generate_cpy_const(True, name, check_value=check_value)

    def _generate_cpy_macro_ctx(self, tp, name):
        if tp == '...':
            if self.target_is_python:
                raise VerificationError(
                    "cannot use the syntax '...' in '#define %s ...' when "
                    "using the ABI mode" % (name,))
            check_value = None
        else:
            check_value = tp     # an integer
        type_op = CffiOp(OP_CONSTANT_INT, -1)
        self._lsts["global"].append(
            GlobalExpr(name, '_cffi_const_%s' % name, type_op,
                       check_value=check_value))

    # ----------
    # global variables

    def _global_type(self, tp, global_name):
        if isinstance(tp, model.ArrayType):
            actual_length = tp.length
            if actual_length == '...':
                actual_length = '_cffi_array_len(%s)' % (global_name,)
            tp_item = self._global_type(tp.item, '%s[0]' % global_name)
            tp = model.ArrayType(tp_item, actual_length)
        return tp

    def _generate_cpy_variable_collecttype(self, tp, name):
        self._do_collect_type(self._global_type(tp, name))

    def _generate_cpy_variable_decl(self, tp, name):
        prnt = self._prnt
        tp = self._global_type(tp, name)
        if isinstance(tp, model.ArrayType) and tp.length is None:
            tp = tp.item
            ampersand = ''
        else:
            ampersand = '&'
        # This code assumes that casts from "tp *" to "void *" is a
        # no-op, i.e. a function that returns a "tp *" can be called
        # as if it returned a "void *".  This should be generally true
        # on any modern machine.  The only exception to that rule (on
        # uncommon architectures, and as far as I can tell) might be
        # if 'tp' were a function type, but that is not possible here.
        # (If 'tp' is a function _pointer_ type, then casts from "fn_t
        # **" to "void *" are again no-ops, as far as I can tell.)
        decl = '*_cffi_var_%s(void)' % (name,)
        prnt('static ' + tp.get_c_name(decl, quals=self._current_quals))
        prnt('{')
        prnt('  return %s(%s);' % (ampersand, name))
        prnt('}')
        prnt()

    def _generate_cpy_variable_ctx(self, tp, name):
        tp = self._global_type(tp, name)
        type_index = self._typesdict[tp]
        if self.target_is_python:
            op = OP_GLOBAL_VAR
        else:
            op = OP_GLOBAL_VAR_F
        self._lsts["global"].append(
            GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index)))

    # ----------
    # extern "Python"

    def _generate_cpy_extern_python_collecttype(self, tp, name):
        assert isinstance(tp, model.FunctionPtrType)
        self._do_collect_type(tp)
    _generate_cpy_dllexport_python_collecttype = \
      _generate_cpy_extern_python_plus_c_collecttype = \
      _generate_cpy_extern_python_collecttype

    def _extern_python_decl(self, tp, name, tag_and_space):
        prnt = self._prnt
        if isinstance(tp.result, model.VoidType):
            size_of_result = '0'
        else:
            context = 'result of %s' % name
            size_of_result = '(int)sizeof(%s)' % (
                tp.result.get_c_name('', context),)
        prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name)
        prnt('  { "%s.%s", %s, 0, 0 };' % (
            self.module_name, name, size_of_result))
        prnt()
        #
        arguments = []
        context = 'argument of %s' % name
        for i, type in enumerate(tp.args):
            arg = type.get_c_name(' a%d' % i, context)
            arguments.append(arg)
        #
        repr_arguments = ', '.join(arguments)
        repr_arguments = repr_arguments or 'void'
        name_and_arguments = '%s(%s)' % (name, repr_arguments)
        if tp.abi == "__stdcall":
            name_and_arguments = '_cffi_stdcall ' + name_and_arguments
        #
        def may_need_128_bits(tp):
            return (isinstance(tp, model.PrimitiveType) and
                    tp.name == 'long double')
        #
        size_of_a = max(len(tp.args)*8, 8)
        if may_need_128_bits(tp.result):
            size_of_a = max(size_of_a, 16)
        if isinstance(tp.result, model.StructOrUnion):
            size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % (
                tp.result.get_c_name(''), size_of_a,
                tp.result.get_c_name(''), size_of_a)
        prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments)))
        prnt('{')
        prnt('  char a[%s];' % size_of_a)
        prnt('  char *p = a;')
        for i, type in enumerate(tp.args):
            arg = 'a%d' % i
            if (isinstance(type, model.StructOrUnion) or
                    may_need_128_bits(type)):
                arg = '&' + arg
                type = model.PointerType(type)
            prnt('  *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg))
        prnt('  _cffi_call_python(&_cffi_externpy__%s, p);' % name)
        if not isinstance(tp.result, model.VoidType):
            prnt('  return *(%s)p;' % (tp.result.get_c_name('*'),))
        prnt('}')
        prnt()
        self._num_externpy += 1

    def _generate_cpy_extern_python_decl(self, tp, name):
        self._extern_python_decl(tp, name, 'static ')

    def _generate_cpy_dllexport_python_decl(self, tp, name):
        self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ')

    def _generate_cpy_extern_python_plus_c_decl(self, tp, name):
        self._extern_python_decl(tp, name, '')

    def _generate_cpy_extern_python_ctx(self, tp, name):
        if self.target_is_python:
            raise VerificationError(
                "cannot use 'extern \"Python\"' in the ABI mode")
        if tp.ellipsis:
            raise NotImplementedError("a vararg function is extern \"Python\"")
        type_index = self._typesdict[tp]
        type_op = CffiOp(OP_EXTERN_PYTHON, type_index)
        self._lsts["global"].append(
            GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name))

    _generate_cpy_dllexport_python_ctx = \
      _generate_cpy_extern_python_plus_c_ctx = \
      _generate_cpy_extern_python_ctx

    def _print_string_literal_in_array(self, s):
        prnt = self._prnt
        prnt('// # NB. this is not a string because of a size limit in MSVC')
        if not isinstance(s, bytes):    # unicode
            s = s.encode('utf-8')       # -> bytes
        else:
            s.decode('utf-8')           # got bytes, check for valid utf-8
        try:
            s.decode('ascii')
        except UnicodeDecodeError:
            s = b'# -*- encoding: utf8 -*-\n' + s
        for line in s.splitlines(True):
            comment = line
            if type('//') is bytes:     # python2
                line = map(ord, line)   #     make a list of integers
            else:                       # python3
                # type(line) is bytes, which enumerates like a list of integers
                comment = ascii(comment)[1:-1]
            prnt(('// ' + comment).rstrip())
            printed_line = ''
            for c in line:
                if len(printed_line) >= 76:
                    prnt(printed_line)
                    printed_line = ''
                printed_line += '%d,' % (c,)
            prnt(printed_line)

    # ----------
    # emitting the opcodes for individual types

    def _emit_bytecode_VoidType(self, tp, index):
        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID)

    def _emit_bytecode_PrimitiveType(self, tp, index):
        prim_index = PRIMITIVE_TO_INDEX[tp.name]
        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index)

    def _emit_bytecode_UnknownIntegerType(self, tp, index):
        s = ('_cffi_prim_int(sizeof(%s), (\n'
             '           ((%s)-1) | 0 /* check that %s is an integer type */\n'
             '         ) <= 0)' % (tp.name, tp.name, tp.name))
        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)

    def _emit_bytecode_UnknownFloatType(self, tp, index):
        s = ('_cffi_prim_float(sizeof(%s) *\n'
             '           (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n'
             '         )' % (tp.name, tp.name))
        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)

    def _emit_bytecode_RawFunctionType(self, tp, index):
        self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result])
        index += 1
        for tp1 in tp.args:
            realindex = self._typesdict[tp1]
            if index != realindex:
                if isinstance(tp1, model.PrimitiveType):
                    self._emit_bytecode_PrimitiveType(tp1, index)
                else:
                    self.cffi_types[index] = CffiOp(OP_NOOP, realindex)
            index += 1
        flags = int(tp.ellipsis)
        if tp.abi is not None:
            if tp.abi == '__stdcall':
                flags |= 2
            else:
                raise NotImplementedError("abi=%r" % (tp.abi,))
        self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags)

    def _emit_bytecode_PointerType(self, tp, index):
        self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype])

    _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType
    _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType

    def _emit_bytecode_FunctionPtrType(self, tp, index):
        raw = tp.as_raw_function()
        self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw])

    def _emit_bytecode_ArrayType(self, tp, index):
        item_index = self._typesdict[tp.item]
        if tp.length is None:
            self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index)
        elif tp.length == '...':
            raise VerificationError(
                "type %s badly placed: the '...' array length can only be "
                "used on global arrays or on fields of structures" % (
                    str(tp).replace('/*...*/', '...'),))
        else:
            assert self.cffi_types[index + 1] == 'LEN'
            self.cffi_types[index] = CffiOp(OP_ARRAY, item_index)
            self.cffi_types[index + 1] = CffiOp(None, str(tp.length))

    def _emit_bytecode_StructType(self, tp, index):
        struct_index = self._struct_unions[tp]
        self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index)
    _emit_bytecode_UnionType = _emit_bytecode_StructType

    def _emit_bytecode_EnumType(self, tp, index):
        enum_index = self._enums[tp]
        self.cffi_types[index] = CffiOp(OP_ENUM, enum_index)


if sys.version_info >= (3,):
    NativeIO = io.StringIO
else:
    class NativeIO(io.BytesIO):
        def write(self, s):
            if isinstance(s, unicode):
                s = s.encode('ascii')
            super(NativeIO, self).write(s)

def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose):
    if verbose:
        print("generating %s" % (target_file,))
    recompiler = Recompiler(ffi, module_name,
                            target_is_python=(preamble is None))
    recompiler.collect_type_table()
    recompiler.collect_step_tables()
    f = NativeIO()
    recompiler.write_source_to_f(f, preamble)
    output = f.getvalue()
    try:
        with open(target_file, 'r') as f1:
            if f1.read(len(output) + 1) != output:
                raise IOError
        if verbose:
            print("(already up-to-date)")
        return False     # already up-to-date
    except IOError:
        tmp_file = '%s.~%d' % (target_file, os.getpid())
        with open(tmp_file, 'w') as f1:
            f1.write(output)
        try:
            os.rename(tmp_file, target_file)
        except OSError:
            os.unlink(target_file)
            os.rename(tmp_file, target_file)
        return True

def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False):
    assert preamble is not None
    return _make_c_or_py_source(ffi, module_name, preamble, target_c_file,
                                verbose)

def make_py_source(ffi, module_name, target_py_file, verbose=False):
    return _make_c_or_py_source(ffi, module_name, None, target_py_file,
                                verbose)

def _modname_to_file(outputdir, modname, extension):
    parts = modname.split('.')
    try:
        os.makedirs(os.path.join(outputdir, *parts[:-1]))
    except OSError:
        pass
    parts[-1] += extension
    return os.path.join(outputdir, *parts), parts


# Aaargh.  Distutils is not tested at all for the purpose of compiling
# DLLs that are not extension modules.  Here are some hacks to work
# around that, in the _patch_for_*() functions...

def _patch_meth(patchlist, cls, name, new_meth):
    old = getattr(cls, name)
    patchlist.append((cls, name, old))
    setattr(cls, name, new_meth)
    return old

def _unpatch_meths(patchlist):
    for cls, name, old_meth in reversed(patchlist):
        setattr(cls, name, old_meth)

def _patch_for_embedding(patchlist):
    if sys.platform == 'win32':
        # we must not remove the manifest when building for embedding!
        from cffi._shimmed_dist_utils import MSVCCompiler
        _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref',
                    lambda self, manifest_file: manifest_file)

    if sys.platform == 'darwin':
        # we must not make a '-bundle', but a '-dynamiclib' instead
        from cffi._shimmed_dist_utils import CCompiler
        def my_link_shared_object(self, *args, **kwds):
            if '-bundle' in self.linker_so:
                self.linker_so = list(self.linker_so)
                i = self.linker_so.index('-bundle')
                self.linker_so[i] = '-dynamiclib'
            return old_link_shared_object(self, *args, **kwds)
        old_link_shared_object = _patch_meth(patchlist, CCompiler,
                                             'link_shared_object',
                                             my_link_shared_object)

def _patch_for_target(patchlist, target):
    from cffi._shimmed_dist_utils import build_ext
    # if 'target' is different from '*', we need to patch some internal
    # method to just return this 'target' value, instead of having it
    # built from module_name
    if target.endswith('.*'):
        target = target[:-2]
        if sys.platform == 'win32':
            target += '.dll'
        elif sys.platform == 'darwin':
            target += '.dylib'
        else:
            target += '.so'
    _patch_meth(patchlist, build_ext, 'get_ext_filename',
                lambda self, ext_name: target)


def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
              c_file=None, source_extension='.c', extradir=None,
              compiler_verbose=1, target=None, debug=None, **kwds):
    if not isinstance(module_name, str):
        module_name = module_name.encode('ascii')
    if ffi._windows_unicode:
        ffi._apply_windows_unicode(kwds)
    if preamble is not None:
        embedding = (ffi._embedding is not None)
        if embedding:
            ffi._apply_embedding_fix(kwds)
        if c_file is None:
            c_file, parts = _modname_to_file(tmpdir, module_name,
                                             source_extension)
            if extradir:
                parts = [extradir] + parts
            ext_c_file = os.path.join(*parts)
        else:
            ext_c_file = c_file
        #
        if target is None:
            if embedding:
                target = '%s.*' % module_name
            else:
                target = '*'
        #
        ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
        updated = make_c_source(ffi, module_name, preamble, c_file,
                                verbose=compiler_verbose)
        if call_c_compiler:
            patchlist = []
            cwd = os.getcwd()
            try:
                if embedding:
                    _patch_for_embedding(patchlist)
                if target != '*':
                    _patch_for_target(patchlist, target)
                if compiler_verbose:
                    if tmpdir == '.':
                        msg = 'the current directory is'
                    else:
                        msg = 'setting the current directory to'
                    print('%s %r' % (msg, os.path.abspath(tmpdir)))
                os.chdir(tmpdir)
                outputfilename = ffiplatform.compile('.', ext,
                                                     compiler_verbose, debug)
            finally:
                os.chdir(cwd)
                _unpatch_meths(patchlist)
            return outputfilename
        else:
            return ext, updated
    else:
        if c_file is None:
            c_file, _ = _modname_to_file(tmpdir, module_name, '.py')
        updated = make_py_source(ffi, module_name, c_file,
                                 verbose=compiler_verbose)
        if call_c_compiler:
            return c_file
        else:
            return None, updated


back/venvmsg/Lib/site-packages/cffi/recompiler.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import os
import sys

try:
    basestring
except NameError:
    # Python 3.x
    basestring = str

def error(msg):
    from cffi._shimmed_dist_utils import DistutilsSetupError
    raise DistutilsSetupError(msg)


def execfile(filename, glob):
    # We use execfile() (here rewritten for Python 3) instead of
    # __import__() to load the build script.  The problem with
    # a normal import is that in some packages, the intermediate
    # __init__.py files may already try to import the file that
    # we are generating.
    with open(filename) as f:
        src = f.read()
    src += '\n'      # Python 2.6 compatibility
    code = compile(src, filename, 'exec')
    exec(code, glob, glob)


def add_cffi_module(dist, mod_spec):
    from cffi.api import FFI

    if not isinstance(mod_spec, basestring):
        error("argument to 'cffi_modules=...' must be a str or a list of str,"
              " not %r" % (type(mod_spec).__name__,))
    mod_spec = str(mod_spec)
    try:
        build_file_name, ffi_var_name = mod_spec.split(':')
    except ValueError:
        error("%r must be of the form 'path/build.py:ffi_variable'" %
              (mod_spec,))
    if not os.path.exists(build_file_name):
        ext = ''
        rewritten = build_file_name.replace('.', '/') + '.py'
        if os.path.exists(rewritten):
            ext = ' (rewrite cffi_modules to [%r])' % (
                rewritten + ':' + ffi_var_name,)
        error("%r does not name an existing file%s" % (build_file_name, ext))

    mod_vars = {'__name__': '__cffi__', '__file__': build_file_name}
    execfile(build_file_name, mod_vars)

    try:
        ffi = mod_vars[ffi_var_name]
    except KeyError:
        error("%r: object %r not found in module" % (mod_spec,
                                                     ffi_var_name))
    if not isinstance(ffi, FFI):
        ffi = ffi()      # maybe it's a function instead of directly an ffi
    if not isinstance(ffi, FFI):
        error("%r is not an FFI instance (got %r)" % (mod_spec,
                                                      type(ffi).__name__))
    if not hasattr(ffi, '_assigned_source'):
        error("%r: the set_source() method was not called" % (mod_spec,))
    module_name, source, source_extension, kwds = ffi._assigned_source
    if ffi._windows_unicode:
        kwds = kwds.copy()
        ffi._apply_windows_unicode(kwds)

    if source is None:
        _add_py_module(dist, ffi, module_name)
    else:
        _add_c_module(dist, ffi, module_name, source, source_extension, kwds)

def _set_py_limited_api(Extension, kwds):
    """
    Add py_limited_api to kwds if setuptools >= 26 is in use.
    Do not alter the setting if it already exists.
    Setuptools takes care of ignoring the flag on Python 2 and PyPy.

    CPython itself should ignore the flag in a debugging version
    (by not listing .abi3.so in the extensions it supports), but
    it doesn't so far, creating troubles.  That's why we check
    for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent
    of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401)

    On Windows, with CPython <= 3.4, it's better not to use py_limited_api
    because virtualenv *still* doesn't copy PYTHON3.DLL on these versions.
    Recently (2020) we started shipping only >= 3.5 wheels, though.  So
    we'll give it another try and set py_limited_api on Windows >= 3.5.
    """
    from cffi import recompiler

    if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount')
            and recompiler.USE_LIMITED_API):
        import setuptools
        try:
            setuptools_major_version = int(setuptools.__version__.partition('.')[0])
            if setuptools_major_version >= 26:
                kwds['py_limited_api'] = True
        except ValueError:  # certain development versions of setuptools
            # If we don't know the version number of setuptools, we
            # try to set 'py_limited_api' anyway.  At worst, we get a
            # warning.
            kwds['py_limited_api'] = True
    return kwds

def _add_c_module(dist, ffi, module_name, source, source_extension, kwds):
    # We are a setuptools extension. Need this build_ext for py_limited_api.
    from setuptools.command.build_ext import build_ext
    from cffi._shimmed_dist_utils import Extension, log, mkpath
    from cffi import recompiler

    allsources = ['$PLACEHOLDER']
    allsources.extend(kwds.pop('sources', []))
    kwds = _set_py_limited_api(Extension, kwds)
    ext = Extension(name=module_name, sources=allsources, **kwds)

    def make_mod(tmpdir, pre_run=None):
        c_file = os.path.join(tmpdir, module_name + source_extension)
        log.info("generating cffi module %r" % c_file)
        mkpath(tmpdir)
        # a setuptools-only, API-only hook: called with the "ext" and "ffi"
        # arguments just before we turn the ffi into C code.  To use it,
        # subclass the 'distutils.command.build_ext.build_ext' class and
        # add a method 'def pre_run(self, ext, ffi)'.
        if pre_run is not None:
            pre_run(ext, ffi)
        updated = recompiler.make_c_source(ffi, module_name, source, c_file)
        if not updated:
            log.info("already up-to-date")
        return c_file

    if dist.ext_modules is None:
        dist.ext_modules = []
    dist.ext_modules.append(ext)

    base_class = dist.cmdclass.get('build_ext', build_ext)
    class build_ext_make_mod(base_class):
        def run(self):
            if ext.sources[0] == '$PLACEHOLDER':
                pre_run = getattr(self, 'pre_run', None)
                ext.sources[0] = make_mod(self.build_temp, pre_run)
            base_class.run(self)
    dist.cmdclass['build_ext'] = build_ext_make_mod
    # NB. multiple runs here will create multiple 'build_ext_make_mod'
    # classes.  Even in this case the 'build_ext' command should be
    # run once; but just in case, the logic above does nothing if
    # called again.


def _add_py_module(dist, ffi, module_name):
    from setuptools.command.build_py import build_py
    from setuptools.command.build_ext import build_ext
    from cffi._shimmed_dist_utils import log, mkpath
    from cffi import recompiler

    def generate_mod(py_file):
        log.info("generating cffi module %r" % py_file)
        mkpath(os.path.dirname(py_file))
        updated = recompiler.make_py_source(ffi, module_name, py_file)
        if not updated:
            log.info("already up-to-date")

    base_class = dist.cmdclass.get('build_py', build_py)
    class build_py_make_mod(base_class):
        def run(self):
            base_class.run(self)
            module_path = module_name.split('.')
            module_path[-1] += '.py'
            generate_mod(os.path.join(self.build_lib, *module_path))
        def get_source_files(self):
            # This is called from 'setup.py sdist' only.  Exclude
            # the generate .py module in this case.
            saved_py_modules = self.py_modules
            try:
                if saved_py_modules:
                    self.py_modules = [m for m in saved_py_modules
                                         if m != module_name]
                return base_class.get_source_files(self)
            finally:
                self.py_modules = saved_py_modules
    dist.cmdclass['build_py'] = build_py_make_mod

    # distutils and setuptools have no notion I could find of a
    # generated python module.  If we don't add module_name to
    # dist.py_modules, then things mostly work but there are some
    # combination of options (--root and --record) that will miss
    # the module.  So we add it here, which gives a few apparently
    # harmless warnings about not finding the file outside the
    # build directory.
    # Then we need to hack more in get_source_files(); see above.
    if dist.py_modules is None:
        dist.py_modules = []
    dist.py_modules.append(module_name)

    # the following is only for "build_ext -i"
    base_class_2 = dist.cmdclass.get('build_ext', build_ext)
    class build_ext_make_mod(base_class_2):
        def run(self):
            base_class_2.run(self)
            if self.inplace:
                # from get_ext_fullpath() in distutils/command/build_ext.py
                module_path = module_name.split('.')
                package = '.'.join(module_path[:-1])
                build_py = self.get_finalized_command('build_py')
                package_dir = build_py.get_package_dir(package)
                file_name = module_path[-1] + '.py'
                generate_mod(os.path.join(package_dir, file_name))
    dist.cmdclass['build_ext'] = build_ext_make_mod

def cffi_modules(dist, attr, value):
    assert attr == 'cffi_modules'
    if isinstance(value, basestring):
        value = [value]

    for cffi_module in value:
        add_cffi_module(dist, cffi_module)

back/venvmsg/Lib/site-packages/cffi/setuptools_ext.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
#
# DEPRECATED: implementation for ffi.verify()
#
import sys
from . import model
from .error import VerificationError
from . import _imp_emulation as imp


class VCPythonEngine(object):
    _class_key = 'x'
    _gen_python_module = True

    def __init__(self, verifier):
        self.verifier = verifier
        self.ffi = verifier.ffi
        self._struct_pending_verification = {}
        self._types_of_builtin_functions = {}

    def patch_extension_kwds(self, kwds):
        pass

    def find_module(self, module_name, path, so_suffixes):
        try:
            f, filename, descr = imp.find_module(module_name, path)
        except ImportError:
            return None
        if f is not None:
            f.close()
        # Note that after a setuptools installation, there are both .py
        # and .so files with the same basename.  The code here relies on
        # imp.find_module() locating the .so in priority.
        if descr[0] not in so_suffixes:
            return None
        return filename

    def collect_types(self):
        self._typesdict = {}
        self._generate("collecttype")

    def _prnt(self, what=''):
        self._f.write(what + '\n')

    def _gettypenum(self, type):
        # a KeyError here is a bug.  please report it! :-)
        return self._typesdict[type]

    def _do_collect_type(self, tp):
        if ((not isinstance(tp, model.PrimitiveType)
             or tp.name == 'long double')
                and tp not in self._typesdict):
            num = len(self._typesdict)
            self._typesdict[tp] = num

    def write_source_to_f(self):
        self.collect_types()
        #
        # The new module will have a _cffi_setup() function that receives
        # objects from the ffi world, and that calls some setup code in
        # the module.  This setup code is split in several independent
        # functions, e.g. one per constant.  The functions are "chained"
        # by ending in a tail call to each other.
        #
        # This is further split in two chained lists, depending on if we
        # can do it at import-time or if we must wait for _cffi_setup() to
        # provide us with the <ctype> objects.  This is needed because we
        # need the values of the enum constants in order to build the
        # <ctype 'enum'> that we may have to pass to _cffi_setup().
        #
        # The following two 'chained_list_constants' items contains
        # the head of these two chained lists, as a string that gives the
        # call to do, if any.
        self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)']
        #
        prnt = self._prnt
        # first paste some standard set of lines that are mostly '#define'
        prnt(cffimod_header)
        prnt()
        # then paste the C source given by the user, verbatim.
        prnt(self.verifier.preamble)
        prnt()
        #
        # call generate_cpy_xxx_decl(), for every xxx found from
        # ffi._parser._declarations.  This generates all the functions.
        self._generate("decl")
        #
        # implement the function _cffi_setup_custom() as calling the
        # head of the chained list.
        self._generate_setup_custom()
        prnt()
        #
        # produce the method table, including the entries for the
        # generated Python->C function wrappers, which are done
        # by generate_cpy_function_method().
        prnt('static PyMethodDef _cffi_methods[] = {')
        self._generate("method")
        prnt('  {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},')
        prnt('  {NULL, NULL, 0, NULL}    /* Sentinel */')
        prnt('};')
        prnt()
        #
        # standard init.
        modname = self.verifier.get_module_name()
        constants = self._chained_list_constants[False]
        prnt('#if PY_MAJOR_VERSION >= 3')
        prnt()
        prnt('static struct PyModuleDef _cffi_module_def = {')
        prnt('  PyModuleDef_HEAD_INIT,')
        prnt('  "%s",' % modname)
        prnt('  NULL,')
        prnt('  -1,')
        prnt('  _cffi_methods,')
        prnt('  NULL, NULL, NULL, NULL')
        prnt('};')
        prnt()
        prnt('PyMODINIT_FUNC')
        prnt('PyInit_%s(void)' % modname)
        prnt('{')
        prnt('  PyObject *lib;')
        prnt('  lib = PyModule_Create(&_cffi_module_def);')
        prnt('  if (lib == NULL)')
        prnt('    return NULL;')
        prnt('  if (%s < 0 || _cffi_init() < 0) {' % (constants,))
        prnt('    Py_DECREF(lib);')
        prnt('    return NULL;')
        prnt('  }')
        prnt('  return lib;')
        prnt('}')
        prnt()
        prnt('#else')
        prnt()
        prnt('PyMODINIT_FUNC')
        prnt('init%s(void)' % modname)
        prnt('{')
        prnt('  PyObject *lib;')
        prnt('  lib = Py_InitModule("%s", _cffi_methods);' % modname)
        prnt('  if (lib == NULL)')
        prnt('    return;')
        prnt('  if (%s < 0 || _cffi_init() < 0)' % (constants,))
        prnt('    return;')
        prnt('  return;')
        prnt('}')
        prnt()
        prnt('#endif')

    def load_library(self, flags=None):
        # XXX review all usages of 'self' here!
        # import it as a new extension module
        imp.acquire_lock()
        try:
            if hasattr(sys, "getdlopenflags"):
                previous_flags = sys.getdlopenflags()
            try:
                if hasattr(sys, "setdlopenflags") and flags is not None:
                    sys.setdlopenflags(flags)
                module = imp.load_dynamic(self.verifier.get_module_name(),
                                          self.verifier.modulefilename)
            except ImportError as e:
                error = "importing %r: %s" % (self.verifier.modulefilename, e)
                raise VerificationError(error)
            finally:
                if hasattr(sys, "setdlopenflags"):
                    sys.setdlopenflags(previous_flags)
        finally:
            imp.release_lock()
        #
        # call loading_cpy_struct() to get the struct layout inferred by
        # the C compiler
        self._load(module, 'loading')
        #
        # the C code will need the <ctype> objects.  Collect them in
        # order in a list.
        revmapping = dict([(value, key)
                           for (key, value) in self._typesdict.items()])
        lst = [revmapping[i] for i in range(len(revmapping))]
        lst = list(map(self.ffi._get_cached_btype, lst))
        #
        # build the FFILibrary class and instance and call _cffi_setup().
        # this will set up some fields like '_cffi_types', and only then
        # it will invoke the chained list of functions that will really
        # build (notably) the constant objects, as <cdata> if they are
        # pointers, and store them as attributes on the 'library' object.
        class FFILibrary(object):
            _cffi_python_module = module
            _cffi_ffi = self.ffi
            _cffi_dir = []
            def __dir__(self):
                return FFILibrary._cffi_dir + list(self.__dict__)
        library = FFILibrary()
        if module._cffi_setup(lst, VerificationError, library):
            import warnings
            warnings.warn("reimporting %r might overwrite older definitions"
                          % (self.verifier.get_module_name()))
        #
        # finally, call the loaded_cpy_xxx() functions.  This will perform
        # the final adjustments, like copying the Python->C wrapper
        # functions from the module to the 'library' object, and setting
        # up the FFILibrary class with properties for the global C variables.
        self._load(module, 'loaded', library=library)
        module._cffi_original_ffi = self.ffi
        module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions
        return library

    def _get_declarations(self):
        lst = [(key, tp) for (key, (tp, qual)) in
                                self.ffi._parser._declarations.items()]
        lst.sort()
        return lst

    def _generate(self, step_name):
        for name, tp in self._get_declarations():
            kind, realname = name.split(' ', 1)
            try:
                method = getattr(self, '_generate_cpy_%s_%s' % (kind,
                                                                step_name))
            except AttributeError:
                raise VerificationError(
                    "not implemented in verify(): %r" % name)
            try:
                method(tp, realname)
            except Exception as e:
                model.attach_exception_info(e, name)
                raise

    def _load(self, module, step_name, **kwds):
        for name, tp in self._get_declarations():
            kind, realname = name.split(' ', 1)
            method = getattr(self, '_%s_cpy_%s' % (step_name, kind))
            try:
                method(tp, realname, module, **kwds)
            except Exception as e:
                model.attach_exception_info(e, name)
                raise

    def _generate_nothing(self, tp, name):
        pass

    def _loaded_noop(self, tp, name, module, **kwds):
        pass

    # ----------

    def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
        extraarg = ''
        if isinstance(tp, model.PrimitiveType):
            if tp.is_integer_type() and tp.name != '_Bool':
                converter = '_cffi_to_c_int'
                extraarg = ', %s' % tp.name
            else:
                converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''),
                                                   tp.name.replace(' ', '_'))
            errvalue = '-1'
        #
        elif isinstance(tp, model.PointerType):
            self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
                                                    tovar, errcode)
            return
        #
        elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
            # a struct (not a struct pointer) as a function argument
            self._prnt('  if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
                      % (tovar, self._gettypenum(tp), fromvar))
            self._prnt('    %s;' % errcode)
            return
        #
        elif isinstance(tp, model.FunctionPtrType):
            converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
            extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
            errvalue = 'NULL'
        #
        else:
            raise NotImplementedError(tp)
        #
        self._prnt('  %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
        self._prnt('  if (%s == (%s)%s && PyErr_Occurred())' % (
            tovar, tp.get_c_name(''), errvalue))
        self._prnt('    %s;' % errcode)

    def _extra_local_variables(self, tp, localvars, freelines):
        if isinstance(tp, model.PointerType):
            localvars.add('Py_ssize_t datasize')
            localvars.add('struct _cffi_freeme_s *large_args_free = NULL')
            freelines.add('if (large_args_free != NULL)'
                          ' _cffi_free_array_arguments(large_args_free);')

    def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
        self._prnt('  datasize = _cffi_prepare_pointer_call_argument(')
        self._prnt('      _cffi_type(%d), %s, (char **)&%s);' % (
            self._gettypenum(tp), fromvar, tovar))
        self._prnt('  if (datasize != 0) {')
        self._prnt('    %s = ((size_t)datasize) <= 640 ? '
                   'alloca((size_t)datasize) : NULL;' % (tovar,))
        self._prnt('    if (_cffi_convert_array_argument(_cffi_type(%d), %s, '
                   '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))
        self._prnt('            datasize, &large_args_free) < 0)')
        self._prnt('      %s;' % errcode)
        self._prnt('  }')

    def _convert_expr_from_c(self, tp, var, context):
        if isinstance(tp, model.PrimitiveType):
            if tp.is_integer_type() and tp.name != '_Bool':
                return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
            elif tp.name != 'long double':
                return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
            else:
                return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
                    var, self._gettypenum(tp))
        elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
                var, self._gettypenum(tp))
        elif isinstance(tp, model.ArrayType):
            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
                var, self._gettypenum(model.PointerType(tp.item)))
        elif isinstance(tp, model.StructOrUnion):
            if tp.fldnames is None:
                raise TypeError("'%s' is used as %s, but is opaque" % (
                    tp._get_c_name(), context))
            return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
                var, self._gettypenum(tp))
        elif isinstance(tp, model.EnumType):
            return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
                var, self._gettypenum(tp))
        else:
            raise NotImplementedError(tp)

    # ----------
    # typedefs: generates no code so far

    _generate_cpy_typedef_collecttype = _generate_nothing
    _generate_cpy_typedef_decl   = _generate_nothing
    _generate_cpy_typedef_method = _generate_nothing
    _loading_cpy_typedef         = _loaded_noop
    _loaded_cpy_typedef          = _loaded_noop

    # ----------
    # function declarations

    def _generate_cpy_function_collecttype(self, tp, name):
        assert isinstance(tp, model.FunctionPtrType)
        if tp.ellipsis:
            self._do_collect_type(tp)
        else:
            # don't call _do_collect_type(tp) in this common case,
            # otherwise test_autofilled_struct_as_argument fails
            for type in tp.args:
                self._do_collect_type(type)
            self._do_collect_type(tp.result)

    def _generate_cpy_function_decl(self, tp, name):
        assert isinstance(tp, model.FunctionPtrType)
        if tp.ellipsis:
            # cannot support vararg functions better than this: check for its
            # exact type (including the fixed arguments), and build it as a
            # constant function pointer (no CPython wrapper)
            self._generate_cpy_const(False, name, tp)
            return
        prnt = self._prnt
        numargs = len(tp.args)
        if numargs == 0:
            argname = 'noarg'
        elif numargs == 1:
            argname = 'arg0'
        else:
            argname = 'args'
        prnt('static PyObject *')
        prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
        prnt('{')
        #
        context = 'argument of %s' % name
        for i, type in enumerate(tp.args):
            prnt('  %s;' % type.get_c_name(' x%d' % i, context))
        #
        localvars = set()
        freelines = set()
        for type in tp.args:
            self._extra_local_variables(type, localvars, freelines)
        for decl in sorted(localvars):
            prnt('  %s;' % (decl,))
        #
        if not isinstance(tp.result, model.VoidType):
            result_code = 'result = '
            context = 'result of %s' % name
            prnt('  %s;' % tp.result.get_c_name(' result', context))
            prnt('  PyObject *pyresult;')
        else:
            result_code = ''
        #
        if len(tp.args) > 1:
            rng = range(len(tp.args))
            for i in rng:
                prnt('  PyObject *arg%d;' % i)
            prnt()
            prnt('  if (!PyArg_ParseTuple(args, "%s:%s", %s))' % (
                'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
            prnt('    return NULL;')
        prnt()
        #
        for i, type in enumerate(tp.args):
            self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
                                       'return NULL')
            prnt()
        #
        prnt('  Py_BEGIN_ALLOW_THREADS')
        prnt('  _cffi_restore_errno();')
        prnt('  { %s%s(%s); }' % (
            result_code, name,
            ', '.join(['x%d' % i for i in range(len(tp.args))])))
        prnt('  _cffi_save_errno();')
        prnt('  Py_END_ALLOW_THREADS')
        prnt()
        #
        prnt('  (void)self; /* unused */')
        if numargs == 0:
            prnt('  (void)noarg; /* unused */')
        if result_code:
            prnt('  pyresult = %s;' %
                 self._convert_expr_from_c(tp.result, 'result', 'result type'))
            for freeline in freelines:
                prnt('  ' + freeline)
            prnt('  return pyresult;')
        else:
            for freeline in freelines:
                prnt('  ' + freeline)
            prnt('  Py_INCREF(Py_None);')
            prnt('  return Py_None;')
        prnt('}')
        prnt()

    def _generate_cpy_function_method(self, tp, name):
        if tp.ellipsis:
            return
        numargs = len(tp.args)
        if numargs == 0:
            meth = 'METH_NOARGS'
        elif numargs == 1:
            meth = 'METH_O'
        else:
            meth = 'METH_VARARGS'
        self._prnt('  {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth))

    _loading_cpy_function = _loaded_noop

    def _loaded_cpy_function(self, tp, name, module, library):
        if tp.ellipsis:
            return
        func = getattr(module, name)
        setattr(library, name, func)
        self._types_of_builtin_functions[func] = tp

    # ----------
    # named structs

    _generate_cpy_struct_collecttype = _generate_nothing
    def _generate_cpy_struct_decl(self, tp, name):
        assert name == tp.name
        self._generate_struct_or_union_decl(tp, 'struct', name)
    def _generate_cpy_struct_method(self, tp, name):
        self._generate_struct_or_union_method(tp, 'struct', name)
    def _loading_cpy_struct(self, tp, name, module):
        self._loading_struct_or_union(tp, 'struct', name, module)
    def _loaded_cpy_struct(self, tp, name, module, **kwds):
        self._loaded_struct_or_union(tp)

    _generate_cpy_union_collecttype = _generate_nothing
    def _generate_cpy_union_decl(self, tp, name):
        assert name == tp.name
        self._generate_struct_or_union_decl(tp, 'union', name)
    def _generate_cpy_union_method(self, tp, name):
        self._generate_struct_or_union_method(tp, 'union', name)
    def _loading_cpy_union(self, tp, name, module):
        self._loading_struct_or_union(tp, 'union', name, module)
    def _loaded_cpy_union(self, tp, name, module, **kwds):
        self._loaded_struct_or_union(tp)

    def _generate_struct_or_union_decl(self, tp, prefix, name):
        if tp.fldnames is None:
            return     # nothing to do with opaque structs
        checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
        cname = ('%s %s' % (prefix, name)).strip()
        #
        prnt = self._prnt
        prnt('static void %s(%s *p)' % (checkfuncname, cname))
        prnt('{')
        prnt('  /* only to generate compile-time warnings or errors */')
        prnt('  (void)p;')
        for fname, ftype, fbitsize, fqual in tp.enumfields():
            if (isinstance(ftype, model.PrimitiveType)
                and ftype.is_integer_type()) or fbitsize >= 0:
                # accept all integers, but complain on float or double
                prnt('  (void)((p->%s) << 1);' % fname)
            else:
                # only accept exactly the type declared.
                try:
                    prnt('  { %s = &p->%s; (void)tmp; }' % (
                        ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
                        fname))
                except VerificationError as e:
                    prnt('  /* %s */' % str(e))   # cannot verify it, ignore
        prnt('}')
        prnt('static PyObject *')
        prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,))
        prnt('{')
        prnt('  struct _cffi_aligncheck { char x; %s y; };' % cname)
        prnt('  static Py_ssize_t nums[] = {')
        prnt('    sizeof(%s),' % cname)
        prnt('    offsetof(struct _cffi_aligncheck, y),')
        for fname, ftype, fbitsize, fqual in tp.enumfields():
            if fbitsize >= 0:
                continue      # xxx ignore fbitsize for now
            prnt('    offsetof(%s, %s),' % (cname, fname))
            if isinstance(ftype, model.ArrayType) and ftype.length is None:
                prnt('    0,  /* %s */' % ftype._get_c_name())
            else:
                prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
        prnt('    -1')
        prnt('  };')
        prnt('  (void)self; /* unused */')
        prnt('  (void)noarg; /* unused */')
        prnt('  return _cffi_get_struct_layout(nums);')
        prnt('  /* the next line is not executed, but compiled */')
        prnt('  %s(0);' % (checkfuncname,))
        prnt('}')
        prnt()

    def _generate_struct_or_union_method(self, tp, prefix, name):
        if tp.fldnames is None:
            return     # nothing to do with opaque structs
        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
        self._prnt('  {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname,
                                                         layoutfuncname))

    def _loading_struct_or_union(self, tp, prefix, name, module):
        if tp.fldnames is None:
            return     # nothing to do with opaque structs
        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
        #
        function = getattr(module, layoutfuncname)
        layout = function()
        if isinstance(tp, model.StructOrUnion) and tp.partial:
            # use the function()'s sizes and offsets to guide the
            # layout of the struct
            totalsize = layout[0]
            totalalignment = layout[1]
            fieldofs = layout[2::2]
            fieldsize = layout[3::2]
            tp.force_flatten()
            assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
            tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
        else:
            cname = ('%s %s' % (prefix, name)).strip()
            self._struct_pending_verification[tp] = layout, cname

    def _loaded_struct_or_union(self, tp):
        if tp.fldnames is None:
            return     # nothing to do with opaque structs
        self.ffi._get_cached_btype(tp)   # force 'fixedlayout' to be considered

        if tp in self._struct_pending_verification:
            # check that the layout sizes and offsets match the real ones
            def check(realvalue, expectedvalue, msg):
                if realvalue != expectedvalue:
                    raise VerificationError(
                        "%s (we have %d, but C compiler says %d)"
                        % (msg, expectedvalue, realvalue))
            ffi = self.ffi
            BStruct = ffi._get_cached_btype(tp)
            layout, cname = self._struct_pending_verification.pop(tp)
            check(layout[0], ffi.sizeof(BStruct), "wrong total size")
            check(layout[1], ffi.alignof(BStruct), "wrong total alignment")
            i = 2
            for fname, ftype, fbitsize, fqual in tp.enumfields():
                if fbitsize >= 0:
                    continue        # xxx ignore fbitsize for now
                check(layout[i], ffi.offsetof(BStruct, fname),
                      "wrong offset for field %r" % (fname,))
                if layout[i+1] != 0:
                    BField = ffi._get_cached_btype(ftype)
                    check(layout[i+1], ffi.sizeof(BField),
                          "wrong size for field %r" % (fname,))
                i += 2
            assert i == len(layout)

    # ----------
    # 'anonymous' declarations.  These are produced for anonymous structs
    # or unions; the 'name' is obtained by a typedef.

    _generate_cpy_anonymous_collecttype = _generate_nothing

    def _generate_cpy_anonymous_decl(self, tp, name):
        if isinstance(tp, model.EnumType):
            self._generate_cpy_enum_decl(tp, name, '')
        else:
            self._generate_struct_or_union_decl(tp, '', name)

    def _generate_cpy_anonymous_method(self, tp, name):
        if not isinstance(tp, model.EnumType):
            self._generate_struct_or_union_method(tp, '', name)

    def _loading_cpy_anonymous(self, tp, name, module):
        if isinstance(tp, model.EnumType):
            self._loading_cpy_enum(tp, name, module)
        else:
            self._loading_struct_or_union(tp, '', name, module)

    def _loaded_cpy_anonymous(self, tp, name, module, **kwds):
        if isinstance(tp, model.EnumType):
            self._loaded_cpy_enum(tp, name, module, **kwds)
        else:
            self._loaded_struct_or_union(tp)

    # ----------
    # constants, likely declared with '#define'

    def _generate_cpy_const(self, is_int, name, tp=None, category='const',
                            vartp=None, delayed=True, size_too=False,
                            check_value=None):
        prnt = self._prnt
        funcname = '_cffi_%s_%s' % (category, name)
        prnt('static int %s(PyObject *lib)' % funcname)
        prnt('{')
        prnt('  PyObject *o;')
        prnt('  int res;')
        if not is_int:
            prnt('  %s;' % (vartp or tp).get_c_name(' i', name))
        else:
            assert category == 'const'
        #
        if check_value is not None:
            self._check_int_constant_value(name, check_value)
        #
        if not is_int:
            if category == 'var':
                realexpr = '&' + name
            else:
                realexpr = name
            prnt('  i = (%s);' % (realexpr,))
            prnt('  o = %s;' % (self._convert_expr_from_c(tp, 'i',
                                                          'variable type'),))
            assert delayed
        else:
            prnt('  o = _cffi_from_c_int_const(%s);' % name)
        prnt('  if (o == NULL)')
        prnt('    return -1;')
        if size_too:
            prnt('  {')
            prnt('    PyObject *o1 = o;')
            prnt('    o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));'
                 % (name,))
            prnt('    Py_DECREF(o1);')
            prnt('    if (o == NULL)')
            prnt('      return -1;')
            prnt('  }')
        prnt('  res = PyObject_SetAttrString(lib, "%s", o);' % name)
        prnt('  Py_DECREF(o);')
        prnt('  if (res < 0)')
        prnt('    return -1;')
        prnt('  return %s;' % self._chained_list_constants[delayed])
        self._chained_list_constants[delayed] = funcname + '(lib)'
        prnt('}')
        prnt()

    def _generate_cpy_constant_collecttype(self, tp, name):
        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
        if not is_int:
            self._do_collect_type(tp)

    def _generate_cpy_constant_decl(self, tp, name):
        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
        self._generate_cpy_const(is_int, name, tp)

    _generate_cpy_constant_method = _generate_nothing
    _loading_cpy_constant = _loaded_noop
    _loaded_cpy_constant  = _loaded_noop

    # ----------
    # enums

    def _check_int_constant_value(self, name, value, err_prefix=''):
        prnt = self._prnt
        if value <= 0:
            prnt('  if ((%s) > 0 || (long)(%s) != %dL) {' % (
                name, name, value))
        else:
            prnt('  if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % (
                name, name, value))
        prnt('    char buf[64];')
        prnt('    if ((%s) <= 0)' % name)
        prnt('        snprintf(buf, 63, "%%ld", (long)(%s));' % name)
        prnt('    else')
        prnt('        snprintf(buf, 63, "%%lu", (unsigned long)(%s));' %
             name)
        prnt('    PyErr_Format(_cffi_VerificationError,')
        prnt('                 "%s%s has the real value %s, not %s",')
        prnt('                 "%s", "%s", buf, "%d");' % (
            err_prefix, name, value))
        prnt('    return -1;')
        prnt('  }')

    def _enum_funcname(self, prefix, name):
        # "$enum_$1" => "___D_enum____D_1"
        name = name.replace('$', '___D_')
        return '_cffi_e_%s_%s' % (prefix, name)

    def _generate_cpy_enum_decl(self, tp, name, prefix='enum'):
        if tp.partial:
            for enumerator in tp.enumerators:
                self._generate_cpy_const(True, enumerator, delayed=False)
            return
        #
        funcname = self._enum_funcname(prefix, name)
        prnt = self._prnt
        prnt('static int %s(PyObject *lib)' % funcname)
        prnt('{')
        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
            self._check_int_constant_value(enumerator, enumvalue,
                                           "enum %s: " % name)
        prnt('  return %s;' % self._chained_list_constants[True])
        self._chained_list_constants[True] = funcname + '(lib)'
        prnt('}')
        prnt()

    _generate_cpy_enum_collecttype = _generate_nothing
    _generate_cpy_enum_method = _generate_nothing

    def _loading_cpy_enum(self, tp, name, module):
        if tp.partial:
            enumvalues = [getattr(module, enumerator)
                          for enumerator in tp.enumerators]
            tp.enumvalues = tuple(enumvalues)
            tp.partial_resolved = True

    def _loaded_cpy_enum(self, tp, name, module, library):
        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
            setattr(library, enumerator, enumvalue)

    # ----------
    # macros: for now only for integers

    def _generate_cpy_macro_decl(self, tp, name):
        if tp == '...':
            check_value = None
        else:
            check_value = tp     # an integer
        self._generate_cpy_const(True, name, check_value=check_value)

    _generate_cpy_macro_collecttype = _generate_nothing
    _generate_cpy_macro_method = _generate_nothing
    _loading_cpy_macro = _loaded_noop
    _loaded_cpy_macro  = _loaded_noop

    # ----------
    # global variables

    def _generate_cpy_variable_collecttype(self, tp, name):
        if isinstance(tp, model.ArrayType):
            tp_ptr = model.PointerType(tp.item)
        else:
            tp_ptr = model.PointerType(tp)
        self._do_collect_type(tp_ptr)

    def _generate_cpy_variable_decl(self, tp, name):
        if isinstance(tp, model.ArrayType):
            tp_ptr = model.PointerType(tp.item)
            self._generate_cpy_const(False, name, tp, vartp=tp_ptr,
                                     size_too = tp.length_is_unknown())
        else:
            tp_ptr = model.PointerType(tp)
            self._generate_cpy_const(False, name, tp_ptr, category='var')

    _generate_cpy_variable_method = _generate_nothing
    _loading_cpy_variable = _loaded_noop

    def _loaded_cpy_variable(self, tp, name, module, library):
        value = getattr(library, name)
        if isinstance(tp, model.ArrayType):   # int a[5] is "constant" in the
                                              # sense that "a=..." is forbidden
            if tp.length_is_unknown():
                assert isinstance(value, tuple)
                (value, size) = value
                BItemType = self.ffi._get_cached_btype(tp.item)
                length, rest = divmod(size, self.ffi.sizeof(BItemType))
                if rest != 0:
                    raise VerificationError(
                        "bad size: %r does not seem to be an array of %s" %
                        (name, tp.item))
                tp = tp.resolve_length(length)
            # 'value' is a <cdata 'type *'> which we have to replace with
            # a <cdata 'type[N]'> if the N is actually known
            if tp.length is not None:
                BArray = self.ffi._get_cached_btype(tp)
                value = self.ffi.cast(BArray, value)
                setattr(library, name, value)
            return
        # remove ptr=<cdata 'int *'> from the library instance, and replace
        # it by a property on the class, which reads/writes into ptr[0].
        ptr = value
        delattr(library, name)
        def getter(library):
            return ptr[0]
        def setter(library, value):
            ptr[0] = value
        setattr(type(library), name, property(getter, setter))
        type(library)._cffi_dir.append(name)

    # ----------

    def _generate_setup_custom(self):
        prnt = self._prnt
        prnt('static int _cffi_setup_custom(PyObject *lib)')
        prnt('{')
        prnt('  return %s;' % self._chained_list_constants[True])
        prnt('}')

cffimod_header = r'''
#include <Python.h>
#include <stddef.h>

/* this block of #ifs should be kept exactly identical between
   c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py
   and cffi/_cffi_include.h */
#if defined(_MSC_VER)
# include <malloc.h>   /* for alloca() */
# if _MSC_VER < 1600   /* MSVC < 2010 */
   typedef __int8 int8_t;
   typedef __int16 int16_t;
   typedef __int32 int32_t;
   typedef __int64 int64_t;
   typedef unsigned __int8 uint8_t;
   typedef unsigned __int16 uint16_t;
   typedef unsigned __int32 uint32_t;
   typedef unsigned __int64 uint64_t;
   typedef __int8 int_least8_t;
   typedef __int16 int_least16_t;
   typedef __int32 int_least32_t;
   typedef __int64 int_least64_t;
   typedef unsigned __int8 uint_least8_t;
   typedef unsigned __int16 uint_least16_t;
   typedef unsigned __int32 uint_least32_t;
   typedef unsigned __int64 uint_least64_t;
   typedef __int8 int_fast8_t;
   typedef __int16 int_fast16_t;
   typedef __int32 int_fast32_t;
   typedef __int64 int_fast64_t;
   typedef unsigned __int8 uint_fast8_t;
   typedef unsigned __int16 uint_fast16_t;
   typedef unsigned __int32 uint_fast32_t;
   typedef unsigned __int64 uint_fast64_t;
   typedef __int64 intmax_t;
   typedef unsigned __int64 uintmax_t;
# else
#  include <stdint.h>
# endif
# if _MSC_VER < 1800   /* MSVC < 2013 */
#  ifndef __cplusplus
    typedef unsigned char _Bool;
#  endif
# endif
#else
# include <stdint.h>
# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux)
#  include <alloca.h>
# endif
#endif

#if PY_MAJOR_VERSION < 3
# undef PyCapsule_CheckExact
# undef PyCapsule_GetPointer
# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule))
# define PyCapsule_GetPointer(capsule, name) \
    (PyCObject_AsVoidPtr(capsule))
#endif

#if PY_MAJOR_VERSION >= 3
# define PyInt_FromLong PyLong_FromLong
#endif

#define _cffi_from_c_double PyFloat_FromDouble
#define _cffi_from_c_float PyFloat_FromDouble
#define _cffi_from_c_long PyInt_FromLong
#define _cffi_from_c_ulong PyLong_FromUnsignedLong
#define _cffi_from_c_longlong PyLong_FromLongLong
#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong
#define _cffi_from_c__Bool PyBool_FromLong

#define _cffi_to_c_double PyFloat_AsDouble
#define _cffi_to_c_float PyFloat_AsDouble

#define _cffi_from_c_int_const(x)                                        \
    (((x) > 0) ?                                                         \
        ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ?      \
            PyInt_FromLong((long)(x)) :                                  \
            PyLong_FromUnsignedLongLong((unsigned long long)(x)) :       \
        ((long long)(x) >= (long long)LONG_MIN) ?                        \
            PyInt_FromLong((long)(x)) :                                  \
            PyLong_FromLongLong((long long)(x)))

#define _cffi_from_c_int(x, type)                                        \
    (((type)-1) > 0 ? /* unsigned */                                     \
        (sizeof(type) < sizeof(long) ?                                   \
            PyInt_FromLong((long)x) :                                    \
         sizeof(type) == sizeof(long) ?                                  \
            PyLong_FromUnsignedLong((unsigned long)x) :                  \
            PyLong_FromUnsignedLongLong((unsigned long long)x)) :        \
        (sizeof(type) <= sizeof(long) ?                                  \
            PyInt_FromLong((long)x) :                                    \
            PyLong_FromLongLong((long long)x)))

#define _cffi_to_c_int(o, type)                                          \
    ((type)(                                                             \
     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
                                         : (type)_cffi_to_c_i8(o)) :     \
     sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
                                         : (type)_cffi_to_c_i16(o)) :    \
     sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o)       \
                                         : (type)_cffi_to_c_i32(o)) :    \
     sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
                                         : (type)_cffi_to_c_i64(o)) :    \
     (Py_FatalError("unsupported size for type " #type), (type)0)))

#define _cffi_to_c_i8                                                    \
                 ((int(*)(PyObject *))_cffi_exports[1])
#define _cffi_to_c_u8                                                    \
                 ((int(*)(PyObject *))_cffi_exports[2])
#define _cffi_to_c_i16                                                   \
                 ((int(*)(PyObject *))_cffi_exports[3])
#define _cffi_to_c_u16                                                   \
                 ((int(*)(PyObject *))_cffi_exports[4])
#define _cffi_to_c_i32                                                   \
                 ((int(*)(PyObject *))_cffi_exports[5])
#define _cffi_to_c_u32                                                   \
                 ((unsigned int(*)(PyObject *))_cffi_exports[6])
#define _cffi_to_c_i64                                                   \
                 ((long long(*)(PyObject *))_cffi_exports[7])
#define _cffi_to_c_u64                                                   \
                 ((unsigned long long(*)(PyObject *))_cffi_exports[8])
#define _cffi_to_c_char                                                  \
                 ((int(*)(PyObject *))_cffi_exports[9])
#define _cffi_from_c_pointer                                             \
    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10])
#define _cffi_to_c_pointer                                               \
    ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11])
#define _cffi_get_struct_layout                                          \
    ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12])
#define _cffi_restore_errno                                              \
    ((void(*)(void))_cffi_exports[13])
#define _cffi_save_errno                                                 \
    ((void(*)(void))_cffi_exports[14])
#define _cffi_from_c_char                                                \
    ((PyObject *(*)(char))_cffi_exports[15])
#define _cffi_from_c_deref                                               \
    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
#define _cffi_to_c                                                       \
    ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
#define _cffi_from_c_struct                                              \
    ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18])
#define _cffi_to_c_wchar_t                                               \
    ((wchar_t(*)(PyObject *))_cffi_exports[19])
#define _cffi_from_c_wchar_t                                             \
    ((PyObject *(*)(wchar_t))_cffi_exports[20])
#define _cffi_to_c_long_double                                           \
    ((long double(*)(PyObject *))_cffi_exports[21])
#define _cffi_to_c__Bool                                                 \
    ((_Bool(*)(PyObject *))_cffi_exports[22])
#define _cffi_prepare_pointer_call_argument                              \
    ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23])
#define _cffi_convert_array_from_object                                  \
    ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24])
#define _CFFI_NUM_EXPORTS 25

typedef struct _ctypedescr CTypeDescrObject;

static void *_cffi_exports[_CFFI_NUM_EXPORTS];
static PyObject *_cffi_types, *_cffi_VerificationError;

static int _cffi_setup_custom(PyObject *lib);   /* forward */

static PyObject *_cffi_setup(PyObject *self, PyObject *args)
{
    PyObject *library;
    int was_alive = (_cffi_types != NULL);
    (void)self; /* unused */
    if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError,
                                       &library))
        return NULL;
    Py_INCREF(_cffi_types);
    Py_INCREF(_cffi_VerificationError);
    if (_cffi_setup_custom(library) < 0)
        return NULL;
    return PyBool_FromLong(was_alive);
}

union _cffi_union_alignment_u {
    unsigned char m_char;
    unsigned short m_short;
    unsigned int m_int;
    unsigned long m_long;
    unsigned long long m_longlong;
    float m_float;
    double m_double;
    long double m_longdouble;
};

struct _cffi_freeme_s {
    struct _cffi_freeme_s *next;
    union _cffi_union_alignment_u alignment;
};

#ifdef __GNUC__
  __attribute__((unused))
#endif
static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg,
                                        char **output_data, Py_ssize_t datasize,
                                        struct _cffi_freeme_s **freeme)
{
    char *p;
    if (datasize < 0)
        return -1;

    p = *output_data;
    if (p == NULL) {
        struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc(
            offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize);
        if (fp == NULL)
            return -1;
        fp->next = *freeme;
        *freeme = fp;
        p = *output_data = (char *)&fp->alignment;
    }
    memset((void *)p, 0, (size_t)datasize);
    return _cffi_convert_array_from_object(p, ctptr, arg);
}

#ifdef __GNUC__
  __attribute__((unused))
#endif
static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme)
{
    do {
        void *p = (void *)freeme;
        freeme = freeme->next;
        PyObject_Free(p);
    } while (freeme != NULL);
}

static int _cffi_init(void)
{
    PyObject *module, *c_api_object = NULL;

    module = PyImport_ImportModule("_cffi_backend");
    if (module == NULL)
        goto failure;

    c_api_object = PyObject_GetAttrString(module, "_C_API");
    if (c_api_object == NULL)
        goto failure;
    if (!PyCapsule_CheckExact(c_api_object)) {
        PyErr_SetNone(PyExc_ImportError);
        goto failure;
    }
    memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"),
           _CFFI_NUM_EXPORTS * sizeof(void *));

    Py_DECREF(module);
    Py_DECREF(c_api_object);
    return 0;

  failure:
    Py_XDECREF(module);
    Py_XDECREF(c_api_object);
    return -1;
}

#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num))

/**********/
'''

back/venvmsg/Lib/site-packages/cffi/vengine_cpy.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
#
# DEPRECATED: implementation for ffi.verify()
#
import sys, os, binascii, shutil, io
from . import __version_verifier_modules__
from . import ffiplatform
from .error import VerificationError

if sys.version_info >= (3, 3):
    import importlib.machinery
    def _extension_suffixes():
        return importlib.machinery.EXTENSION_SUFFIXES[:]
else:
    import imp
    def _extension_suffixes():
        return [suffix for suffix, _, type in imp.get_suffixes()
                if type == imp.C_EXTENSION]


if sys.version_info >= (3,):
    NativeIO = io.StringIO
else:
    class NativeIO(io.BytesIO):
        def write(self, s):
            if isinstance(s, unicode):
                s = s.encode('ascii')
            super(NativeIO, self).write(s)


class Verifier(object):

    def __init__(self, ffi, preamble, tmpdir=None, modulename=None,
                 ext_package=None, tag='', force_generic_engine=False,
                 source_extension='.c', flags=None, relative_to=None, **kwds):
        if ffi._parser._uses_new_feature:
            raise VerificationError(
                "feature not supported with ffi.verify(), but only "
                "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,))
        self.ffi = ffi
        self.preamble = preamble
        if not modulename:
            flattened_kwds = ffiplatform.flatten(kwds)
        vengine_class = _locate_engine_class(ffi, force_generic_engine)
        self._vengine = vengine_class(self)
        self._vengine.patch_extension_kwds(kwds)
        self.flags = flags
        self.kwds = self.make_relative_to(kwds, relative_to)
        #
        if modulename:
            if tag:
                raise TypeError("can't specify both 'modulename' and 'tag'")
        else:
            key = '\x00'.join(['%d.%d' % sys.version_info[:2],
                               __version_verifier_modules__,
                               preamble, flattened_kwds] +
                              ffi._cdefsources)
            if sys.version_info >= (3,):
                key = key.encode('utf-8')
            k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
            k1 = k1.lstrip('0x').rstrip('L')
            k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
            k2 = k2.lstrip('0').rstrip('L')
            modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key,
                                              k1, k2)
        suffix = _get_so_suffixes()[0]
        self.tmpdir = tmpdir or _caller_dir_pycache()
        self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension)
        self.modulefilename = os.path.join(self.tmpdir, modulename + suffix)
        self.ext_package = ext_package
        self._has_source = False
        self._has_module = False

    def write_source(self, file=None):
        """Write the C source code.  It is produced in 'self.sourcefilename',
        which can be tweaked beforehand."""
        with self.ffi._lock:
            if self._has_source and file is None:
                raise VerificationError(
                    "source code already written")
            self._write_source(file)

    def compile_module(self):
        """Write the C source code (if not done already) and compile it.
        This produces a dynamic link library in 'self.modulefilename'."""
        with self.ffi._lock:
            if self._has_module:
                raise VerificationError("module already compiled")
            if not self._has_source:
                self._write_source()
            self._compile_module()

    def load_library(self):
        """Get a C module from this Verifier instance.
        Returns an instance of a FFILibrary class that behaves like the
        objects returned by ffi.dlopen(), but that delegates all
        operations to the C module.  If necessary, the C code is written
        and compiled first.
        """
        with self.ffi._lock:
            if not self._has_module:
                self._locate_module()
                if not self._has_module:
                    if not self._has_source:
                        self._write_source()
                    self._compile_module()
            return self._load_library()

    def get_module_name(self):
        basename = os.path.basename(self.modulefilename)
        # kill both the .so extension and the other .'s, as introduced
        # by Python 3: 'basename.cpython-33m.so'
        basename = basename.split('.', 1)[0]
        # and the _d added in Python 2 debug builds --- but try to be
        # conservative and not kill a legitimate _d
        if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'):
            basename = basename[:-2]
        return basename

    def get_extension(self):
        if not self._has_source:
            with self.ffi._lock:
                if not self._has_source:
                    self._write_source()
        sourcename = ffiplatform.maybe_relative_path(self.sourcefilename)
        modname = self.get_module_name()
        return ffiplatform.get_extension(sourcename, modname, **self.kwds)

    def generates_python_module(self):
        return self._vengine._gen_python_module

    def make_relative_to(self, kwds, relative_to):
        if relative_to and os.path.dirname(relative_to):
            dirname = os.path.dirname(relative_to)
            kwds = kwds.copy()
            for key in ffiplatform.LIST_OF_FILE_NAMES:
                if key in kwds:
                    lst = kwds[key]
                    if not isinstance(lst, (list, tuple)):
                        raise TypeError("keyword '%s' should be a list or tuple"
                                        % (key,))
                    lst = [os.path.join(dirname, fn) for fn in lst]
                    kwds[key] = lst
        return kwds

    # ----------

    def _locate_module(self):
        if not os.path.isfile(self.modulefilename):
            if self.ext_package:
                try:
                    pkg = __import__(self.ext_package, None, None, ['__doc__'])
                except ImportError:
                    return      # cannot import the package itself, give up
                    # (e.g. it might be called differently before installation)
                path = pkg.__path__
            else:
                path = None
            filename = self._vengine.find_module(self.get_module_name(), path,
                                                 _get_so_suffixes())
            if filename is None:
                return
            self.modulefilename = filename
        self._vengine.collect_types()
        self._has_module = True

    def _write_source_to(self, file):
        self._vengine._f = file
        try:
            self._vengine.write_source_to_f()
        finally:
            del self._vengine._f

    def _write_source(self, file=None):
        if file is not None:
            self._write_source_to(file)
        else:
            # Write our source file to an in memory file.
            f = NativeIO()
            self._write_source_to(f)
            source_data = f.getvalue()

            # Determine if this matches the current file
            if os.path.exists(self.sourcefilename):
                with open(self.sourcefilename, "r") as fp:
                    needs_written = not (fp.read() == source_data)
            else:
                needs_written = True

            # Actually write the file out if it doesn't match
            if needs_written:
                _ensure_dir(self.sourcefilename)
                with open(self.sourcefilename, "w") as fp:
                    fp.write(source_data)

            # Set this flag
            self._has_source = True

    def _compile_module(self):
        # compile this C source
        tmpdir = os.path.dirname(self.sourcefilename)
        outputfilename = ffiplatform.compile(tmpdir, self.get_extension())
        try:
            same = ffiplatform.samefile(outputfilename, self.modulefilename)
        except OSError:
            same = False
        if not same:
            _ensure_dir(self.modulefilename)
            shutil.move(outputfilename, self.modulefilename)
        self._has_module = True

    def _load_library(self):
        assert self._has_module
        if self.flags is not None:
            return self._vengine.load_library(self.flags)
        else:
            return self._vengine.load_library()

# ____________________________________________________________

_FORCE_GENERIC_ENGINE = False      # for tests

def _locate_engine_class(ffi, force_generic_engine):
    if _FORCE_GENERIC_ENGINE:
        force_generic_engine = True
    if not force_generic_engine:
        if '__pypy__' in sys.builtin_module_names:
            force_generic_engine = True
        else:
            try:
                import _cffi_backend
            except ImportError:
                _cffi_backend = '?'
            if ffi._backend is not _cffi_backend:
                force_generic_engine = True
    if force_generic_engine:
        from . import vengine_gen
        return vengine_gen.VGenericEngine
    else:
        from . import vengine_cpy
        return vengine_cpy.VCPythonEngine

# ____________________________________________________________

_TMPDIR = None

def _caller_dir_pycache():
    if _TMPDIR:
        return _TMPDIR
    result = os.environ.get('CFFI_TMPDIR')
    if result:
        return result
    filename = sys._getframe(2).f_code.co_filename
    return os.path.abspath(os.path.join(os.path.dirname(filename),
                           '__pycache__'))

def set_tmpdir(dirname):
    """Set the temporary directory to use instead of __pycache__."""
    global _TMPDIR
    _TMPDIR = dirname

def cleanup_tmpdir(tmpdir=None, keep_so=False):
    """Clean up the temporary directory by removing all files in it
    called `_cffi_*.{c,so}` as well as the `build` subdirectory."""
    tmpdir = tmpdir or _caller_dir_pycache()
    try:
        filelist = os.listdir(tmpdir)
    except OSError:
        return
    if keep_so:
        suffix = '.c'   # only remove .c files
    else:
        suffix = _get_so_suffixes()[0].lower()
    for fn in filelist:
        if fn.lower().startswith('_cffi_') and (
                fn.lower().endswith(suffix) or fn.lower().endswith('.c')):
            try:
                os.unlink(os.path.join(tmpdir, fn))
            except OSError:
                pass
    clean_dir = [os.path.join(tmpdir, 'build')]
    for dir in clean_dir:
        try:
            for fn in os.listdir(dir):
                fn = os.path.join(dir, fn)
                if os.path.isdir(fn):
                    clean_dir.append(fn)
                else:
                    os.unlink(fn)
        except OSError:
            pass

def _get_so_suffixes():
    suffixes = _extension_suffixes()
    if not suffixes:
        # bah, no C_EXTENSION available.  Occurs on pypy without cpyext
        if sys.platform == 'win32':
            suffixes = [".pyd"]
        else:
            suffixes = [".so"]

    return suffixes

def _ensure_dir(filename):
    dirname = os.path.dirname(filename)
    if dirname and not os.path.isdir(dirname):
        os.makedirs(dirname)

back/venvmsg/Lib/site-packages/cffi/verifier.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions

if typing.TYPE_CHECKING:
    from cryptography.hazmat.bindings._rust import openssl as rust_openssl

_Reasons = rust_exceptions._Reasons


class UnsupportedAlgorithm(Exception):
    def __init__(
        self, message: str, reason: typing.Optional[_Reasons] = None
    ) -> None:
        super().__init__(message)
        self._reason = reason


class AlreadyFinalized(Exception):
    pass


class AlreadyUpdated(Exception):
    pass


class NotYetFinalized(Exception):
    pass


class InvalidTag(Exception):
    pass


class InvalidSignature(Exception):
    pass


class InternalError(Exception):
    def __init__(
        self, msg: str, err_code: typing.List[rust_openssl.OpenSSLError]
    ) -> None:
        super().__init__(msg)
        self.err_code = err_code


class InvalidKey(Exception):
    pass

back/venvmsg/Lib/site-packages/cryptography/exceptions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import base64
import binascii
import os
import time
import typing

from cryptography import utils
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes, padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.hmac import HMAC


class InvalidToken(Exception):
    pass


_MAX_CLOCK_SKEW = 60


class Fernet:
    def __init__(
        self,
        key: typing.Union[bytes, str],
        backend: typing.Any = None,
    ) -> None:
        try:
            key = base64.urlsafe_b64decode(key)
        except binascii.Error as exc:
            raise ValueError(
                "Fernet key must be 32 url-safe base64-encoded bytes."
            ) from exc
        if len(key) != 32:
            raise ValueError(
                "Fernet key must be 32 url-safe base64-encoded bytes."
            )

        self._signing_key = key[:16]
        self._encryption_key = key[16:]

    @classmethod
    def generate_key(cls) -> bytes:
        return base64.urlsafe_b64encode(os.urandom(32))

    def encrypt(self, data: bytes) -> bytes:
        return self.encrypt_at_time(data, int(time.time()))

    def encrypt_at_time(self, data: bytes, current_time: int) -> bytes:
        iv = os.urandom(16)
        return self._encrypt_from_parts(data, current_time, iv)

    def _encrypt_from_parts(
        self, data: bytes, current_time: int, iv: bytes
    ) -> bytes:
        utils._check_bytes("data", data)

        padder = padding.PKCS7(algorithms.AES.block_size).padder()
        padded_data = padder.update(data) + padder.finalize()
        encryptor = Cipher(
            algorithms.AES(self._encryption_key),
            modes.CBC(iv),
        ).encryptor()
        ciphertext = encryptor.update(padded_data) + encryptor.finalize()

        basic_parts = (
            b"\x80"
            + current_time.to_bytes(length=8, byteorder="big")
            + iv
            + ciphertext
        )

        h = HMAC(self._signing_key, hashes.SHA256())
        h.update(basic_parts)
        hmac = h.finalize()
        return base64.urlsafe_b64encode(basic_parts + hmac)

    def decrypt(
        self, token: typing.Union[bytes, str], ttl: typing.Optional[int] = None
    ) -> bytes:
        timestamp, data = Fernet._get_unverified_token_data(token)
        if ttl is None:
            time_info = None
        else:
            time_info = (ttl, int(time.time()))
        return self._decrypt_data(data, timestamp, time_info)

    def decrypt_at_time(
        self, token: typing.Union[bytes, str], ttl: int, current_time: int
    ) -> bytes:
        if ttl is None:
            raise ValueError(
                "decrypt_at_time() can only be used with a non-None ttl"
            )
        timestamp, data = Fernet._get_unverified_token_data(token)
        return self._decrypt_data(data, timestamp, (ttl, current_time))

    def extract_timestamp(self, token: typing.Union[bytes, str]) -> int:
        timestamp, data = Fernet._get_unverified_token_data(token)
        # Verify the token was not tampered with.
        self._verify_signature(data)
        return timestamp

    @staticmethod
    def _get_unverified_token_data(
        token: typing.Union[bytes, str]
    ) -> typing.Tuple[int, bytes]:
        if not isinstance(token, (str, bytes)):
            raise TypeError("token must be bytes or str")

        try:
            data = base64.urlsafe_b64decode(token)
        except (TypeError, binascii.Error):
            raise InvalidToken

        if not data or data[0] != 0x80:
            raise InvalidToken

        if len(data) < 9:
            raise InvalidToken

        timestamp = int.from_bytes(data[1:9], byteorder="big")
        return timestamp, data

    def _verify_signature(self, data: bytes) -> None:
        h = HMAC(self._signing_key, hashes.SHA256())
        h.update(data[:-32])
        try:
            h.verify(data[-32:])
        except InvalidSignature:
            raise InvalidToken

    def _decrypt_data(
        self,
        data: bytes,
        timestamp: int,
        time_info: typing.Optional[typing.Tuple[int, int]],
    ) -> bytes:
        if time_info is not None:
            ttl, current_time = time_info
            if timestamp + ttl < current_time:
                raise InvalidToken

            if current_time + _MAX_CLOCK_SKEW < timestamp:
                raise InvalidToken

        self._verify_signature(data)

        iv = data[9:25]
        ciphertext = data[25:-32]
        decryptor = Cipher(
            algorithms.AES(self._encryption_key), modes.CBC(iv)
        ).decryptor()
        plaintext_padded = decryptor.update(ciphertext)
        try:
            plaintext_padded += decryptor.finalize()
        except ValueError:
            raise InvalidToken
        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()

        unpadded = unpadder.update(plaintext_padded)
        try:
            unpadded += unpadder.finalize()
        except ValueError:
            raise InvalidToken
        return unpadded


class MultiFernet:
    def __init__(self, fernets: typing.Iterable[Fernet]):
        fernets = list(fernets)
        if not fernets:
            raise ValueError(
                "MultiFernet requires at least one Fernet instance"
            )
        self._fernets = fernets

    def encrypt(self, msg: bytes) -> bytes:
        return self.encrypt_at_time(msg, int(time.time()))

    def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes:
        return self._fernets[0].encrypt_at_time(msg, current_time)

    def rotate(self, msg: typing.Union[bytes, str]) -> bytes:
        timestamp, data = Fernet._get_unverified_token_data(msg)
        for f in self._fernets:
            try:
                p = f._decrypt_data(data, timestamp, None)
                break
            except InvalidToken:
                pass
        else:
            raise InvalidToken

        iv = os.urandom(16)
        return self._fernets[0]._encrypt_from_parts(p, timestamp, iv)

    def decrypt(
        self, msg: typing.Union[bytes, str], ttl: typing.Optional[int] = None
    ) -> bytes:
        for f in self._fernets:
            try:
                return f.decrypt(msg, ttl)
            except InvalidToken:
                pass
        raise InvalidToken

    def decrypt_at_time(
        self, msg: typing.Union[bytes, str], ttl: int, current_time: int
    ) -> bytes:
        for f in self._fernets:
            try:
                return f.decrypt_at_time(msg, ttl, current_time)
            except InvalidToken:
                pass
        raise InvalidToken

back/venvmsg/Lib/site-packages/cryptography/fernet.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography.hazmat.bindings._rust import (
    ObjectIdentifier as ObjectIdentifier,
)
from cryptography.hazmat.primitives import hashes


class ExtensionOID:
    SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9")
    SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14")
    KEY_USAGE = ObjectIdentifier("2.5.29.15")
    SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17")
    ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18")
    BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19")
    NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30")
    CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31")
    CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32")
    POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33")
    AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35")
    POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36")
    EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37")
    FRESHEST_CRL = ObjectIdentifier("2.5.29.46")
    INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54")
    ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28")
    AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1")
    SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11")
    OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5")
    TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24")
    CRL_NUMBER = ObjectIdentifier("2.5.29.20")
    DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27")
    PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier(
        "1.3.6.1.4.1.11129.2.4.2"
    )
    PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")
    SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5")
    MS_CERTIFICATE_TEMPLATE = ObjectIdentifier("1.3.6.1.4.1.311.21.7")


class OCSPExtensionOID:
    NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2")
    ACCEPTABLE_RESPONSES = ObjectIdentifier("1.3.6.1.5.5.7.48.1.4")


class CRLEntryExtensionOID:
    CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29")
    CRL_REASON = ObjectIdentifier("2.5.29.21")
    INVALIDITY_DATE = ObjectIdentifier("2.5.29.24")


class NameOID:
    COMMON_NAME = ObjectIdentifier("2.5.4.3")
    COUNTRY_NAME = ObjectIdentifier("2.5.4.6")
    LOCALITY_NAME = ObjectIdentifier("2.5.4.7")
    STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8")
    STREET_ADDRESS = ObjectIdentifier("2.5.4.9")
    ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10")
    ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11")
    SERIAL_NUMBER = ObjectIdentifier("2.5.4.5")
    SURNAME = ObjectIdentifier("2.5.4.4")
    GIVEN_NAME = ObjectIdentifier("2.5.4.42")
    TITLE = ObjectIdentifier("2.5.4.12")
    INITIALS = ObjectIdentifier("2.5.4.43")
    GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44")
    X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45")
    DN_QUALIFIER = ObjectIdentifier("2.5.4.46")
    PSEUDONYM = ObjectIdentifier("2.5.4.65")
    USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1")
    DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25")
    EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1")
    JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3")
    JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1")
    JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier(
        "1.3.6.1.4.1.311.60.2.1.2"
    )
    BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15")
    POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16")
    POSTAL_CODE = ObjectIdentifier("2.5.4.17")
    INN = ObjectIdentifier("1.2.643.3.131.1.1")
    OGRN = ObjectIdentifier("1.2.643.100.1")
    SNILS = ObjectIdentifier("1.2.643.100.3")
    UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2")


class SignatureAlgorithmOID:
    RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4")
    RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5")
    # This is an alternate OID for RSA with SHA1 that is occasionally seen
    _RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29")
    RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14")
    RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11")
    RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12")
    RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13")
    RSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.13")
    RSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.14")
    RSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.15")
    RSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.16")
    RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10")
    ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1")
    ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1")
    ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2")
    ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3")
    ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4")
    ECDSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.9")
    ECDSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.10")
    ECDSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.11")
    ECDSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.12")
    DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3")
    DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1")
    DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2")
    DSA_WITH_SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.3.3")
    DSA_WITH_SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.3.4")
    ED25519 = ObjectIdentifier("1.3.101.112")
    ED448 = ObjectIdentifier("1.3.101.113")
    GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3")
    GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2")
    GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3")


_SIG_OIDS_TO_HASH: typing.Dict[
    ObjectIdentifier, typing.Optional[hashes.HashAlgorithm]
] = {
    SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(),
    SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(),
    SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(),
    SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(),
    SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(),
    SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(),
    SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(),
    SignatureAlgorithmOID.RSA_WITH_SHA3_224: hashes.SHA3_224(),
    SignatureAlgorithmOID.RSA_WITH_SHA3_256: hashes.SHA3_256(),
    SignatureAlgorithmOID.RSA_WITH_SHA3_384: hashes.SHA3_384(),
    SignatureAlgorithmOID.RSA_WITH_SHA3_512: hashes.SHA3_512(),
    SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(),
    SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(),
    SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(),
    SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(),
    SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(),
    SignatureAlgorithmOID.ECDSA_WITH_SHA3_224: hashes.SHA3_224(),
    SignatureAlgorithmOID.ECDSA_WITH_SHA3_256: hashes.SHA3_256(),
    SignatureAlgorithmOID.ECDSA_WITH_SHA3_384: hashes.SHA3_384(),
    SignatureAlgorithmOID.ECDSA_WITH_SHA3_512: hashes.SHA3_512(),
    SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(),
    SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(),
    SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(),
    SignatureAlgorithmOID.ED25519: None,
    SignatureAlgorithmOID.ED448: None,
    SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None,
    SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None,
    SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None,
}


class ExtendedKeyUsageOID:
    SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1")
    CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2")
    CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3")
    EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4")
    TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8")
    OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9")
    ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0")
    SMARTCARD_LOGON = ObjectIdentifier("1.3.6.1.4.1.311.20.2.2")
    KERBEROS_PKINIT_KDC = ObjectIdentifier("1.3.6.1.5.2.3.5")
    IPSEC_IKE = ObjectIdentifier("1.3.6.1.5.5.7.3.17")
    CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4")


class AuthorityInformationAccessOID:
    CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2")
    OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1")


class SubjectInformationAccessOID:
    CA_REPOSITORY = ObjectIdentifier("1.3.6.1.5.5.7.48.5")


class CertificatePoliciesOID:
    CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1")
    CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2")
    ANY_POLICY = ObjectIdentifier("2.5.29.32.0")


class AttributeOID:
    CHALLENGE_PASSWORD = ObjectIdentifier("1.2.840.113549.1.9.7")
    UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2")


_OID_NAMES = {
    NameOID.COMMON_NAME: "commonName",
    NameOID.COUNTRY_NAME: "countryName",
    NameOID.LOCALITY_NAME: "localityName",
    NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName",
    NameOID.STREET_ADDRESS: "streetAddress",
    NameOID.ORGANIZATION_NAME: "organizationName",
    NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName",
    NameOID.SERIAL_NUMBER: "serialNumber",
    NameOID.SURNAME: "surname",
    NameOID.GIVEN_NAME: "givenName",
    NameOID.TITLE: "title",
    NameOID.GENERATION_QUALIFIER: "generationQualifier",
    NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier",
    NameOID.DN_QUALIFIER: "dnQualifier",
    NameOID.PSEUDONYM: "pseudonym",
    NameOID.USER_ID: "userID",
    NameOID.DOMAIN_COMPONENT: "domainComponent",
    NameOID.EMAIL_ADDRESS: "emailAddress",
    NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName",
    NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName",
    NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: (
        "jurisdictionStateOrProvinceName"
    ),
    NameOID.BUSINESS_CATEGORY: "businessCategory",
    NameOID.POSTAL_ADDRESS: "postalAddress",
    NameOID.POSTAL_CODE: "postalCode",
    NameOID.INN: "INN",
    NameOID.OGRN: "OGRN",
    NameOID.SNILS: "SNILS",
    NameOID.UNSTRUCTURED_NAME: "unstructuredName",
    SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption",
    SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption",
    SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption",
    SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption",
    SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption",
    SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption",
    SignatureAlgorithmOID.RSASSA_PSS: "RSASSA-PSS",
    SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1",
    SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224",
    SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256",
    SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384",
    SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512",
    SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1",
    SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224",
    SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256",
    SignatureAlgorithmOID.ED25519: "ed25519",
    SignatureAlgorithmOID.ED448: "ed448",
    SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: (
        "GOST R 34.11-94 with GOST R 34.10-2001"
    ),
    SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: (
        "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)"
    ),
    SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: (
        "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)"
    ),
    ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth",
    ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth",
    ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning",
    ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection",
    ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping",
    ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning",
    ExtendedKeyUsageOID.SMARTCARD_LOGON: "msSmartcardLogin",
    ExtendedKeyUsageOID.KERBEROS_PKINIT_KDC: "pkInitKDC",
    ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes",
    ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier",
    ExtensionOID.KEY_USAGE: "keyUsage",
    ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName",
    ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName",
    ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints",
    ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: (
        "signedCertificateTimestampList"
    ),
    ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: (
        "signedCertificateTimestampList"
    ),
    ExtensionOID.PRECERT_POISON: "ctPoison",
    ExtensionOID.MS_CERTIFICATE_TEMPLATE: "msCertificateTemplate",
    CRLEntryExtensionOID.CRL_REASON: "cRLReason",
    CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate",
    CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer",
    ExtensionOID.NAME_CONSTRAINTS: "nameConstraints",
    ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints",
    ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies",
    ExtensionOID.POLICY_MAPPINGS: "policyMappings",
    ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier",
    ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints",
    ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage",
    ExtensionOID.FRESHEST_CRL: "freshestCRL",
    ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy",
    ExtensionOID.ISSUING_DISTRIBUTION_POINT: ("issuingDistributionPoint"),
    ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess",
    ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess",
    ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck",
    ExtensionOID.CRL_NUMBER: "cRLNumber",
    ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator",
    ExtensionOID.TLS_FEATURE: "TLSFeature",
    AuthorityInformationAccessOID.OCSP: "OCSP",
    AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers",
    SubjectInformationAccessOID.CA_REPOSITORY: "caRepository",
    CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps",
    CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice",
    OCSPExtensionOID.NONCE: "OCSPNonce",
    AttributeOID.CHALLENGE_PASSWORD: "challengePassword",
}

back/venvmsg/Lib/site-packages/cryptography/hazmat/_oid.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography.exceptions import InvalidTag

if typing.TYPE_CHECKING:
    from cryptography.hazmat.backends.openssl.backend import Backend
    from cryptography.hazmat.primitives.ciphers.aead import (
        AESCCM,
        AESGCM,
        AESOCB3,
        AESSIV,
        ChaCha20Poly1305,
    )

    _AEADTypes = typing.Union[
        AESCCM, AESGCM, AESOCB3, AESSIV, ChaCha20Poly1305
    ]


def _is_evp_aead_supported_cipher(
    backend: Backend, cipher: _AEADTypes
) -> bool:
    """
    Checks whether the given cipher is supported through
    EVP_AEAD rather than the normal OpenSSL EVP_CIPHER API.
    """
    from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305

    return backend._lib.Cryptography_HAS_EVP_AEAD and isinstance(
        cipher, ChaCha20Poly1305
    )


def _aead_cipher_supported(backend: Backend, cipher: _AEADTypes) -> bool:
    if _is_evp_aead_supported_cipher(backend, cipher):
        return True
    else:
        cipher_name = _evp_cipher_cipher_name(cipher)
        if backend._fips_enabled and cipher_name not in backend._fips_aead:
            return False
        # SIV isn't loaded through get_cipherbyname but instead a new fetch API
        # only available in 3.0+. But if we know we're on 3.0+ then we know
        # it's supported.
        if cipher_name.endswith(b"-siv"):
            return backend._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER == 1
        else:
            return (
                backend._lib.EVP_get_cipherbyname(cipher_name)
                != backend._ffi.NULL
            )


def _aead_create_ctx(
    backend: Backend,
    cipher: _AEADTypes,
    key: bytes,
):
    if _is_evp_aead_supported_cipher(backend, cipher):
        return _evp_aead_create_ctx(backend, cipher, key)
    else:
        return _evp_cipher_create_ctx(backend, cipher, key)


def _encrypt(
    backend: Backend,
    cipher: _AEADTypes,
    nonce: bytes,
    data: bytes,
    associated_data: typing.List[bytes],
    tag_length: int,
    ctx: typing.Any = None,
) -> bytes:
    if _is_evp_aead_supported_cipher(backend, cipher):
        return _evp_aead_encrypt(
            backend, cipher, nonce, data, associated_data, tag_length, ctx
        )
    else:
        return _evp_cipher_encrypt(
            backend, cipher, nonce, data, associated_data, tag_length, ctx
        )


def _decrypt(
    backend: Backend,
    cipher: _AEADTypes,
    nonce: bytes,
    data: bytes,
    associated_data: typing.List[bytes],
    tag_length: int,
    ctx: typing.Any = None,
) -> bytes:
    if _is_evp_aead_supported_cipher(backend, cipher):
        return _evp_aead_decrypt(
            backend, cipher, nonce, data, associated_data, tag_length, ctx
        )
    else:
        return _evp_cipher_decrypt(
            backend, cipher, nonce, data, associated_data, tag_length, ctx
        )


def _evp_aead_create_ctx(
    backend: Backend,
    cipher: _AEADTypes,
    key: bytes,
    tag_len: typing.Optional[int] = None,
):
    aead_cipher = _evp_aead_get_cipher(backend, cipher)
    assert aead_cipher is not None
    key_ptr = backend._ffi.from_buffer(key)
    tag_len = (
        backend._lib.EVP_AEAD_DEFAULT_TAG_LENGTH
        if tag_len is None
        else tag_len
    )
    ctx = backend._lib.Cryptography_EVP_AEAD_CTX_new(
        aead_cipher, key_ptr, len(key), tag_len
    )
    backend.openssl_assert(ctx != backend._ffi.NULL)
    ctx = backend._ffi.gc(ctx, backend._lib.EVP_AEAD_CTX_free)
    return ctx


def _evp_aead_get_cipher(backend: Backend, cipher: _AEADTypes):
    from cryptography.hazmat.primitives.ciphers.aead import (
        ChaCha20Poly1305,
    )

    # Currently only ChaCha20-Poly1305 is supported using this API
    assert isinstance(cipher, ChaCha20Poly1305)
    return backend._lib.EVP_aead_chacha20_poly1305()


def _evp_aead_encrypt(
    backend: Backend,
    cipher: _AEADTypes,
    nonce: bytes,
    data: bytes,
    associated_data: typing.List[bytes],
    tag_length: int,
    ctx: typing.Any,
) -> bytes:
    assert ctx is not None

    aead_cipher = _evp_aead_get_cipher(backend, cipher)
    assert aead_cipher is not None

    out_len = backend._ffi.new("size_t *")
    # max_out_len should be in_len plus the result of
    # EVP_AEAD_max_overhead.
    max_out_len = len(data) + backend._lib.EVP_AEAD_max_overhead(aead_cipher)
    out_buf = backend._ffi.new("uint8_t[]", max_out_len)
    data_ptr = backend._ffi.from_buffer(data)
    nonce_ptr = backend._ffi.from_buffer(nonce)
    aad = b"".join(associated_data)
    aad_ptr = backend._ffi.from_buffer(aad)

    res = backend._lib.EVP_AEAD_CTX_seal(
        ctx,
        out_buf,
        out_len,
        max_out_len,
        nonce_ptr,
        len(nonce),
        data_ptr,
        len(data),
        aad_ptr,
        len(aad),
    )
    backend.openssl_assert(res == 1)
    encrypted_data = backend._ffi.buffer(out_buf, out_len[0])[:]
    return encrypted_data


def _evp_aead_decrypt(
    backend: Backend,
    cipher: _AEADTypes,
    nonce: bytes,
    data: bytes,
    associated_data: typing.List[bytes],
    tag_length: int,
    ctx: typing.Any,
) -> bytes:
    if len(data) < tag_length:
        raise InvalidTag

    assert ctx is not None

    out_len = backend._ffi.new("size_t *")
    #  max_out_len should at least in_len
    max_out_len = len(data)
    out_buf = backend._ffi.new("uint8_t[]", max_out_len)
    data_ptr = backend._ffi.from_buffer(data)
    nonce_ptr = backend._ffi.from_buffer(nonce)
    aad = b"".join(associated_data)
    aad_ptr = backend._ffi.from_buffer(aad)

    res = backend._lib.EVP_AEAD_CTX_open(
        ctx,
        out_buf,
        out_len,
        max_out_len,
        nonce_ptr,
        len(nonce),
        data_ptr,
        len(data),
        aad_ptr,
        len(aad),
    )

    if res == 0:
        backend._consume_errors()
        raise InvalidTag

    decrypted_data = backend._ffi.buffer(out_buf, out_len[0])[:]
    return decrypted_data


_ENCRYPT = 1
_DECRYPT = 0


def _evp_cipher_cipher_name(cipher: _AEADTypes) -> bytes:
    from cryptography.hazmat.primitives.ciphers.aead import (
        AESCCM,
        AESGCM,
        AESOCB3,
        AESSIV,
        ChaCha20Poly1305,
    )

    if isinstance(cipher, ChaCha20Poly1305):
        return b"chacha20-poly1305"
    elif isinstance(cipher, AESCCM):
        return f"aes-{len(cipher._key) * 8}-ccm".encode("ascii")
    elif isinstance(cipher, AESOCB3):
        return f"aes-{len(cipher._key) * 8}-ocb".encode("ascii")
    elif isinstance(cipher, AESSIV):
        return f"aes-{len(cipher._key) * 8 // 2}-siv".encode("ascii")
    else:
        assert isinstance(cipher, AESGCM)
        return f"aes-{len(cipher._key) * 8}-gcm".encode("ascii")


def _evp_cipher(cipher_name: bytes, backend: Backend):
    if cipher_name.endswith(b"-siv"):
        evp_cipher = backend._lib.EVP_CIPHER_fetch(
            backend._ffi.NULL,
            cipher_name,
            backend._ffi.NULL,
        )
        backend.openssl_assert(evp_cipher != backend._ffi.NULL)
        evp_cipher = backend._ffi.gc(evp_cipher, backend._lib.EVP_CIPHER_free)
    else:
        evp_cipher = backend._lib.EVP_get_cipherbyname(cipher_name)
        backend.openssl_assert(evp_cipher != backend._ffi.NULL)

    return evp_cipher


def _evp_cipher_create_ctx(
    backend: Backend,
    cipher: _AEADTypes,
    key: bytes,
):
    ctx = backend._lib.EVP_CIPHER_CTX_new()
    backend.openssl_assert(ctx != backend._ffi.NULL)
    ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free)
    cipher_name = _evp_cipher_cipher_name(cipher)
    evp_cipher = _evp_cipher(cipher_name, backend)
    key_ptr = backend._ffi.from_buffer(key)
    res = backend._lib.EVP_CipherInit_ex(
        ctx,
        evp_cipher,
        backend._ffi.NULL,
        key_ptr,
        backend._ffi.NULL,
        0,
    )
    backend.openssl_assert(res != 0)
    return ctx


def _evp_cipher_aead_setup(
    backend: Backend,
    cipher_name: bytes,
    key: bytes,
    nonce: bytes,
    tag: typing.Optional[bytes],
    tag_len: int,
    operation: int,
):
    evp_cipher = _evp_cipher(cipher_name, backend)
    ctx = backend._lib.EVP_CIPHER_CTX_new()
    ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free)
    res = backend._lib.EVP_CipherInit_ex(
        ctx,
        evp_cipher,
        backend._ffi.NULL,
        backend._ffi.NULL,
        backend._ffi.NULL,
        int(operation == _ENCRYPT),
    )
    backend.openssl_assert(res != 0)
    # CCM requires the IVLEN to be set before calling SET_TAG on decrypt
    res = backend._lib.EVP_CIPHER_CTX_ctrl(
        ctx,
        backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
        len(nonce),
        backend._ffi.NULL,
    )
    backend.openssl_assert(res != 0)
    if operation == _DECRYPT:
        assert tag is not None
        _evp_cipher_set_tag(backend, ctx, tag)
    elif cipher_name.endswith(b"-ccm"):
        res = backend._lib.EVP_CIPHER_CTX_ctrl(
            ctx,
            backend._lib.EVP_CTRL_AEAD_SET_TAG,
            tag_len,
            backend._ffi.NULL,
        )
        backend.openssl_assert(res != 0)

    nonce_ptr = backend._ffi.from_buffer(nonce)
    key_ptr = backend._ffi.from_buffer(key)
    res = backend._lib.EVP_CipherInit_ex(
        ctx,
        backend._ffi.NULL,
        backend._ffi.NULL,
        key_ptr,
        nonce_ptr,
        int(operation == _ENCRYPT),
    )
    backend.openssl_assert(res != 0)
    return ctx


def _evp_cipher_set_tag(backend, ctx, tag: bytes) -> None:
    tag_ptr = backend._ffi.from_buffer(tag)
    res = backend._lib.EVP_CIPHER_CTX_ctrl(
        ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag_ptr
    )
    backend.openssl_assert(res != 0)


def _evp_cipher_set_nonce_operation(
    backend, ctx, nonce: bytes, operation: int
) -> None:
    nonce_ptr = backend._ffi.from_buffer(nonce)
    res = backend._lib.EVP_CipherInit_ex(
        ctx,
        backend._ffi.NULL,
        backend._ffi.NULL,
        backend._ffi.NULL,
        nonce_ptr,
        int(operation == _ENCRYPT),
    )
    backend.openssl_assert(res != 0)


def _evp_cipher_set_length(backend: Backend, ctx, data_len: int) -> None:
    intptr = backend._ffi.new("int *")
    res = backend._lib.EVP_CipherUpdate(
        ctx, backend._ffi.NULL, intptr, backend._ffi.NULL, data_len
    )
    backend.openssl_assert(res != 0)


def _evp_cipher_process_aad(
    backend: Backend, ctx, associated_data: bytes
) -> None:
    outlen = backend._ffi.new("int *")
    a_data_ptr = backend._ffi.from_buffer(associated_data)
    res = backend._lib.EVP_CipherUpdate(
        ctx, backend._ffi.NULL, outlen, a_data_ptr, len(associated_data)
    )
    backend.openssl_assert(res != 0)


def _evp_cipher_process_data(backend: Backend, ctx, data: bytes) -> bytes:
    outlen = backend._ffi.new("int *")
    buf = backend._ffi.new("unsigned char[]", len(data))
    data_ptr = backend._ffi.from_buffer(data)
    res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, data_ptr, len(data))
    if res == 0:
        # AES SIV can error here if the data is invalid on decrypt
        backend._consume_errors()
        raise InvalidTag
    return backend._ffi.buffer(buf, outlen[0])[:]


def _evp_cipher_encrypt(
    backend: Backend,
    cipher: _AEADTypes,
    nonce: bytes,
    data: bytes,
    associated_data: typing.List[bytes],
    tag_length: int,
    ctx: typing.Any = None,
) -> bytes:
    from cryptography.hazmat.primitives.ciphers.aead import AESCCM, AESSIV

    if ctx is None:
        cipher_name = _evp_cipher_cipher_name(cipher)
        ctx = _evp_cipher_aead_setup(
            backend,
            cipher_name,
            cipher._key,
            nonce,
            None,
            tag_length,
            _ENCRYPT,
        )
    else:
        _evp_cipher_set_nonce_operation(backend, ctx, nonce, _ENCRYPT)

    # CCM requires us to pass the length of the data before processing
    # anything.
    # However calling this with any other AEAD results in an error
    if isinstance(cipher, AESCCM):
        _evp_cipher_set_length(backend, ctx, len(data))

    for ad in associated_data:
        _evp_cipher_process_aad(backend, ctx, ad)
    processed_data = _evp_cipher_process_data(backend, ctx, data)
    outlen = backend._ffi.new("int *")
    # All AEADs we support besides OCB are streaming so they return nothing
    # in finalization. OCB can return up to (16 byte block - 1) bytes so
    # we need a buffer here too.
    buf = backend._ffi.new("unsigned char[]", 16)
    res = backend._lib.EVP_CipherFinal_ex(ctx, buf, outlen)
    backend.openssl_assert(res != 0)
    processed_data += backend._ffi.buffer(buf, outlen[0])[:]
    tag_buf = backend._ffi.new("unsigned char[]", tag_length)
    res = backend._lib.EVP_CIPHER_CTX_ctrl(
        ctx, backend._lib.EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf
    )
    backend.openssl_assert(res != 0)
    tag = backend._ffi.buffer(tag_buf)[:]

    if isinstance(cipher, AESSIV):
        # RFC 5297 defines the output as IV || C, where the tag we generate
        # is the "IV" and C is the ciphertext. This is the opposite of our
        # other AEADs, which are Ciphertext || Tag
        backend.openssl_assert(len(tag) == 16)
        return tag + processed_data
    else:
        return processed_data + tag


def _evp_cipher_decrypt(
    backend: Backend,
    cipher: _AEADTypes,
    nonce: bytes,
    data: bytes,
    associated_data: typing.List[bytes],
    tag_length: int,
    ctx: typing.Any = None,
) -> bytes:
    from cryptography.hazmat.primitives.ciphers.aead import AESCCM, AESSIV

    if len(data) < tag_length:
        raise InvalidTag

    if isinstance(cipher, AESSIV):
        # RFC 5297 defines the output as IV || C, where the tag we generate
        # is the "IV" and C is the ciphertext. This is the opposite of our
        # other AEADs, which are Ciphertext || Tag
        tag = data[:tag_length]
        data = data[tag_length:]
    else:
        tag = data[-tag_length:]
        data = data[:-tag_length]
    if ctx is None:
        cipher_name = _evp_cipher_cipher_name(cipher)
        ctx = _evp_cipher_aead_setup(
            backend,
            cipher_name,
            cipher._key,
            nonce,
            tag,
            tag_length,
            _DECRYPT,
        )
    else:
        _evp_cipher_set_nonce_operation(backend, ctx, nonce, _DECRYPT)
        _evp_cipher_set_tag(backend, ctx, tag)

    # CCM requires us to pass the length of the data before processing
    # anything.
    # However calling this with any other AEAD results in an error
    if isinstance(cipher, AESCCM):
        _evp_cipher_set_length(backend, ctx, len(data))

    for ad in associated_data:
        _evp_cipher_process_aad(backend, ctx, ad)
    # CCM has a different error path if the tag doesn't match. Errors are
    # raised in Update and Final is irrelevant.
    if isinstance(cipher, AESCCM):
        outlen = backend._ffi.new("int *")
        buf = backend._ffi.new("unsigned char[]", len(data))
        d_ptr = backend._ffi.from_buffer(data)
        res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, d_ptr, len(data))
        if res != 1:
            backend._consume_errors()
            raise InvalidTag

        processed_data = backend._ffi.buffer(buf, outlen[0])[:]
    else:
        processed_data = _evp_cipher_process_data(backend, ctx, data)
        outlen = backend._ffi.new("int *")
        # OCB can return up to 15 bytes (16 byte block - 1) in finalization
        buf = backend._ffi.new("unsigned char[]", 16)
        res = backend._lib.EVP_CipherFinal_ex(ctx, buf, outlen)
        processed_data += backend._ffi.buffer(buf, outlen[0])[:]
        if res == 0:
            backend._consume_errors()
            raise InvalidTag

    return processed_data

back/venvmsg/Lib/site-packages/cryptography/hazmat/backends/openssl/aead.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import collections
import contextlib
import itertools
import typing
from contextlib import contextmanager

from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.backends.openssl import aead
from cryptography.hazmat.backends.openssl.ciphers import _CipherContext
from cryptography.hazmat.backends.openssl.cmac import _CMACContext
from cryptography.hazmat.backends.openssl.ec import (
    _EllipticCurvePrivateKey,
    _EllipticCurvePublicKey,
)
from cryptography.hazmat.backends.openssl.rsa import (
    _RSAPrivateKey,
    _RSAPublicKey,
)
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.bindings.openssl import binding
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding
from cryptography.hazmat.primitives.asymmetric import (
    dh,
    dsa,
    ec,
    ed448,
    ed25519,
    rsa,
    x448,
    x25519,
)
from cryptography.hazmat.primitives.asymmetric.padding import (
    MGF1,
    OAEP,
    PSS,
    PKCS1v15,
)
from cryptography.hazmat.primitives.asymmetric.types import (
    PrivateKeyTypes,
    PublicKeyTypes,
)
from cryptography.hazmat.primitives.ciphers import (
    BlockCipherAlgorithm,
    CipherAlgorithm,
)
from cryptography.hazmat.primitives.ciphers.algorithms import (
    AES,
    AES128,
    AES256,
    ARC4,
    SM4,
    Camellia,
    ChaCha20,
    TripleDES,
    _BlowfishInternal,
    _CAST5Internal,
    _IDEAInternal,
    _SEEDInternal,
)
from cryptography.hazmat.primitives.ciphers.modes import (
    CBC,
    CFB,
    CFB8,
    CTR,
    ECB,
    GCM,
    OFB,
    XTS,
    Mode,
)
from cryptography.hazmat.primitives.serialization import ssh
from cryptography.hazmat.primitives.serialization.pkcs12 import (
    PBES,
    PKCS12Certificate,
    PKCS12KeyAndCertificates,
    PKCS12PrivateKeyTypes,
    _PKCS12CATypes,
)

_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"])


# Not actually supported, just used as a marker for some serialization tests.
class _RC2:
    pass


class Backend:
    """
    OpenSSL API binding interfaces.
    """

    name = "openssl"

    # FIPS has opinions about acceptable algorithms and key sizes, but the
    # disallowed algorithms are still present in OpenSSL. They just error if
    # you try to use them. To avoid that we allowlist the algorithms in
    # FIPS 140-3. This isn't ideal, but FIPS 140-3 is trash so here we are.
    _fips_aead = {
        b"aes-128-ccm",
        b"aes-192-ccm",
        b"aes-256-ccm",
        b"aes-128-gcm",
        b"aes-192-gcm",
        b"aes-256-gcm",
    }
    # TripleDES encryption is disallowed/deprecated throughout 2023 in
    # FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA).
    _fips_ciphers = (AES,)
    # Sometimes SHA1 is still permissible. That logic is contained
    # within the various *_supported methods.
    _fips_hashes = (
        hashes.SHA224,
        hashes.SHA256,
        hashes.SHA384,
        hashes.SHA512,
        hashes.SHA512_224,
        hashes.SHA512_256,
        hashes.SHA3_224,
        hashes.SHA3_256,
        hashes.SHA3_384,
        hashes.SHA3_512,
        hashes.SHAKE128,
        hashes.SHAKE256,
    )
    _fips_ecdh_curves = (
        ec.SECP224R1,
        ec.SECP256R1,
        ec.SECP384R1,
        ec.SECP521R1,
    )
    _fips_rsa_min_key_size = 2048
    _fips_rsa_min_public_exponent = 65537
    _fips_dsa_min_modulus = 1 << 2048
    _fips_dh_min_key_size = 2048
    _fips_dh_min_modulus = 1 << _fips_dh_min_key_size

    def __init__(self) -> None:
        self._binding = binding.Binding()
        self._ffi = self._binding.ffi
        self._lib = self._binding.lib
        self._fips_enabled = rust_openssl.is_fips_enabled()

        self._cipher_registry: typing.Dict[
            typing.Tuple[typing.Type[CipherAlgorithm], typing.Type[Mode]],
            typing.Callable,
        ] = {}
        self._register_default_ciphers()
        self._dh_types = [self._lib.EVP_PKEY_DH]
        if self._lib.Cryptography_HAS_EVP_PKEY_DHX:
            self._dh_types.append(self._lib.EVP_PKEY_DHX)

    def __repr__(self) -> str:
        return "<OpenSSLBackend(version: {}, FIPS: {}, Legacy: {})>".format(
            self.openssl_version_text(),
            self._fips_enabled,
            self._binding._legacy_provider_loaded,
        )

    def openssl_assert(
        self,
        ok: bool,
        errors: typing.Optional[typing.List[rust_openssl.OpenSSLError]] = None,
    ) -> None:
        return binding._openssl_assert(self._lib, ok, errors=errors)

    def _enable_fips(self) -> None:
        # This function enables FIPS mode for OpenSSL 3.0.0 on installs that
        # have the FIPS provider installed properly.
        self._binding._enable_fips()
        assert rust_openssl.is_fips_enabled()
        self._fips_enabled = rust_openssl.is_fips_enabled()

    def openssl_version_text(self) -> str:
        """
        Friendly string name of the loaded OpenSSL library. This is not
        necessarily the same version as it was compiled against.

        Example: OpenSSL 1.1.1d  10 Sep 2019
        """
        return self._ffi.string(
            self._lib.OpenSSL_version(self._lib.OPENSSL_VERSION)
        ).decode("ascii")

    def openssl_version_number(self) -> int:
        return self._lib.OpenSSL_version_num()

    def _evp_md_from_algorithm(self, algorithm: hashes.HashAlgorithm):
        if algorithm.name == "blake2b" or algorithm.name == "blake2s":
            alg = "{}{}".format(
                algorithm.name, algorithm.digest_size * 8
            ).encode("ascii")
        else:
            alg = algorithm.name.encode("ascii")

        evp_md = self._lib.EVP_get_digestbyname(alg)
        return evp_md

    def _evp_md_non_null_from_algorithm(self, algorithm: hashes.HashAlgorithm):
        evp_md = self._evp_md_from_algorithm(algorithm)
        self.openssl_assert(evp_md != self._ffi.NULL)
        return evp_md

    def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
        if self._fips_enabled and not isinstance(algorithm, self._fips_hashes):
            return False

        evp_md = self._evp_md_from_algorithm(algorithm)
        return evp_md != self._ffi.NULL

    def signature_hash_supported(
        self, algorithm: hashes.HashAlgorithm
    ) -> bool:
        # Dedicated check for hashing algorithm use in message digest for
        # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption).
        if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
            return False
        return self.hash_supported(algorithm)

    def scrypt_supported(self) -> bool:
        if self._fips_enabled:
            return False
        else:
            return self._lib.Cryptography_HAS_SCRYPT == 1

    def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
        # FIPS mode still allows SHA1 for HMAC
        if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
            return True

        return self.hash_supported(algorithm)

    def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool:
        if self._fips_enabled:
            # FIPS mode requires AES. TripleDES is disallowed/deprecated in
            # FIPS 140-3.
            if not isinstance(cipher, self._fips_ciphers):
                return False

        try:
            adapter = self._cipher_registry[type(cipher), type(mode)]
        except KeyError:
            return False
        evp_cipher = adapter(self, cipher, mode)
        return self._ffi.NULL != evp_cipher

    def register_cipher_adapter(self, cipher_cls, mode_cls, adapter) -> None:
        if (cipher_cls, mode_cls) in self._cipher_registry:
            raise ValueError(
                "Duplicate registration for: {} {}.".format(
                    cipher_cls, mode_cls
                )
            )
        self._cipher_registry[cipher_cls, mode_cls] = adapter

    def _register_default_ciphers(self) -> None:
        for cipher_cls in [AES, AES128, AES256]:
            for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8, GCM]:
                self.register_cipher_adapter(
                    cipher_cls,
                    mode_cls,
                    GetCipherByName(
                        "{cipher.name}-{cipher.key_size}-{mode.name}"
                    ),
                )
        for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
            self.register_cipher_adapter(
                Camellia,
                mode_cls,
                GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"),
            )
        for mode_cls in [CBC, CFB, CFB8, OFB]:
            self.register_cipher_adapter(
                TripleDES, mode_cls, GetCipherByName("des-ede3-{mode.name}")
            )
        self.register_cipher_adapter(
            TripleDES, ECB, GetCipherByName("des-ede3")
        )
        self.register_cipher_adapter(
            ChaCha20, type(None), GetCipherByName("chacha20")
        )
        self.register_cipher_adapter(AES, XTS, _get_xts_cipher)
        for mode_cls in [ECB, CBC, OFB, CFB, CTR]:
            self.register_cipher_adapter(
                SM4, mode_cls, GetCipherByName("sm4-{mode.name}")
            )
        # Don't register legacy ciphers if they're unavailable. Hypothetically
        # this wouldn't be necessary because we test availability by seeing if
        # we get an EVP_CIPHER * in the _CipherContext __init__, but OpenSSL 3
        # will return a valid pointer even though the cipher is unavailable.
        if (
            self._binding._legacy_provider_loaded
            or not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
        ):
            for mode_cls in [CBC, CFB, OFB, ECB]:
                self.register_cipher_adapter(
                    _BlowfishInternal,
                    mode_cls,
                    GetCipherByName("bf-{mode.name}"),
                )
            for mode_cls in [CBC, CFB, OFB, ECB]:
                self.register_cipher_adapter(
                    _SEEDInternal,
                    mode_cls,
                    GetCipherByName("seed-{mode.name}"),
                )
            for cipher_cls, mode_cls in itertools.product(
                [_CAST5Internal, _IDEAInternal],
                [CBC, OFB, CFB, ECB],
            ):
                self.register_cipher_adapter(
                    cipher_cls,
                    mode_cls,
                    GetCipherByName("{cipher.name}-{mode.name}"),
                )
            self.register_cipher_adapter(
                ARC4, type(None), GetCipherByName("rc4")
            )
            # We don't actually support RC2, this is just used by some tests.
            self.register_cipher_adapter(
                _RC2, type(None), GetCipherByName("rc2")
            )

    def create_symmetric_encryption_ctx(
        self, cipher: CipherAlgorithm, mode: Mode
    ) -> _CipherContext:
        return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)

    def create_symmetric_decryption_ctx(
        self, cipher: CipherAlgorithm, mode: Mode
    ) -> _CipherContext:
        return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)

    def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
        return self.hmac_supported(algorithm)

    def _consume_errors(self) -> typing.List[rust_openssl.OpenSSLError]:
        return rust_openssl.capture_error_stack()

    def _bn_to_int(self, bn) -> int:
        assert bn != self._ffi.NULL
        self.openssl_assert(not self._lib.BN_is_negative(bn))

        bn_num_bytes = self._lib.BN_num_bytes(bn)
        bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes)
        bin_len = self._lib.BN_bn2bin(bn, bin_ptr)
        # A zero length means the BN has value 0
        self.openssl_assert(bin_len >= 0)
        val = int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big")
        return val

    def _int_to_bn(self, num: int):
        """
        Converts a python integer to a BIGNUM. The returned BIGNUM will not
        be garbage collected (to support adding them to structs that take
        ownership of the object). Be sure to register it for GC if it will
        be discarded after use.
        """
        binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big")
        bn_ptr = self._lib.BN_bin2bn(binary, len(binary), self._ffi.NULL)
        self.openssl_assert(bn_ptr != self._ffi.NULL)
        return bn_ptr

    def generate_rsa_private_key(
        self, public_exponent: int, key_size: int
    ) -> rsa.RSAPrivateKey:
        rsa._verify_rsa_parameters(public_exponent, key_size)

        rsa_cdata = self._lib.RSA_new()
        self.openssl_assert(rsa_cdata != self._ffi.NULL)
        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)

        bn = self._int_to_bn(public_exponent)
        bn = self._ffi.gc(bn, self._lib.BN_free)

        res = self._lib.RSA_generate_key_ex(
            rsa_cdata, key_size, bn, self._ffi.NULL
        )
        self.openssl_assert(res == 1)
        evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)

        # We can skip RSA key validation here since we just generated the key
        return _RSAPrivateKey(
            self, rsa_cdata, evp_pkey, unsafe_skip_rsa_key_validation=True
        )

    def generate_rsa_parameters_supported(
        self, public_exponent: int, key_size: int
    ) -> bool:
        return (
            public_exponent >= 3
            and public_exponent & 1 != 0
            and key_size >= 512
        )

    def load_rsa_private_numbers(
        self,
        numbers: rsa.RSAPrivateNumbers,
        unsafe_skip_rsa_key_validation: bool,
    ) -> rsa.RSAPrivateKey:
        rsa._check_private_key_components(
            numbers.p,
            numbers.q,
            numbers.d,
            numbers.dmp1,
            numbers.dmq1,
            numbers.iqmp,
            numbers.public_numbers.e,
            numbers.public_numbers.n,
        )
        rsa_cdata = self._lib.RSA_new()
        self.openssl_assert(rsa_cdata != self._ffi.NULL)
        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
        p = self._int_to_bn(numbers.p)
        q = self._int_to_bn(numbers.q)
        d = self._int_to_bn(numbers.d)
        dmp1 = self._int_to_bn(numbers.dmp1)
        dmq1 = self._int_to_bn(numbers.dmq1)
        iqmp = self._int_to_bn(numbers.iqmp)
        e = self._int_to_bn(numbers.public_numbers.e)
        n = self._int_to_bn(numbers.public_numbers.n)
        res = self._lib.RSA_set0_factors(rsa_cdata, p, q)
        self.openssl_assert(res == 1)
        res = self._lib.RSA_set0_key(rsa_cdata, n, e, d)
        self.openssl_assert(res == 1)
        res = self._lib.RSA_set0_crt_params(rsa_cdata, dmp1, dmq1, iqmp)
        self.openssl_assert(res == 1)
        evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)

        return _RSAPrivateKey(
            self,
            rsa_cdata,
            evp_pkey,
            unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation,
        )

    def load_rsa_public_numbers(
        self, numbers: rsa.RSAPublicNumbers
    ) -> rsa.RSAPublicKey:
        rsa._check_public_key_components(numbers.e, numbers.n)
        rsa_cdata = self._lib.RSA_new()
        self.openssl_assert(rsa_cdata != self._ffi.NULL)
        rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
        e = self._int_to_bn(numbers.e)
        n = self._int_to_bn(numbers.n)
        res = self._lib.RSA_set0_key(rsa_cdata, n, e, self._ffi.NULL)
        self.openssl_assert(res == 1)
        evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)

        return _RSAPublicKey(self, rsa_cdata, evp_pkey)

    def _create_evp_pkey_gc(self):
        evp_pkey = self._lib.EVP_PKEY_new()
        self.openssl_assert(evp_pkey != self._ffi.NULL)
        evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
        return evp_pkey

    def _rsa_cdata_to_evp_pkey(self, rsa_cdata):
        evp_pkey = self._create_evp_pkey_gc()
        res = self._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata)
        self.openssl_assert(res == 1)
        return evp_pkey

    def _bytes_to_bio(self, data: bytes) -> _MemoryBIO:
        """
        Return a _MemoryBIO namedtuple of (BIO, char*).

        The char* is the storage for the BIO and it must stay alive until the
        BIO is finished with.
        """
        data_ptr = self._ffi.from_buffer(data)
        bio = self._lib.BIO_new_mem_buf(data_ptr, len(data))
        self.openssl_assert(bio != self._ffi.NULL)

        return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_ptr)

    def _create_mem_bio_gc(self):
        """
        Creates an empty memory BIO.
        """
        bio_method = self._lib.BIO_s_mem()
        self.openssl_assert(bio_method != self._ffi.NULL)
        bio = self._lib.BIO_new(bio_method)
        self.openssl_assert(bio != self._ffi.NULL)
        bio = self._ffi.gc(bio, self._lib.BIO_free)
        return bio

    def _read_mem_bio(self, bio) -> bytes:
        """
        Reads a memory BIO. This only works on memory BIOs.
        """
        buf = self._ffi.new("char **")
        buf_len = self._lib.BIO_get_mem_data(bio, buf)
        self.openssl_assert(buf_len > 0)
        self.openssl_assert(buf[0] != self._ffi.NULL)
        bio_data = self._ffi.buffer(buf[0], buf_len)[:]
        return bio_data

    def _evp_pkey_to_private_key(
        self, evp_pkey, unsafe_skip_rsa_key_validation: bool
    ) -> PrivateKeyTypes:
        """
        Return the appropriate type of PrivateKey given an evp_pkey cdata
        pointer.
        """

        key_type = self._lib.EVP_PKEY_id(evp_pkey)

        if key_type == self._lib.EVP_PKEY_RSA:
            rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
            self.openssl_assert(rsa_cdata != self._ffi.NULL)
            rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
            return _RSAPrivateKey(
                self,
                rsa_cdata,
                evp_pkey,
                unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation,
            )
        elif (
            key_type == self._lib.EVP_PKEY_RSA_PSS
            and not self._lib.CRYPTOGRAPHY_IS_LIBRESSL
            and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
            and not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E
        ):
            # At the moment the way we handle RSA PSS keys is to strip the
            # PSS constraints from them and treat them as normal RSA keys
            # Unfortunately the RSA * itself tracks this data so we need to
            # extract, serialize, and reload it without the constraints.
            rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
            self.openssl_assert(rsa_cdata != self._ffi.NULL)
            rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
            bio = self._create_mem_bio_gc()
            res = self._lib.i2d_RSAPrivateKey_bio(bio, rsa_cdata)
            self.openssl_assert(res == 1)
            return self.load_der_private_key(
                self._read_mem_bio(bio),
                password=None,
                unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation,
            )
        elif key_type == self._lib.EVP_PKEY_DSA:
            return rust_openssl.dsa.private_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == self._lib.EVP_PKEY_EC:
            ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey)
            self.openssl_assert(ec_cdata != self._ffi.NULL)
            ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
            return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
        elif key_type in self._dh_types:
            return rust_openssl.dh.private_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None):
            # EVP_PKEY_ED25519 is not present in CRYPTOGRAPHY_IS_LIBRESSL
            return rust_openssl.ed25519.private_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == getattr(self._lib, "EVP_PKEY_X448", None):
            # EVP_PKEY_X448 is not present in CRYPTOGRAPHY_IS_LIBRESSL
            return rust_openssl.x448.private_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == self._lib.EVP_PKEY_X25519:
            return rust_openssl.x25519.private_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None):
            # EVP_PKEY_ED448 is not present in CRYPTOGRAPHY_IS_LIBRESSL
            return rust_openssl.ed448.private_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        else:
            raise UnsupportedAlgorithm("Unsupported key type.")

    def _evp_pkey_to_public_key(self, evp_pkey) -> PublicKeyTypes:
        """
        Return the appropriate type of PublicKey given an evp_pkey cdata
        pointer.
        """

        key_type = self._lib.EVP_PKEY_id(evp_pkey)

        if key_type == self._lib.EVP_PKEY_RSA:
            rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
            self.openssl_assert(rsa_cdata != self._ffi.NULL)
            rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
            return _RSAPublicKey(self, rsa_cdata, evp_pkey)
        elif (
            key_type == self._lib.EVP_PKEY_RSA_PSS
            and not self._lib.CRYPTOGRAPHY_IS_LIBRESSL
            and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
            and not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E
        ):
            rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey)
            self.openssl_assert(rsa_cdata != self._ffi.NULL)
            rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
            bio = self._create_mem_bio_gc()
            res = self._lib.i2d_RSAPublicKey_bio(bio, rsa_cdata)
            self.openssl_assert(res == 1)
            return self.load_der_public_key(self._read_mem_bio(bio))
        elif key_type == self._lib.EVP_PKEY_DSA:
            return rust_openssl.dsa.public_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == self._lib.EVP_PKEY_EC:
            ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey)
            if ec_cdata == self._ffi.NULL:
                errors = self._consume_errors()
                raise ValueError("Unable to load EC key", errors)
            ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)
            return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)
        elif key_type in self._dh_types:
            return rust_openssl.dh.public_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None):
            # EVP_PKEY_ED25519 is not present in CRYPTOGRAPHY_IS_LIBRESSL
            return rust_openssl.ed25519.public_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == getattr(self._lib, "EVP_PKEY_X448", None):
            # EVP_PKEY_X448 is not present in CRYPTOGRAPHY_IS_LIBRESSL
            return rust_openssl.x448.public_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == self._lib.EVP_PKEY_X25519:
            return rust_openssl.x25519.public_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None):
            # EVP_PKEY_ED448 is not present in CRYPTOGRAPHY_IS_LIBRESSL
            return rust_openssl.ed448.public_key_from_ptr(
                int(self._ffi.cast("uintptr_t", evp_pkey))
            )
        else:
            raise UnsupportedAlgorithm("Unsupported key type.")

    def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
        if self._fips_enabled and isinstance(algorithm, hashes.SHA1):
            return False

        return isinstance(
            algorithm,
            (
                hashes.SHA1,
                hashes.SHA224,
                hashes.SHA256,
                hashes.SHA384,
                hashes.SHA512,
            ),
        )

    def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool:
        if isinstance(padding, PKCS1v15):
            return True
        elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1):
            # SHA1 is permissible in MGF1 in FIPS even when SHA1 is blocked
            # as signature algorithm.
            if self._fips_enabled and isinstance(
                padding._mgf._algorithm, hashes.SHA1
            ):
                return True
            else:
                return self.hash_supported(padding._mgf._algorithm)
        elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1):
            return self._oaep_hash_supported(
                padding._mgf._algorithm
            ) and self._oaep_hash_supported(padding._algorithm)
        else:
            return False

    def rsa_encryption_supported(self, padding: AsymmetricPadding) -> bool:
        if self._fips_enabled and isinstance(padding, PKCS1v15):
            return False
        else:
            return self.rsa_padding_supported(padding)

    def generate_dsa_parameters(self, key_size: int) -> dsa.DSAParameters:
        if key_size not in (1024, 2048, 3072, 4096):
            raise ValueError(
                "Key size must be 1024, 2048, 3072, or 4096 bits."
            )

        return rust_openssl.dsa.generate_parameters(key_size)

    def generate_dsa_private_key(
        self, parameters: dsa.DSAParameters
    ) -> dsa.DSAPrivateKey:
        return parameters.generate_private_key()

    def generate_dsa_private_key_and_parameters(
        self, key_size: int
    ) -> dsa.DSAPrivateKey:
        parameters = self.generate_dsa_parameters(key_size)
        return self.generate_dsa_private_key(parameters)

    def load_dsa_private_numbers(
        self, numbers: dsa.DSAPrivateNumbers
    ) -> dsa.DSAPrivateKey:
        dsa._check_dsa_private_numbers(numbers)
        return rust_openssl.dsa.from_private_numbers(numbers)

    def load_dsa_public_numbers(
        self, numbers: dsa.DSAPublicNumbers
    ) -> dsa.DSAPublicKey:
        dsa._check_dsa_parameters(numbers.parameter_numbers)
        return rust_openssl.dsa.from_public_numbers(numbers)

    def load_dsa_parameter_numbers(
        self, numbers: dsa.DSAParameterNumbers
    ) -> dsa.DSAParameters:
        dsa._check_dsa_parameters(numbers)
        return rust_openssl.dsa.from_parameter_numbers(numbers)

    def dsa_supported(self) -> bool:
        return (
            not self._lib.CRYPTOGRAPHY_IS_BORINGSSL and not self._fips_enabled
        )

    def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
        if not self.dsa_supported():
            return False
        return self.signature_hash_supported(algorithm)

    def cmac_algorithm_supported(self, algorithm) -> bool:
        return self.cipher_supported(
            algorithm, CBC(b"\x00" * algorithm.block_size)
        )

    def create_cmac_ctx(self, algorithm: BlockCipherAlgorithm) -> _CMACContext:
        return _CMACContext(self, algorithm)

    def load_pem_private_key(
        self,
        data: bytes,
        password: typing.Optional[bytes],
        unsafe_skip_rsa_key_validation: bool,
    ) -> PrivateKeyTypes:
        return self._load_key(
            self._lib.PEM_read_bio_PrivateKey,
            data,
            password,
            unsafe_skip_rsa_key_validation,
        )

    def load_pem_public_key(self, data: bytes) -> PublicKeyTypes:
        mem_bio = self._bytes_to_bio(data)
        # In OpenSSL 3.0.x the PEM_read_bio_PUBKEY function will invoke
        # the default password callback if you pass an encrypted private
        # key. This is very, very, very bad as the default callback can
        # trigger an interactive console prompt, which will hang the
        # Python process. We therefore provide our own callback to
        # catch this and error out properly.
        userdata = self._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *")
        evp_pkey = self._lib.PEM_read_bio_PUBKEY(
            mem_bio.bio,
            self._ffi.NULL,
            self._ffi.addressof(
                self._lib._original_lib, "Cryptography_pem_password_cb"
            ),
            userdata,
        )
        if evp_pkey != self._ffi.NULL:
            evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
            return self._evp_pkey_to_public_key(evp_pkey)
        else:
            # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still
            # need to check to see if it is a pure PKCS1 RSA public key (not
            # embedded in a subjectPublicKeyInfo)
            self._consume_errors()
            res = self._lib.BIO_reset(mem_bio.bio)
            self.openssl_assert(res == 1)
            rsa_cdata = self._lib.PEM_read_bio_RSAPublicKey(
                mem_bio.bio,
                self._ffi.NULL,
                self._ffi.addressof(
                    self._lib._original_lib, "Cryptography_pem_password_cb"
                ),
                userdata,
            )
            if rsa_cdata != self._ffi.NULL:
                rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
                evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)
                return _RSAPublicKey(self, rsa_cdata, evp_pkey)
            else:
                self._handle_key_loading_error()

    def load_pem_parameters(self, data: bytes) -> dh.DHParameters:
        return rust_openssl.dh.from_pem_parameters(data)

    def load_der_private_key(
        self,
        data: bytes,
        password: typing.Optional[bytes],
        unsafe_skip_rsa_key_validation: bool,
    ) -> PrivateKeyTypes:
        # OpenSSL has a function called d2i_AutoPrivateKey that in theory
        # handles this automatically, however it doesn't handle encrypted
        # private keys. Instead we try to load the key two different ways.
        # First we'll try to load it as a traditional key.
        bio_data = self._bytes_to_bio(data)
        key = self._evp_pkey_from_der_traditional_key(bio_data, password)
        if key:
            return self._evp_pkey_to_private_key(
                key, unsafe_skip_rsa_key_validation
            )
        else:
            # Finally we try to load it with the method that handles encrypted
            # PKCS8 properly.
            return self._load_key(
                self._lib.d2i_PKCS8PrivateKey_bio,
                data,
                password,
                unsafe_skip_rsa_key_validation,
            )

    def _evp_pkey_from_der_traditional_key(self, bio_data, password):
        key = self._lib.d2i_PrivateKey_bio(bio_data.bio, self._ffi.NULL)
        if key != self._ffi.NULL:
            key = self._ffi.gc(key, self._lib.EVP_PKEY_free)
            if password is not None:
                raise TypeError(
                    "Password was given but private key is not encrypted."
                )

            return key
        else:
            self._consume_errors()
            return None

    def load_der_public_key(self, data: bytes) -> PublicKeyTypes:
        mem_bio = self._bytes_to_bio(data)
        evp_pkey = self._lib.d2i_PUBKEY_bio(mem_bio.bio, self._ffi.NULL)
        if evp_pkey != self._ffi.NULL:
            evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
            return self._evp_pkey_to_public_key(evp_pkey)
        else:
            # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still
            # need to check to see if it is a pure PKCS1 RSA public key (not
            # embedded in a subjectPublicKeyInfo)
            self._consume_errors()
            res = self._lib.BIO_reset(mem_bio.bio)
            self.openssl_assert(res == 1)
            rsa_cdata = self._lib.d2i_RSAPublicKey_bio(
                mem_bio.bio, self._ffi.NULL
            )
            if rsa_cdata != self._ffi.NULL:
                rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
                evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata)
                return _RSAPublicKey(self, rsa_cdata, evp_pkey)
            else:
                self._handle_key_loading_error()

    def load_der_parameters(self, data: bytes) -> dh.DHParameters:
        return rust_openssl.dh.from_der_parameters(data)

    def _cert2ossl(self, cert: x509.Certificate) -> typing.Any:
        data = cert.public_bytes(serialization.Encoding.DER)
        mem_bio = self._bytes_to_bio(data)
        x509 = self._lib.d2i_X509_bio(mem_bio.bio, self._ffi.NULL)
        self.openssl_assert(x509 != self._ffi.NULL)
        x509 = self._ffi.gc(x509, self._lib.X509_free)
        return x509

    def _ossl2cert(self, x509_ptr: typing.Any) -> x509.Certificate:
        bio = self._create_mem_bio_gc()
        res = self._lib.i2d_X509_bio(bio, x509_ptr)
        self.openssl_assert(res == 1)
        return x509.load_der_x509_certificate(self._read_mem_bio(bio))

    def _key2ossl(self, key: PKCS12PrivateKeyTypes) -> typing.Any:
        data = key.private_bytes(
            serialization.Encoding.DER,
            serialization.PrivateFormat.PKCS8,
            serialization.NoEncryption(),
        )
        mem_bio = self._bytes_to_bio(data)

        evp_pkey = self._lib.d2i_PrivateKey_bio(
            mem_bio.bio,
            self._ffi.NULL,
        )
        self.openssl_assert(evp_pkey != self._ffi.NULL)
        return self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)

    def _load_key(
        self, openssl_read_func, data, password, unsafe_skip_rsa_key_validation
    ) -> PrivateKeyTypes:
        mem_bio = self._bytes_to_bio(data)

        userdata = self._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *")
        if password is not None:
            utils._check_byteslike("password", password)
            password_ptr = self._ffi.from_buffer(password)
            userdata.password = password_ptr
            userdata.length = len(password)

        evp_pkey = openssl_read_func(
            mem_bio.bio,
            self._ffi.NULL,
            self._ffi.addressof(
                self._lib._original_lib, "Cryptography_pem_password_cb"
            ),
            userdata,
        )

        if evp_pkey == self._ffi.NULL:
            if userdata.error != 0:
                self._consume_errors()
                if userdata.error == -1:
                    raise TypeError(
                        "Password was not given but private key is encrypted"
                    )
                else:
                    assert userdata.error == -2
                    raise ValueError(
                        "Passwords longer than {} bytes are not supported "
                        "by this backend.".format(userdata.maxsize - 1)
                    )
            else:
                self._handle_key_loading_error()

        evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)

        if password is not None and userdata.called == 0:
            raise TypeError(
                "Password was given but private key is not encrypted."
            )

        assert (
            password is not None and userdata.called == 1
        ) or password is None

        return self._evp_pkey_to_private_key(
            evp_pkey, unsafe_skip_rsa_key_validation
        )

    def _handle_key_loading_error(self) -> typing.NoReturn:
        errors = self._consume_errors()

        if not errors:
            raise ValueError(
                "Could not deserialize key data. The data may be in an "
                "incorrect format or it may be encrypted with an unsupported "
                "algorithm."
            )

        elif (
            errors[0]._lib_reason_match(
                self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT
            )
            or errors[0]._lib_reason_match(
                self._lib.ERR_LIB_PKCS12,
                self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR,
            )
            or (
                self._lib.Cryptography_HAS_PROVIDERS
                and errors[0]._lib_reason_match(
                    self._lib.ERR_LIB_PROV,
                    self._lib.PROV_R_BAD_DECRYPT,
                )
            )
        ):
            raise ValueError("Bad decrypt. Incorrect password?")

        elif any(
            error._lib_reason_match(
                self._lib.ERR_LIB_EVP,
                self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM,
            )
            for error in errors
        ):
            raise ValueError("Unsupported public key algorithm.")

        else:
            raise ValueError(
                "Could not deserialize key data. The data may be in an "
                "incorrect format, it may be encrypted with an unsupported "
                "algorithm, or it may be an unsupported key type (e.g. EC "
                "curves with explicit parameters).",
                errors,
            )

    def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool:
        try:
            curve_nid = self._elliptic_curve_to_nid(curve)
        except UnsupportedAlgorithm:
            curve_nid = self._lib.NID_undef

        group = self._lib.EC_GROUP_new_by_curve_name(curve_nid)

        if group == self._ffi.NULL:
            self._consume_errors()
            return False
        else:
            self.openssl_assert(curve_nid != self._lib.NID_undef)
            self._lib.EC_GROUP_free(group)
            return True

    def elliptic_curve_signature_algorithm_supported(
        self,
        signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
        curve: ec.EllipticCurve,
    ) -> bool:
        # We only support ECDSA right now.
        if not isinstance(signature_algorithm, ec.ECDSA):
            return False

        return self.elliptic_curve_supported(curve)

    def generate_elliptic_curve_private_key(
        self, curve: ec.EllipticCurve
    ) -> ec.EllipticCurvePrivateKey:
        """
        Generate a new private key on the named curve.
        """

        if self.elliptic_curve_supported(curve):
            ec_cdata = self._ec_key_new_by_curve(curve)

            res = self._lib.EC_KEY_generate_key(ec_cdata)
            self.openssl_assert(res == 1)

            evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)

            return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)
        else:
            raise UnsupportedAlgorithm(
                f"Backend object does not support {curve.name}.",
                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
            )

    def load_elliptic_curve_private_numbers(
        self, numbers: ec.EllipticCurvePrivateNumbers
    ) -> ec.EllipticCurvePrivateKey:
        public = numbers.public_numbers

        ec_cdata = self._ec_key_new_by_curve(public.curve)

        private_value = self._ffi.gc(
            self._int_to_bn(numbers.private_value), self._lib.BN_clear_free
        )
        res = self._lib.EC_KEY_set_private_key(ec_cdata, private_value)
        if res != 1:
            self._consume_errors()
            raise ValueError("Invalid EC key.")

        with self._tmp_bn_ctx() as bn_ctx:
            self._ec_key_set_public_key_affine_coordinates(
                ec_cdata, public.x, public.y, bn_ctx
            )
            # derive the expected public point and compare it to the one we
            # just set based on the values we were given. If they don't match
            # this isn't a valid key pair.
            group = self._lib.EC_KEY_get0_group(ec_cdata)
            self.openssl_assert(group != self._ffi.NULL)
            set_point = backend._lib.EC_KEY_get0_public_key(ec_cdata)
            self.openssl_assert(set_point != self._ffi.NULL)
            computed_point = self._lib.EC_POINT_new(group)
            self.openssl_assert(computed_point != self._ffi.NULL)
            computed_point = self._ffi.gc(
                computed_point, self._lib.EC_POINT_free
            )
            res = self._lib.EC_POINT_mul(
                group,
                computed_point,
                private_value,
                self._ffi.NULL,
                self._ffi.NULL,
                bn_ctx,
            )
            self.openssl_assert(res == 1)
            if (
                self._lib.EC_POINT_cmp(
                    group, set_point, computed_point, bn_ctx
                )
                != 0
            ):
                raise ValueError("Invalid EC key.")

        evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)

        return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)

    def load_elliptic_curve_public_numbers(
        self, numbers: ec.EllipticCurvePublicNumbers
    ) -> ec.EllipticCurvePublicKey:
        ec_cdata = self._ec_key_new_by_curve(numbers.curve)
        with self._tmp_bn_ctx() as bn_ctx:
            self._ec_key_set_public_key_affine_coordinates(
                ec_cdata, numbers.x, numbers.y, bn_ctx
            )
        evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)

        return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)

    def load_elliptic_curve_public_bytes(
        self, curve: ec.EllipticCurve, point_bytes: bytes
    ) -> ec.EllipticCurvePublicKey:
        ec_cdata = self._ec_key_new_by_curve(curve)
        group = self._lib.EC_KEY_get0_group(ec_cdata)
        self.openssl_assert(group != self._ffi.NULL)
        point = self._lib.EC_POINT_new(group)
        self.openssl_assert(point != self._ffi.NULL)
        point = self._ffi.gc(point, self._lib.EC_POINT_free)
        with self._tmp_bn_ctx() as bn_ctx:
            res = self._lib.EC_POINT_oct2point(
                group, point, point_bytes, len(point_bytes), bn_ctx
            )
            if res != 1:
                self._consume_errors()
                raise ValueError("Invalid public bytes for the given curve")

        res = self._lib.EC_KEY_set_public_key(ec_cdata, point)
        self.openssl_assert(res == 1)
        evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)
        return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey)

    def derive_elliptic_curve_private_key(
        self, private_value: int, curve: ec.EllipticCurve
    ) -> ec.EllipticCurvePrivateKey:
        ec_cdata = self._ec_key_new_by_curve(curve)

        group = self._lib.EC_KEY_get0_group(ec_cdata)
        self.openssl_assert(group != self._ffi.NULL)

        point = self._lib.EC_POINT_new(group)
        self.openssl_assert(point != self._ffi.NULL)
        point = self._ffi.gc(point, self._lib.EC_POINT_free)

        value = self._int_to_bn(private_value)
        value = self._ffi.gc(value, self._lib.BN_clear_free)

        with self._tmp_bn_ctx() as bn_ctx:
            res = self._lib.EC_POINT_mul(
                group, point, value, self._ffi.NULL, self._ffi.NULL, bn_ctx
            )
            self.openssl_assert(res == 1)

            bn_x = self._lib.BN_CTX_get(bn_ctx)
            bn_y = self._lib.BN_CTX_get(bn_ctx)

            res = self._lib.EC_POINT_get_affine_coordinates(
                group, point, bn_x, bn_y, bn_ctx
            )
            if res != 1:
                self._consume_errors()
                raise ValueError("Unable to derive key from private_value")

        res = self._lib.EC_KEY_set_public_key(ec_cdata, point)
        self.openssl_assert(res == 1)
        private = self._int_to_bn(private_value)
        private = self._ffi.gc(private, self._lib.BN_clear_free)
        res = self._lib.EC_KEY_set_private_key(ec_cdata, private)
        self.openssl_assert(res == 1)

        evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata)

        return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey)

    def _ec_key_new_by_curve(self, curve: ec.EllipticCurve):
        curve_nid = self._elliptic_curve_to_nid(curve)
        return self._ec_key_new_by_curve_nid(curve_nid)

    def _ec_key_new_by_curve_nid(self, curve_nid: int):
        ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid)
        self.openssl_assert(ec_cdata != self._ffi.NULL)
        return self._ffi.gc(ec_cdata, self._lib.EC_KEY_free)

    def elliptic_curve_exchange_algorithm_supported(
        self, algorithm: ec.ECDH, curve: ec.EllipticCurve
    ) -> bool:
        if self._fips_enabled and not isinstance(
            curve, self._fips_ecdh_curves
        ):
            return False

        return self.elliptic_curve_supported(curve) and isinstance(
            algorithm, ec.ECDH
        )

    def _ec_cdata_to_evp_pkey(self, ec_cdata):
        evp_pkey = self._create_evp_pkey_gc()
        res = self._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, ec_cdata)
        self.openssl_assert(res == 1)
        return evp_pkey

    def _elliptic_curve_to_nid(self, curve: ec.EllipticCurve) -> int:
        """
        Get the NID for a curve name.
        """

        curve_aliases = {"secp192r1": "prime192v1", "secp256r1": "prime256v1"}

        curve_name = curve_aliases.get(curve.name, curve.name)

        curve_nid = self._lib.OBJ_sn2nid(curve_name.encode())
        if curve_nid == self._lib.NID_undef:
            raise UnsupportedAlgorithm(
                f"{curve.name} is not a supported elliptic curve",
                _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
            )
        return curve_nid

    @contextmanager
    def _tmp_bn_ctx(self):
        bn_ctx = self._lib.BN_CTX_new()
        self.openssl_assert(bn_ctx != self._ffi.NULL)
        bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free)
        self._lib.BN_CTX_start(bn_ctx)
        try:
            yield bn_ctx
        finally:
            self._lib.BN_CTX_end(bn_ctx)

    def _ec_key_set_public_key_affine_coordinates(
        self,
        ec_cdata,
        x: int,
        y: int,
        bn_ctx,
    ) -> None:
        """
        Sets the public key point in the EC_KEY context to the affine x and y
        values.
        """

        if x < 0 or y < 0:
            raise ValueError(
                "Invalid EC key. Both x and y must be non-negative."
            )

        x = self._ffi.gc(self._int_to_bn(x), self._lib.BN_free)
        y = self._ffi.gc(self._int_to_bn(y), self._lib.BN_free)
        group = self._lib.EC_KEY_get0_group(ec_cdata)
        self.openssl_assert(group != self._ffi.NULL)
        point = self._lib.EC_POINT_new(group)
        self.openssl_assert(point != self._ffi.NULL)
        point = self._ffi.gc(point, self._lib.EC_POINT_free)
        res = self._lib.EC_POINT_set_affine_coordinates(
            group, point, x, y, bn_ctx
        )
        if res != 1:
            self._consume_errors()
            raise ValueError("Invalid EC key.")
        res = self._lib.EC_KEY_set_public_key(ec_cdata, point)
        self.openssl_assert(res == 1)

    def _private_key_bytes(
        self,
        encoding: serialization.Encoding,
        format: serialization.PrivateFormat,
        encryption_algorithm: serialization.KeySerializationEncryption,
        key,
        evp_pkey,
        cdata,
    ) -> bytes:
        # validate argument types
        if not isinstance(encoding, serialization.Encoding):
            raise TypeError("encoding must be an item from the Encoding enum")
        if not isinstance(format, serialization.PrivateFormat):
            raise TypeError(
                "format must be an item from the PrivateFormat enum"
            )
        if not isinstance(
            encryption_algorithm, serialization.KeySerializationEncryption
        ):
            raise TypeError(
                "Encryption algorithm must be a KeySerializationEncryption "
                "instance"
            )

        # validate password
        if isinstance(encryption_algorithm, serialization.NoEncryption):
            password = b""
        elif isinstance(
            encryption_algorithm, serialization.BestAvailableEncryption
        ):
            password = encryption_algorithm.password
            if len(password) > 1023:
                raise ValueError(
                    "Passwords longer than 1023 bytes are not supported by "
                    "this backend"
                )
        elif (
            isinstance(
                encryption_algorithm, serialization._KeySerializationEncryption
            )
            and encryption_algorithm._format
            is format
            is serialization.PrivateFormat.OpenSSH
        ):
            password = encryption_algorithm.password
        else:
            raise ValueError("Unsupported encryption type")

        # PKCS8 + PEM/DER
        if format is serialization.PrivateFormat.PKCS8:
            if encoding is serialization.Encoding.PEM:
                write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey
            elif encoding is serialization.Encoding.DER:
                write_bio = self._lib.i2d_PKCS8PrivateKey_bio
            else:
                raise ValueError("Unsupported encoding for PKCS8")
            return self._private_key_bytes_via_bio(
                write_bio, evp_pkey, password
            )

        # TraditionalOpenSSL + PEM/DER
        if format is serialization.PrivateFormat.TraditionalOpenSSL:
            if self._fips_enabled and not isinstance(
                encryption_algorithm, serialization.NoEncryption
            ):
                raise ValueError(
                    "Encrypted traditional OpenSSL format is not "
                    "supported in FIPS mode."
                )
            key_type = self._lib.EVP_PKEY_id(evp_pkey)

            if encoding is serialization.Encoding.PEM:
                if key_type == self._lib.EVP_PKEY_RSA:
                    write_bio = self._lib.PEM_write_bio_RSAPrivateKey
                else:
                    assert key_type == self._lib.EVP_PKEY_EC
                    write_bio = self._lib.PEM_write_bio_ECPrivateKey
                return self._private_key_bytes_via_bio(
                    write_bio, cdata, password
                )

            if encoding is serialization.Encoding.DER:
                if password:
                    raise ValueError(
                        "Encryption is not supported for DER encoded "
                        "traditional OpenSSL keys"
                    )
                if key_type == self._lib.EVP_PKEY_RSA:
                    write_bio = self._lib.i2d_RSAPrivateKey_bio
                else:
                    assert key_type == self._lib.EVP_PKEY_EC
                    write_bio = self._lib.i2d_ECPrivateKey_bio
                return self._bio_func_output(write_bio, cdata)

            raise ValueError("Unsupported encoding for TraditionalOpenSSL")

        # OpenSSH + PEM
        if format is serialization.PrivateFormat.OpenSSH:
            if encoding is serialization.Encoding.PEM:
                return ssh._serialize_ssh_private_key(
                    key, password, encryption_algorithm
                )

            raise ValueError(
                "OpenSSH private key format can only be used"
                " with PEM encoding"
            )

        # Anything that key-specific code was supposed to handle earlier,
        # like Raw.
        raise ValueError("format is invalid with this key")

    def _private_key_bytes_via_bio(
        self, write_bio, evp_pkey, password
    ) -> bytes:
        if not password:
            evp_cipher = self._ffi.NULL
        else:
            # This is a curated value that we will update over time.
            evp_cipher = self._lib.EVP_get_cipherbyname(b"aes-256-cbc")

        return self._bio_func_output(
            write_bio,
            evp_pkey,
            evp_cipher,
            password,
            len(password),
            self._ffi.NULL,
            self._ffi.NULL,
        )

    def _bio_func_output(self, write_bio, *args) -> bytes:
        bio = self._create_mem_bio_gc()
        res = write_bio(bio, *args)
        self.openssl_assert(res == 1)
        return self._read_mem_bio(bio)

    def _public_key_bytes(
        self,
        encoding: serialization.Encoding,
        format: serialization.PublicFormat,
        key,
        evp_pkey,
        cdata,
    ) -> bytes:
        if not isinstance(encoding, serialization.Encoding):
            raise TypeError("encoding must be an item from the Encoding enum")
        if not isinstance(format, serialization.PublicFormat):
            raise TypeError(
                "format must be an item from the PublicFormat enum"
            )

        # SubjectPublicKeyInfo + PEM/DER
        if format is serialization.PublicFormat.SubjectPublicKeyInfo:
            if encoding is serialization.Encoding.PEM:
                write_bio = self._lib.PEM_write_bio_PUBKEY
            elif encoding is serialization.Encoding.DER:
                write_bio = self._lib.i2d_PUBKEY_bio
            else:
                raise ValueError(
                    "SubjectPublicKeyInfo works only with PEM or DER encoding"
                )
            return self._bio_func_output(write_bio, evp_pkey)

        # PKCS1 + PEM/DER
        if format is serialization.PublicFormat.PKCS1:
            # Only RSA is supported here.
            key_type = self._lib.EVP_PKEY_id(evp_pkey)
            if key_type != self._lib.EVP_PKEY_RSA:
                raise ValueError("PKCS1 format is supported only for RSA keys")

            if encoding is serialization.Encoding.PEM:
                write_bio = self._lib.PEM_write_bio_RSAPublicKey
            elif encoding is serialization.Encoding.DER:
                write_bio = self._lib.i2d_RSAPublicKey_bio
            else:
                raise ValueError("PKCS1 works only with PEM or DER encoding")
            return self._bio_func_output(write_bio, cdata)

        # OpenSSH + OpenSSH
        if format is serialization.PublicFormat.OpenSSH:
            if encoding is serialization.Encoding.OpenSSH:
                return ssh.serialize_ssh_public_key(key)

            raise ValueError(
                "OpenSSH format must be used with OpenSSH encoding"
            )

        # Anything that key-specific code was supposed to handle earlier,
        # like Raw, CompressedPoint, UncompressedPoint
        raise ValueError("format is invalid with this key")

    def dh_supported(self) -> bool:
        return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL

    def generate_dh_parameters(
        self, generator: int, key_size: int
    ) -> dh.DHParameters:
        return rust_openssl.dh.generate_parameters(generator, key_size)

    def generate_dh_private_key(
        self, parameters: dh.DHParameters
    ) -> dh.DHPrivateKey:
        return parameters.generate_private_key()

    def generate_dh_private_key_and_parameters(
        self, generator: int, key_size: int
    ) -> dh.DHPrivateKey:
        return self.generate_dh_private_key(
            self.generate_dh_parameters(generator, key_size)
        )

    def load_dh_private_numbers(
        self, numbers: dh.DHPrivateNumbers
    ) -> dh.DHPrivateKey:
        return rust_openssl.dh.from_private_numbers(numbers)

    def load_dh_public_numbers(
        self, numbers: dh.DHPublicNumbers
    ) -> dh.DHPublicKey:
        return rust_openssl.dh.from_public_numbers(numbers)

    def load_dh_parameter_numbers(
        self, numbers: dh.DHParameterNumbers
    ) -> dh.DHParameters:
        return rust_openssl.dh.from_parameter_numbers(numbers)

    def dh_parameters_supported(
        self, p: int, g: int, q: typing.Optional[int] = None
    ) -> bool:
        try:
            rust_openssl.dh.from_parameter_numbers(
                dh.DHParameterNumbers(p=p, g=g, q=q)
            )
        except ValueError:
            return False
        else:
            return True

    def dh_x942_serialization_supported(self) -> bool:
        return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1

    def x25519_load_public_bytes(self, data: bytes) -> x25519.X25519PublicKey:
        return rust_openssl.x25519.from_public_bytes(data)

    def x25519_load_private_bytes(
        self, data: bytes
    ) -> x25519.X25519PrivateKey:
        return rust_openssl.x25519.from_private_bytes(data)

    def x25519_generate_key(self) -> x25519.X25519PrivateKey:
        return rust_openssl.x25519.generate_key()

    def x25519_supported(self) -> bool:
        if self._fips_enabled:
            return False
        return not self._lib.CRYPTOGRAPHY_LIBRESSL_LESS_THAN_370

    def x448_load_public_bytes(self, data: bytes) -> x448.X448PublicKey:
        return rust_openssl.x448.from_public_bytes(data)

    def x448_load_private_bytes(self, data: bytes) -> x448.X448PrivateKey:
        return rust_openssl.x448.from_private_bytes(data)

    def x448_generate_key(self) -> x448.X448PrivateKey:
        return rust_openssl.x448.generate_key()

    def x448_supported(self) -> bool:
        if self._fips_enabled:
            return False
        return (
            not self._lib.CRYPTOGRAPHY_IS_LIBRESSL
            and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
        )

    def ed25519_supported(self) -> bool:
        if self._fips_enabled:
            return False
        return self._lib.CRYPTOGRAPHY_HAS_WORKING_ED25519

    def ed25519_load_public_bytes(
        self, data: bytes
    ) -> ed25519.Ed25519PublicKey:
        return rust_openssl.ed25519.from_public_bytes(data)

    def ed25519_load_private_bytes(
        self, data: bytes
    ) -> ed25519.Ed25519PrivateKey:
        return rust_openssl.ed25519.from_private_bytes(data)

    def ed25519_generate_key(self) -> ed25519.Ed25519PrivateKey:
        return rust_openssl.ed25519.generate_key()

    def ed448_supported(self) -> bool:
        if self._fips_enabled:
            return False
        return (
            not self._lib.CRYPTOGRAPHY_IS_LIBRESSL
            and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
        )

    def ed448_load_public_bytes(self, data: bytes) -> ed448.Ed448PublicKey:
        return rust_openssl.ed448.from_public_bytes(data)

    def ed448_load_private_bytes(self, data: bytes) -> ed448.Ed448PrivateKey:
        return rust_openssl.ed448.from_private_bytes(data)

    def ed448_generate_key(self) -> ed448.Ed448PrivateKey:
        return rust_openssl.ed448.generate_key()

    def aead_cipher_supported(self, cipher) -> bool:
        return aead._aead_cipher_supported(self, cipher)

    def _zero_data(self, data, length: int) -> None:
        # We clear things this way because at the moment we're not
        # sure of a better way that can guarantee it overwrites the
        # memory of a bytearray and doesn't just replace the underlying char *.
        for i in range(length):
            data[i] = 0

    @contextlib.contextmanager
    def _zeroed_null_terminated_buf(self, data):
        """
        This method takes bytes, which can be a bytestring or a mutable
        buffer like a bytearray, and yields a null-terminated version of that
        data. This is required because PKCS12_parse doesn't take a length with
        its password char * and ffi.from_buffer doesn't provide null
        termination. So, to support zeroing the data via bytearray we
        need to build this ridiculous construct that copies the memory, but
        zeroes it after use.
        """
        if data is None:
            yield self._ffi.NULL
        else:
            data_len = len(data)
            buf = self._ffi.new("char[]", data_len + 1)
            self._ffi.memmove(buf, data, data_len)
            try:
                yield buf
            finally:
                # Cast to a uint8_t * so we can assign by integer
                self._zero_data(self._ffi.cast("uint8_t *", buf), data_len)

    def load_key_and_certificates_from_pkcs12(
        self, data: bytes, password: typing.Optional[bytes]
    ) -> typing.Tuple[
        typing.Optional[PrivateKeyTypes],
        typing.Optional[x509.Certificate],
        typing.List[x509.Certificate],
    ]:
        pkcs12 = self.load_pkcs12(data, password)
        return (
            pkcs12.key,
            pkcs12.cert.certificate if pkcs12.cert else None,
            [cert.certificate for cert in pkcs12.additional_certs],
        )

    def load_pkcs12(
        self, data: bytes, password: typing.Optional[bytes]
    ) -> PKCS12KeyAndCertificates:
        if password is not None:
            utils._check_byteslike("password", password)

        bio = self._bytes_to_bio(data)
        p12 = self._lib.d2i_PKCS12_bio(bio.bio, self._ffi.NULL)
        if p12 == self._ffi.NULL:
            self._consume_errors()
            raise ValueError("Could not deserialize PKCS12 data")

        p12 = self._ffi.gc(p12, self._lib.PKCS12_free)
        evp_pkey_ptr = self._ffi.new("EVP_PKEY **")
        x509_ptr = self._ffi.new("X509 **")
        sk_x509_ptr = self._ffi.new("Cryptography_STACK_OF_X509 **")
        with self._zeroed_null_terminated_buf(password) as password_buf:
            res = self._lib.PKCS12_parse(
                p12, password_buf, evp_pkey_ptr, x509_ptr, sk_x509_ptr
            )
        if res == 0:
            self._consume_errors()
            raise ValueError("Invalid password or PKCS12 data")

        cert = None
        key = None
        additional_certificates = []

        if evp_pkey_ptr[0] != self._ffi.NULL:
            evp_pkey = self._ffi.gc(evp_pkey_ptr[0], self._lib.EVP_PKEY_free)
            # We don't support turning off RSA key validation when loading
            # PKCS12 keys
            key = self._evp_pkey_to_private_key(
                evp_pkey, unsafe_skip_rsa_key_validation=False
            )

        if x509_ptr[0] != self._ffi.NULL:
            x509 = self._ffi.gc(x509_ptr[0], self._lib.X509_free)
            cert_obj = self._ossl2cert(x509)
            name = None
            maybe_name = self._lib.X509_alias_get0(x509, self._ffi.NULL)
            if maybe_name != self._ffi.NULL:
                name = self._ffi.string(maybe_name)
            cert = PKCS12Certificate(cert_obj, name)

        if sk_x509_ptr[0] != self._ffi.NULL:
            sk_x509 = self._ffi.gc(sk_x509_ptr[0], self._lib.sk_X509_free)
            num = self._lib.sk_X509_num(sk_x509_ptr[0])

            # In OpenSSL < 3.0.0 PKCS12 parsing reverses the order of the
            # certificates.
            indices: typing.Iterable[int]
            if (
                self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
                or self._lib.CRYPTOGRAPHY_IS_BORINGSSL
            ):
                indices = range(num)
            else:
                indices = reversed(range(num))

            for i in indices:
                x509 = self._lib.sk_X509_value(sk_x509, i)
                self.openssl_assert(x509 != self._ffi.NULL)
                x509 = self._ffi.gc(x509, self._lib.X509_free)
                addl_cert = self._ossl2cert(x509)
                addl_name = None
                maybe_name = self._lib.X509_alias_get0(x509, self._ffi.NULL)
                if maybe_name != self._ffi.NULL:
                    addl_name = self._ffi.string(maybe_name)
                additional_certificates.append(
                    PKCS12Certificate(addl_cert, addl_name)
                )

        return PKCS12KeyAndCertificates(key, cert, additional_certificates)

    def serialize_key_and_certificates_to_pkcs12(
        self,
        name: typing.Optional[bytes],
        key: typing.Optional[PKCS12PrivateKeyTypes],
        cert: typing.Optional[x509.Certificate],
        cas: typing.Optional[typing.List[_PKCS12CATypes]],
        encryption_algorithm: serialization.KeySerializationEncryption,
    ) -> bytes:
        password = None
        if name is not None:
            utils._check_bytes("name", name)

        if isinstance(encryption_algorithm, serialization.NoEncryption):
            nid_cert = -1
            nid_key = -1
            pkcs12_iter = 0
            mac_iter = 0
            mac_alg = self._ffi.NULL
        elif isinstance(
            encryption_algorithm, serialization.BestAvailableEncryption
        ):
            # PKCS12 encryption is hopeless trash and can never be fixed.
            # OpenSSL 3 supports PBESv2, but Libre and Boring do not, so
            # we use PBESv1 with 3DES on the older paths.
            if self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
                nid_cert = self._lib.NID_aes_256_cbc
                nid_key = self._lib.NID_aes_256_cbc
            else:
                nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
                nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
            # At least we can set this higher than OpenSSL's default
            pkcs12_iter = 20000
            # mac_iter chosen for compatibility reasons, see:
            # https://www.openssl.org/docs/man1.1.1/man3/PKCS12_create.html
            # Did we mention how lousy PKCS12 encryption is?
            mac_iter = 1
            # MAC algorithm can only be set on OpenSSL 3.0.0+
            mac_alg = self._ffi.NULL
            password = encryption_algorithm.password
        elif (
            isinstance(
                encryption_algorithm, serialization._KeySerializationEncryption
            )
            and encryption_algorithm._format
            is serialization.PrivateFormat.PKCS12
        ):
            # Default to OpenSSL's defaults. Behavior will vary based on the
            # version of OpenSSL cryptography is compiled against.
            nid_cert = 0
            nid_key = 0
            # Use the default iters we use in best available
            pkcs12_iter = 20000
            # See the Best Available comment for why this is 1
            mac_iter = 1
            password = encryption_algorithm.password
            keycertalg = encryption_algorithm._key_cert_algorithm
            if keycertalg is PBES.PBESv1SHA1And3KeyTripleDESCBC:
                nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
                nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC
            elif keycertalg is PBES.PBESv2SHA256AndAES256CBC:
                if not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
                    raise UnsupportedAlgorithm(
                        "PBESv2 is not supported by this version of OpenSSL"
                    )
                nid_cert = self._lib.NID_aes_256_cbc
                nid_key = self._lib.NID_aes_256_cbc
            else:
                assert keycertalg is None
                # We use OpenSSL's defaults

            if encryption_algorithm._hmac_hash is not None:
                if not self._lib.Cryptography_HAS_PKCS12_SET_MAC:
                    raise UnsupportedAlgorithm(
                        "Setting MAC algorithm is not supported by this "
                        "version of OpenSSL."
                    )
                mac_alg = self._evp_md_non_null_from_algorithm(
                    encryption_algorithm._hmac_hash
                )
                self.openssl_assert(mac_alg != self._ffi.NULL)
            else:
                mac_alg = self._ffi.NULL

            if encryption_algorithm._kdf_rounds is not None:
                pkcs12_iter = encryption_algorithm._kdf_rounds

        else:
            raise ValueError("Unsupported key encryption type")

        if cas is None or len(cas) == 0:
            sk_x509 = self._ffi.NULL
        else:
            sk_x509 = self._lib.sk_X509_new_null()
            sk_x509 = self._ffi.gc(sk_x509, self._lib.sk_X509_free)

            # This list is to keep the x509 values alive until end of function
            ossl_cas = []
            for ca in cas:
                if isinstance(ca, PKCS12Certificate):
                    ca_alias = ca.friendly_name
                    ossl_ca = self._cert2ossl(ca.certificate)
                    if ca_alias is None:
                        res = self._lib.X509_alias_set1(
                            ossl_ca, self._ffi.NULL, -1
                        )
                    else:
                        res = self._lib.X509_alias_set1(
                            ossl_ca, ca_alias, len(ca_alias)
                        )
                    self.openssl_assert(res == 1)
                else:
                    ossl_ca = self._cert2ossl(ca)
                ossl_cas.append(ossl_ca)
                res = self._lib.sk_X509_push(sk_x509, ossl_ca)
                backend.openssl_assert(res >= 1)

        with self._zeroed_null_terminated_buf(password) as password_buf:
            with self._zeroed_null_terminated_buf(name) as name_buf:
                ossl_cert = self._cert2ossl(cert) if cert else self._ffi.NULL
                ossl_pkey = (
                    self._key2ossl(key) if key is not None else self._ffi.NULL
                )

                p12 = self._lib.PKCS12_create(
                    password_buf,
                    name_buf,
                    ossl_pkey,
                    ossl_cert,
                    sk_x509,
                    nid_key,
                    nid_cert,
                    pkcs12_iter,
                    mac_iter,
                    0,
                )

            if (
                self._lib.Cryptography_HAS_PKCS12_SET_MAC
                and mac_alg != self._ffi.NULL
            ):
                self._lib.PKCS12_set_mac(
                    p12,
                    password_buf,
                    -1,
                    self._ffi.NULL,
                    0,
                    mac_iter,
                    mac_alg,
                )

        self.openssl_assert(p12 != self._ffi.NULL)
        p12 = self._ffi.gc(p12, self._lib.PKCS12_free)

        bio = self._create_mem_bio_gc()
        res = self._lib.i2d_PKCS12_bio(bio, p12)
        self.openssl_assert(res > 0)
        return self._read_mem_bio(bio)

    def poly1305_supported(self) -> bool:
        if self._fips_enabled:
            return False
        return self._lib.Cryptography_HAS_POLY1305 == 1

    def pkcs7_supported(self) -> bool:
        return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL

    def load_pem_pkcs7_certificates(
        self, data: bytes
    ) -> typing.List[x509.Certificate]:
        utils._check_bytes("data", data)
        bio = self._bytes_to_bio(data)
        p7 = self._lib.PEM_read_bio_PKCS7(
            bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
        )
        if p7 == self._ffi.NULL:
            self._consume_errors()
            raise ValueError("Unable to parse PKCS7 data")

        p7 = self._ffi.gc(p7, self._lib.PKCS7_free)
        return self._load_pkcs7_certificates(p7)

    def load_der_pkcs7_certificates(
        self, data: bytes
    ) -> typing.List[x509.Certificate]:
        utils._check_bytes("data", data)
        bio = self._bytes_to_bio(data)
        p7 = self._lib.d2i_PKCS7_bio(bio.bio, self._ffi.NULL)
        if p7 == self._ffi.NULL:
            self._consume_errors()
            raise ValueError("Unable to parse PKCS7 data")

        p7 = self._ffi.gc(p7, self._lib.PKCS7_free)
        return self._load_pkcs7_certificates(p7)

    def _load_pkcs7_certificates(self, p7) -> typing.List[x509.Certificate]:
        nid = self._lib.OBJ_obj2nid(p7.type)
        self.openssl_assert(nid != self._lib.NID_undef)
        if nid != self._lib.NID_pkcs7_signed:
            raise UnsupportedAlgorithm(
                "Only basic signed structures are currently supported. NID"
                " for this data was {}".format(nid),
                _Reasons.UNSUPPORTED_SERIALIZATION,
            )

        sk_x509 = p7.d.sign.cert
        num = self._lib.sk_X509_num(sk_x509)
        certs = []
        for i in range(num):
            x509 = self._lib.sk_X509_value(sk_x509, i)
            self.openssl_assert(x509 != self._ffi.NULL)
            cert = self._ossl2cert(x509)
            certs.append(cert)

        return certs


class GetCipherByName:
    def __init__(self, fmt: str):
        self._fmt = fmt

    def __call__(self, backend: Backend, cipher: CipherAlgorithm, mode: Mode):
        cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower()
        evp_cipher = backend._lib.EVP_get_cipherbyname(
            cipher_name.encode("ascii")
        )

        # try EVP_CIPHER_fetch if present
        if (
            evp_cipher == backend._ffi.NULL
            and backend._lib.Cryptography_HAS_300_EVP_CIPHER
        ):
            evp_cipher = backend._lib.EVP_CIPHER_fetch(
                backend._ffi.NULL,
                cipher_name.encode("ascii"),
                backend._ffi.NULL,
            )

        backend._consume_errors()
        return evp_cipher


def _get_xts_cipher(backend: Backend, cipher: AES, mode):
    cipher_name = f"aes-{cipher.key_size // 2}-xts"
    return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))


backend = Backend()

back/venvmsg/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography.exceptions import InvalidTag, UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.primitives import ciphers
from cryptography.hazmat.primitives.ciphers import algorithms, modes

if typing.TYPE_CHECKING:
    from cryptography.hazmat.backends.openssl.backend import Backend


class _CipherContext:
    _ENCRYPT = 1
    _DECRYPT = 0
    _MAX_CHUNK_SIZE = 2**30 - 1

    def __init__(self, backend: Backend, cipher, mode, operation: int) -> None:
        self._backend = backend
        self._cipher = cipher
        self._mode = mode
        self._operation = operation
        self._tag: typing.Optional[bytes] = None

        if isinstance(self._cipher, ciphers.BlockCipherAlgorithm):
            self._block_size_bytes = self._cipher.block_size // 8
        else:
            self._block_size_bytes = 1

        ctx = self._backend._lib.EVP_CIPHER_CTX_new()
        ctx = self._backend._ffi.gc(
            ctx, self._backend._lib.EVP_CIPHER_CTX_free
        )

        registry = self._backend._cipher_registry
        try:
            adapter = registry[type(cipher), type(mode)]
        except KeyError:
            raise UnsupportedAlgorithm(
                "cipher {} in {} mode is not supported "
                "by this backend.".format(
                    cipher.name, mode.name if mode else mode
                ),
                _Reasons.UNSUPPORTED_CIPHER,
            )

        evp_cipher = adapter(self._backend, cipher, mode)
        if evp_cipher == self._backend._ffi.NULL:
            msg = f"cipher {cipher.name} "
            if mode is not None:
                msg += f"in {mode.name} mode "
            msg += (
                "is not supported by this backend (Your version of OpenSSL "
                "may be too old. Current version: {}.)"
            ).format(self._backend.openssl_version_text())
            raise UnsupportedAlgorithm(msg, _Reasons.UNSUPPORTED_CIPHER)

        if isinstance(mode, modes.ModeWithInitializationVector):
            iv_nonce = self._backend._ffi.from_buffer(
                mode.initialization_vector
            )
        elif isinstance(mode, modes.ModeWithTweak):
            iv_nonce = self._backend._ffi.from_buffer(mode.tweak)
        elif isinstance(mode, modes.ModeWithNonce):
            iv_nonce = self._backend._ffi.from_buffer(mode.nonce)
        elif isinstance(cipher, algorithms.ChaCha20):
            iv_nonce = self._backend._ffi.from_buffer(cipher.nonce)
        else:
            iv_nonce = self._backend._ffi.NULL
        # begin init with cipher and operation type
        res = self._backend._lib.EVP_CipherInit_ex(
            ctx,
            evp_cipher,
            self._backend._ffi.NULL,
            self._backend._ffi.NULL,
            self._backend._ffi.NULL,
            operation,
        )
        self._backend.openssl_assert(res != 0)
        # set the key length to handle variable key ciphers
        res = self._backend._lib.EVP_CIPHER_CTX_set_key_length(
            ctx, len(cipher.key)
        )
        self._backend.openssl_assert(res != 0)
        if isinstance(mode, modes.GCM):
            res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
                ctx,
                self._backend._lib.EVP_CTRL_AEAD_SET_IVLEN,
                len(iv_nonce),
                self._backend._ffi.NULL,
            )
            self._backend.openssl_assert(res != 0)
            if mode.tag is not None:
                res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
                    ctx,
                    self._backend._lib.EVP_CTRL_AEAD_SET_TAG,
                    len(mode.tag),
                    mode.tag,
                )
                self._backend.openssl_assert(res != 0)
                self._tag = mode.tag

        # pass key/iv
        res = self._backend._lib.EVP_CipherInit_ex(
            ctx,
            self._backend._ffi.NULL,
            self._backend._ffi.NULL,
            self._backend._ffi.from_buffer(cipher.key),
            iv_nonce,
            operation,
        )

        # Check for XTS mode duplicate keys error
        errors = self._backend._consume_errors()
        lib = self._backend._lib
        if res == 0 and (
            (
                not lib.CRYPTOGRAPHY_IS_LIBRESSL
                and errors[0]._lib_reason_match(
                    lib.ERR_LIB_EVP, lib.EVP_R_XTS_DUPLICATED_KEYS
                )
            )
            or (
                lib.Cryptography_HAS_PROVIDERS
                and errors[0]._lib_reason_match(
                    lib.ERR_LIB_PROV, lib.PROV_R_XTS_DUPLICATED_KEYS
                )
            )
        ):
            raise ValueError("In XTS mode duplicated keys are not allowed")

        self._backend.openssl_assert(res != 0, errors=errors)

        # We purposely disable padding here as it's handled higher up in the
        # API.
        self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0)
        self._ctx = ctx

    def update(self, data: bytes) -> bytes:
        buf = bytearray(len(data) + self._block_size_bytes - 1)
        n = self.update_into(data, buf)
        return bytes(buf[:n])

    def update_into(self, data: bytes, buf: bytes) -> int:
        total_data_len = len(data)
        if len(buf) < (total_data_len + self._block_size_bytes - 1):
            raise ValueError(
                "buffer must be at least {} bytes for this "
                "payload".format(len(data) + self._block_size_bytes - 1)
            )

        data_processed = 0
        total_out = 0
        outlen = self._backend._ffi.new("int *")
        baseoutbuf = self._backend._ffi.from_buffer(buf, require_writable=True)
        baseinbuf = self._backend._ffi.from_buffer(data)

        while data_processed != total_data_len:
            outbuf = baseoutbuf + total_out
            inbuf = baseinbuf + data_processed
            inlen = min(self._MAX_CHUNK_SIZE, total_data_len - data_processed)

            res = self._backend._lib.EVP_CipherUpdate(
                self._ctx, outbuf, outlen, inbuf, inlen
            )
            if res == 0 and isinstance(self._mode, modes.XTS):
                self._backend._consume_errors()
                raise ValueError(
                    "In XTS mode you must supply at least a full block in the "
                    "first update call. For AES this is 16 bytes."
                )
            else:
                self._backend.openssl_assert(res != 0)
            data_processed += inlen
            total_out += outlen[0]

        return total_out

    def finalize(self) -> bytes:
        if (
            self._operation == self._DECRYPT
            and isinstance(self._mode, modes.ModeWithAuthenticationTag)
            and self.tag is None
        ):
            raise ValueError(
                "Authentication tag must be provided when decrypting."
            )

        buf = self._backend._ffi.new("unsigned char[]", self._block_size_bytes)
        outlen = self._backend._ffi.new("int *")
        res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen)
        if res == 0:
            errors = self._backend._consume_errors()

            if not errors and isinstance(self._mode, modes.GCM):
                raise InvalidTag

            lib = self._backend._lib
            self._backend.openssl_assert(
                errors[0]._lib_reason_match(
                    lib.ERR_LIB_EVP,
                    lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,
                )
                or (
                    lib.Cryptography_HAS_PROVIDERS
                    and errors[0]._lib_reason_match(
                        lib.ERR_LIB_PROV,
                        lib.PROV_R_WRONG_FINAL_BLOCK_LENGTH,
                    )
                )
                or (
                    lib.CRYPTOGRAPHY_IS_BORINGSSL
                    and errors[0].reason
                    == lib.CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
                ),
                errors=errors,
            )
            raise ValueError(
                "The length of the provided data is not a multiple of "
                "the block length."
            )

        if (
            isinstance(self._mode, modes.GCM)
            and self._operation == self._ENCRYPT
        ):
            tag_buf = self._backend._ffi.new(
                "unsigned char[]", self._block_size_bytes
            )
            res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
                self._ctx,
                self._backend._lib.EVP_CTRL_AEAD_GET_TAG,
                self._block_size_bytes,
                tag_buf,
            )
            self._backend.openssl_assert(res != 0)
            self._tag = self._backend._ffi.buffer(tag_buf)[:]

        res = self._backend._lib.EVP_CIPHER_CTX_reset(self._ctx)
        self._backend.openssl_assert(res == 1)
        return self._backend._ffi.buffer(buf)[: outlen[0]]

    def finalize_with_tag(self, tag: bytes) -> bytes:
        tag_len = len(tag)
        if tag_len < self._mode._min_tag_length:
            raise ValueError(
                "Authentication tag must be {} bytes or longer.".format(
                    self._mode._min_tag_length
                )
            )
        elif tag_len > self._block_size_bytes:
            raise ValueError(
                "Authentication tag cannot be more than {} bytes.".format(
                    self._block_size_bytes
                )
            )
        res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
            self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag
        )
        self._backend.openssl_assert(res != 0)
        self._tag = tag
        return self.finalize()

    def authenticate_additional_data(self, data: bytes) -> None:
        outlen = self._backend._ffi.new("int *")
        res = self._backend._lib.EVP_CipherUpdate(
            self._ctx,
            self._backend._ffi.NULL,
            outlen,
            self._backend._ffi.from_buffer(data),
            len(data),
        )
        self._backend.openssl_assert(res != 0)

    @property
    def tag(self) -> typing.Optional[bytes]:
        return self._tag

back/venvmsg/Lib/site-packages/cryptography/hazmat/backends/openssl/ciphers.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography.exceptions import (
    InvalidSignature,
    UnsupportedAlgorithm,
    _Reasons,
)
from cryptography.hazmat.backends.openssl.utils import (
    _calculate_digest_and_algorithm,
    _evp_pkey_derive,
)
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec

if typing.TYPE_CHECKING:
    from cryptography.hazmat.backends.openssl.backend import Backend


def _check_signature_algorithm(
    signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
) -> None:
    if not isinstance(signature_algorithm, ec.ECDSA):
        raise UnsupportedAlgorithm(
            "Unsupported elliptic curve signature algorithm.",
            _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
        )


def _ec_key_curve_sn(backend: Backend, ec_key) -> str:
    group = backend._lib.EC_KEY_get0_group(ec_key)
    backend.openssl_assert(group != backend._ffi.NULL)

    nid = backend._lib.EC_GROUP_get_curve_name(group)
    # The following check is to find EC keys with unnamed curves and raise
    # an error for now.
    if nid == backend._lib.NID_undef:
        raise ValueError(
            "ECDSA keys with explicit parameters are unsupported at this time"
        )

    # This is like the above check, but it also catches the case where you
    # explicitly encoded a curve with the same parameters as a named curve.
    # Don't do that.
    if (
        not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL
        and backend._lib.EC_GROUP_get_asn1_flag(group) == 0
    ):
        raise ValueError(
            "ECDSA keys with explicit parameters are unsupported at this time"
        )

    curve_name = backend._lib.OBJ_nid2sn(nid)
    backend.openssl_assert(curve_name != backend._ffi.NULL)

    sn = backend._ffi.string(curve_name).decode("ascii")
    return sn


def _mark_asn1_named_ec_curve(backend: Backend, ec_cdata):
    """
    Set the named curve flag on the EC_KEY. This causes OpenSSL to
    serialize EC keys along with their curve OID which makes
    deserialization easier.
    """

    backend._lib.EC_KEY_set_asn1_flag(
        ec_cdata, backend._lib.OPENSSL_EC_NAMED_CURVE
    )


def _check_key_infinity(backend: Backend, ec_cdata) -> None:
    point = backend._lib.EC_KEY_get0_public_key(ec_cdata)
    backend.openssl_assert(point != backend._ffi.NULL)
    group = backend._lib.EC_KEY_get0_group(ec_cdata)
    backend.openssl_assert(group != backend._ffi.NULL)
    if backend._lib.EC_POINT_is_at_infinity(group, point):
        raise ValueError(
            "Cannot load an EC public key where the point is at infinity"
        )


def _sn_to_elliptic_curve(backend: Backend, sn: str) -> ec.EllipticCurve:
    try:
        return ec._CURVE_TYPES[sn]()
    except KeyError:
        raise UnsupportedAlgorithm(
            f"{sn} is not a supported elliptic curve",
            _Reasons.UNSUPPORTED_ELLIPTIC_CURVE,
        )


def _ecdsa_sig_sign(
    backend: Backend, private_key: _EllipticCurvePrivateKey, data: bytes
) -> bytes:
    max_size = backend._lib.ECDSA_size(private_key._ec_key)
    backend.openssl_assert(max_size > 0)

    sigbuf = backend._ffi.new("unsigned char[]", max_size)
    siglen_ptr = backend._ffi.new("unsigned int[]", 1)
    res = backend._lib.ECDSA_sign(
        0, data, len(data), sigbuf, siglen_ptr, private_key._ec_key
    )
    backend.openssl_assert(res == 1)
    return backend._ffi.buffer(sigbuf)[: siglen_ptr[0]]


def _ecdsa_sig_verify(
    backend: Backend,
    public_key: _EllipticCurvePublicKey,
    signature: bytes,
    data: bytes,
) -> None:
    res = backend._lib.ECDSA_verify(
        0, data, len(data), signature, len(signature), public_key._ec_key
    )
    if res != 1:
        backend._consume_errors()
        raise InvalidSignature


class _EllipticCurvePrivateKey(ec.EllipticCurvePrivateKey):
    def __init__(self, backend: Backend, ec_key_cdata, evp_pkey):
        self._backend = backend
        self._ec_key = ec_key_cdata
        self._evp_pkey = evp_pkey

        sn = _ec_key_curve_sn(backend, ec_key_cdata)
        self._curve = _sn_to_elliptic_curve(backend, sn)
        _mark_asn1_named_ec_curve(backend, ec_key_cdata)
        _check_key_infinity(backend, ec_key_cdata)

    @property
    def curve(self) -> ec.EllipticCurve:
        return self._curve

    @property
    def key_size(self) -> int:
        return self.curve.key_size

    def exchange(
        self, algorithm: ec.ECDH, peer_public_key: ec.EllipticCurvePublicKey
    ) -> bytes:
        if not (
            self._backend.elliptic_curve_exchange_algorithm_supported(
                algorithm, self.curve
            )
        ):
            raise UnsupportedAlgorithm(
                "This backend does not support the ECDH algorithm.",
                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
            )

        if peer_public_key.curve.name != self.curve.name:
            raise ValueError(
                "peer_public_key and self are not on the same curve"
            )

        return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key)

    def public_key(self) -> ec.EllipticCurvePublicKey:
        group = self._backend._lib.EC_KEY_get0_group(self._ec_key)
        self._backend.openssl_assert(group != self._backend._ffi.NULL)

        curve_nid = self._backend._lib.EC_GROUP_get_curve_name(group)
        public_ec_key = self._backend._ec_key_new_by_curve_nid(curve_nid)

        point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
        self._backend.openssl_assert(point != self._backend._ffi.NULL)

        res = self._backend._lib.EC_KEY_set_public_key(public_ec_key, point)
        self._backend.openssl_assert(res == 1)

        evp_pkey = self._backend._ec_cdata_to_evp_pkey(public_ec_key)

        return _EllipticCurvePublicKey(self._backend, public_ec_key, evp_pkey)

    def private_numbers(self) -> ec.EllipticCurvePrivateNumbers:
        bn = self._backend._lib.EC_KEY_get0_private_key(self._ec_key)
        private_value = self._backend._bn_to_int(bn)
        return ec.EllipticCurvePrivateNumbers(
            private_value=private_value,
            public_numbers=self.public_key().public_numbers(),
        )

    def private_bytes(
        self,
        encoding: serialization.Encoding,
        format: serialization.PrivateFormat,
        encryption_algorithm: serialization.KeySerializationEncryption,
    ) -> bytes:
        return self._backend._private_key_bytes(
            encoding,
            format,
            encryption_algorithm,
            self,
            self._evp_pkey,
            self._ec_key,
        )

    def sign(
        self,
        data: bytes,
        signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
    ) -> bytes:
        _check_signature_algorithm(signature_algorithm)
        data, _ = _calculate_digest_and_algorithm(
            data,
            signature_algorithm.algorithm,
        )
        return _ecdsa_sig_sign(self._backend, self, data)


class _EllipticCurvePublicKey(ec.EllipticCurvePublicKey):
    def __init__(self, backend: Backend, ec_key_cdata, evp_pkey):
        self._backend = backend
        self._ec_key = ec_key_cdata
        self._evp_pkey = evp_pkey

        sn = _ec_key_curve_sn(backend, ec_key_cdata)
        self._curve = _sn_to_elliptic_curve(backend, sn)
        _mark_asn1_named_ec_curve(backend, ec_key_cdata)
        _check_key_infinity(backend, ec_key_cdata)

    @property
    def curve(self) -> ec.EllipticCurve:
        return self._curve

    @property
    def key_size(self) -> int:
        return self.curve.key_size

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, _EllipticCurvePublicKey):
            return NotImplemented

        return (
            self._backend._lib.EVP_PKEY_cmp(self._evp_pkey, other._evp_pkey)
            == 1
        )

    def public_numbers(self) -> ec.EllipticCurvePublicNumbers:
        group = self._backend._lib.EC_KEY_get0_group(self._ec_key)
        self._backend.openssl_assert(group != self._backend._ffi.NULL)

        point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
        self._backend.openssl_assert(point != self._backend._ffi.NULL)

        with self._backend._tmp_bn_ctx() as bn_ctx:
            bn_x = self._backend._lib.BN_CTX_get(bn_ctx)
            bn_y = self._backend._lib.BN_CTX_get(bn_ctx)

            res = self._backend._lib.EC_POINT_get_affine_coordinates(
                group, point, bn_x, bn_y, bn_ctx
            )
            self._backend.openssl_assert(res == 1)

            x = self._backend._bn_to_int(bn_x)
            y = self._backend._bn_to_int(bn_y)

        return ec.EllipticCurvePublicNumbers(x=x, y=y, curve=self._curve)

    def _encode_point(self, format: serialization.PublicFormat) -> bytes:
        if format is serialization.PublicFormat.CompressedPoint:
            conversion = self._backend._lib.POINT_CONVERSION_COMPRESSED
        else:
            assert format is serialization.PublicFormat.UncompressedPoint
            conversion = self._backend._lib.POINT_CONVERSION_UNCOMPRESSED

        group = self._backend._lib.EC_KEY_get0_group(self._ec_key)
        self._backend.openssl_assert(group != self._backend._ffi.NULL)
        point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key)
        self._backend.openssl_assert(point != self._backend._ffi.NULL)
        with self._backend._tmp_bn_ctx() as bn_ctx:
            buflen = self._backend._lib.EC_POINT_point2oct(
                group, point, conversion, self._backend._ffi.NULL, 0, bn_ctx
            )
            self._backend.openssl_assert(buflen > 0)
            buf = self._backend._ffi.new("char[]", buflen)
            res = self._backend._lib.EC_POINT_point2oct(
                group, point, conversion, buf, buflen, bn_ctx
            )
            self._backend.openssl_assert(buflen == res)

        return self._backend._ffi.buffer(buf)[:]

    def public_bytes(
        self,
        encoding: serialization.Encoding,
        format: serialization.PublicFormat,
    ) -> bytes:
        if (
            encoding is serialization.Encoding.X962
            or format is serialization.PublicFormat.CompressedPoint
            or format is serialization.PublicFormat.UncompressedPoint
        ):
            if encoding is not serialization.Encoding.X962 or format not in (
                serialization.PublicFormat.CompressedPoint,
                serialization.PublicFormat.UncompressedPoint,
            ):
                raise ValueError(
                    "X962 encoding must be used with CompressedPoint or "
                    "UncompressedPoint format"
                )

            return self._encode_point(format)
        else:
            return self._backend._public_key_bytes(
                encoding, format, self, self._evp_pkey, None
            )

    def verify(
        self,
        signature: bytes,
        data: bytes,
        signature_algorithm: ec.EllipticCurveSignatureAlgorithm,
    ) -> None:
        _check_signature_algorithm(signature_algorithm)
        data, _ = _calculate_digest_and_algorithm(
            data,
            signature_algorithm.algorithm,
        )
        _ecdsa_sig_verify(self._backend, self, signature, data)

back/venvmsg/Lib/site-packages/cryptography/hazmat/backends/openssl/ec.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import threading
import typing

from cryptography.exceptions import (
    InvalidSignature,
    UnsupportedAlgorithm,
    _Reasons,
)
from cryptography.hazmat.backends.openssl.utils import (
    _calculate_digest_and_algorithm,
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
from cryptography.hazmat.primitives.asymmetric.padding import (
    MGF1,
    OAEP,
    PSS,
    AsymmetricPadding,
    PKCS1v15,
    _Auto,
    _DigestLength,
    _MaxLength,
    calculate_max_pss_salt_length,
)
from cryptography.hazmat.primitives.asymmetric.rsa import (
    RSAPrivateKey,
    RSAPrivateNumbers,
    RSAPublicKey,
    RSAPublicNumbers,
)

if typing.TYPE_CHECKING:
    from cryptography.hazmat.backends.openssl.backend import Backend


def _get_rsa_pss_salt_length(
    backend: Backend,
    pss: PSS,
    key: typing.Union[RSAPrivateKey, RSAPublicKey],
    hash_algorithm: hashes.HashAlgorithm,
) -> int:
    salt = pss._salt_length

    if isinstance(salt, _MaxLength):
        return calculate_max_pss_salt_length(key, hash_algorithm)
    elif isinstance(salt, _DigestLength):
        return hash_algorithm.digest_size
    elif isinstance(salt, _Auto):
        if isinstance(key, RSAPrivateKey):
            raise ValueError(
                "PSS salt length can only be set to AUTO when verifying"
            )
        return backend._lib.RSA_PSS_SALTLEN_AUTO
    else:
        return salt


def _enc_dec_rsa(
    backend: Backend,
    key: typing.Union[_RSAPrivateKey, _RSAPublicKey],
    data: bytes,
    padding: AsymmetricPadding,
) -> bytes:
    if not isinstance(padding, AsymmetricPadding):
        raise TypeError("Padding must be an instance of AsymmetricPadding.")

    if isinstance(padding, PKCS1v15):
        padding_enum = backend._lib.RSA_PKCS1_PADDING
    elif isinstance(padding, OAEP):
        padding_enum = backend._lib.RSA_PKCS1_OAEP_PADDING

        if not isinstance(padding._mgf, MGF1):
            raise UnsupportedAlgorithm(
                "Only MGF1 is supported by this backend.",
                _Reasons.UNSUPPORTED_MGF,
            )

        if not backend.rsa_padding_supported(padding):
            raise UnsupportedAlgorithm(
                "This combination of padding and hash algorithm is not "
                "supported by this backend.",
                _Reasons.UNSUPPORTED_PADDING,
            )

    else:
        raise UnsupportedAlgorithm(
            f"{padding.name} is not supported by this backend.",
            _Reasons.UNSUPPORTED_PADDING,
        )

    return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding)


def _enc_dec_rsa_pkey_ctx(
    backend: Backend,
    key: typing.Union[_RSAPrivateKey, _RSAPublicKey],
    data: bytes,
    padding_enum: int,
    padding: AsymmetricPadding,
) -> bytes:
    init: typing.Callable[[typing.Any], int]
    crypt: typing.Callable[[typing.Any, typing.Any, int, bytes, int], int]
    if isinstance(key, _RSAPublicKey):
        init = backend._lib.EVP_PKEY_encrypt_init
        crypt = backend._lib.EVP_PKEY_encrypt
    else:
        init = backend._lib.EVP_PKEY_decrypt_init
        crypt = backend._lib.EVP_PKEY_decrypt

    pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL)
    backend.openssl_assert(pkey_ctx != backend._ffi.NULL)
    pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free)
    res = init(pkey_ctx)
    backend.openssl_assert(res == 1)
    res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
    backend.openssl_assert(res > 0)
    buf_size = backend._lib.EVP_PKEY_size(key._evp_pkey)
    backend.openssl_assert(buf_size > 0)
    if isinstance(padding, OAEP):
        mgf1_md = backend._evp_md_non_null_from_algorithm(
            padding._mgf._algorithm
        )
        res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
        backend.openssl_assert(res > 0)
        oaep_md = backend._evp_md_non_null_from_algorithm(padding._algorithm)
        res = backend._lib.EVP_PKEY_CTX_set_rsa_oaep_md(pkey_ctx, oaep_md)
        backend.openssl_assert(res > 0)

    if (
        isinstance(padding, OAEP)
        and padding._label is not None
        and len(padding._label) > 0
    ):
        # set0_rsa_oaep_label takes ownership of the char * so we need to
        # copy it into some new memory
        labelptr = backend._lib.OPENSSL_malloc(len(padding._label))
        backend.openssl_assert(labelptr != backend._ffi.NULL)
        backend._ffi.memmove(labelptr, padding._label, len(padding._label))
        res = backend._lib.EVP_PKEY_CTX_set0_rsa_oaep_label(
            pkey_ctx, labelptr, len(padding._label)
        )
        backend.openssl_assert(res == 1)

    outlen = backend._ffi.new("size_t *", buf_size)
    buf = backend._ffi.new("unsigned char[]", buf_size)
    # Everything from this line onwards is written with the goal of being as
    # constant-time as is practical given the constraints of Python and our
    # API. See Bleichenbacher's '98 attack on RSA, and its many many variants.
    # As such, you should not attempt to change this (particularly to "clean it
    # up") without understanding why it was written this way (see
    # Chesterton's Fence), and without measuring to verify you have not
    # introduced observable time differences.
    res = crypt(pkey_ctx, buf, outlen, data, len(data))
    resbuf = backend._ffi.buffer(buf)[: outlen[0]]
    backend._lib.ERR_clear_error()
    if res <= 0:
        raise ValueError("Encryption/decryption failed.")
    return resbuf


def _rsa_sig_determine_padding(
    backend: Backend,
    key: typing.Union[_RSAPrivateKey, _RSAPublicKey],
    padding: AsymmetricPadding,
    algorithm: typing.Optional[hashes.HashAlgorithm],
) -> int:
    if not isinstance(padding, AsymmetricPadding):
        raise TypeError("Expected provider of AsymmetricPadding.")

    pkey_size = backend._lib.EVP_PKEY_size(key._evp_pkey)
    backend.openssl_assert(pkey_size > 0)

    if isinstance(padding, PKCS1v15):
        # Hash algorithm is ignored for PKCS1v15-padding, may be None.
        padding_enum = backend._lib.RSA_PKCS1_PADDING
    elif isinstance(padding, PSS):
        if not isinstance(padding._mgf, MGF1):
            raise UnsupportedAlgorithm(
                "Only MGF1 is supported by this backend.",
                _Reasons.UNSUPPORTED_MGF,
            )

        # PSS padding requires a hash algorithm
        if not isinstance(algorithm, hashes.HashAlgorithm):
            raise TypeError("Expected instance of hashes.HashAlgorithm.")

        # Size of key in bytes - 2 is the maximum
        # PSS signature length (salt length is checked later)
        if pkey_size - algorithm.digest_size - 2 < 0:
            raise ValueError(
                "Digest too large for key size. Use a larger "
                "key or different digest."
            )

        padding_enum = backend._lib.RSA_PKCS1_PSS_PADDING
    else:
        raise UnsupportedAlgorithm(
            f"{padding.name} is not supported by this backend.",
            _Reasons.UNSUPPORTED_PADDING,
        )

    return padding_enum


# Hash algorithm can be absent (None) to initialize the context without setting
# any message digest algorithm. This is currently only valid for the PKCS1v15
# padding type, where it means that the signature data is encoded/decoded
# as provided, without being wrapped in a DigestInfo structure.
def _rsa_sig_setup(
    backend: Backend,
    padding: AsymmetricPadding,
    algorithm: typing.Optional[hashes.HashAlgorithm],
    key: typing.Union[_RSAPublicKey, _RSAPrivateKey],
    init_func: typing.Callable[[typing.Any], int],
):
    padding_enum = _rsa_sig_determine_padding(backend, key, padding, algorithm)
    pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL)
    backend.openssl_assert(pkey_ctx != backend._ffi.NULL)
    pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free)
    res = init_func(pkey_ctx)
    if res != 1:
        errors = backend._consume_errors()
        raise ValueError("Unable to sign/verify with this key", errors)

    if algorithm is not None:
        evp_md = backend._evp_md_non_null_from_algorithm(algorithm)
        res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md)
        if res <= 0:
            backend._consume_errors()
            raise UnsupportedAlgorithm(
                "{} is not supported by this backend for RSA signing.".format(
                    algorithm.name
                ),
                _Reasons.UNSUPPORTED_HASH,
            )
    res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum)
    if res <= 0:
        backend._consume_errors()
        raise UnsupportedAlgorithm(
            "{} is not supported for the RSA signature operation.".format(
                padding.name
            ),
            _Reasons.UNSUPPORTED_PADDING,
        )
    if isinstance(padding, PSS):
        assert isinstance(algorithm, hashes.HashAlgorithm)
        res = backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen(
            pkey_ctx,
            _get_rsa_pss_salt_length(backend, padding, key, algorithm),
        )
        backend.openssl_assert(res > 0)

        mgf1_md = backend._evp_md_non_null_from_algorithm(
            padding._mgf._algorithm
        )
        res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md)
        backend.openssl_assert(res > 0)

    return pkey_ctx


def _rsa_sig_sign(
    backend: Backend,
    padding: AsymmetricPadding,
    algorithm: hashes.HashAlgorithm,
    private_key: _RSAPrivateKey,
    data: bytes,
) -> bytes:
    pkey_ctx = _rsa_sig_setup(
        backend,
        padding,
        algorithm,
        private_key,
        backend._lib.EVP_PKEY_sign_init,
    )
    buflen = backend._ffi.new("size_t *")
    res = backend._lib.EVP_PKEY_sign(
        pkey_ctx, backend._ffi.NULL, buflen, data, len(data)
    )
    backend.openssl_assert(res == 1)
    buf = backend._ffi.new("unsigned char[]", buflen[0])
    res = backend._lib.EVP_PKEY_sign(pkey_ctx, buf, buflen, data, len(data))
    if res != 1:
        errors = backend._consume_errors()
        raise ValueError(
            "Digest or salt length too long for key size. Use a larger key "
            "or shorter salt length if you are specifying a PSS salt",
            errors,
        )

    return backend._ffi.buffer(buf)[:]


def _rsa_sig_verify(
    backend: Backend,
    padding: AsymmetricPadding,
    algorithm: hashes.HashAlgorithm,
    public_key: _RSAPublicKey,
    signature: bytes,
    data: bytes,
) -> None:
    pkey_ctx = _rsa_sig_setup(
        backend,
        padding,
        algorithm,
        public_key,
        backend._lib.EVP_PKEY_verify_init,
    )
    res = backend._lib.EVP_PKEY_verify(
        pkey_ctx, signature, len(signature), data, len(data)
    )
    # The previous call can return negative numbers in the event of an
    # error. This is not a signature failure but we need to fail if it
    # occurs.
    backend.openssl_assert(res >= 0)
    if res == 0:
        backend._consume_errors()
        raise InvalidSignature


def _rsa_sig_recover(
    backend: Backend,
    padding: AsymmetricPadding,
    algorithm: typing.Optional[hashes.HashAlgorithm],
    public_key: _RSAPublicKey,
    signature: bytes,
) -> bytes:
    pkey_ctx = _rsa_sig_setup(
        backend,
        padding,
        algorithm,
        public_key,
        backend._lib.EVP_PKEY_verify_recover_init,
    )

    # Attempt to keep the rest of the code in this function as constant/time
    # as possible. See the comment in _enc_dec_rsa_pkey_ctx. Note that the
    # buflen parameter is used even though its value may be undefined in the
    # error case. Due to the tolerant nature of Python slicing this does not
    # trigger any exceptions.
    maxlen = backend._lib.EVP_PKEY_size(public_key._evp_pkey)
    backend.openssl_assert(maxlen > 0)
    buf = backend._ffi.new("unsigned char[]", maxlen)
    buflen = backend._ffi.new("size_t *", maxlen)
    res = backend._lib.EVP_PKEY_verify_recover(
        pkey_ctx, buf, buflen, signature, len(signature)
    )
    resbuf = backend._ffi.buffer(buf)[: buflen[0]]
    backend._lib.ERR_clear_error()
    # Assume that all parameter errors are handled during the setup phase and
    # any error here is due to invalid signature.
    if res != 1:
        raise InvalidSignature
    return resbuf


class _RSAPrivateKey(RSAPrivateKey):
    _evp_pkey: object
    _rsa_cdata: object
    _key_size: int

    def __init__(
        self,
        backend: Backend,
        rsa_cdata,
        evp_pkey,
        *,
        unsafe_skip_rsa_key_validation: bool,
    ):
        res: int
        # RSA_check_key is slower in OpenSSL 3.0.0 due to improved
        # primality checking. In normal use this is unlikely to be a problem
        # since users don't load new keys constantly, but for TESTING we've
        # added an init arg that allows skipping the checks. You should not
        # use this in production code unless you understand the consequences.
        if not unsafe_skip_rsa_key_validation:
            res = backend._lib.RSA_check_key(rsa_cdata)
            if res != 1:
                errors = backend._consume_errors()
                raise ValueError("Invalid private key", errors)
            # 2 is prime and passes an RSA key check, so we also check
            # if p and q are odd just to be safe.
            p = backend._ffi.new("BIGNUM **")
            q = backend._ffi.new("BIGNUM **")
            backend._lib.RSA_get0_factors(rsa_cdata, p, q)
            backend.openssl_assert(p[0] != backend._ffi.NULL)
            backend.openssl_assert(q[0] != backend._ffi.NULL)
            p_odd = backend._lib.BN_is_odd(p[0])
            q_odd = backend._lib.BN_is_odd(q[0])
            if p_odd != 1 or q_odd != 1:
                errors = backend._consume_errors()
                raise ValueError("Invalid private key", errors)

        self._backend = backend
        self._rsa_cdata = rsa_cdata
        self._evp_pkey = evp_pkey
        # Used for lazy blinding
        self._blinded = False
        self._blinding_lock = threading.Lock()

        n = self._backend._ffi.new("BIGNUM **")
        self._backend._lib.RSA_get0_key(
            self._rsa_cdata,
            n,
            self._backend._ffi.NULL,
            self._backend._ffi.NULL,
        )
        self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
        self._key_size = self._backend._lib.BN_num_bits(n[0])

    def _enable_blinding(self) -> None:
        # If you call blind on an already blinded RSA key OpenSSL will turn
        # it off and back on, which is a performance hit we want to avoid.
        if not self._blinded:
            with self._blinding_lock:
                self._non_threadsafe_enable_blinding()

    def _non_threadsafe_enable_blinding(self) -> None:
        # This is only a separate function to allow for testing to cover both
        # branches. It should never be invoked except through _enable_blinding.
        # Check if it's not True again in case another thread raced past the
        # first non-locked check.
        if not self._blinded:
            res = self._backend._lib.RSA_blinding_on(
                self._rsa_cdata, self._backend._ffi.NULL
            )
            self._backend.openssl_assert(res == 1)
            self._blinded = True

    @property
    def key_size(self) -> int:
        return self._key_size

    def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes:
        self._enable_blinding()
        key_size_bytes = (self.key_size + 7) // 8
        if key_size_bytes != len(ciphertext):
            raise ValueError("Ciphertext length must be equal to key size.")

        return _enc_dec_rsa(self._backend, self, ciphertext, padding)

    def public_key(self) -> RSAPublicKey:
        ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata)
        self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
        ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free)
        evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx)
        return _RSAPublicKey(self._backend, ctx, evp_pkey)

    def private_numbers(self) -> RSAPrivateNumbers:
        n = self._backend._ffi.new("BIGNUM **")
        e = self._backend._ffi.new("BIGNUM **")
        d = self._backend._ffi.new("BIGNUM **")
        p = self._backend._ffi.new("BIGNUM **")
        q = self._backend._ffi.new("BIGNUM **")
        dmp1 = self._backend._ffi.new("BIGNUM **")
        dmq1 = self._backend._ffi.new("BIGNUM **")
        iqmp = self._backend._ffi.new("BIGNUM **")
        self._backend._lib.RSA_get0_key(self._rsa_cdata, n, e, d)
        self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
        self._backend.openssl_assert(e[0] != self._backend._ffi.NULL)
        self._backend.openssl_assert(d[0] != self._backend._ffi.NULL)
        self._backend._lib.RSA_get0_factors(self._rsa_cdata, p, q)
        self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
        self._backend.openssl_assert(q[0] != self._backend._ffi.NULL)
        self._backend._lib.RSA_get0_crt_params(
            self._rsa_cdata, dmp1, dmq1, iqmp
        )
        self._backend.openssl_assert(dmp1[0] != self._backend._ffi.NULL)
        self._backend.openssl_assert(dmq1[0] != self._backend._ffi.NULL)
        self._backend.openssl_assert(iqmp[0] != self._backend._ffi.NULL)
        return RSAPrivateNumbers(
            p=self._backend._bn_to_int(p[0]),
            q=self._backend._bn_to_int(q[0]),
            d=self._backend._bn_to_int(d[0]),
            dmp1=self._backend._bn_to_int(dmp1[0]),
            dmq1=self._backend._bn_to_int(dmq1[0]),
            iqmp=self._backend._bn_to_int(iqmp[0]),
            public_numbers=RSAPublicNumbers(
                e=self._backend._bn_to_int(e[0]),
                n=self._backend._bn_to_int(n[0]),
            ),
        )

    def private_bytes(
        self,
        encoding: serialization.Encoding,
        format: serialization.PrivateFormat,
        encryption_algorithm: serialization.KeySerializationEncryption,
    ) -> bytes:
        return self._backend._private_key_bytes(
            encoding,
            format,
            encryption_algorithm,
            self,
            self._evp_pkey,
            self._rsa_cdata,
        )

    def sign(
        self,
        data: bytes,
        padding: AsymmetricPadding,
        algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
    ) -> bytes:
        self._enable_blinding()
        data, algorithm = _calculate_digest_and_algorithm(data, algorithm)
        return _rsa_sig_sign(self._backend, padding, algorithm, self, data)


class _RSAPublicKey(RSAPublicKey):
    _evp_pkey: object
    _rsa_cdata: object
    _key_size: int

    def __init__(self, backend: Backend, rsa_cdata, evp_pkey):
        self._backend = backend
        self._rsa_cdata = rsa_cdata
        self._evp_pkey = evp_pkey

        n = self._backend._ffi.new("BIGNUM **")
        self._backend._lib.RSA_get0_key(
            self._rsa_cdata,
            n,
            self._backend._ffi.NULL,
            self._backend._ffi.NULL,
        )
        self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
        self._key_size = self._backend._lib.BN_num_bits(n[0])

    @property
    def key_size(self) -> int:
        return self._key_size

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, _RSAPublicKey):
            return NotImplemented

        return (
            self._backend._lib.EVP_PKEY_cmp(self._evp_pkey, other._evp_pkey)
            == 1
        )

    def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes:
        return _enc_dec_rsa(self._backend, self, plaintext, padding)

    def public_numbers(self) -> RSAPublicNumbers:
        n = self._backend._ffi.new("BIGNUM **")
        e = self._backend._ffi.new("BIGNUM **")
        self._backend._lib.RSA_get0_key(
            self._rsa_cdata, n, e, self._backend._ffi.NULL
        )
        self._backend.openssl_assert(n[0] != self._backend._ffi.NULL)
        self._backend.openssl_assert(e[0] != self._backend._ffi.NULL)
        return RSAPublicNumbers(
            e=self._backend._bn_to_int(e[0]),
            n=self._backend._bn_to_int(n[0]),
        )

    def public_bytes(
        self,
        encoding: serialization.Encoding,
        format: serialization.PublicFormat,
    ) -> bytes:
        return self._backend._public_key_bytes(
            encoding, format, self, self._evp_pkey, self._rsa_cdata
        )

    def verify(
        self,
        signature: bytes,
        data: bytes,
        padding: AsymmetricPadding,
        algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
    ) -> None:
        data, algorithm = _calculate_digest_and_algorithm(data, algorithm)
        _rsa_sig_verify(
            self._backend, padding, algorithm, self, signature, data
        )

    def recover_data_from_signature(
        self,
        signature: bytes,
        padding: AsymmetricPadding,
        algorithm: typing.Optional[hashes.HashAlgorithm],
    ) -> bytes:
        if isinstance(algorithm, asym_utils.Prehashed):
            raise TypeError(
                "Prehashed is only supported in the sign and verify methods. "
                "It cannot be used with recover_data_from_signature."
            )
        return _rsa_sig_recover(
            self._backend, padding, algorithm, self, signature
        )

back/venvmsg/Lib/site-packages/cryptography/hazmat/backends/openssl/rsa.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed

if typing.TYPE_CHECKING:
    from cryptography.hazmat.backends.openssl.backend import Backend


def _evp_pkey_derive(backend: Backend, evp_pkey, peer_public_key) -> bytes:
    ctx = backend._lib.EVP_PKEY_CTX_new(evp_pkey, backend._ffi.NULL)
    backend.openssl_assert(ctx != backend._ffi.NULL)
    ctx = backend._ffi.gc(ctx, backend._lib.EVP_PKEY_CTX_free)
    res = backend._lib.EVP_PKEY_derive_init(ctx)
    backend.openssl_assert(res == 1)

    if backend._lib.Cryptography_HAS_EVP_PKEY_SET_PEER_EX:
        res = backend._lib.EVP_PKEY_derive_set_peer_ex(
            ctx, peer_public_key._evp_pkey, 0
        )
    else:
        res = backend._lib.EVP_PKEY_derive_set_peer(
            ctx, peer_public_key._evp_pkey
        )
    backend.openssl_assert(res == 1)

    keylen = backend._ffi.new("size_t *")
    res = backend._lib.EVP_PKEY_derive(ctx, backend._ffi.NULL, keylen)
    backend.openssl_assert(res == 1)
    backend.openssl_assert(keylen[0] > 0)
    buf = backend._ffi.new("unsigned char[]", keylen[0])
    res = backend._lib.EVP_PKEY_derive(ctx, buf, keylen)
    if res != 1:
        errors = backend._consume_errors()
        raise ValueError("Error computing shared key.", errors)

    return backend._ffi.buffer(buf, keylen[0])[:]


def _calculate_digest_and_algorithm(
    data: bytes,
    algorithm: typing.Union[Prehashed, hashes.HashAlgorithm],
) -> typing.Tuple[bytes, hashes.HashAlgorithm]:
    if not isinstance(algorithm, Prehashed):
        hash_ctx = hashes.Hash(algorithm)
        hash_ctx.update(data)
        data = hash_ctx.finalize()
    else:
        algorithm = algorithm._algorithm

    if len(data) != algorithm.digest_size:
        raise ValueError(
            "The provided data must be the same length as the hash "
            "algorithm's digest size."
        )

    return (data, algorithm)

back/venvmsg/Lib/site-packages/cryptography/hazmat/backends/openssl/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing


def cryptography_has_set_cert_cb() -> typing.List[str]:
    return [
        "SSL_CTX_set_cert_cb",
        "SSL_set_cert_cb",
    ]


def cryptography_has_ssl_st() -> typing.List[str]:
    return [
        "SSL_ST_BEFORE",
        "SSL_ST_OK",
        "SSL_ST_INIT",
        "SSL_ST_RENEGOTIATE",
    ]


def cryptography_has_tls_st() -> typing.List[str]:
    return [
        "TLS_ST_BEFORE",
        "TLS_ST_OK",
    ]


def cryptography_has_evp_pkey_dhx() -> typing.List[str]:
    return [
        "EVP_PKEY_DHX",
    ]


def cryptography_has_mem_functions() -> typing.List[str]:
    return [
        "Cryptography_CRYPTO_set_mem_functions",
    ]


def cryptography_has_x509_store_ctx_get_issuer() -> typing.List[str]:
    return [
        "X509_STORE_set_get_issuer",
    ]


def cryptography_has_ed448() -> typing.List[str]:
    return [
        "EVP_PKEY_ED448",
        "NID_ED448",
    ]


def cryptography_has_ed25519() -> typing.List[str]:
    return [
        "NID_ED25519",
        "EVP_PKEY_ED25519",
    ]


def cryptography_has_poly1305() -> typing.List[str]:
    return [
        "NID_poly1305",
        "EVP_PKEY_POLY1305",
    ]


def cryptography_has_evp_digestfinal_xof() -> typing.List[str]:
    return [
        "EVP_DigestFinalXOF",
    ]


def cryptography_has_fips() -> typing.List[str]:
    return [
        "FIPS_mode_set",
        "FIPS_mode",
    ]


def cryptography_has_ssl_sigalgs() -> typing.List[str]:
    return [
        "SSL_CTX_set1_sigalgs_list",
    ]


def cryptography_has_psk() -> typing.List[str]:
    return [
        "SSL_CTX_use_psk_identity_hint",
        "SSL_CTX_set_psk_server_callback",
        "SSL_CTX_set_psk_client_callback",
    ]


def cryptography_has_psk_tlsv13() -> typing.List[str]:
    return [
        "SSL_CTX_set_psk_find_session_callback",
        "SSL_CTX_set_psk_use_session_callback",
        "Cryptography_SSL_SESSION_new",
        "SSL_CIPHER_find",
        "SSL_SESSION_set1_master_key",
        "SSL_SESSION_set_cipher",
        "SSL_SESSION_set_protocol_version",
    ]


def cryptography_has_custom_ext() -> typing.List[str]:
    return [
        "SSL_CTX_add_client_custom_ext",
        "SSL_CTX_add_server_custom_ext",
        "SSL_extension_supported",
    ]


def cryptography_has_tlsv13_functions() -> typing.List[str]:
    return [
        "SSL_VERIFY_POST_HANDSHAKE",
        "SSL_CTX_set_ciphersuites",
        "SSL_verify_client_post_handshake",
        "SSL_CTX_set_post_handshake_auth",
        "SSL_set_post_handshake_auth",
        "SSL_SESSION_get_max_early_data",
        "SSL_write_early_data",
        "SSL_read_early_data",
        "SSL_CTX_set_max_early_data",
    ]


def cryptography_has_raw_key() -> typing.List[str]:
    return [
        "EVP_PKEY_new_raw_private_key",
        "EVP_PKEY_new_raw_public_key",
        "EVP_PKEY_get_raw_private_key",
        "EVP_PKEY_get_raw_public_key",
    ]


def cryptography_has_engine() -> typing.List[str]:
    return [
        "ENGINE_by_id",
        "ENGINE_init",
        "ENGINE_finish",
        "ENGINE_get_default_RAND",
        "ENGINE_set_default_RAND",
        "ENGINE_unregister_RAND",
        "ENGINE_ctrl_cmd",
        "ENGINE_free",
        "ENGINE_get_name",
        "ENGINE_ctrl_cmd_string",
        "ENGINE_load_builtin_engines",
        "ENGINE_load_private_key",
        "ENGINE_load_public_key",
        "SSL_CTX_set_client_cert_engine",
    ]


def cryptography_has_verified_chain() -> typing.List[str]:
    return [
        "SSL_get0_verified_chain",
    ]


def cryptography_has_srtp() -> typing.List[str]:
    return [
        "SSL_CTX_set_tlsext_use_srtp",
        "SSL_set_tlsext_use_srtp",
        "SSL_get_selected_srtp_profile",
    ]


def cryptography_has_providers() -> typing.List[str]:
    return [
        "OSSL_PROVIDER_load",
        "OSSL_PROVIDER_unload",
        "ERR_LIB_PROV",
        "PROV_R_WRONG_FINAL_BLOCK_LENGTH",
        "PROV_R_BAD_DECRYPT",
    ]


def cryptography_has_op_no_renegotiation() -> typing.List[str]:
    return [
        "SSL_OP_NO_RENEGOTIATION",
    ]


def cryptography_has_dtls_get_data_mtu() -> typing.List[str]:
    return [
        "DTLS_get_data_mtu",
    ]


def cryptography_has_300_fips() -> typing.List[str]:
    return [
        "EVP_default_properties_is_fips_enabled",
        "EVP_default_properties_enable_fips",
    ]


def cryptography_has_ssl_cookie() -> typing.List[str]:
    return [
        "SSL_OP_COOKIE_EXCHANGE",
        "DTLSv1_listen",
        "SSL_CTX_set_cookie_generate_cb",
        "SSL_CTX_set_cookie_verify_cb",
    ]


def cryptography_has_pkcs7_funcs() -> typing.List[str]:
    return [
        "SMIME_write_PKCS7",
        "PEM_write_bio_PKCS7_stream",
        "PKCS7_sign_add_signer",
        "PKCS7_final",
        "PKCS7_verify",
        "SMIME_read_PKCS7",
        "PKCS7_get0_signers",
    ]


def cryptography_has_bn_flags() -> typing.List[str]:
    return [
        "BN_FLG_CONSTTIME",
        "BN_set_flags",
        "BN_prime_checks_for_size",
    ]


def cryptography_has_evp_pkey_dh() -> typing.List[str]:
    return [
        "EVP_PKEY_set1_DH",
    ]


def cryptography_has_300_evp_cipher() -> typing.List[str]:
    return ["EVP_CIPHER_fetch", "EVP_CIPHER_free"]


def cryptography_has_unexpected_eof_while_reading() -> typing.List[str]:
    return ["SSL_R_UNEXPECTED_EOF_WHILE_READING"]


def cryptography_has_pkcs12_set_mac() -> typing.List[str]:
    return ["PKCS12_set_mac"]


def cryptography_has_ssl_op_ignore_unexpected_eof() -> typing.List[str]:
    return [
        "SSL_OP_IGNORE_UNEXPECTED_EOF",
    ]


def cryptography_has_get_extms_support() -> typing.List[str]:
    return ["SSL_get_extms_support"]


def cryptography_has_evp_pkey_set_peer_ex() -> typing.List[str]:
    return ["EVP_PKEY_derive_set_peer_ex"]


def cryptography_has_evp_aead() -> typing.List[str]:
    return [
        "EVP_aead_chacha20_poly1305",
        "EVP_AEAD_CTX_free",
        "EVP_AEAD_CTX_seal",
        "EVP_AEAD_CTX_open",
        "EVP_AEAD_max_overhead",
        "Cryptography_EVP_AEAD_CTX_new",
    ]


# This is a mapping of
# {condition: function-returning-names-dependent-on-that-condition} so we can
# loop over them and delete unsupported names at runtime. It will be removed
# when cffi supports #if in cdef. We use functions instead of just a dict of
# lists so we can use coverage to measure which are used.
CONDITIONAL_NAMES = {
    "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb,
    "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st,
    "Cryptography_HAS_TLS_ST": cryptography_has_tls_st,
    "Cryptography_HAS_EVP_PKEY_DHX": cryptography_has_evp_pkey_dhx,
    "Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions,
    "Cryptography_HAS_X509_STORE_CTX_GET_ISSUER": (
        cryptography_has_x509_store_ctx_get_issuer
    ),
    "Cryptography_HAS_ED448": cryptography_has_ed448,
    "Cryptography_HAS_ED25519": cryptography_has_ed25519,
    "Cryptography_HAS_POLY1305": cryptography_has_poly1305,
    "Cryptography_HAS_FIPS": cryptography_has_fips,
    "Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs,
    "Cryptography_HAS_PSK": cryptography_has_psk,
    "Cryptography_HAS_PSK_TLSv1_3": cryptography_has_psk_tlsv13,
    "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext,
    "Cryptography_HAS_TLSv1_3_FUNCTIONS": cryptography_has_tlsv13_functions,
    "Cryptography_HAS_RAW_KEY": cryptography_has_raw_key,
    "Cryptography_HAS_EVP_DIGESTFINAL_XOF": (
        cryptography_has_evp_digestfinal_xof
    ),
    "Cryptography_HAS_ENGINE": cryptography_has_engine,
    "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain,
    "Cryptography_HAS_SRTP": cryptography_has_srtp,
    "Cryptography_HAS_PROVIDERS": cryptography_has_providers,
    "Cryptography_HAS_OP_NO_RENEGOTIATION": (
        cryptography_has_op_no_renegotiation
    ),
    "Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu,
    "Cryptography_HAS_300_FIPS": cryptography_has_300_fips,
    "Cryptography_HAS_SSL_COOKIE": cryptography_has_ssl_cookie,
    "Cryptography_HAS_PKCS7_FUNCS": cryptography_has_pkcs7_funcs,
    "Cryptography_HAS_BN_FLAGS": cryptography_has_bn_flags,
    "Cryptography_HAS_EVP_PKEY_DH": cryptography_has_evp_pkey_dh,
    "Cryptography_HAS_300_EVP_CIPHER": cryptography_has_300_evp_cipher,
    "Cryptography_HAS_UNEXPECTED_EOF_WHILE_READING": (
        cryptography_has_unexpected_eof_while_reading
    ),
    "Cryptography_HAS_PKCS12_SET_MAC": cryptography_has_pkcs12_set_mac,
    "Cryptography_HAS_SSL_OP_IGNORE_UNEXPECTED_EOF": (
        cryptography_has_ssl_op_ignore_unexpected_eof
    ),
    "Cryptography_HAS_GET_EXTMS_SUPPORT": cryptography_has_get_extms_support,
    "Cryptography_HAS_EVP_PKEY_SET_PEER_EX": (
        cryptography_has_evp_pkey_set_peer_ex
    ),
    "Cryptography_HAS_EVP_AEAD": (cryptography_has_evp_aead),
}

back/venvmsg/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import os
import sys
import threading
import types
import typing
import warnings

import cryptography
from cryptography.exceptions import InternalError
from cryptography.hazmat.bindings._rust import _openssl, openssl
from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES


def _openssl_assert(
    lib,
    ok: bool,
    errors: typing.Optional[typing.List[openssl.OpenSSLError]] = None,
) -> None:
    if not ok:
        if errors is None:
            errors = openssl.capture_error_stack()

        raise InternalError(
            "Unknown OpenSSL error. This error is commonly encountered when "
            "another library is not cleaning up the OpenSSL error stack. If "
            "you are using cryptography with another library that uses "
            "OpenSSL try disabling it before reporting a bug. Otherwise "
            "please file an issue at https://github.com/pyca/cryptography/"
            "issues with information on how to reproduce "
            "this. ({!r})".format(errors),
            errors,
        )


def _legacy_provider_error(loaded: bool) -> None:
    if not loaded:
        raise RuntimeError(
            "OpenSSL 3.0's legacy provider failed to load. This is a fatal "
            "error by default, but cryptography supports running without "
            "legacy algorithms by setting the environment variable "
            "CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this error,"
            " you have likely made a mistake with your OpenSSL configuration."
        )


def build_conditional_library(
    lib: typing.Any,
    conditional_names: typing.Dict[str, typing.Callable[[], typing.List[str]]],
) -> typing.Any:
    conditional_lib = types.ModuleType("lib")
    conditional_lib._original_lib = lib  # type: ignore[attr-defined]
    excluded_names = set()
    for condition, names_cb in conditional_names.items():
        if not getattr(lib, condition):
            excluded_names.update(names_cb())

    for attr in dir(lib):
        if attr not in excluded_names:
            setattr(conditional_lib, attr, getattr(lib, attr))

    return conditional_lib


class Binding:
    """
    OpenSSL API wrapper.
    """

    lib: typing.ClassVar = None
    ffi = _openssl.ffi
    _lib_loaded = False
    _init_lock = threading.Lock()
    _legacy_provider: typing.Any = ffi.NULL
    _legacy_provider_loaded = False
    _default_provider: typing.Any = ffi.NULL

    def __init__(self) -> None:
        self._ensure_ffi_initialized()

    def _enable_fips(self) -> None:
        # This function enables FIPS mode for OpenSSL 3.0.0 on installs that
        # have the FIPS provider installed properly.
        _openssl_assert(self.lib, self.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)
        self._base_provider = self.lib.OSSL_PROVIDER_load(
            self.ffi.NULL, b"base"
        )
        _openssl_assert(self.lib, self._base_provider != self.ffi.NULL)
        self.lib._fips_provider = self.lib.OSSL_PROVIDER_load(
            self.ffi.NULL, b"fips"
        )
        _openssl_assert(self.lib, self.lib._fips_provider != self.ffi.NULL)

        res = self.lib.EVP_default_properties_enable_fips(self.ffi.NULL, 1)
        _openssl_assert(self.lib, res == 1)

    @classmethod
    def _ensure_ffi_initialized(cls) -> None:
        with cls._init_lock:
            if not cls._lib_loaded:
                cls.lib = build_conditional_library(
                    _openssl.lib, CONDITIONAL_NAMES
                )
                cls._lib_loaded = True
                # As of OpenSSL 3.0.0 we must register a legacy cipher provider
                # to get RC2 (needed for junk asymmetric private key
                # serialization), RC4, Blowfish, IDEA, SEED, etc. These things
                # are ugly legacy, but we aren't going to get rid of them
                # any time soon.
                if cls.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
                    if not os.environ.get("CRYPTOGRAPHY_OPENSSL_NO_LEGACY"):
                        cls._legacy_provider = cls.lib.OSSL_PROVIDER_load(
                            cls.ffi.NULL, b"legacy"
                        )
                        cls._legacy_provider_loaded = (
                            cls._legacy_provider != cls.ffi.NULL
                        )
                        _legacy_provider_error(cls._legacy_provider_loaded)

                    cls._default_provider = cls.lib.OSSL_PROVIDER_load(
                        cls.ffi.NULL, b"default"
                    )
                    _openssl_assert(
                        cls.lib, cls._default_provider != cls.ffi.NULL
                    )

    @classmethod
    def init_static_locks(cls) -> None:
        cls._ensure_ffi_initialized()


def _verify_package_version(version: str) -> None:
    # Occasionally we run into situations where the version of the Python
    # package does not match the version of the shared object that is loaded.
    # This may occur in environments where multiple versions of cryptography
    # are installed and available in the python path. To avoid errors cropping
    # up later this code checks that the currently imported package and the
    # shared object that were loaded have the same version and raise an
    # ImportError if they do not
    so_package_version = _openssl.ffi.string(
        _openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION
    )
    if version.encode("ascii") != so_package_version:
        raise ImportError(
            "The version of cryptography does not match the loaded "
            "shared object. This can happen if you have multiple copies of "
            "cryptography installed in your Python path. Please try creating "
            "a new virtual environment to resolve this issue. "
            "Loaded python version: {}, shared object version: {}".format(
                version, so_package_version
            )
        )

    _openssl_assert(
        _openssl.lib,
        _openssl.lib.OpenSSL_version_num() == openssl.openssl_version(),
    )


_verify_package_version(cryptography.__version__)

Binding.init_static_locks()

if (
    sys.platform == "win32"
    and os.environ.get("PROCESSOR_ARCHITEW6432") is not None
):
    warnings.warn(
        "You are using cryptography on a 32-bit Python on a 64-bit Windows "
        "Operating System. Cryptography will be significantly faster if you "
        "switch to using a 64-bit Python.",
        UserWarning,
        stacklevel=2,
    )

back/venvmsg/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

# This exists to break an import cycle. It is normally accessible from the
# ciphers module.


class CipherAlgorithm(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def name(self) -> str:
        """
        A string naming this mode (e.g. "AES", "Camellia").
        """

    @property
    @abc.abstractmethod
    def key_sizes(self) -> typing.FrozenSet[int]:
        """
        Valid key sizes for this algorithm in bits
        """

    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        The size of the key being used as an integer in bits (e.g. 128, 256).
        """


class BlockCipherAlgorithm(CipherAlgorithm):
    key: bytes

    @property
    @abc.abstractmethod
    def block_size(self) -> int:
        """
        The size of a block as an integer in bits (e.g. 64, 128).
        """

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

from cryptography import utils
from cryptography.hazmat.primitives.hashes import HashAlgorithm

# This exists to break an import cycle. These classes are normally accessible
# from the serialization module.


class PBES(utils.Enum):
    PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES"
    PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC"


class Encoding(utils.Enum):
    PEM = "PEM"
    DER = "DER"
    OpenSSH = "OpenSSH"
    Raw = "Raw"
    X962 = "ANSI X9.62"
    SMIME = "S/MIME"


class PrivateFormat(utils.Enum):
    PKCS8 = "PKCS8"
    TraditionalOpenSSL = "TraditionalOpenSSL"
    Raw = "Raw"
    OpenSSH = "OpenSSH"
    PKCS12 = "PKCS12"

    def encryption_builder(self) -> KeySerializationEncryptionBuilder:
        if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12):
            raise ValueError(
                "encryption_builder only supported with PrivateFormat.OpenSSH"
                " and PrivateFormat.PKCS12"
            )
        return KeySerializationEncryptionBuilder(self)


class PublicFormat(utils.Enum):
    SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1"
    PKCS1 = "Raw PKCS#1"
    OpenSSH = "OpenSSH"
    Raw = "Raw"
    CompressedPoint = "X9.62 Compressed Point"
    UncompressedPoint = "X9.62 Uncompressed Point"


class ParameterFormat(utils.Enum):
    PKCS3 = "PKCS3"


class KeySerializationEncryption(metaclass=abc.ABCMeta):
    pass


class BestAvailableEncryption(KeySerializationEncryption):
    def __init__(self, password: bytes):
        if not isinstance(password, bytes) or len(password) == 0:
            raise ValueError("Password must be 1 or more bytes.")

        self.password = password


class NoEncryption(KeySerializationEncryption):
    pass


class KeySerializationEncryptionBuilder:
    def __init__(
        self,
        format: PrivateFormat,
        *,
        _kdf_rounds: typing.Optional[int] = None,
        _hmac_hash: typing.Optional[HashAlgorithm] = None,
        _key_cert_algorithm: typing.Optional[PBES] = None,
    ) -> None:
        self._format = format

        self._kdf_rounds = _kdf_rounds
        self._hmac_hash = _hmac_hash
        self._key_cert_algorithm = _key_cert_algorithm

    def kdf_rounds(self, rounds: int) -> KeySerializationEncryptionBuilder:
        if self._kdf_rounds is not None:
            raise ValueError("kdf_rounds already set")

        if not isinstance(rounds, int):
            raise TypeError("kdf_rounds must be an integer")

        if rounds < 1:
            raise ValueError("kdf_rounds must be a positive integer")

        return KeySerializationEncryptionBuilder(
            self._format,
            _kdf_rounds=rounds,
            _hmac_hash=self._hmac_hash,
            _key_cert_algorithm=self._key_cert_algorithm,
        )

    def hmac_hash(
        self, algorithm: HashAlgorithm
    ) -> KeySerializationEncryptionBuilder:
        if self._format is not PrivateFormat.PKCS12:
            raise TypeError(
                "hmac_hash only supported with PrivateFormat.PKCS12"
            )

        if self._hmac_hash is not None:
            raise ValueError("hmac_hash already set")
        return KeySerializationEncryptionBuilder(
            self._format,
            _kdf_rounds=self._kdf_rounds,
            _hmac_hash=algorithm,
            _key_cert_algorithm=self._key_cert_algorithm,
        )

    def key_cert_algorithm(
        self, algorithm: PBES
    ) -> KeySerializationEncryptionBuilder:
        if self._format is not PrivateFormat.PKCS12:
            raise TypeError(
                "key_cert_algorithm only supported with "
                "PrivateFormat.PKCS12"
            )
        if self._key_cert_algorithm is not None:
            raise ValueError("key_cert_algorithm already set")
        return KeySerializationEncryptionBuilder(
            self._format,
            _kdf_rounds=self._kdf_rounds,
            _hmac_hash=self._hmac_hash,
            _key_cert_algorithm=algorithm,
        )

    def build(self, password: bytes) -> KeySerializationEncryption:
        if not isinstance(password, bytes) or len(password) == 0:
            raise ValueError("Password must be 1 or more bytes.")

        return _KeySerializationEncryption(
            self._format,
            password,
            kdf_rounds=self._kdf_rounds,
            hmac_hash=self._hmac_hash,
            key_cert_algorithm=self._key_cert_algorithm,
        )


class _KeySerializationEncryption(KeySerializationEncryption):
    def __init__(
        self,
        format: PrivateFormat,
        password: bytes,
        *,
        kdf_rounds: typing.Optional[int],
        hmac_hash: typing.Optional[HashAlgorithm],
        key_cert_algorithm: typing.Optional[PBES],
    ):
        self._format = format
        self.password = password

        self._kdf_rounds = kdf_rounds
        self._hmac_hash = hmac_hash
        self._key_cert_algorithm = key_cert_algorithm

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization


def generate_parameters(
    generator: int, key_size: int, backend: typing.Any = None
) -> DHParameters:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.generate_dh_parameters(generator, key_size)


class DHParameterNumbers:
    def __init__(self, p: int, g: int, q: typing.Optional[int] = None) -> None:
        if not isinstance(p, int) or not isinstance(g, int):
            raise TypeError("p and g must be integers")
        if q is not None and not isinstance(q, int):
            raise TypeError("q must be integer or None")

        if g < 2:
            raise ValueError("DH generator must be 2 or greater")

        if p.bit_length() < rust_openssl.dh.MIN_MODULUS_SIZE:
            raise ValueError(
                f"p (modulus) must be at least "
                f"{rust_openssl.dh.MIN_MODULUS_SIZE}-bit"
            )

        self._p = p
        self._g = g
        self._q = q

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DHParameterNumbers):
            return NotImplemented

        return (
            self._p == other._p and self._g == other._g and self._q == other._q
        )

    def parameters(self, backend: typing.Any = None) -> DHParameters:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_dh_parameter_numbers(self)

    @property
    def p(self) -> int:
        return self._p

    @property
    def g(self) -> int:
        return self._g

    @property
    def q(self) -> typing.Optional[int]:
        return self._q


class DHPublicNumbers:
    def __init__(self, y: int, parameter_numbers: DHParameterNumbers) -> None:
        if not isinstance(y, int):
            raise TypeError("y must be an integer.")

        if not isinstance(parameter_numbers, DHParameterNumbers):
            raise TypeError(
                "parameters must be an instance of DHParameterNumbers."
            )

        self._y = y
        self._parameter_numbers = parameter_numbers

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DHPublicNumbers):
            return NotImplemented

        return (
            self._y == other._y
            and self._parameter_numbers == other._parameter_numbers
        )

    def public_key(self, backend: typing.Any = None) -> DHPublicKey:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_dh_public_numbers(self)

    @property
    def y(self) -> int:
        return self._y

    @property
    def parameter_numbers(self) -> DHParameterNumbers:
        return self._parameter_numbers


class DHPrivateNumbers:
    def __init__(self, x: int, public_numbers: DHPublicNumbers) -> None:
        if not isinstance(x, int):
            raise TypeError("x must be an integer.")

        if not isinstance(public_numbers, DHPublicNumbers):
            raise TypeError(
                "public_numbers must be an instance of " "DHPublicNumbers."
            )

        self._x = x
        self._public_numbers = public_numbers

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DHPrivateNumbers):
            return NotImplemented

        return (
            self._x == other._x
            and self._public_numbers == other._public_numbers
        )

    def private_key(self, backend: typing.Any = None) -> DHPrivateKey:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_dh_private_numbers(self)

    @property
    def public_numbers(self) -> DHPublicNumbers:
        return self._public_numbers

    @property
    def x(self) -> int:
        return self._x


class DHParameters(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def generate_private_key(self) -> DHPrivateKey:
        """
        Generates and returns a DHPrivateKey.
        """

    @abc.abstractmethod
    def parameter_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.ParameterFormat,
    ) -> bytes:
        """
        Returns the parameters serialized as bytes.
        """

    @abc.abstractmethod
    def parameter_numbers(self) -> DHParameterNumbers:
        """
        Returns a DHParameterNumbers.
        """


DHParametersWithSerialization = DHParameters
DHParameters.register(rust_openssl.dh.DHParameters)


class DHPublicKey(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        The bit length of the prime modulus.
        """

    @abc.abstractmethod
    def parameters(self) -> DHParameters:
        """
        The DHParameters object associated with this public key.
        """

    @abc.abstractmethod
    def public_numbers(self) -> DHPublicNumbers:
        """
        Returns a DHPublicNumbers.
        """

    @abc.abstractmethod
    def public_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PublicFormat,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """


DHPublicKeyWithSerialization = DHPublicKey
DHPublicKey.register(rust_openssl.dh.DHPublicKey)


class DHPrivateKey(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        The bit length of the prime modulus.
        """

    @abc.abstractmethod
    def public_key(self) -> DHPublicKey:
        """
        The DHPublicKey associated with this private key.
        """

    @abc.abstractmethod
    def parameters(self) -> DHParameters:
        """
        The DHParameters object associated with this private key.
        """

    @abc.abstractmethod
    def exchange(self, peer_public_key: DHPublicKey) -> bytes:
        """
        Given peer's DHPublicKey, carry out the key exchange and
        return shared key as bytes.
        """

    @abc.abstractmethod
    def private_numbers(self) -> DHPrivateNumbers:
        """
        Returns a DHPrivateNumbers.
        """

    @abc.abstractmethod
    def private_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PrivateFormat,
        encryption_algorithm: _serialization.KeySerializationEncryption,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """


DHPrivateKeyWithSerialization = DHPrivateKey
DHPrivateKey.register(rust_openssl.dh.DHPrivateKey)

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization, hashes
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils


class DSAParameters(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def generate_private_key(self) -> DSAPrivateKey:
        """
        Generates and returns a DSAPrivateKey.
        """

    @abc.abstractmethod
    def parameter_numbers(self) -> DSAParameterNumbers:
        """
        Returns a DSAParameterNumbers.
        """


DSAParametersWithNumbers = DSAParameters
DSAParameters.register(rust_openssl.dsa.DSAParameters)


class DSAPrivateKey(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        The bit length of the prime modulus.
        """

    @abc.abstractmethod
    def public_key(self) -> DSAPublicKey:
        """
        The DSAPublicKey associated with this private key.
        """

    @abc.abstractmethod
    def parameters(self) -> DSAParameters:
        """
        The DSAParameters object associated with this private key.
        """

    @abc.abstractmethod
    def sign(
        self,
        data: bytes,
        algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
    ) -> bytes:
        """
        Signs the data
        """

    @abc.abstractmethod
    def private_numbers(self) -> DSAPrivateNumbers:
        """
        Returns a DSAPrivateNumbers.
        """

    @abc.abstractmethod
    def private_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PrivateFormat,
        encryption_algorithm: _serialization.KeySerializationEncryption,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """


DSAPrivateKeyWithSerialization = DSAPrivateKey
DSAPrivateKey.register(rust_openssl.dsa.DSAPrivateKey)


class DSAPublicKey(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        The bit length of the prime modulus.
        """

    @abc.abstractmethod
    def parameters(self) -> DSAParameters:
        """
        The DSAParameters object associated with this public key.
        """

    @abc.abstractmethod
    def public_numbers(self) -> DSAPublicNumbers:
        """
        Returns a DSAPublicNumbers.
        """

    @abc.abstractmethod
    def public_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PublicFormat,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """

    @abc.abstractmethod
    def verify(
        self,
        signature: bytes,
        data: bytes,
        algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
    ) -> None:
        """
        Verifies the signature of the data.
        """

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """


DSAPublicKeyWithSerialization = DSAPublicKey
DSAPublicKey.register(rust_openssl.dsa.DSAPublicKey)


class DSAParameterNumbers:
    def __init__(self, p: int, q: int, g: int):
        if (
            not isinstance(p, int)
            or not isinstance(q, int)
            or not isinstance(g, int)
        ):
            raise TypeError(
                "DSAParameterNumbers p, q, and g arguments must be integers."
            )

        self._p = p
        self._q = q
        self._g = g

    @property
    def p(self) -> int:
        return self._p

    @property
    def q(self) -> int:
        return self._q

    @property
    def g(self) -> int:
        return self._g

    def parameters(self, backend: typing.Any = None) -> DSAParameters:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_dsa_parameter_numbers(self)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DSAParameterNumbers):
            return NotImplemented

        return self.p == other.p and self.q == other.q and self.g == other.g

    def __repr__(self) -> str:
        return (
            "<DSAParameterNumbers(p={self.p}, q={self.q}, "
            "g={self.g})>".format(self=self)
        )


class DSAPublicNumbers:
    def __init__(self, y: int, parameter_numbers: DSAParameterNumbers):
        if not isinstance(y, int):
            raise TypeError("DSAPublicNumbers y argument must be an integer.")

        if not isinstance(parameter_numbers, DSAParameterNumbers):
            raise TypeError(
                "parameter_numbers must be a DSAParameterNumbers instance."
            )

        self._y = y
        self._parameter_numbers = parameter_numbers

    @property
    def y(self) -> int:
        return self._y

    @property
    def parameter_numbers(self) -> DSAParameterNumbers:
        return self._parameter_numbers

    def public_key(self, backend: typing.Any = None) -> DSAPublicKey:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_dsa_public_numbers(self)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DSAPublicNumbers):
            return NotImplemented

        return (
            self.y == other.y
            and self.parameter_numbers == other.parameter_numbers
        )

    def __repr__(self) -> str:
        return (
            "<DSAPublicNumbers(y={self.y}, "
            "parameter_numbers={self.parameter_numbers})>".format(self=self)
        )


class DSAPrivateNumbers:
    def __init__(self, x: int, public_numbers: DSAPublicNumbers):
        if not isinstance(x, int):
            raise TypeError("DSAPrivateNumbers x argument must be an integer.")

        if not isinstance(public_numbers, DSAPublicNumbers):
            raise TypeError(
                "public_numbers must be a DSAPublicNumbers instance."
            )
        self._public_numbers = public_numbers
        self._x = x

    @property
    def x(self) -> int:
        return self._x

    @property
    def public_numbers(self) -> DSAPublicNumbers:
        return self._public_numbers

    def private_key(self, backend: typing.Any = None) -> DSAPrivateKey:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_dsa_private_numbers(self)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DSAPrivateNumbers):
            return NotImplemented

        return (
            self.x == other.x and self.public_numbers == other.public_numbers
        )


def generate_parameters(
    key_size: int, backend: typing.Any = None
) -> DSAParameters:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.generate_dsa_parameters(key_size)


def generate_private_key(
    key_size: int, backend: typing.Any = None
) -> DSAPrivateKey:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.generate_dsa_private_key_and_parameters(key_size)


def _check_dsa_parameters(parameters: DSAParameterNumbers) -> None:
    if parameters.p.bit_length() not in [1024, 2048, 3072, 4096]:
        raise ValueError(
            "p must be exactly 1024, 2048, 3072, or 4096 bits long"
        )
    if parameters.q.bit_length() not in [160, 224, 256]:
        raise ValueError("q must be exactly 160, 224, or 256 bits long")

    if not (1 < parameters.g < parameters.p):
        raise ValueError("g, p don't satisfy 1 < g < p.")


def _check_dsa_private_numbers(numbers: DSAPrivateNumbers) -> None:
    parameters = numbers.public_numbers.parameter_numbers
    _check_dsa_parameters(parameters)
    if numbers.x <= 0 or numbers.x >= parameters.q:
        raise ValueError("x must be > 0 and < q.")

    if numbers.public_numbers.y != pow(parameters.g, numbers.x, parameters.p):
        raise ValueError("y must be equal to (g ** x % p).")

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

from cryptography import utils
from cryptography.hazmat._oid import ObjectIdentifier
from cryptography.hazmat.primitives import _serialization, hashes
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils


class EllipticCurveOID:
    SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1")
    SECP224R1 = ObjectIdentifier("1.3.132.0.33")
    SECP256K1 = ObjectIdentifier("1.3.132.0.10")
    SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7")
    SECP384R1 = ObjectIdentifier("1.3.132.0.34")
    SECP521R1 = ObjectIdentifier("1.3.132.0.35")
    BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7")
    BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11")
    BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13")
    SECT163K1 = ObjectIdentifier("1.3.132.0.1")
    SECT163R2 = ObjectIdentifier("1.3.132.0.15")
    SECT233K1 = ObjectIdentifier("1.3.132.0.26")
    SECT233R1 = ObjectIdentifier("1.3.132.0.27")
    SECT283K1 = ObjectIdentifier("1.3.132.0.16")
    SECT283R1 = ObjectIdentifier("1.3.132.0.17")
    SECT409K1 = ObjectIdentifier("1.3.132.0.36")
    SECT409R1 = ObjectIdentifier("1.3.132.0.37")
    SECT571K1 = ObjectIdentifier("1.3.132.0.38")
    SECT571R1 = ObjectIdentifier("1.3.132.0.39")


class EllipticCurve(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def name(self) -> str:
        """
        The name of the curve. e.g. secp256r1.
        """

    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        Bit size of a secret scalar for the curve.
        """


class EllipticCurveSignatureAlgorithm(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def algorithm(
        self,
    ) -> typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm]:
        """
        The digest algorithm used with this signature.
        """


class EllipticCurvePrivateKey(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def exchange(
        self, algorithm: ECDH, peer_public_key: EllipticCurvePublicKey
    ) -> bytes:
        """
        Performs a key exchange operation using the provided algorithm with the
        provided peer's public key.
        """

    @abc.abstractmethod
    def public_key(self) -> EllipticCurvePublicKey:
        """
        The EllipticCurvePublicKey for this private key.
        """

    @property
    @abc.abstractmethod
    def curve(self) -> EllipticCurve:
        """
        The EllipticCurve that this key is on.
        """

    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        Bit size of a secret scalar for the curve.
        """

    @abc.abstractmethod
    def sign(
        self,
        data: bytes,
        signature_algorithm: EllipticCurveSignatureAlgorithm,
    ) -> bytes:
        """
        Signs the data
        """

    @abc.abstractmethod
    def private_numbers(self) -> EllipticCurvePrivateNumbers:
        """
        Returns an EllipticCurvePrivateNumbers.
        """

    @abc.abstractmethod
    def private_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PrivateFormat,
        encryption_algorithm: _serialization.KeySerializationEncryption,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """


EllipticCurvePrivateKeyWithSerialization = EllipticCurvePrivateKey


class EllipticCurvePublicKey(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def curve(self) -> EllipticCurve:
        """
        The EllipticCurve that this key is on.
        """

    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        Bit size of a secret scalar for the curve.
        """

    @abc.abstractmethod
    def public_numbers(self) -> EllipticCurvePublicNumbers:
        """
        Returns an EllipticCurvePublicNumbers.
        """

    @abc.abstractmethod
    def public_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PublicFormat,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """

    @abc.abstractmethod
    def verify(
        self,
        signature: bytes,
        data: bytes,
        signature_algorithm: EllipticCurveSignatureAlgorithm,
    ) -> None:
        """
        Verifies the signature of the data.
        """

    @classmethod
    def from_encoded_point(
        cls, curve: EllipticCurve, data: bytes
    ) -> EllipticCurvePublicKey:
        utils._check_bytes("data", data)

        if not isinstance(curve, EllipticCurve):
            raise TypeError("curve must be an EllipticCurve instance")

        if len(data) == 0:
            raise ValueError("data must not be an empty byte string")

        if data[0] not in [0x02, 0x03, 0x04]:
            raise ValueError("Unsupported elliptic curve point type")

        from cryptography.hazmat.backends.openssl.backend import backend

        return backend.load_elliptic_curve_public_bytes(curve, data)

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """


EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey


class SECT571R1(EllipticCurve):
    name = "sect571r1"
    key_size = 570


class SECT409R1(EllipticCurve):
    name = "sect409r1"
    key_size = 409


class SECT283R1(EllipticCurve):
    name = "sect283r1"
    key_size = 283


class SECT233R1(EllipticCurve):
    name = "sect233r1"
    key_size = 233


class SECT163R2(EllipticCurve):
    name = "sect163r2"
    key_size = 163


class SECT571K1(EllipticCurve):
    name = "sect571k1"
    key_size = 571


class SECT409K1(EllipticCurve):
    name = "sect409k1"
    key_size = 409


class SECT283K1(EllipticCurve):
    name = "sect283k1"
    key_size = 283


class SECT233K1(EllipticCurve):
    name = "sect233k1"
    key_size = 233


class SECT163K1(EllipticCurve):
    name = "sect163k1"
    key_size = 163


class SECP521R1(EllipticCurve):
    name = "secp521r1"
    key_size = 521


class SECP384R1(EllipticCurve):
    name = "secp384r1"
    key_size = 384


class SECP256R1(EllipticCurve):
    name = "secp256r1"
    key_size = 256


class SECP256K1(EllipticCurve):
    name = "secp256k1"
    key_size = 256


class SECP224R1(EllipticCurve):
    name = "secp224r1"
    key_size = 224


class SECP192R1(EllipticCurve):
    name = "secp192r1"
    key_size = 192


class BrainpoolP256R1(EllipticCurve):
    name = "brainpoolP256r1"
    key_size = 256


class BrainpoolP384R1(EllipticCurve):
    name = "brainpoolP384r1"
    key_size = 384


class BrainpoolP512R1(EllipticCurve):
    name = "brainpoolP512r1"
    key_size = 512


_CURVE_TYPES: typing.Dict[str, typing.Type[EllipticCurve]] = {
    "prime192v1": SECP192R1,
    "prime256v1": SECP256R1,
    "secp192r1": SECP192R1,
    "secp224r1": SECP224R1,
    "secp256r1": SECP256R1,
    "secp384r1": SECP384R1,
    "secp521r1": SECP521R1,
    "secp256k1": SECP256K1,
    "sect163k1": SECT163K1,
    "sect233k1": SECT233K1,
    "sect283k1": SECT283K1,
    "sect409k1": SECT409K1,
    "sect571k1": SECT571K1,
    "sect163r2": SECT163R2,
    "sect233r1": SECT233R1,
    "sect283r1": SECT283R1,
    "sect409r1": SECT409R1,
    "sect571r1": SECT571R1,
    "brainpoolP256r1": BrainpoolP256R1,
    "brainpoolP384r1": BrainpoolP384R1,
    "brainpoolP512r1": BrainpoolP512R1,
}


class ECDSA(EllipticCurveSignatureAlgorithm):
    def __init__(
        self,
        algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
    ):
        self._algorithm = algorithm

    @property
    def algorithm(
        self,
    ) -> typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm]:
        return self._algorithm


def generate_private_key(
    curve: EllipticCurve, backend: typing.Any = None
) -> EllipticCurvePrivateKey:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.generate_elliptic_curve_private_key(curve)


def derive_private_key(
    private_value: int,
    curve: EllipticCurve,
    backend: typing.Any = None,
) -> EllipticCurvePrivateKey:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    if not isinstance(private_value, int):
        raise TypeError("private_value must be an integer type.")

    if private_value <= 0:
        raise ValueError("private_value must be a positive integer.")

    if not isinstance(curve, EllipticCurve):
        raise TypeError("curve must provide the EllipticCurve interface.")

    return ossl.derive_elliptic_curve_private_key(private_value, curve)


class EllipticCurvePublicNumbers:
    def __init__(self, x: int, y: int, curve: EllipticCurve):
        if not isinstance(x, int) or not isinstance(y, int):
            raise TypeError("x and y must be integers.")

        if not isinstance(curve, EllipticCurve):
            raise TypeError("curve must provide the EllipticCurve interface.")

        self._y = y
        self._x = x
        self._curve = curve

    def public_key(self, backend: typing.Any = None) -> EllipticCurvePublicKey:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_elliptic_curve_public_numbers(self)

    @property
    def curve(self) -> EllipticCurve:
        return self._curve

    @property
    def x(self) -> int:
        return self._x

    @property
    def y(self) -> int:
        return self._y

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, EllipticCurvePublicNumbers):
            return NotImplemented

        return (
            self.x == other.x
            and self.y == other.y
            and self.curve.name == other.curve.name
            and self.curve.key_size == other.curve.key_size
        )

    def __hash__(self) -> int:
        return hash((self.x, self.y, self.curve.name, self.curve.key_size))

    def __repr__(self) -> str:
        return (
            "<EllipticCurvePublicNumbers(curve={0.curve.name}, x={0.x}, "
            "y={0.y}>".format(self)
        )


class EllipticCurvePrivateNumbers:
    def __init__(
        self, private_value: int, public_numbers: EllipticCurvePublicNumbers
    ):
        if not isinstance(private_value, int):
            raise TypeError("private_value must be an integer.")

        if not isinstance(public_numbers, EllipticCurvePublicNumbers):
            raise TypeError(
                "public_numbers must be an EllipticCurvePublicNumbers "
                "instance."
            )

        self._private_value = private_value
        self._public_numbers = public_numbers

    def private_key(
        self, backend: typing.Any = None
    ) -> EllipticCurvePrivateKey:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_elliptic_curve_private_numbers(self)

    @property
    def private_value(self) -> int:
        return self._private_value

    @property
    def public_numbers(self) -> EllipticCurvePublicNumbers:
        return self._public_numbers

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, EllipticCurvePrivateNumbers):
            return NotImplemented

        return (
            self.private_value == other.private_value
            and self.public_numbers == other.public_numbers
        )

    def __hash__(self) -> int:
        return hash((self.private_value, self.public_numbers))


class ECDH:
    pass


_OID_TO_CURVE = {
    EllipticCurveOID.SECP192R1: SECP192R1,
    EllipticCurveOID.SECP224R1: SECP224R1,
    EllipticCurveOID.SECP256K1: SECP256K1,
    EllipticCurveOID.SECP256R1: SECP256R1,
    EllipticCurveOID.SECP384R1: SECP384R1,
    EllipticCurveOID.SECP521R1: SECP521R1,
    EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1,
    EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1,
    EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1,
    EllipticCurveOID.SECT163K1: SECT163K1,
    EllipticCurveOID.SECT163R2: SECT163R2,
    EllipticCurveOID.SECT233K1: SECT233K1,
    EllipticCurveOID.SECT233R1: SECT233R1,
    EllipticCurveOID.SECT283K1: SECT283K1,
    EllipticCurveOID.SECT283R1: SECT283R1,
    EllipticCurveOID.SECT409K1: SECT409K1,
    EllipticCurveOID.SECT409R1: SECT409R1,
    EllipticCurveOID.SECT571K1: SECT571K1,
    EllipticCurveOID.SECT571R1: SECT571R1,
}


def get_curve_for_oid(oid: ObjectIdentifier) -> typing.Type[EllipticCurve]:
    try:
        return _OID_TO_CURVE[oid]
    except KeyError:
        raise LookupError(
            "The provided object identifier has no matching elliptic "
            "curve class"
        )

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc

from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization


class Ed25519PublicKey(metaclass=abc.ABCMeta):
    @classmethod
    def from_public_bytes(cls, data: bytes) -> Ed25519PublicKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.ed25519_supported():
            raise UnsupportedAlgorithm(
                "ed25519 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
            )

        return backend.ed25519_load_public_bytes(data)

    @abc.abstractmethod
    def public_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PublicFormat,
    ) -> bytes:
        """
        The serialized bytes of the public key.
        """

    @abc.abstractmethod
    def public_bytes_raw(self) -> bytes:
        """
        The raw bytes of the public key.
        Equivalent to public_bytes(Raw, Raw).
        """

    @abc.abstractmethod
    def verify(self, signature: bytes, data: bytes) -> None:
        """
        Verify the signature.
        """

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """


if hasattr(rust_openssl, "ed25519"):
    Ed25519PublicKey.register(rust_openssl.ed25519.Ed25519PublicKey)


class Ed25519PrivateKey(metaclass=abc.ABCMeta):
    @classmethod
    def generate(cls) -> Ed25519PrivateKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.ed25519_supported():
            raise UnsupportedAlgorithm(
                "ed25519 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
            )

        return backend.ed25519_generate_key()

    @classmethod
    def from_private_bytes(cls, data: bytes) -> Ed25519PrivateKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.ed25519_supported():
            raise UnsupportedAlgorithm(
                "ed25519 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
            )

        return backend.ed25519_load_private_bytes(data)

    @abc.abstractmethod
    def public_key(self) -> Ed25519PublicKey:
        """
        The Ed25519PublicKey derived from the private key.
        """

    @abc.abstractmethod
    def private_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PrivateFormat,
        encryption_algorithm: _serialization.KeySerializationEncryption,
    ) -> bytes:
        """
        The serialized bytes of the private key.
        """

    @abc.abstractmethod
    def private_bytes_raw(self) -> bytes:
        """
        The raw bytes of the private key.
        Equivalent to private_bytes(Raw, Raw, NoEncryption()).
        """

    @abc.abstractmethod
    def sign(self, data: bytes) -> bytes:
        """
        Signs the data.
        """


if hasattr(rust_openssl, "x25519"):
    Ed25519PrivateKey.register(rust_openssl.ed25519.Ed25519PrivateKey)

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc

from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization


class Ed448PublicKey(metaclass=abc.ABCMeta):
    @classmethod
    def from_public_bytes(cls, data: bytes) -> Ed448PublicKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.ed448_supported():
            raise UnsupportedAlgorithm(
                "ed448 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
            )

        return backend.ed448_load_public_bytes(data)

    @abc.abstractmethod
    def public_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PublicFormat,
    ) -> bytes:
        """
        The serialized bytes of the public key.
        """

    @abc.abstractmethod
    def public_bytes_raw(self) -> bytes:
        """
        The raw bytes of the public key.
        Equivalent to public_bytes(Raw, Raw).
        """

    @abc.abstractmethod
    def verify(self, signature: bytes, data: bytes) -> None:
        """
        Verify the signature.
        """

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """


if hasattr(rust_openssl, "ed448"):
    Ed448PublicKey.register(rust_openssl.ed448.Ed448PublicKey)


class Ed448PrivateKey(metaclass=abc.ABCMeta):
    @classmethod
    def generate(cls) -> Ed448PrivateKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.ed448_supported():
            raise UnsupportedAlgorithm(
                "ed448 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
            )
        return backend.ed448_generate_key()

    @classmethod
    def from_private_bytes(cls, data: bytes) -> Ed448PrivateKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.ed448_supported():
            raise UnsupportedAlgorithm(
                "ed448 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM,
            )

        return backend.ed448_load_private_bytes(data)

    @abc.abstractmethod
    def public_key(self) -> Ed448PublicKey:
        """
        The Ed448PublicKey derived from the private key.
        """

    @abc.abstractmethod
    def sign(self, data: bytes) -> bytes:
        """
        Signs the data.
        """

    @abc.abstractmethod
    def private_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PrivateFormat,
        encryption_algorithm: _serialization.KeySerializationEncryption,
    ) -> bytes:
        """
        The serialized bytes of the private key.
        """

    @abc.abstractmethod
    def private_bytes_raw(self) -> bytes:
        """
        The raw bytes of the private key.
        Equivalent to private_bytes(Raw, Raw, NoEncryption()).
        """


if hasattr(rust_openssl, "x448"):
    Ed448PrivateKey.register(rust_openssl.ed448.Ed448PrivateKey)

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives._asymmetric import (
    AsymmetricPadding as AsymmetricPadding,
)
from cryptography.hazmat.primitives.asymmetric import rsa


class PKCS1v15(AsymmetricPadding):
    name = "EMSA-PKCS1-v1_5"


class _MaxLength:
    "Sentinel value for `MAX_LENGTH`."


class _Auto:
    "Sentinel value for `AUTO`."


class _DigestLength:
    "Sentinel value for `DIGEST_LENGTH`."


class PSS(AsymmetricPadding):
    MAX_LENGTH = _MaxLength()
    AUTO = _Auto()
    DIGEST_LENGTH = _DigestLength()
    name = "EMSA-PSS"
    _salt_length: typing.Union[int, _MaxLength, _Auto, _DigestLength]

    def __init__(
        self,
        mgf: MGF,
        salt_length: typing.Union[int, _MaxLength, _Auto, _DigestLength],
    ) -> None:
        self._mgf = mgf

        if not isinstance(
            salt_length, (int, _MaxLength, _Auto, _DigestLength)
        ):
            raise TypeError(
                "salt_length must be an integer, MAX_LENGTH, "
                "DIGEST_LENGTH, or AUTO"
            )

        if isinstance(salt_length, int) and salt_length < 0:
            raise ValueError("salt_length must be zero or greater.")

        self._salt_length = salt_length


class OAEP(AsymmetricPadding):
    name = "EME-OAEP"

    def __init__(
        self,
        mgf: MGF,
        algorithm: hashes.HashAlgorithm,
        label: typing.Optional[bytes],
    ):
        if not isinstance(algorithm, hashes.HashAlgorithm):
            raise TypeError("Expected instance of hashes.HashAlgorithm.")

        self._mgf = mgf
        self._algorithm = algorithm
        self._label = label


class MGF(metaclass=abc.ABCMeta):
    _algorithm: hashes.HashAlgorithm


class MGF1(MGF):
    MAX_LENGTH = _MaxLength()

    def __init__(self, algorithm: hashes.HashAlgorithm):
        if not isinstance(algorithm, hashes.HashAlgorithm):
            raise TypeError("Expected instance of hashes.HashAlgorithm.")

        self._algorithm = algorithm


def calculate_max_pss_salt_length(
    key: typing.Union[rsa.RSAPrivateKey, rsa.RSAPublicKey],
    hash_algorithm: hashes.HashAlgorithm,
) -> int:
    if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
        raise TypeError("key must be an RSA public or private key")
    # bit length - 1 per RFC 3447
    emlen = (key.key_size + 6) // 8
    salt_length = emlen - hash_algorithm.digest_size - 2
    assert salt_length >= 0
    return salt_length

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing
from math import gcd

from cryptography.hazmat.primitives import _serialization, hashes
from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils


class RSAPrivateKey(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes:
        """
        Decrypts the provided ciphertext.
        """

    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        The bit length of the public modulus.
        """

    @abc.abstractmethod
    def public_key(self) -> RSAPublicKey:
        """
        The RSAPublicKey associated with this private key.
        """

    @abc.abstractmethod
    def sign(
        self,
        data: bytes,
        padding: AsymmetricPadding,
        algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
    ) -> bytes:
        """
        Signs the data.
        """

    @abc.abstractmethod
    def private_numbers(self) -> RSAPrivateNumbers:
        """
        Returns an RSAPrivateNumbers.
        """

    @abc.abstractmethod
    def private_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PrivateFormat,
        encryption_algorithm: _serialization.KeySerializationEncryption,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """


RSAPrivateKeyWithSerialization = RSAPrivateKey


class RSAPublicKey(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes:
        """
        Encrypts the given plaintext.
        """

    @property
    @abc.abstractmethod
    def key_size(self) -> int:
        """
        The bit length of the public modulus.
        """

    @abc.abstractmethod
    def public_numbers(self) -> RSAPublicNumbers:
        """
        Returns an RSAPublicNumbers
        """

    @abc.abstractmethod
    def public_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PublicFormat,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """

    @abc.abstractmethod
    def verify(
        self,
        signature: bytes,
        data: bytes,
        padding: AsymmetricPadding,
        algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
    ) -> None:
        """
        Verifies the signature of the data.
        """

    @abc.abstractmethod
    def recover_data_from_signature(
        self,
        signature: bytes,
        padding: AsymmetricPadding,
        algorithm: typing.Optional[hashes.HashAlgorithm],
    ) -> bytes:
        """
        Recovers the original data from the signature.
        """

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """


RSAPublicKeyWithSerialization = RSAPublicKey


def generate_private_key(
    public_exponent: int,
    key_size: int,
    backend: typing.Any = None,
) -> RSAPrivateKey:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    _verify_rsa_parameters(public_exponent, key_size)
    return ossl.generate_rsa_private_key(public_exponent, key_size)


def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None:
    if public_exponent not in (3, 65537):
        raise ValueError(
            "public_exponent must be either 3 (for legacy compatibility) or "
            "65537. Almost everyone should choose 65537 here!"
        )

    if key_size < 512:
        raise ValueError("key_size must be at least 512-bits.")


def _check_private_key_components(
    p: int,
    q: int,
    private_exponent: int,
    dmp1: int,
    dmq1: int,
    iqmp: int,
    public_exponent: int,
    modulus: int,
) -> None:
    if modulus < 3:
        raise ValueError("modulus must be >= 3.")

    if p >= modulus:
        raise ValueError("p must be < modulus.")

    if q >= modulus:
        raise ValueError("q must be < modulus.")

    if dmp1 >= modulus:
        raise ValueError("dmp1 must be < modulus.")

    if dmq1 >= modulus:
        raise ValueError("dmq1 must be < modulus.")

    if iqmp >= modulus:
        raise ValueError("iqmp must be < modulus.")

    if private_exponent >= modulus:
        raise ValueError("private_exponent must be < modulus.")

    if public_exponent < 3 or public_exponent >= modulus:
        raise ValueError("public_exponent must be >= 3 and < modulus.")

    if public_exponent & 1 == 0:
        raise ValueError("public_exponent must be odd.")

    if dmp1 & 1 == 0:
        raise ValueError("dmp1 must be odd.")

    if dmq1 & 1 == 0:
        raise ValueError("dmq1 must be odd.")

    if p * q != modulus:
        raise ValueError("p*q must equal modulus.")


def _check_public_key_components(e: int, n: int) -> None:
    if n < 3:
        raise ValueError("n must be >= 3.")

    if e < 3 or e >= n:
        raise ValueError("e must be >= 3 and < n.")

    if e & 1 == 0:
        raise ValueError("e must be odd.")


def _modinv(e: int, m: int) -> int:
    """
    Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1
    """
    x1, x2 = 1, 0
    a, b = e, m
    while b > 0:
        q, r = divmod(a, b)
        xn = x1 - q * x2
        a, b, x1, x2 = b, r, x2, xn
    return x1 % m


def rsa_crt_iqmp(p: int, q: int) -> int:
    """
    Compute the CRT (q ** -1) % p value from RSA primes p and q.
    """
    return _modinv(q, p)


def rsa_crt_dmp1(private_exponent: int, p: int) -> int:
    """
    Compute the CRT private_exponent % (p - 1) value from the RSA
    private_exponent (d) and p.
    """
    return private_exponent % (p - 1)


def rsa_crt_dmq1(private_exponent: int, q: int) -> int:
    """
    Compute the CRT private_exponent % (q - 1) value from the RSA
    private_exponent (d) and q.
    """
    return private_exponent % (q - 1)


# Controls the number of iterations rsa_recover_prime_factors will perform
# to obtain the prime factors. Each iteration increments by 2 so the actual
# maximum attempts is half this number.
_MAX_RECOVERY_ATTEMPTS = 1000


def rsa_recover_prime_factors(
    n: int, e: int, d: int
) -> typing.Tuple[int, int]:
    """
    Compute factors p and q from the private exponent d. We assume that n has
    no more than two factors. This function is adapted from code in PyCrypto.
    """
    # See 8.2.2(i) in Handbook of Applied Cryptography.
    ktot = d * e - 1
    # The quantity d*e-1 is a multiple of phi(n), even,
    # and can be represented as t*2^s.
    t = ktot
    while t % 2 == 0:
        t = t // 2
    # Cycle through all multiplicative inverses in Zn.
    # The algorithm is non-deterministic, but there is a 50% chance
    # any candidate a leads to successful factoring.
    # See "Digitalized Signatures and Public Key Functions as Intractable
    # as Factorization", M. Rabin, 1979
    spotted = False
    a = 2
    while not spotted and a < _MAX_RECOVERY_ATTEMPTS:
        k = t
        # Cycle through all values a^{t*2^i}=a^k
        while k < ktot:
            cand = pow(a, k, n)
            # Check if a^k is a non-trivial root of unity (mod n)
            if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1:
                # We have found a number such that (cand-1)(cand+1)=0 (mod n).
                # Either of the terms divides n.
                p = gcd(cand + 1, n)
                spotted = True
                break
            k *= 2
        # This value was not any good... let's try another!
        a += 2
    if not spotted:
        raise ValueError("Unable to compute factors p and q from exponent d.")
    # Found !
    q, r = divmod(n, p)
    assert r == 0
    p, q = sorted((p, q), reverse=True)
    return (p, q)


class RSAPrivateNumbers:
    def __init__(
        self,
        p: int,
        q: int,
        d: int,
        dmp1: int,
        dmq1: int,
        iqmp: int,
        public_numbers: RSAPublicNumbers,
    ):
        if (
            not isinstance(p, int)
            or not isinstance(q, int)
            or not isinstance(d, int)
            or not isinstance(dmp1, int)
            or not isinstance(dmq1, int)
            or not isinstance(iqmp, int)
        ):
            raise TypeError(
                "RSAPrivateNumbers p, q, d, dmp1, dmq1, iqmp arguments must"
                " all be an integers."
            )

        if not isinstance(public_numbers, RSAPublicNumbers):
            raise TypeError(
                "RSAPrivateNumbers public_numbers must be an RSAPublicNumbers"
                " instance."
            )

        self._p = p
        self._q = q
        self._d = d
        self._dmp1 = dmp1
        self._dmq1 = dmq1
        self._iqmp = iqmp
        self._public_numbers = public_numbers

    @property
    def p(self) -> int:
        return self._p

    @property
    def q(self) -> int:
        return self._q

    @property
    def d(self) -> int:
        return self._d

    @property
    def dmp1(self) -> int:
        return self._dmp1

    @property
    def dmq1(self) -> int:
        return self._dmq1

    @property
    def iqmp(self) -> int:
        return self._iqmp

    @property
    def public_numbers(self) -> RSAPublicNumbers:
        return self._public_numbers

    def private_key(
        self,
        backend: typing.Any = None,
        *,
        unsafe_skip_rsa_key_validation: bool = False,
    ) -> RSAPrivateKey:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_rsa_private_numbers(
            self, unsafe_skip_rsa_key_validation
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, RSAPrivateNumbers):
            return NotImplemented

        return (
            self.p == other.p
            and self.q == other.q
            and self.d == other.d
            and self.dmp1 == other.dmp1
            and self.dmq1 == other.dmq1
            and self.iqmp == other.iqmp
            and self.public_numbers == other.public_numbers
        )

    def __hash__(self) -> int:
        return hash(
            (
                self.p,
                self.q,
                self.d,
                self.dmp1,
                self.dmq1,
                self.iqmp,
                self.public_numbers,
            )
        )


class RSAPublicNumbers:
    def __init__(self, e: int, n: int):
        if not isinstance(e, int) or not isinstance(n, int):
            raise TypeError("RSAPublicNumbers arguments must be integers.")

        self._e = e
        self._n = n

    @property
    def e(self) -> int:
        return self._e

    @property
    def n(self) -> int:
        return self._n

    def public_key(self, backend: typing.Any = None) -> RSAPublicKey:
        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        return ossl.load_rsa_public_numbers(self)

    def __repr__(self) -> str:
        return "<RSAPublicNumbers(e={0.e}, n={0.n})>".format(self)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, RSAPublicNumbers):
            return NotImplemented

        return self.e == other.e and self.n == other.n

    def __hash__(self) -> int:
        return hash((self.e, self.n))

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc

from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization


class X25519PublicKey(metaclass=abc.ABCMeta):
    @classmethod
    def from_public_bytes(cls, data: bytes) -> X25519PublicKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.x25519_supported():
            raise UnsupportedAlgorithm(
                "X25519 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
            )

        return backend.x25519_load_public_bytes(data)

    @abc.abstractmethod
    def public_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PublicFormat,
    ) -> bytes:
        """
        The serialized bytes of the public key.
        """

    @abc.abstractmethod
    def public_bytes_raw(self) -> bytes:
        """
        The raw bytes of the public key.
        Equivalent to public_bytes(Raw, Raw).
        """

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """


# For LibreSSL
if hasattr(rust_openssl, "x25519"):
    X25519PublicKey.register(rust_openssl.x25519.X25519PublicKey)


class X25519PrivateKey(metaclass=abc.ABCMeta):
    @classmethod
    def generate(cls) -> X25519PrivateKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.x25519_supported():
            raise UnsupportedAlgorithm(
                "X25519 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
            )
        return backend.x25519_generate_key()

    @classmethod
    def from_private_bytes(cls, data: bytes) -> X25519PrivateKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.x25519_supported():
            raise UnsupportedAlgorithm(
                "X25519 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
            )

        return backend.x25519_load_private_bytes(data)

    @abc.abstractmethod
    def public_key(self) -> X25519PublicKey:
        """
        Returns the public key assosciated with this private key
        """

    @abc.abstractmethod
    def private_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PrivateFormat,
        encryption_algorithm: _serialization.KeySerializationEncryption,
    ) -> bytes:
        """
        The serialized bytes of the private key.
        """

    @abc.abstractmethod
    def private_bytes_raw(self) -> bytes:
        """
        The raw bytes of the private key.
        Equivalent to private_bytes(Raw, Raw, NoEncryption()).
        """

    @abc.abstractmethod
    def exchange(self, peer_public_key: X25519PublicKey) -> bytes:
        """
        Performs a key exchange operation using the provided peer's public key.
        """


# For LibreSSL
if hasattr(rust_openssl, "x25519"):
    X25519PrivateKey.register(rust_openssl.x25519.X25519PrivateKey)

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc

from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
from cryptography.hazmat.primitives import _serialization


class X448PublicKey(metaclass=abc.ABCMeta):
    @classmethod
    def from_public_bytes(cls, data: bytes) -> X448PublicKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.x448_supported():
            raise UnsupportedAlgorithm(
                "X448 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
            )

        return backend.x448_load_public_bytes(data)

    @abc.abstractmethod
    def public_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PublicFormat,
    ) -> bytes:
        """
        The serialized bytes of the public key.
        """

    @abc.abstractmethod
    def public_bytes_raw(self) -> bytes:
        """
        The raw bytes of the public key.
        Equivalent to public_bytes(Raw, Raw).
        """

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """


if hasattr(rust_openssl, "x448"):
    X448PublicKey.register(rust_openssl.x448.X448PublicKey)


class X448PrivateKey(metaclass=abc.ABCMeta):
    @classmethod
    def generate(cls) -> X448PrivateKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.x448_supported():
            raise UnsupportedAlgorithm(
                "X448 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
            )
        return backend.x448_generate_key()

    @classmethod
    def from_private_bytes(cls, data: bytes) -> X448PrivateKey:
        from cryptography.hazmat.backends.openssl.backend import backend

        if not backend.x448_supported():
            raise UnsupportedAlgorithm(
                "X448 is not supported by this version of OpenSSL.",
                _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM,
            )

        return backend.x448_load_private_bytes(data)

    @abc.abstractmethod
    def public_key(self) -> X448PublicKey:
        """
        Returns the public key associated with this private key
        """

    @abc.abstractmethod
    def private_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PrivateFormat,
        encryption_algorithm: _serialization.KeySerializationEncryption,
    ) -> bytes:
        """
        The serialized bytes of the private key.
        """

    @abc.abstractmethod
    def private_bytes_raw(self) -> bytes:
        """
        The raw bytes of the private key.
        Equivalent to private_bytes(Raw, Raw, NoEncryption()).
        """

    @abc.abstractmethod
    def exchange(self, peer_public_key: X448PublicKey) -> bytes:
        """
        Performs a key exchange operation using the provided peer's public key.
        """


if hasattr(rust_openssl, "x448"):
    X448PrivateKey.register(rust_openssl.x448.X448PrivateKey)

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import os
import typing

from cryptography import exceptions, utils
from cryptography.hazmat.backends.openssl import aead
from cryptography.hazmat.backends.openssl.backend import backend
from cryptography.hazmat.bindings._rust import FixedPool


class ChaCha20Poly1305:
    _MAX_SIZE = 2**31 - 1

    def __init__(self, key: bytes):
        if not backend.aead_cipher_supported(self):
            raise exceptions.UnsupportedAlgorithm(
                "ChaCha20Poly1305 is not supported by this version of OpenSSL",
                exceptions._Reasons.UNSUPPORTED_CIPHER,
            )
        utils._check_byteslike("key", key)

        if len(key) != 32:
            raise ValueError("ChaCha20Poly1305 key must be 32 bytes.")

        self._key = key
        self._pool = FixedPool(self._create_fn)

    @classmethod
    def generate_key(cls) -> bytes:
        return os.urandom(32)

    def _create_fn(self):
        return aead._aead_create_ctx(backend, self, self._key)

    def encrypt(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: typing.Optional[bytes],
    ) -> bytes:
        if associated_data is None:
            associated_data = b""

        if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
            # This is OverflowError to match what cffi would raise
            raise OverflowError(
                "Data or associated data too long. Max 2**31 - 1 bytes"
            )

        self._check_params(nonce, data, associated_data)
        with self._pool.acquire() as ctx:
            return aead._encrypt(
                backend, self, nonce, data, [associated_data], 16, ctx
            )

    def decrypt(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: typing.Optional[bytes],
    ) -> bytes:
        if associated_data is None:
            associated_data = b""

        self._check_params(nonce, data, associated_data)
        with self._pool.acquire() as ctx:
            return aead._decrypt(
                backend, self, nonce, data, [associated_data], 16, ctx
            )

    def _check_params(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: bytes,
    ) -> None:
        utils._check_byteslike("nonce", nonce)
        utils._check_byteslike("data", data)
        utils._check_byteslike("associated_data", associated_data)
        if len(nonce) != 12:
            raise ValueError("Nonce must be 12 bytes")


class AESCCM:
    _MAX_SIZE = 2**31 - 1

    def __init__(self, key: bytes, tag_length: int = 16):
        utils._check_byteslike("key", key)
        if len(key) not in (16, 24, 32):
            raise ValueError("AESCCM key must be 128, 192, or 256 bits.")

        self._key = key
        if not isinstance(tag_length, int):
            raise TypeError("tag_length must be an integer")

        if tag_length not in (4, 6, 8, 10, 12, 14, 16):
            raise ValueError("Invalid tag_length")

        self._tag_length = tag_length

        if not backend.aead_cipher_supported(self):
            raise exceptions.UnsupportedAlgorithm(
                "AESCCM is not supported by this version of OpenSSL",
                exceptions._Reasons.UNSUPPORTED_CIPHER,
            )

    @classmethod
    def generate_key(cls, bit_length: int) -> bytes:
        if not isinstance(bit_length, int):
            raise TypeError("bit_length must be an integer")

        if bit_length not in (128, 192, 256):
            raise ValueError("bit_length must be 128, 192, or 256")

        return os.urandom(bit_length // 8)

    def encrypt(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: typing.Optional[bytes],
    ) -> bytes:
        if associated_data is None:
            associated_data = b""

        if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
            # This is OverflowError to match what cffi would raise
            raise OverflowError(
                "Data or associated data too long. Max 2**31 - 1 bytes"
            )

        self._check_params(nonce, data, associated_data)
        self._validate_lengths(nonce, len(data))
        return aead._encrypt(
            backend, self, nonce, data, [associated_data], self._tag_length
        )

    def decrypt(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: typing.Optional[bytes],
    ) -> bytes:
        if associated_data is None:
            associated_data = b""

        self._check_params(nonce, data, associated_data)
        return aead._decrypt(
            backend, self, nonce, data, [associated_data], self._tag_length
        )

    def _validate_lengths(self, nonce: bytes, data_len: int) -> None:
        # For information about computing this, see
        # https://tools.ietf.org/html/rfc3610#section-2.1
        l_val = 15 - len(nonce)
        if 2 ** (8 * l_val) < data_len:
            raise ValueError("Data too long for nonce")

    def _check_params(
        self, nonce: bytes, data: bytes, associated_data: bytes
    ) -> None:
        utils._check_byteslike("nonce", nonce)
        utils._check_byteslike("data", data)
        utils._check_byteslike("associated_data", associated_data)
        if not 7 <= len(nonce) <= 13:
            raise ValueError("Nonce must be between 7 and 13 bytes")


class AESGCM:
    _MAX_SIZE = 2**31 - 1

    def __init__(self, key: bytes):
        utils._check_byteslike("key", key)
        if len(key) not in (16, 24, 32):
            raise ValueError("AESGCM key must be 128, 192, or 256 bits.")

        self._key = key

    @classmethod
    def generate_key(cls, bit_length: int) -> bytes:
        if not isinstance(bit_length, int):
            raise TypeError("bit_length must be an integer")

        if bit_length not in (128, 192, 256):
            raise ValueError("bit_length must be 128, 192, or 256")

        return os.urandom(bit_length // 8)

    def encrypt(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: typing.Optional[bytes],
    ) -> bytes:
        if associated_data is None:
            associated_data = b""

        if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
            # This is OverflowError to match what cffi would raise
            raise OverflowError(
                "Data or associated data too long. Max 2**31 - 1 bytes"
            )

        self._check_params(nonce, data, associated_data)
        return aead._encrypt(backend, self, nonce, data, [associated_data], 16)

    def decrypt(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: typing.Optional[bytes],
    ) -> bytes:
        if associated_data is None:
            associated_data = b""

        self._check_params(nonce, data, associated_data)
        return aead._decrypt(backend, self, nonce, data, [associated_data], 16)

    def _check_params(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: bytes,
    ) -> None:
        utils._check_byteslike("nonce", nonce)
        utils._check_byteslike("data", data)
        utils._check_byteslike("associated_data", associated_data)
        if len(nonce) < 8 or len(nonce) > 128:
            raise ValueError("Nonce must be between 8 and 128 bytes")


class AESOCB3:
    _MAX_SIZE = 2**31 - 1

    def __init__(self, key: bytes):
        utils._check_byteslike("key", key)
        if len(key) not in (16, 24, 32):
            raise ValueError("AESOCB3 key must be 128, 192, or 256 bits.")

        self._key = key

        if not backend.aead_cipher_supported(self):
            raise exceptions.UnsupportedAlgorithm(
                "OCB3 is not supported by this version of OpenSSL",
                exceptions._Reasons.UNSUPPORTED_CIPHER,
            )

    @classmethod
    def generate_key(cls, bit_length: int) -> bytes:
        if not isinstance(bit_length, int):
            raise TypeError("bit_length must be an integer")

        if bit_length not in (128, 192, 256):
            raise ValueError("bit_length must be 128, 192, or 256")

        return os.urandom(bit_length // 8)

    def encrypt(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: typing.Optional[bytes],
    ) -> bytes:
        if associated_data is None:
            associated_data = b""

        if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE:
            # This is OverflowError to match what cffi would raise
            raise OverflowError(
                "Data or associated data too long. Max 2**31 - 1 bytes"
            )

        self._check_params(nonce, data, associated_data)
        return aead._encrypt(backend, self, nonce, data, [associated_data], 16)

    def decrypt(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: typing.Optional[bytes],
    ) -> bytes:
        if associated_data is None:
            associated_data = b""

        self._check_params(nonce, data, associated_data)
        return aead._decrypt(backend, self, nonce, data, [associated_data], 16)

    def _check_params(
        self,
        nonce: bytes,
        data: bytes,
        associated_data: bytes,
    ) -> None:
        utils._check_byteslike("nonce", nonce)
        utils._check_byteslike("data", data)
        utils._check_byteslike("associated_data", associated_data)
        if len(nonce) < 12 or len(nonce) > 15:
            raise ValueError("Nonce must be between 12 and 15 bytes")


class AESSIV:
    _MAX_SIZE = 2**31 - 1

    def __init__(self, key: bytes):
        utils._check_byteslike("key", key)
        if len(key) not in (32, 48, 64):
            raise ValueError("AESSIV key must be 256, 384, or 512 bits.")

        self._key = key

        if not backend.aead_cipher_supported(self):
            raise exceptions.UnsupportedAlgorithm(
                "AES-SIV is not supported by this version of OpenSSL",
                exceptions._Reasons.UNSUPPORTED_CIPHER,
            )

    @classmethod
    def generate_key(cls, bit_length: int) -> bytes:
        if not isinstance(bit_length, int):
            raise TypeError("bit_length must be an integer")

        if bit_length not in (256, 384, 512):
            raise ValueError("bit_length must be 256, 384, or 512")

        return os.urandom(bit_length // 8)

    def encrypt(
        self,
        data: bytes,
        associated_data: typing.Optional[typing.List[bytes]],
    ) -> bytes:
        if associated_data is None:
            associated_data = []

        self._check_params(data, associated_data)

        if len(data) > self._MAX_SIZE or any(
            len(ad) > self._MAX_SIZE for ad in associated_data
        ):
            # This is OverflowError to match what cffi would raise
            raise OverflowError(
                "Data or associated data too long. Max 2**31 - 1 bytes"
            )

        return aead._encrypt(backend, self, b"", data, associated_data, 16)

    def decrypt(
        self,
        data: bytes,
        associated_data: typing.Optional[typing.List[bytes]],
    ) -> bytes:
        if associated_data is None:
            associated_data = []

        self._check_params(data, associated_data)

        return aead._decrypt(backend, self, b"", data, associated_data, 16)

    def _check_params(
        self,
        data: bytes,
        associated_data: typing.List[bytes],
    ) -> None:
        utils._check_byteslike("data", data)
        if len(data) == 0:
            raise ValueError("data must not be zero length")

        if not isinstance(associated_data, list):
            raise TypeError(
                "associated_data must be a list of bytes-like objects or None"
            )
        for x in associated_data:
            utils._check_byteslike("associated_data elements", x)

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

from cryptography import utils
from cryptography.hazmat.primitives.ciphers import (
    BlockCipherAlgorithm,
    CipherAlgorithm,
)


def _verify_key_size(algorithm: CipherAlgorithm, key: bytes) -> bytes:
    # Verify that the key is instance of bytes
    utils._check_byteslike("key", key)

    # Verify that the key size matches the expected key size
    if len(key) * 8 not in algorithm.key_sizes:
        raise ValueError(
            "Invalid key size ({}) for {}.".format(
                len(key) * 8, algorithm.name
            )
        )
    return key


class AES(BlockCipherAlgorithm):
    name = "AES"
    block_size = 128
    # 512 added to support AES-256-XTS, which uses 512-bit keys
    key_sizes = frozenset([128, 192, 256, 512])

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)

    @property
    def key_size(self) -> int:
        return len(self.key) * 8


class AES128(BlockCipherAlgorithm):
    name = "AES"
    block_size = 128
    key_sizes = frozenset([128])
    key_size = 128

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)


class AES256(BlockCipherAlgorithm):
    name = "AES"
    block_size = 128
    key_sizes = frozenset([256])
    key_size = 256

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)


class Camellia(BlockCipherAlgorithm):
    name = "camellia"
    block_size = 128
    key_sizes = frozenset([128, 192, 256])

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)

    @property
    def key_size(self) -> int:
        return len(self.key) * 8


class TripleDES(BlockCipherAlgorithm):
    name = "3DES"
    block_size = 64
    key_sizes = frozenset([64, 128, 192])

    def __init__(self, key: bytes):
        if len(key) == 8:
            key += key + key
        elif len(key) == 16:
            key += key[:8]
        self.key = _verify_key_size(self, key)

    @property
    def key_size(self) -> int:
        return len(self.key) * 8


class Blowfish(BlockCipherAlgorithm):
    name = "Blowfish"
    block_size = 64
    key_sizes = frozenset(range(32, 449, 8))

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)

    @property
    def key_size(self) -> int:
        return len(self.key) * 8


_BlowfishInternal = Blowfish
utils.deprecated(
    Blowfish,
    __name__,
    "Blowfish has been deprecated",
    utils.DeprecatedIn37,
    name="Blowfish",
)


class CAST5(BlockCipherAlgorithm):
    name = "CAST5"
    block_size = 64
    key_sizes = frozenset(range(40, 129, 8))

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)

    @property
    def key_size(self) -> int:
        return len(self.key) * 8


_CAST5Internal = CAST5
utils.deprecated(
    CAST5,
    __name__,
    "CAST5 has been deprecated",
    utils.DeprecatedIn37,
    name="CAST5",
)


class ARC4(CipherAlgorithm):
    name = "RC4"
    key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256])

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)

    @property
    def key_size(self) -> int:
        return len(self.key) * 8


class IDEA(BlockCipherAlgorithm):
    name = "IDEA"
    block_size = 64
    key_sizes = frozenset([128])

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)

    @property
    def key_size(self) -> int:
        return len(self.key) * 8


_IDEAInternal = IDEA
utils.deprecated(
    IDEA,
    __name__,
    "IDEA has been deprecated",
    utils.DeprecatedIn37,
    name="IDEA",
)


class SEED(BlockCipherAlgorithm):
    name = "SEED"
    block_size = 128
    key_sizes = frozenset([128])

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)

    @property
    def key_size(self) -> int:
        return len(self.key) * 8


_SEEDInternal = SEED
utils.deprecated(
    SEED,
    __name__,
    "SEED has been deprecated",
    utils.DeprecatedIn37,
    name="SEED",
)


class ChaCha20(CipherAlgorithm):
    name = "ChaCha20"
    key_sizes = frozenset([256])

    def __init__(self, key: bytes, nonce: bytes):
        self.key = _verify_key_size(self, key)
        utils._check_byteslike("nonce", nonce)

        if len(nonce) != 16:
            raise ValueError("nonce must be 128-bits (16 bytes)")

        self._nonce = nonce

    @property
    def nonce(self) -> bytes:
        return self._nonce

    @property
    def key_size(self) -> int:
        return len(self.key) * 8


class SM4(BlockCipherAlgorithm):
    name = "SM4"
    block_size = 128
    key_sizes = frozenset([128])

    def __init__(self, key: bytes):
        self.key = _verify_key_size(self, key)

    @property
    def key_size(self) -> int:
        return len(self.key) * 8

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

from cryptography.exceptions import (
    AlreadyFinalized,
    AlreadyUpdated,
    NotYetFinalized,
)
from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm
from cryptography.hazmat.primitives.ciphers import modes

if typing.TYPE_CHECKING:
    from cryptography.hazmat.backends.openssl.ciphers import (
        _CipherContext as _BackendCipherContext,
    )


class CipherContext(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def update(self, data: bytes) -> bytes:
        """
        Processes the provided bytes through the cipher and returns the results
        as bytes.
        """

    @abc.abstractmethod
    def update_into(self, data: bytes, buf: bytes) -> int:
        """
        Processes the provided bytes and writes the resulting data into the
        provided buffer. Returns the number of bytes written.
        """

    @abc.abstractmethod
    def finalize(self) -> bytes:
        """
        Returns the results of processing the final block as bytes.
        """


class AEADCipherContext(CipherContext, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def authenticate_additional_data(self, data: bytes) -> None:
        """
        Authenticates the provided bytes.
        """


class AEADDecryptionContext(AEADCipherContext, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def finalize_with_tag(self, tag: bytes) -> bytes:
        """
        Returns the results of processing the final block as bytes and allows
        delayed passing of the authentication tag.
        """


class AEADEncryptionContext(AEADCipherContext, metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def tag(self) -> bytes:
        """
        Returns tag bytes. This is only available after encryption is
        finalized.
        """


Mode = typing.TypeVar(
    "Mode", bound=typing.Optional[modes.Mode], covariant=True
)


class Cipher(typing.Generic[Mode]):
    def __init__(
        self,
        algorithm: CipherAlgorithm,
        mode: Mode,
        backend: typing.Any = None,
    ) -> None:
        if not isinstance(algorithm, CipherAlgorithm):
            raise TypeError("Expected interface of CipherAlgorithm.")

        if mode is not None:
            # mypy needs this assert to narrow the type from our generic
            # type. Maybe it won't some time in the future.
            assert isinstance(mode, modes.Mode)
            mode.validate_for_algorithm(algorithm)

        self.algorithm = algorithm
        self.mode = mode

    @typing.overload
    def encryptor(
        self: Cipher[modes.ModeWithAuthenticationTag],
    ) -> AEADEncryptionContext:
        ...

    @typing.overload
    def encryptor(
        self: _CIPHER_TYPE,
    ) -> CipherContext:
        ...

    def encryptor(self):
        if isinstance(self.mode, modes.ModeWithAuthenticationTag):
            if self.mode.tag is not None:
                raise ValueError(
                    "Authentication tag must be None when encrypting."
                )
        from cryptography.hazmat.backends.openssl.backend import backend

        ctx = backend.create_symmetric_encryption_ctx(
            self.algorithm, self.mode
        )
        return self._wrap_ctx(ctx, encrypt=True)

    @typing.overload
    def decryptor(
        self: Cipher[modes.ModeWithAuthenticationTag],
    ) -> AEADDecryptionContext:
        ...

    @typing.overload
    def decryptor(
        self: _CIPHER_TYPE,
    ) -> CipherContext:
        ...

    def decryptor(self):
        from cryptography.hazmat.backends.openssl.backend import backend

        ctx = backend.create_symmetric_decryption_ctx(
            self.algorithm, self.mode
        )
        return self._wrap_ctx(ctx, encrypt=False)

    def _wrap_ctx(
        self, ctx: _BackendCipherContext, encrypt: bool
    ) -> typing.Union[
        AEADEncryptionContext, AEADDecryptionContext, CipherContext
    ]:
        if isinstance(self.mode, modes.ModeWithAuthenticationTag):
            if encrypt:
                return _AEADEncryptionContext(ctx)
            else:
                return _AEADDecryptionContext(ctx)
        else:
            return _CipherContext(ctx)


_CIPHER_TYPE = Cipher[
    typing.Union[
        modes.ModeWithNonce,
        modes.ModeWithTweak,
        None,
        modes.ECB,
        modes.ModeWithInitializationVector,
    ]
]


class _CipherContext(CipherContext):
    _ctx: typing.Optional[_BackendCipherContext]

    def __init__(self, ctx: _BackendCipherContext) -> None:
        self._ctx = ctx

    def update(self, data: bytes) -> bytes:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")
        return self._ctx.update(data)

    def update_into(self, data: bytes, buf: bytes) -> int:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")
        return self._ctx.update_into(data, buf)

    def finalize(self) -> bytes:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")
        data = self._ctx.finalize()
        self._ctx = None
        return data


class _AEADCipherContext(AEADCipherContext):
    _ctx: typing.Optional[_BackendCipherContext]
    _tag: typing.Optional[bytes]

    def __init__(self, ctx: _BackendCipherContext) -> None:
        self._ctx = ctx
        self._bytes_processed = 0
        self._aad_bytes_processed = 0
        self._tag = None
        self._updated = False

    def _check_limit(self, data_size: int) -> None:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")
        self._updated = True
        self._bytes_processed += data_size
        if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES:
            raise ValueError(
                "{} has a maximum encrypted byte limit of {}".format(
                    self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES
                )
            )

    def update(self, data: bytes) -> bytes:
        self._check_limit(len(data))
        # mypy needs this assert even though _check_limit already checked
        assert self._ctx is not None
        return self._ctx.update(data)

    def update_into(self, data: bytes, buf: bytes) -> int:
        self._check_limit(len(data))
        # mypy needs this assert even though _check_limit already checked
        assert self._ctx is not None
        return self._ctx.update_into(data, buf)

    def finalize(self) -> bytes:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")
        data = self._ctx.finalize()
        self._tag = self._ctx.tag
        self._ctx = None
        return data

    def authenticate_additional_data(self, data: bytes) -> None:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")
        if self._updated:
            raise AlreadyUpdated("Update has been called on this context.")

        self._aad_bytes_processed += len(data)
        if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES:
            raise ValueError(
                "{} has a maximum AAD byte limit of {}".format(
                    self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES
                )
            )

        self._ctx.authenticate_additional_data(data)


class _AEADDecryptionContext(_AEADCipherContext, AEADDecryptionContext):
    def finalize_with_tag(self, tag: bytes) -> bytes:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")
        data = self._ctx.finalize_with_tag(tag)
        self._tag = self._ctx.tag
        self._ctx = None
        return data


class _AEADEncryptionContext(_AEADCipherContext, AEADEncryptionContext):
    @property
    def tag(self) -> bytes:
        if self._ctx is not None:
            raise NotYetFinalized(
                "You must finalize encryption before " "getting the tag."
            )
        assert self._tag is not None
        return self._tag

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.primitives._cipheralgorithm import (
    BlockCipherAlgorithm,
    CipherAlgorithm,
)
from cryptography.hazmat.primitives.ciphers import algorithms


class Mode(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def name(self) -> str:
        """
        A string naming this mode (e.g. "ECB", "CBC").
        """

    @abc.abstractmethod
    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
        """
        Checks that all the necessary invariants of this (mode, algorithm)
        combination are met.
        """


class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def initialization_vector(self) -> bytes:
        """
        The value of the initialization vector for this mode as bytes.
        """


class ModeWithTweak(Mode, metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def tweak(self) -> bytes:
        """
        The value of the tweak for this mode as bytes.
        """


class ModeWithNonce(Mode, metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def nonce(self) -> bytes:
        """
        The value of the nonce for this mode as bytes.
        """


class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def tag(self) -> typing.Optional[bytes]:
        """
        The value of the tag supplied to the constructor of this mode.
        """


def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None:
    if algorithm.key_size > 256 and algorithm.name == "AES":
        raise ValueError(
            "Only 128, 192, and 256 bit keys are allowed for this AES mode"
        )


def _check_iv_length(
    self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm
) -> None:
    if len(self.initialization_vector) * 8 != algorithm.block_size:
        raise ValueError(
            "Invalid IV size ({}) for {}.".format(
                len(self.initialization_vector), self.name
            )
        )


def _check_nonce_length(
    nonce: bytes, name: str, algorithm: CipherAlgorithm
) -> None:
    if not isinstance(algorithm, BlockCipherAlgorithm):
        raise UnsupportedAlgorithm(
            f"{name} requires a block cipher algorithm",
            _Reasons.UNSUPPORTED_CIPHER,
        )
    if len(nonce) * 8 != algorithm.block_size:
        raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.")


def _check_iv_and_key_length(
    self: ModeWithInitializationVector, algorithm: CipherAlgorithm
) -> None:
    if not isinstance(algorithm, BlockCipherAlgorithm):
        raise UnsupportedAlgorithm(
            f"{self} requires a block cipher algorithm",
            _Reasons.UNSUPPORTED_CIPHER,
        )
    _check_aes_key_length(self, algorithm)
    _check_iv_length(self, algorithm)


class CBC(ModeWithInitializationVector):
    name = "CBC"

    def __init__(self, initialization_vector: bytes):
        utils._check_byteslike("initialization_vector", initialization_vector)
        self._initialization_vector = initialization_vector

    @property
    def initialization_vector(self) -> bytes:
        return self._initialization_vector

    validate_for_algorithm = _check_iv_and_key_length


class XTS(ModeWithTweak):
    name = "XTS"

    def __init__(self, tweak: bytes):
        utils._check_byteslike("tweak", tweak)

        if len(tweak) != 16:
            raise ValueError("tweak must be 128-bits (16 bytes)")

        self._tweak = tweak

    @property
    def tweak(self) -> bytes:
        return self._tweak

    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
        if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)):
            raise TypeError(
                "The AES128 and AES256 classes do not support XTS, please use "
                "the standard AES class instead."
            )

        if algorithm.key_size not in (256, 512):
            raise ValueError(
                "The XTS specification requires a 256-bit key for AES-128-XTS"
                " and 512-bit key for AES-256-XTS"
            )


class ECB(Mode):
    name = "ECB"

    validate_for_algorithm = _check_aes_key_length


class OFB(ModeWithInitializationVector):
    name = "OFB"

    def __init__(self, initialization_vector: bytes):
        utils._check_byteslike("initialization_vector", initialization_vector)
        self._initialization_vector = initialization_vector

    @property
    def initialization_vector(self) -> bytes:
        return self._initialization_vector

    validate_for_algorithm = _check_iv_and_key_length


class CFB(ModeWithInitializationVector):
    name = "CFB"

    def __init__(self, initialization_vector: bytes):
        utils._check_byteslike("initialization_vector", initialization_vector)
        self._initialization_vector = initialization_vector

    @property
    def initialization_vector(self) -> bytes:
        return self._initialization_vector

    validate_for_algorithm = _check_iv_and_key_length


class CFB8(ModeWithInitializationVector):
    name = "CFB8"

    def __init__(self, initialization_vector: bytes):
        utils._check_byteslike("initialization_vector", initialization_vector)
        self._initialization_vector = initialization_vector

    @property
    def initialization_vector(self) -> bytes:
        return self._initialization_vector

    validate_for_algorithm = _check_iv_and_key_length


class CTR(ModeWithNonce):
    name = "CTR"

    def __init__(self, nonce: bytes):
        utils._check_byteslike("nonce", nonce)
        self._nonce = nonce

    @property
    def nonce(self) -> bytes:
        return self._nonce

    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
        _check_aes_key_length(self, algorithm)
        _check_nonce_length(self.nonce, self.name, algorithm)


class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag):
    name = "GCM"
    _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8
    _MAX_AAD_BYTES = (2**64) // 8

    def __init__(
        self,
        initialization_vector: bytes,
        tag: typing.Optional[bytes] = None,
        min_tag_length: int = 16,
    ):
        # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive
        # This is a sane limit anyway so we'll enforce it here.
        utils._check_byteslike("initialization_vector", initialization_vector)
        if len(initialization_vector) < 8 or len(initialization_vector) > 128:
            raise ValueError(
                "initialization_vector must be between 8 and 128 bytes (64 "
                "and 1024 bits)."
            )
        self._initialization_vector = initialization_vector
        if tag is not None:
            utils._check_bytes("tag", tag)
            if min_tag_length < 4:
                raise ValueError("min_tag_length must be >= 4")
            if len(tag) < min_tag_length:
                raise ValueError(
                    "Authentication tag must be {} bytes or longer.".format(
                        min_tag_length
                    )
                )
        self._tag = tag
        self._min_tag_length = min_tag_length

    @property
    def tag(self) -> typing.Optional[bytes]:
        return self._tag

    @property
    def initialization_vector(self) -> bytes:
        return self._initialization_vector

    def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None:
        _check_aes_key_length(self, algorithm)
        if not isinstance(algorithm, BlockCipherAlgorithm):
            raise UnsupportedAlgorithm(
                "GCM requires a block cipher algorithm",
                _Reasons.UNSUPPORTED_CIPHER,
            )
        block_size_bytes = algorithm.block_size // 8
        if self._tag is not None and len(self._tag) > block_size_bytes:
            raise ValueError(
                "Authentication tag cannot be more than {} bytes.".format(
                    block_size_bytes
                )
            )

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography import utils
from cryptography.exceptions import AlreadyFinalized
from cryptography.hazmat.primitives import ciphers

if typing.TYPE_CHECKING:
    from cryptography.hazmat.backends.openssl.cmac import _CMACContext


class CMAC:
    _ctx: typing.Optional[_CMACContext]
    _algorithm: ciphers.BlockCipherAlgorithm

    def __init__(
        self,
        algorithm: ciphers.BlockCipherAlgorithm,
        backend: typing.Any = None,
        ctx: typing.Optional[_CMACContext] = None,
    ) -> None:
        if not isinstance(algorithm, ciphers.BlockCipherAlgorithm):
            raise TypeError("Expected instance of BlockCipherAlgorithm.")
        self._algorithm = algorithm

        if ctx is None:
            from cryptography.hazmat.backends.openssl.backend import (
                backend as ossl,
            )

            self._ctx = ossl.create_cmac_ctx(self._algorithm)
        else:
            self._ctx = ctx

    def update(self, data: bytes) -> None:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")

        utils._check_bytes("data", data)
        self._ctx.update(data)

    def finalize(self) -> bytes:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")
        digest = self._ctx.finalize()
        self._ctx = None
        return digest

    def verify(self, signature: bytes) -> None:
        utils._check_bytes("signature", signature)
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")

        ctx, self._ctx = self._ctx, None
        ctx.verify(signature)

    def copy(self) -> CMAC:
        if self._ctx is None:
            raise AlreadyFinalized("Context was already finalized.")
        return CMAC(self._algorithm, ctx=self._ctx.copy())

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/cmac.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

from cryptography.hazmat.bindings._rust import openssl as rust_openssl

__all__ = [
    "HashAlgorithm",
    "HashContext",
    "Hash",
    "ExtendableOutputFunction",
    "SHA1",
    "SHA512_224",
    "SHA512_256",
    "SHA224",
    "SHA256",
    "SHA384",
    "SHA512",
    "SHA3_224",
    "SHA3_256",
    "SHA3_384",
    "SHA3_512",
    "SHAKE128",
    "SHAKE256",
    "MD5",
    "BLAKE2b",
    "BLAKE2s",
    "SM3",
]


class HashAlgorithm(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def name(self) -> str:
        """
        A string naming this algorithm (e.g. "sha256", "md5").
        """

    @property
    @abc.abstractmethod
    def digest_size(self) -> int:
        """
        The size of the resulting digest in bytes.
        """

    @property
    @abc.abstractmethod
    def block_size(self) -> typing.Optional[int]:
        """
        The internal block size of the hash function, or None if the hash
        function does not use blocks internally (e.g. SHA3).
        """


class HashContext(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def algorithm(self) -> HashAlgorithm:
        """
        A HashAlgorithm that will be used by this context.
        """

    @abc.abstractmethod
    def update(self, data: bytes) -> None:
        """
        Processes the provided bytes through the hash.
        """

    @abc.abstractmethod
    def finalize(self) -> bytes:
        """
        Finalizes the hash context and returns the hash digest as bytes.
        """

    @abc.abstractmethod
    def copy(self) -> HashContext:
        """
        Return a HashContext that is a copy of the current context.
        """


Hash = rust_openssl.hashes.Hash
HashContext.register(Hash)


class ExtendableOutputFunction(metaclass=abc.ABCMeta):
    """
    An interface for extendable output functions.
    """


class SHA1(HashAlgorithm):
    name = "sha1"
    digest_size = 20
    block_size = 64


class SHA512_224(HashAlgorithm):  # noqa: N801
    name = "sha512-224"
    digest_size = 28
    block_size = 128


class SHA512_256(HashAlgorithm):  # noqa: N801
    name = "sha512-256"
    digest_size = 32
    block_size = 128


class SHA224(HashAlgorithm):
    name = "sha224"
    digest_size = 28
    block_size = 64


class SHA256(HashAlgorithm):
    name = "sha256"
    digest_size = 32
    block_size = 64


class SHA384(HashAlgorithm):
    name = "sha384"
    digest_size = 48
    block_size = 128


class SHA512(HashAlgorithm):
    name = "sha512"
    digest_size = 64
    block_size = 128


class SHA3_224(HashAlgorithm):  # noqa: N801
    name = "sha3-224"
    digest_size = 28
    block_size = None


class SHA3_256(HashAlgorithm):  # noqa: N801
    name = "sha3-256"
    digest_size = 32
    block_size = None


class SHA3_384(HashAlgorithm):  # noqa: N801
    name = "sha3-384"
    digest_size = 48
    block_size = None


class SHA3_512(HashAlgorithm):  # noqa: N801
    name = "sha3-512"
    digest_size = 64
    block_size = None


class SHAKE128(HashAlgorithm, ExtendableOutputFunction):
    name = "shake128"
    block_size = None

    def __init__(self, digest_size: int):
        if not isinstance(digest_size, int):
            raise TypeError("digest_size must be an integer")

        if digest_size < 1:
            raise ValueError("digest_size must be a positive integer")

        self._digest_size = digest_size

    @property
    def digest_size(self) -> int:
        return self._digest_size


class SHAKE256(HashAlgorithm, ExtendableOutputFunction):
    name = "shake256"
    block_size = None

    def __init__(self, digest_size: int):
        if not isinstance(digest_size, int):
            raise TypeError("digest_size must be an integer")

        if digest_size < 1:
            raise ValueError("digest_size must be a positive integer")

        self._digest_size = digest_size

    @property
    def digest_size(self) -> int:
        return self._digest_size


class MD5(HashAlgorithm):
    name = "md5"
    digest_size = 16
    block_size = 64


class BLAKE2b(HashAlgorithm):
    name = "blake2b"
    _max_digest_size = 64
    _min_digest_size = 1
    block_size = 128

    def __init__(self, digest_size: int):
        if digest_size != 64:
            raise ValueError("Digest size must be 64")

        self._digest_size = digest_size

    @property
    def digest_size(self) -> int:
        return self._digest_size


class BLAKE2s(HashAlgorithm):
    name = "blake2s"
    block_size = 64
    _max_digest_size = 32
    _min_digest_size = 1

    def __init__(self, digest_size: int):
        if digest_size != 32:
            raise ValueError("Digest size must be 32")

        self._digest_size = digest_size

    @property
    def digest_size(self) -> int:
        return self._digest_size


class SM3(HashAlgorithm):
    name = "sm3"
    digest_size = 32
    block_size = 64

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/hashes.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography import utils
from cryptography.exceptions import AlreadyFinalized, InvalidKey
from cryptography.hazmat.primitives import constant_time, hashes, hmac
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


def _int_to_u32be(n: int) -> bytes:
    return n.to_bytes(length=4, byteorder="big")


def _common_args_checks(
    algorithm: hashes.HashAlgorithm,
    length: int,
    otherinfo: typing.Optional[bytes],
) -> None:
    max_length = algorithm.digest_size * (2**32 - 1)
    if length > max_length:
        raise ValueError(f"Cannot derive keys larger than {max_length} bits.")
    if otherinfo is not None:
        utils._check_bytes("otherinfo", otherinfo)


def _concatkdf_derive(
    key_material: bytes,
    length: int,
    auxfn: typing.Callable[[], hashes.HashContext],
    otherinfo: bytes,
) -> bytes:
    utils._check_byteslike("key_material", key_material)
    output = [b""]
    outlen = 0
    counter = 1

    while length > outlen:
        h = auxfn()
        h.update(_int_to_u32be(counter))
        h.update(key_material)
        h.update(otherinfo)
        output.append(h.finalize())
        outlen += len(output[-1])
        counter += 1

    return b"".join(output)[:length]


class ConcatKDFHash(KeyDerivationFunction):
    def __init__(
        self,
        algorithm: hashes.HashAlgorithm,
        length: int,
        otherinfo: typing.Optional[bytes],
        backend: typing.Any = None,
    ):
        _common_args_checks(algorithm, length, otherinfo)
        self._algorithm = algorithm
        self._length = length
        self._otherinfo: bytes = otherinfo if otherinfo is not None else b""

        self._used = False

    def _hash(self) -> hashes.Hash:
        return hashes.Hash(self._algorithm)

    def derive(self, key_material: bytes) -> bytes:
        if self._used:
            raise AlreadyFinalized
        self._used = True
        return _concatkdf_derive(
            key_material, self._length, self._hash, self._otherinfo
        )

    def verify(self, key_material: bytes, expected_key: bytes) -> None:
        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
            raise InvalidKey


class ConcatKDFHMAC(KeyDerivationFunction):
    def __init__(
        self,
        algorithm: hashes.HashAlgorithm,
        length: int,
        salt: typing.Optional[bytes],
        otherinfo: typing.Optional[bytes],
        backend: typing.Any = None,
    ):
        _common_args_checks(algorithm, length, otherinfo)
        self._algorithm = algorithm
        self._length = length
        self._otherinfo: bytes = otherinfo if otherinfo is not None else b""

        if algorithm.block_size is None:
            raise TypeError(f"{algorithm.name} is unsupported for ConcatKDF")

        if salt is None:
            salt = b"\x00" * algorithm.block_size
        else:
            utils._check_bytes("salt", salt)

        self._salt = salt

        self._used = False

    def _hmac(self) -> hmac.HMAC:
        return hmac.HMAC(self._salt, self._algorithm)

    def derive(self, key_material: bytes) -> bytes:
        if self._used:
            raise AlreadyFinalized
        self._used = True
        return _concatkdf_derive(
            key_material, self._length, self._hmac, self._otherinfo
        )

    def verify(self, key_material: bytes, expected_key: bytes) -> None:
        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
            raise InvalidKey

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography import utils
from cryptography.exceptions import AlreadyFinalized, InvalidKey
from cryptography.hazmat.primitives import constant_time, hashes, hmac
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


class HKDF(KeyDerivationFunction):
    def __init__(
        self,
        algorithm: hashes.HashAlgorithm,
        length: int,
        salt: typing.Optional[bytes],
        info: typing.Optional[bytes],
        backend: typing.Any = None,
    ):
        self._algorithm = algorithm

        if salt is None:
            salt = b"\x00" * self._algorithm.digest_size
        else:
            utils._check_bytes("salt", salt)

        self._salt = salt

        self._hkdf_expand = HKDFExpand(self._algorithm, length, info)

    def _extract(self, key_material: bytes) -> bytes:
        h = hmac.HMAC(self._salt, self._algorithm)
        h.update(key_material)
        return h.finalize()

    def derive(self, key_material: bytes) -> bytes:
        utils._check_byteslike("key_material", key_material)
        return self._hkdf_expand.derive(self._extract(key_material))

    def verify(self, key_material: bytes, expected_key: bytes) -> None:
        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
            raise InvalidKey


class HKDFExpand(KeyDerivationFunction):
    def __init__(
        self,
        algorithm: hashes.HashAlgorithm,
        length: int,
        info: typing.Optional[bytes],
        backend: typing.Any = None,
    ):
        self._algorithm = algorithm

        max_length = 255 * algorithm.digest_size

        if length > max_length:
            raise ValueError(
                f"Cannot derive keys larger than {max_length} octets."
            )

        self._length = length

        if info is None:
            info = b""
        else:
            utils._check_bytes("info", info)

        self._info = info

        self._used = False

    def _expand(self, key_material: bytes) -> bytes:
        output = [b""]
        counter = 1

        while self._algorithm.digest_size * (len(output) - 1) < self._length:
            h = hmac.HMAC(key_material, self._algorithm)
            h.update(output[-1])
            h.update(self._info)
            h.update(bytes([counter]))
            output.append(h.finalize())
            counter += 1

        return b"".join(output)[: self._length]

    def derive(self, key_material: bytes) -> bytes:
        utils._check_byteslike("key_material", key_material)
        if self._used:
            raise AlreadyFinalized

        self._used = True
        return self._expand(key_material)

    def verify(self, key_material: bytes, expected_key: bytes) -> None:
        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
            raise InvalidKey

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography import utils
from cryptography.exceptions import (
    AlreadyFinalized,
    InvalidKey,
    UnsupportedAlgorithm,
    _Reasons,
)
from cryptography.hazmat.primitives import (
    ciphers,
    cmac,
    constant_time,
    hashes,
    hmac,
)
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


class Mode(utils.Enum):
    CounterMode = "ctr"


class CounterLocation(utils.Enum):
    BeforeFixed = "before_fixed"
    AfterFixed = "after_fixed"
    MiddleFixed = "middle_fixed"


class _KBKDFDeriver:
    def __init__(
        self,
        prf: typing.Callable,
        mode: Mode,
        length: int,
        rlen: int,
        llen: typing.Optional[int],
        location: CounterLocation,
        break_location: typing.Optional[int],
        label: typing.Optional[bytes],
        context: typing.Optional[bytes],
        fixed: typing.Optional[bytes],
    ):
        assert callable(prf)

        if not isinstance(mode, Mode):
            raise TypeError("mode must be of type Mode")

        if not isinstance(location, CounterLocation):
            raise TypeError("location must be of type CounterLocation")

        if break_location is None and location is CounterLocation.MiddleFixed:
            raise ValueError("Please specify a break_location")

        if (
            break_location is not None
            and location != CounterLocation.MiddleFixed
        ):
            raise ValueError(
                "break_location is ignored when location is not"
                " CounterLocation.MiddleFixed"
            )

        if break_location is not None and not isinstance(break_location, int):
            raise TypeError("break_location must be an integer")

        if break_location is not None and break_location < 0:
            raise ValueError("break_location must be a positive integer")

        if (label or context) and fixed:
            raise ValueError(
                "When supplying fixed data, " "label and context are ignored."
            )

        if rlen is None or not self._valid_byte_length(rlen):
            raise ValueError("rlen must be between 1 and 4")

        if llen is None and fixed is None:
            raise ValueError("Please specify an llen")

        if llen is not None and not isinstance(llen, int):
            raise TypeError("llen must be an integer")

        if label is None:
            label = b""

        if context is None:
            context = b""

        utils._check_bytes("label", label)
        utils._check_bytes("context", context)
        self._prf = prf
        self._mode = mode
        self._length = length
        self._rlen = rlen
        self._llen = llen
        self._location = location
        self._break_location = break_location
        self._label = label
        self._context = context
        self._used = False
        self._fixed_data = fixed

    @staticmethod
    def _valid_byte_length(value: int) -> bool:
        if not isinstance(value, int):
            raise TypeError("value must be of type int")

        value_bin = utils.int_to_bytes(1, value)
        if not 1 <= len(value_bin) <= 4:
            return False
        return True

    def derive(self, key_material: bytes, prf_output_size: int) -> bytes:
        if self._used:
            raise AlreadyFinalized

        utils._check_byteslike("key_material", key_material)
        self._used = True

        # inverse floor division (equivalent to ceiling)
        rounds = -(-self._length // prf_output_size)

        output = [b""]

        # For counter mode, the number of iterations shall not be
        # larger than 2^r-1, where r <= 32 is the binary length of the counter
        # This ensures that the counter values used as an input to the
        # PRF will not repeat during a particular call to the KDF function.
        r_bin = utils.int_to_bytes(1, self._rlen)
        if rounds > pow(2, len(r_bin) * 8) - 1:
            raise ValueError("There are too many iterations.")

        fixed = self._generate_fixed_input()

        if self._location == CounterLocation.BeforeFixed:
            data_before_ctr = b""
            data_after_ctr = fixed
        elif self._location == CounterLocation.AfterFixed:
            data_before_ctr = fixed
            data_after_ctr = b""
        else:
            if isinstance(
                self._break_location, int
            ) and self._break_location > len(fixed):
                raise ValueError("break_location offset > len(fixed)")
            data_before_ctr = fixed[: self._break_location]
            data_after_ctr = fixed[self._break_location :]

        for i in range(1, rounds + 1):
            h = self._prf(key_material)

            counter = utils.int_to_bytes(i, self._rlen)
            input_data = data_before_ctr + counter + data_after_ctr

            h.update(input_data)

            output.append(h.finalize())

        return b"".join(output)[: self._length]

    def _generate_fixed_input(self) -> bytes:
        if self._fixed_data and isinstance(self._fixed_data, bytes):
            return self._fixed_data

        l_val = utils.int_to_bytes(self._length * 8, self._llen)

        return b"".join([self._label, b"\x00", self._context, l_val])


class KBKDFHMAC(KeyDerivationFunction):
    def __init__(
        self,
        algorithm: hashes.HashAlgorithm,
        mode: Mode,
        length: int,
        rlen: int,
        llen: typing.Optional[int],
        location: CounterLocation,
        label: typing.Optional[bytes],
        context: typing.Optional[bytes],
        fixed: typing.Optional[bytes],
        backend: typing.Any = None,
        *,
        break_location: typing.Optional[int] = None,
    ):
        if not isinstance(algorithm, hashes.HashAlgorithm):
            raise UnsupportedAlgorithm(
                "Algorithm supplied is not a supported hash algorithm.",
                _Reasons.UNSUPPORTED_HASH,
            )

        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        if not ossl.hmac_supported(algorithm):
            raise UnsupportedAlgorithm(
                "Algorithm supplied is not a supported hmac algorithm.",
                _Reasons.UNSUPPORTED_HASH,
            )

        self._algorithm = algorithm

        self._deriver = _KBKDFDeriver(
            self._prf,
            mode,
            length,
            rlen,
            llen,
            location,
            break_location,
            label,
            context,
            fixed,
        )

    def _prf(self, key_material: bytes) -> hmac.HMAC:
        return hmac.HMAC(key_material, self._algorithm)

    def derive(self, key_material: bytes) -> bytes:
        return self._deriver.derive(key_material, self._algorithm.digest_size)

    def verify(self, key_material: bytes, expected_key: bytes) -> None:
        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
            raise InvalidKey


class KBKDFCMAC(KeyDerivationFunction):
    def __init__(
        self,
        algorithm,
        mode: Mode,
        length: int,
        rlen: int,
        llen: typing.Optional[int],
        location: CounterLocation,
        label: typing.Optional[bytes],
        context: typing.Optional[bytes],
        fixed: typing.Optional[bytes],
        backend: typing.Any = None,
        *,
        break_location: typing.Optional[int] = None,
    ):
        if not issubclass(
            algorithm, ciphers.BlockCipherAlgorithm
        ) or not issubclass(algorithm, ciphers.CipherAlgorithm):
            raise UnsupportedAlgorithm(
                "Algorithm supplied is not a supported cipher algorithm.",
                _Reasons.UNSUPPORTED_CIPHER,
            )

        self._algorithm = algorithm
        self._cipher: typing.Optional[ciphers.BlockCipherAlgorithm] = None

        self._deriver = _KBKDFDeriver(
            self._prf,
            mode,
            length,
            rlen,
            llen,
            location,
            break_location,
            label,
            context,
            fixed,
        )

    def _prf(self, _: bytes) -> cmac.CMAC:
        assert self._cipher is not None

        return cmac.CMAC(self._cipher)

    def derive(self, key_material: bytes) -> bytes:
        self._cipher = self._algorithm(key_material)

        assert self._cipher is not None

        from cryptography.hazmat.backends.openssl.backend import (
            backend as ossl,
        )

        if not ossl.cmac_algorithm_supported(self._cipher):
            raise UnsupportedAlgorithm(
                "Algorithm supplied is not a supported cipher algorithm.",
                _Reasons.UNSUPPORTED_CIPHER,
            )

        return self._deriver.derive(key_material, self._cipher.block_size // 8)

    def verify(self, key_material: bytes, expected_key: bytes) -> None:
        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
            raise InvalidKey

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography import utils
from cryptography.exceptions import AlreadyFinalized, InvalidKey
from cryptography.hazmat.primitives import constant_time, hashes
from cryptography.hazmat.primitives.kdf import KeyDerivationFunction


def _int_to_u32be(n: int) -> bytes:
    return n.to_bytes(length=4, byteorder="big")


class X963KDF(KeyDerivationFunction):
    def __init__(
        self,
        algorithm: hashes.HashAlgorithm,
        length: int,
        sharedinfo: typing.Optional[bytes],
        backend: typing.Any = None,
    ):
        max_len = algorithm.digest_size * (2**32 - 1)
        if length > max_len:
            raise ValueError(f"Cannot derive keys larger than {max_len} bits.")
        if sharedinfo is not None:
            utils._check_bytes("sharedinfo", sharedinfo)

        self._algorithm = algorithm
        self._length = length
        self._sharedinfo = sharedinfo
        self._used = False

    def derive(self, key_material: bytes) -> bytes:
        if self._used:
            raise AlreadyFinalized
        self._used = True
        utils._check_byteslike("key_material", key_material)
        output = [b""]
        outlen = 0
        counter = 1

        while self._length > outlen:
            h = hashes.Hash(self._algorithm)
            h.update(key_material)
            h.update(_int_to_u32be(counter))
            if self._sharedinfo is not None:
                h.update(self._sharedinfo)
            output.append(h.finalize())
            outlen += len(output[-1])
            counter += 1

        return b"".join(output)[: self._length]

    def verify(self, key_material: bytes, expected_key: bytes) -> None:
        if not constant_time.bytes_eq(self.derive(key_material), expected_key):
            raise InvalidKey

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import ECB
from cryptography.hazmat.primitives.constant_time import bytes_eq


def _wrap_core(
    wrapping_key: bytes,
    a: bytes,
    r: typing.List[bytes],
) -> bytes:
    # RFC 3394 Key Wrap - 2.2.1 (index method)
    encryptor = Cipher(AES(wrapping_key), ECB()).encryptor()
    n = len(r)
    for j in range(6):
        for i in range(n):
            # every encryption operation is a discrete 16 byte chunk (because
            # AES has a 128-bit block size) and since we're using ECB it is
            # safe to reuse the encryptor for the entire operation
            b = encryptor.update(a + r[i])
            a = (
                int.from_bytes(b[:8], byteorder="big") ^ ((n * j) + i + 1)
            ).to_bytes(length=8, byteorder="big")
            r[i] = b[-8:]

    assert encryptor.finalize() == b""

    return a + b"".join(r)


def aes_key_wrap(
    wrapping_key: bytes,
    key_to_wrap: bytes,
    backend: typing.Any = None,
) -> bytes:
    if len(wrapping_key) not in [16, 24, 32]:
        raise ValueError("The wrapping key must be a valid AES key length")

    if len(key_to_wrap) < 16:
        raise ValueError("The key to wrap must be at least 16 bytes")

    if len(key_to_wrap) % 8 != 0:
        raise ValueError("The key to wrap must be a multiple of 8 bytes")

    a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
    r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)]
    return _wrap_core(wrapping_key, a, r)


def _unwrap_core(
    wrapping_key: bytes,
    a: bytes,
    r: typing.List[bytes],
) -> typing.Tuple[bytes, typing.List[bytes]]:
    # Implement RFC 3394 Key Unwrap - 2.2.2 (index method)
    decryptor = Cipher(AES(wrapping_key), ECB()).decryptor()
    n = len(r)
    for j in reversed(range(6)):
        for i in reversed(range(n)):
            atr = (
                int.from_bytes(a, byteorder="big") ^ ((n * j) + i + 1)
            ).to_bytes(length=8, byteorder="big") + r[i]
            # every decryption operation is a discrete 16 byte chunk so
            # it is safe to reuse the decryptor for the entire operation
            b = decryptor.update(atr)
            a = b[:8]
            r[i] = b[-8:]

    assert decryptor.finalize() == b""
    return a, r


def aes_key_wrap_with_padding(
    wrapping_key: bytes,
    key_to_wrap: bytes,
    backend: typing.Any = None,
) -> bytes:
    if len(wrapping_key) not in [16, 24, 32]:
        raise ValueError("The wrapping key must be a valid AES key length")

    aiv = b"\xA6\x59\x59\xA6" + len(key_to_wrap).to_bytes(
        length=4, byteorder="big"
    )
    # pad the key to wrap if necessary
    pad = (8 - (len(key_to_wrap) % 8)) % 8
    key_to_wrap = key_to_wrap + b"\x00" * pad
    if len(key_to_wrap) == 8:
        # RFC 5649 - 4.1 - exactly 8 octets after padding
        encryptor = Cipher(AES(wrapping_key), ECB()).encryptor()
        b = encryptor.update(aiv + key_to_wrap)
        assert encryptor.finalize() == b""
        return b
    else:
        r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)]
        return _wrap_core(wrapping_key, aiv, r)


def aes_key_unwrap_with_padding(
    wrapping_key: bytes,
    wrapped_key: bytes,
    backend: typing.Any = None,
) -> bytes:
    if len(wrapped_key) < 16:
        raise InvalidUnwrap("Must be at least 16 bytes")

    if len(wrapping_key) not in [16, 24, 32]:
        raise ValueError("The wrapping key must be a valid AES key length")

    if len(wrapped_key) == 16:
        # RFC 5649 - 4.2 - exactly two 64-bit blocks
        decryptor = Cipher(AES(wrapping_key), ECB()).decryptor()
        out = decryptor.update(wrapped_key)
        assert decryptor.finalize() == b""
        a = out[:8]
        data = out[8:]
        n = 1
    else:
        r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)]
        encrypted_aiv = r.pop(0)
        n = len(r)
        a, r = _unwrap_core(wrapping_key, encrypted_aiv, r)
        data = b"".join(r)

    # 1) Check that MSB(32,A) = A65959A6.
    # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n.  If so, let
    #    MLI = LSB(32,A).
    # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of
    #    the output data are zero.
    mli = int.from_bytes(a[4:], byteorder="big")
    b = (8 * n) - mli
    if (
        not bytes_eq(a[:4], b"\xa6\x59\x59\xa6")
        or not 8 * (n - 1) < mli <= 8 * n
        or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b))
    ):
        raise InvalidUnwrap()

    if b == 0:
        return data
    else:
        return data[:-b]


def aes_key_unwrap(
    wrapping_key: bytes,
    wrapped_key: bytes,
    backend: typing.Any = None,
) -> bytes:
    if len(wrapped_key) < 24:
        raise InvalidUnwrap("Must be at least 24 bytes")

    if len(wrapped_key) % 8 != 0:
        raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes")

    if len(wrapping_key) not in [16, 24, 32]:
        raise ValueError("The wrapping key must be a valid AES key length")

    aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
    r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)]
    a = r.pop(0)
    a, r = _unwrap_core(wrapping_key, a, r)
    if not bytes_eq(a, aiv):
        raise InvalidUnwrap()

    return b"".join(r)


class InvalidUnwrap(Exception):
    pass

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import typing

from cryptography import utils
from cryptography.exceptions import AlreadyFinalized
from cryptography.hazmat.bindings._rust import (
    check_ansix923_padding,
    check_pkcs7_padding,
)


class PaddingContext(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def update(self, data: bytes) -> bytes:
        """
        Pads the provided bytes and returns any available data as bytes.
        """

    @abc.abstractmethod
    def finalize(self) -> bytes:
        """
        Finalize the padding, returns bytes.
        """


def _byte_padding_check(block_size: int) -> None:
    if not (0 <= block_size <= 2040):
        raise ValueError("block_size must be in range(0, 2041).")

    if block_size % 8 != 0:
        raise ValueError("block_size must be a multiple of 8.")


def _byte_padding_update(
    buffer_: typing.Optional[bytes], data: bytes, block_size: int
) -> typing.Tuple[bytes, bytes]:
    if buffer_ is None:
        raise AlreadyFinalized("Context was already finalized.")

    utils._check_byteslike("data", data)

    buffer_ += bytes(data)

    finished_blocks = len(buffer_) // (block_size // 8)

    result = buffer_[: finished_blocks * (block_size // 8)]
    buffer_ = buffer_[finished_blocks * (block_size // 8) :]

    return buffer_, result


def _byte_padding_pad(
    buffer_: typing.Optional[bytes],
    block_size: int,
    paddingfn: typing.Callable[[int], bytes],
) -> bytes:
    if buffer_ is None:
        raise AlreadyFinalized("Context was already finalized.")

    pad_size = block_size // 8 - len(buffer_)
    return buffer_ + paddingfn(pad_size)


def _byte_unpadding_update(
    buffer_: typing.Optional[bytes], data: bytes, block_size: int
) -> typing.Tuple[bytes, bytes]:
    if buffer_ is None:
        raise AlreadyFinalized("Context was already finalized.")

    utils._check_byteslike("data", data)

    buffer_ += bytes(data)

    finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0)

    result = buffer_[: finished_blocks * (block_size // 8)]
    buffer_ = buffer_[finished_blocks * (block_size // 8) :]

    return buffer_, result


def _byte_unpadding_check(
    buffer_: typing.Optional[bytes],
    block_size: int,
    checkfn: typing.Callable[[bytes], int],
) -> bytes:
    if buffer_ is None:
        raise AlreadyFinalized("Context was already finalized.")

    if len(buffer_) != block_size // 8:
        raise ValueError("Invalid padding bytes.")

    valid = checkfn(buffer_)

    if not valid:
        raise ValueError("Invalid padding bytes.")

    pad_size = buffer_[-1]
    return buffer_[:-pad_size]


class PKCS7:
    def __init__(self, block_size: int):
        _byte_padding_check(block_size)
        self.block_size = block_size

    def padder(self) -> PaddingContext:
        return _PKCS7PaddingContext(self.block_size)

    def unpadder(self) -> PaddingContext:
        return _PKCS7UnpaddingContext(self.block_size)


class _PKCS7PaddingContext(PaddingContext):
    _buffer: typing.Optional[bytes]

    def __init__(self, block_size: int):
        self.block_size = block_size
        # TODO: more copies than necessary, we should use zero-buffer (#193)
        self._buffer = b""

    def update(self, data: bytes) -> bytes:
        self._buffer, result = _byte_padding_update(
            self._buffer, data, self.block_size
        )
        return result

    def _padding(self, size: int) -> bytes:
        return bytes([size]) * size

    def finalize(self) -> bytes:
        result = _byte_padding_pad(
            self._buffer, self.block_size, self._padding
        )
        self._buffer = None
        return result


class _PKCS7UnpaddingContext(PaddingContext):
    _buffer: typing.Optional[bytes]

    def __init__(self, block_size: int):
        self.block_size = block_size
        # TODO: more copies than necessary, we should use zero-buffer (#193)
        self._buffer = b""

    def update(self, data: bytes) -> bytes:
        self._buffer, result = _byte_unpadding_update(
            self._buffer, data, self.block_size
        )
        return result

    def finalize(self) -> bytes:
        result = _byte_unpadding_check(
            self._buffer, self.block_size, check_pkcs7_padding
        )
        self._buffer = None
        return result


class ANSIX923:
    def __init__(self, block_size: int):
        _byte_padding_check(block_size)
        self.block_size = block_size

    def padder(self) -> PaddingContext:
        return _ANSIX923PaddingContext(self.block_size)

    def unpadder(self) -> PaddingContext:
        return _ANSIX923UnpaddingContext(self.block_size)


class _ANSIX923PaddingContext(PaddingContext):
    _buffer: typing.Optional[bytes]

    def __init__(self, block_size: int):
        self.block_size = block_size
        # TODO: more copies than necessary, we should use zero-buffer (#193)
        self._buffer = b""

    def update(self, data: bytes) -> bytes:
        self._buffer, result = _byte_padding_update(
            self._buffer, data, self.block_size
        )
        return result

    def _padding(self, size: int) -> bytes:
        return bytes([0]) * (size - 1) + bytes([size])

    def finalize(self) -> bytes:
        result = _byte_padding_pad(
            self._buffer, self.block_size, self._padding
        )
        self._buffer = None
        return result


class _ANSIX923UnpaddingContext(PaddingContext):
    _buffer: typing.Optional[bytes]

    def __init__(self, block_size: int):
        self.block_size = block_size
        # TODO: more copies than necessary, we should use zero-buffer (#193)
        self._buffer = b""

    def update(self, data: bytes) -> bytes:
        self._buffer, result = _byte_unpadding_update(
            self._buffer, data, self.block_size
        )
        return result

    def finalize(self) -> bytes:
        result = _byte_unpadding_check(
            self._buffer,
            self.block_size,
            check_ansix923_padding,
        )
        self._buffer = None
        return result

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/padding.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives.asymmetric.types import (
    PrivateKeyTypes,
    PublicKeyTypes,
)


def load_pem_private_key(
    data: bytes,
    password: typing.Optional[bytes],
    backend: typing.Any = None,
    *,
    unsafe_skip_rsa_key_validation: bool = False,
) -> PrivateKeyTypes:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.load_pem_private_key(
        data, password, unsafe_skip_rsa_key_validation
    )


def load_pem_public_key(
    data: bytes, backend: typing.Any = None
) -> PublicKeyTypes:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.load_pem_public_key(data)


def load_pem_parameters(
    data: bytes, backend: typing.Any = None
) -> dh.DHParameters:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.load_pem_parameters(data)


def load_der_private_key(
    data: bytes,
    password: typing.Optional[bytes],
    backend: typing.Any = None,
    *,
    unsafe_skip_rsa_key_validation: bool = False,
) -> PrivateKeyTypes:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.load_der_private_key(
        data, password, unsafe_skip_rsa_key_validation
    )


def load_der_public_key(
    data: bytes, backend: typing.Any = None
) -> PublicKeyTypes:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.load_der_public_key(data)


def load_der_parameters(
    data: bytes, backend: typing.Any = None
) -> dh.DHParameters:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.load_der_parameters(data)

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography import x509
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives._serialization import PBES as PBES
from cryptography.hazmat.primitives.asymmetric import (
    dsa,
    ec,
    ed448,
    ed25519,
    rsa,
)
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes

__all__ = [
    "PBES",
    "PKCS12PrivateKeyTypes",
    "PKCS12Certificate",
    "PKCS12KeyAndCertificates",
    "load_key_and_certificates",
    "load_pkcs12",
    "serialize_key_and_certificates",
]

PKCS12PrivateKeyTypes = typing.Union[
    rsa.RSAPrivateKey,
    dsa.DSAPrivateKey,
    ec.EllipticCurvePrivateKey,
    ed25519.Ed25519PrivateKey,
    ed448.Ed448PrivateKey,
]


class PKCS12Certificate:
    def __init__(
        self,
        cert: x509.Certificate,
        friendly_name: typing.Optional[bytes],
    ):
        if not isinstance(cert, x509.Certificate):
            raise TypeError("Expecting x509.Certificate object")
        if friendly_name is not None and not isinstance(friendly_name, bytes):
            raise TypeError("friendly_name must be bytes or None")
        self._cert = cert
        self._friendly_name = friendly_name

    @property
    def friendly_name(self) -> typing.Optional[bytes]:
        return self._friendly_name

    @property
    def certificate(self) -> x509.Certificate:
        return self._cert

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, PKCS12Certificate):
            return NotImplemented

        return (
            self.certificate == other.certificate
            and self.friendly_name == other.friendly_name
        )

    def __hash__(self) -> int:
        return hash((self.certificate, self.friendly_name))

    def __repr__(self) -> str:
        return "<PKCS12Certificate({}, friendly_name={!r})>".format(
            self.certificate, self.friendly_name
        )


class PKCS12KeyAndCertificates:
    def __init__(
        self,
        key: typing.Optional[PrivateKeyTypes],
        cert: typing.Optional[PKCS12Certificate],
        additional_certs: typing.List[PKCS12Certificate],
    ):
        if key is not None and not isinstance(
            key,
            (
                rsa.RSAPrivateKey,
                dsa.DSAPrivateKey,
                ec.EllipticCurvePrivateKey,
                ed25519.Ed25519PrivateKey,
                ed448.Ed448PrivateKey,
            ),
        ):
            raise TypeError(
                "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448"
                " private key, or None."
            )
        if cert is not None and not isinstance(cert, PKCS12Certificate):
            raise TypeError("cert must be a PKCS12Certificate object or None")
        if not all(
            isinstance(add_cert, PKCS12Certificate)
            for add_cert in additional_certs
        ):
            raise TypeError(
                "all values in additional_certs must be PKCS12Certificate"
                " objects"
            )
        self._key = key
        self._cert = cert
        self._additional_certs = additional_certs

    @property
    def key(self) -> typing.Optional[PrivateKeyTypes]:
        return self._key

    @property
    def cert(self) -> typing.Optional[PKCS12Certificate]:
        return self._cert

    @property
    def additional_certs(self) -> typing.List[PKCS12Certificate]:
        return self._additional_certs

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, PKCS12KeyAndCertificates):
            return NotImplemented

        return (
            self.key == other.key
            and self.cert == other.cert
            and self.additional_certs == other.additional_certs
        )

    def __hash__(self) -> int:
        return hash((self.key, self.cert, tuple(self.additional_certs)))

    def __repr__(self) -> str:
        fmt = (
            "<PKCS12KeyAndCertificates(key={}, cert={}, additional_certs={})>"
        )
        return fmt.format(self.key, self.cert, self.additional_certs)


def load_key_and_certificates(
    data: bytes,
    password: typing.Optional[bytes],
    backend: typing.Any = None,
) -> typing.Tuple[
    typing.Optional[PrivateKeyTypes],
    typing.Optional[x509.Certificate],
    typing.List[x509.Certificate],
]:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.load_key_and_certificates_from_pkcs12(data, password)


def load_pkcs12(
    data: bytes,
    password: typing.Optional[bytes],
    backend: typing.Any = None,
) -> PKCS12KeyAndCertificates:
    from cryptography.hazmat.backends.openssl.backend import backend as ossl

    return ossl.load_pkcs12(data, password)


_PKCS12CATypes = typing.Union[
    x509.Certificate,
    PKCS12Certificate,
]


def serialize_key_and_certificates(
    name: typing.Optional[bytes],
    key: typing.Optional[PKCS12PrivateKeyTypes],
    cert: typing.Optional[x509.Certificate],
    cas: typing.Optional[typing.Iterable[_PKCS12CATypes]],
    encryption_algorithm: serialization.KeySerializationEncryption,
) -> bytes:
    if key is not None and not isinstance(
        key,
        (
            rsa.RSAPrivateKey,
            dsa.DSAPrivateKey,
            ec.EllipticCurvePrivateKey,
            ed25519.Ed25519PrivateKey,
            ed448.Ed448PrivateKey,
        ),
    ):
        raise TypeError(
            "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448"
            " private key, or None."
        )
    if cert is not None and not isinstance(cert, x509.Certificate):
        raise TypeError("cert must be a certificate or None")

    if cas is not None:
        cas = list(cas)
        if not all(
            isinstance(
                val,
                (
                    x509.Certificate,
                    PKCS12Certificate,
                ),
            )
            for val in cas
        ):
            raise TypeError("all values in cas must be certificates")

    if not isinstance(
        encryption_algorithm, serialization.KeySerializationEncryption
    ):
        raise TypeError(
            "Key encryption algorithm must be a "
            "KeySerializationEncryption instance"
        )

    if key is None and cert is None and not cas:
        raise ValueError("You must supply at least one of key, cert, or cas")

    from cryptography.hazmat.backends.openssl.backend import backend

    return backend.serialize_key_and_certificates_to_pkcs12(
        name, key, cert, cas, encryption_algorithm
    )

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import email.base64mime
import email.generator
import email.message
import email.policy
import io
import typing

from cryptography import utils, x509
from cryptography.hazmat.bindings._rust import pkcs7 as rust_pkcs7
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, rsa
from cryptography.utils import _check_byteslike


def load_pem_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]:
    from cryptography.hazmat.backends.openssl.backend import backend

    return backend.load_pem_pkcs7_certificates(data)


def load_der_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]:
    from cryptography.hazmat.backends.openssl.backend import backend

    return backend.load_der_pkcs7_certificates(data)


def serialize_certificates(
    certs: typing.List[x509.Certificate],
    encoding: serialization.Encoding,
) -> bytes:
    return rust_pkcs7.serialize_certificates(certs, encoding)


PKCS7HashTypes = typing.Union[
    hashes.SHA224,
    hashes.SHA256,
    hashes.SHA384,
    hashes.SHA512,
]

PKCS7PrivateKeyTypes = typing.Union[
    rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey
]


class PKCS7Options(utils.Enum):
    Text = "Add text/plain MIME type"
    Binary = "Don't translate input data into canonical MIME format"
    DetachedSignature = "Don't embed data in the PKCS7 structure"
    NoCapabilities = "Don't embed SMIME capabilities"
    NoAttributes = "Don't embed authenticatedAttributes"
    NoCerts = "Don't embed signer certificate"


class PKCS7SignatureBuilder:
    def __init__(
        self,
        data: typing.Optional[bytes] = None,
        signers: typing.List[
            typing.Tuple[
                x509.Certificate,
                PKCS7PrivateKeyTypes,
                PKCS7HashTypes,
            ]
        ] = [],
        additional_certs: typing.List[x509.Certificate] = [],
    ):
        self._data = data
        self._signers = signers
        self._additional_certs = additional_certs

    def set_data(self, data: bytes) -> PKCS7SignatureBuilder:
        _check_byteslike("data", data)
        if self._data is not None:
            raise ValueError("data may only be set once")

        return PKCS7SignatureBuilder(data, self._signers)

    def add_signer(
        self,
        certificate: x509.Certificate,
        private_key: PKCS7PrivateKeyTypes,
        hash_algorithm: PKCS7HashTypes,
    ) -> PKCS7SignatureBuilder:
        if not isinstance(
            hash_algorithm,
            (
                hashes.SHA224,
                hashes.SHA256,
                hashes.SHA384,
                hashes.SHA512,
            ),
        ):
            raise TypeError(
                "hash_algorithm must be one of hashes.SHA224, "
                "SHA256, SHA384, or SHA512"
            )
        if not isinstance(certificate, x509.Certificate):
            raise TypeError("certificate must be a x509.Certificate")

        if not isinstance(
            private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)
        ):
            raise TypeError("Only RSA & EC keys are supported at this time.")

        return PKCS7SignatureBuilder(
            self._data,
            self._signers + [(certificate, private_key, hash_algorithm)],
        )

    def add_certificate(
        self, certificate: x509.Certificate
    ) -> PKCS7SignatureBuilder:
        if not isinstance(certificate, x509.Certificate):
            raise TypeError("certificate must be a x509.Certificate")

        return PKCS7SignatureBuilder(
            self._data, self._signers, self._additional_certs + [certificate]
        )

    def sign(
        self,
        encoding: serialization.Encoding,
        options: typing.Iterable[PKCS7Options],
        backend: typing.Any = None,
    ) -> bytes:
        if len(self._signers) == 0:
            raise ValueError("Must have at least one signer")
        if self._data is None:
            raise ValueError("You must add data to sign")
        options = list(options)
        if not all(isinstance(x, PKCS7Options) for x in options):
            raise ValueError("options must be from the PKCS7Options enum")
        if encoding not in (
            serialization.Encoding.PEM,
            serialization.Encoding.DER,
            serialization.Encoding.SMIME,
        ):
            raise ValueError(
                "Must be PEM, DER, or SMIME from the Encoding enum"
            )

        # Text is a meaningless option unless it is accompanied by
        # DetachedSignature
        if (
            PKCS7Options.Text in options
            and PKCS7Options.DetachedSignature not in options
        ):
            raise ValueError(
                "When passing the Text option you must also pass "
                "DetachedSignature"
            )

        if PKCS7Options.Text in options and encoding in (
            serialization.Encoding.DER,
            serialization.Encoding.PEM,
        ):
            raise ValueError(
                "The Text option is only available for SMIME serialization"
            )

        # No attributes implies no capabilities so we'll error if you try to
        # pass both.
        if (
            PKCS7Options.NoAttributes in options
            and PKCS7Options.NoCapabilities in options
        ):
            raise ValueError(
                "NoAttributes is a superset of NoCapabilities. Do not pass "
                "both values."
            )

        return rust_pkcs7.sign_and_serialize(self, encoding, options)


def _smime_encode(
    data: bytes, signature: bytes, micalg: str, text_mode: bool
) -> bytes:
    # This function works pretty hard to replicate what OpenSSL does
    # precisely. For good and for ill.

    m = email.message.Message()
    m.add_header("MIME-Version", "1.0")
    m.add_header(
        "Content-Type",
        "multipart/signed",
        protocol="application/x-pkcs7-signature",
        micalg=micalg,
    )

    m.preamble = "This is an S/MIME signed message\n"

    msg_part = OpenSSLMimePart()
    msg_part.set_payload(data)
    if text_mode:
        msg_part.add_header("Content-Type", "text/plain")
    m.attach(msg_part)

    sig_part = email.message.MIMEPart()
    sig_part.add_header(
        "Content-Type", "application/x-pkcs7-signature", name="smime.p7s"
    )
    sig_part.add_header("Content-Transfer-Encoding", "base64")
    sig_part.add_header(
        "Content-Disposition", "attachment", filename="smime.p7s"
    )
    sig_part.set_payload(
        email.base64mime.body_encode(signature, maxlinelen=65)
    )
    del sig_part["MIME-Version"]
    m.attach(sig_part)

    fp = io.BytesIO()
    g = email.generator.BytesGenerator(
        fp,
        maxheaderlen=0,
        mangle_from_=False,
        policy=m.policy.clone(linesep="\r\n"),
    )
    g.flatten(m)
    return fp.getvalue()


class OpenSSLMimePart(email.message.MIMEPart):
    # A MIMEPart subclass that replicates OpenSSL's behavior of not including
    # a newline if there are no headers.
    def _write_headers(self, generator) -> None:
        if list(self.raw_items()):
            generator._write_headers(self)

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import binascii
import enum
import os
import re
import typing
import warnings
from base64 import encodebytes as _base64_encode
from dataclasses import dataclass

from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import (
    dsa,
    ec,
    ed25519,
    padding,
    rsa,
)
from cryptography.hazmat.primitives.asymmetric import utils as asym_utils
from cryptography.hazmat.primitives.ciphers import (
    AEADDecryptionContext,
    Cipher,
    algorithms,
    modes,
)
from cryptography.hazmat.primitives.serialization import (
    Encoding,
    KeySerializationEncryption,
    NoEncryption,
    PrivateFormat,
    PublicFormat,
    _KeySerializationEncryption,
)

try:
    from bcrypt import kdf as _bcrypt_kdf

    _bcrypt_supported = True
except ImportError:
    _bcrypt_supported = False

    def _bcrypt_kdf(
        password: bytes,
        salt: bytes,
        desired_key_bytes: int,
        rounds: int,
        ignore_few_rounds: bool = False,
    ) -> bytes:
        raise UnsupportedAlgorithm("Need bcrypt module")


_SSH_ED25519 = b"ssh-ed25519"
_SSH_RSA = b"ssh-rsa"
_SSH_DSA = b"ssh-dss"
_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256"
_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384"
_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521"
_CERT_SUFFIX = b"-cert-v01@openssh.com"

# These are not key types, only algorithms, so they cannot appear
# as a public key type
_SSH_RSA_SHA256 = b"rsa-sha2-256"
_SSH_RSA_SHA512 = b"rsa-sha2-512"

_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)")
_SK_MAGIC = b"openssh-key-v1\0"
_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----"
_SK_END = b"-----END OPENSSH PRIVATE KEY-----"
_BCRYPT = b"bcrypt"
_NONE = b"none"
_DEFAULT_CIPHER = b"aes256-ctr"
_DEFAULT_ROUNDS = 16

# re is only way to work on bytes-like data
_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL)

# padding for max blocksize
_PADDING = memoryview(bytearray(range(1, 1 + 16)))


@dataclass
class _SSHCipher:
    alg: typing.Type[algorithms.AES]
    key_len: int
    mode: typing.Union[
        typing.Type[modes.CTR],
        typing.Type[modes.CBC],
        typing.Type[modes.GCM],
    ]
    block_len: int
    iv_len: int
    tag_len: typing.Optional[int]
    is_aead: bool


# ciphers that are actually used in key wrapping
_SSH_CIPHERS: typing.Dict[bytes, _SSHCipher] = {
    b"aes256-ctr": _SSHCipher(
        alg=algorithms.AES,
        key_len=32,
        mode=modes.CTR,
        block_len=16,
        iv_len=16,
        tag_len=None,
        is_aead=False,
    ),
    b"aes256-cbc": _SSHCipher(
        alg=algorithms.AES,
        key_len=32,
        mode=modes.CBC,
        block_len=16,
        iv_len=16,
        tag_len=None,
        is_aead=False,
    ),
    b"aes256-gcm@openssh.com": _SSHCipher(
        alg=algorithms.AES,
        key_len=32,
        mode=modes.GCM,
        block_len=16,
        iv_len=12,
        tag_len=16,
        is_aead=True,
    ),
}

# map local curve name to key type
_ECDSA_KEY_TYPE = {
    "secp256r1": _ECDSA_NISTP256,
    "secp384r1": _ECDSA_NISTP384,
    "secp521r1": _ECDSA_NISTP521,
}


def _get_ssh_key_type(
    key: typing.Union[SSHPrivateKeyTypes, SSHPublicKeyTypes]
) -> bytes:
    if isinstance(key, ec.EllipticCurvePrivateKey):
        key_type = _ecdsa_key_type(key.public_key())
    elif isinstance(key, ec.EllipticCurvePublicKey):
        key_type = _ecdsa_key_type(key)
    elif isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
        key_type = _SSH_RSA
    elif isinstance(key, (dsa.DSAPrivateKey, dsa.DSAPublicKey)):
        key_type = _SSH_DSA
    elif isinstance(
        key, (ed25519.Ed25519PrivateKey, ed25519.Ed25519PublicKey)
    ):
        key_type = _SSH_ED25519
    else:
        raise ValueError("Unsupported key type")

    return key_type


def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes:
    """Return SSH key_type and curve_name for private key."""
    curve = public_key.curve
    if curve.name not in _ECDSA_KEY_TYPE:
        raise ValueError(
            f"Unsupported curve for ssh private key: {curve.name!r}"
        )
    return _ECDSA_KEY_TYPE[curve.name]


def _ssh_pem_encode(
    data: bytes,
    prefix: bytes = _SK_START + b"\n",
    suffix: bytes = _SK_END + b"\n",
) -> bytes:
    return b"".join([prefix, _base64_encode(data), suffix])


def _check_block_size(data: bytes, block_len: int) -> None:
    """Require data to be full blocks"""
    if not data or len(data) % block_len != 0:
        raise ValueError("Corrupt data: missing padding")


def _check_empty(data: bytes) -> None:
    """All data should have been parsed."""
    if data:
        raise ValueError("Corrupt data: unparsed data")


def _init_cipher(
    ciphername: bytes,
    password: typing.Optional[bytes],
    salt: bytes,
    rounds: int,
) -> Cipher[typing.Union[modes.CBC, modes.CTR, modes.GCM]]:
    """Generate key + iv and return cipher."""
    if not password:
        raise ValueError("Key is password-protected.")

    ciph = _SSH_CIPHERS[ciphername]
    seed = _bcrypt_kdf(
        password, salt, ciph.key_len + ciph.iv_len, rounds, True
    )
    return Cipher(
        ciph.alg(seed[: ciph.key_len]),
        ciph.mode(seed[ciph.key_len :]),
    )


def _get_u32(data: memoryview) -> typing.Tuple[int, memoryview]:
    """Uint32"""
    if len(data) < 4:
        raise ValueError("Invalid data")
    return int.from_bytes(data[:4], byteorder="big"), data[4:]


def _get_u64(data: memoryview) -> typing.Tuple[int, memoryview]:
    """Uint64"""
    if len(data) < 8:
        raise ValueError("Invalid data")
    return int.from_bytes(data[:8], byteorder="big"), data[8:]


def _get_sshstr(data: memoryview) -> typing.Tuple[memoryview, memoryview]:
    """Bytes with u32 length prefix"""
    n, data = _get_u32(data)
    if n > len(data):
        raise ValueError("Invalid data")
    return data[:n], data[n:]


def _get_mpint(data: memoryview) -> typing.Tuple[int, memoryview]:
    """Big integer."""
    val, data = _get_sshstr(data)
    if val and val[0] > 0x7F:
        raise ValueError("Invalid data")
    return int.from_bytes(val, "big"), data


def _to_mpint(val: int) -> bytes:
    """Storage format for signed bigint."""
    if val < 0:
        raise ValueError("negative mpint not allowed")
    if not val:
        return b""
    nbytes = (val.bit_length() + 8) // 8
    return utils.int_to_bytes(val, nbytes)


class _FragList:
    """Build recursive structure without data copy."""

    flist: typing.List[bytes]

    def __init__(
        self, init: typing.Optional[typing.List[bytes]] = None
    ) -> None:
        self.flist = []
        if init:
            self.flist.extend(init)

    def put_raw(self, val: bytes) -> None:
        """Add plain bytes"""
        self.flist.append(val)

    def put_u32(self, val: int) -> None:
        """Big-endian uint32"""
        self.flist.append(val.to_bytes(length=4, byteorder="big"))

    def put_u64(self, val: int) -> None:
        """Big-endian uint64"""
        self.flist.append(val.to_bytes(length=8, byteorder="big"))

    def put_sshstr(self, val: typing.Union[bytes, _FragList]) -> None:
        """Bytes prefixed with u32 length"""
        if isinstance(val, (bytes, memoryview, bytearray)):
            self.put_u32(len(val))
            self.flist.append(val)
        else:
            self.put_u32(val.size())
            self.flist.extend(val.flist)

    def put_mpint(self, val: int) -> None:
        """Big-endian bigint prefixed with u32 length"""
        self.put_sshstr(_to_mpint(val))

    def size(self) -> int:
        """Current number of bytes"""
        return sum(map(len, self.flist))

    def render(self, dstbuf: memoryview, pos: int = 0) -> int:
        """Write into bytearray"""
        for frag in self.flist:
            flen = len(frag)
            start, pos = pos, pos + flen
            dstbuf[start:pos] = frag
        return pos

    def tobytes(self) -> bytes:
        """Return as bytes"""
        buf = memoryview(bytearray(self.size()))
        self.render(buf)
        return buf.tobytes()


class _SSHFormatRSA:
    """Format for RSA keys.

    Public:
        mpint e, n
    Private:
        mpint n, e, d, iqmp, p, q
    """

    def get_public(self, data: memoryview):
        """RSA public fields"""
        e, data = _get_mpint(data)
        n, data = _get_mpint(data)
        return (e, n), data

    def load_public(
        self, data: memoryview
    ) -> typing.Tuple[rsa.RSAPublicKey, memoryview]:
        """Make RSA public key from data."""
        (e, n), data = self.get_public(data)
        public_numbers = rsa.RSAPublicNumbers(e, n)
        public_key = public_numbers.public_key()
        return public_key, data

    def load_private(
        self, data: memoryview, pubfields
    ) -> typing.Tuple[rsa.RSAPrivateKey, memoryview]:
        """Make RSA private key from data."""
        n, data = _get_mpint(data)
        e, data = _get_mpint(data)
        d, data = _get_mpint(data)
        iqmp, data = _get_mpint(data)
        p, data = _get_mpint(data)
        q, data = _get_mpint(data)

        if (e, n) != pubfields:
            raise ValueError("Corrupt data: rsa field mismatch")
        dmp1 = rsa.rsa_crt_dmp1(d, p)
        dmq1 = rsa.rsa_crt_dmq1(d, q)
        public_numbers = rsa.RSAPublicNumbers(e, n)
        private_numbers = rsa.RSAPrivateNumbers(
            p, q, d, dmp1, dmq1, iqmp, public_numbers
        )
        private_key = private_numbers.private_key()
        return private_key, data

    def encode_public(
        self, public_key: rsa.RSAPublicKey, f_pub: _FragList
    ) -> None:
        """Write RSA public key"""
        pubn = public_key.public_numbers()
        f_pub.put_mpint(pubn.e)
        f_pub.put_mpint(pubn.n)

    def encode_private(
        self, private_key: rsa.RSAPrivateKey, f_priv: _FragList
    ) -> None:
        """Write RSA private key"""
        private_numbers = private_key.private_numbers()
        public_numbers = private_numbers.public_numbers

        f_priv.put_mpint(public_numbers.n)
        f_priv.put_mpint(public_numbers.e)

        f_priv.put_mpint(private_numbers.d)
        f_priv.put_mpint(private_numbers.iqmp)
        f_priv.put_mpint(private_numbers.p)
        f_priv.put_mpint(private_numbers.q)


class _SSHFormatDSA:
    """Format for DSA keys.

    Public:
        mpint p, q, g, y
    Private:
        mpint p, q, g, y, x
    """

    def get_public(
        self, data: memoryview
    ) -> typing.Tuple[typing.Tuple, memoryview]:
        """DSA public fields"""
        p, data = _get_mpint(data)
        q, data = _get_mpint(data)
        g, data = _get_mpint(data)
        y, data = _get_mpint(data)
        return (p, q, g, y), data

    def load_public(
        self, data: memoryview
    ) -> typing.Tuple[dsa.DSAPublicKey, memoryview]:
        """Make DSA public key from data."""
        (p, q, g, y), data = self.get_public(data)
        parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
        public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
        self._validate(public_numbers)
        public_key = public_numbers.public_key()
        return public_key, data

    def load_private(
        self, data: memoryview, pubfields
    ) -> typing.Tuple[dsa.DSAPrivateKey, memoryview]:
        """Make DSA private key from data."""
        (p, q, g, y), data = self.get_public(data)
        x, data = _get_mpint(data)

        if (p, q, g, y) != pubfields:
            raise ValueError("Corrupt data: dsa field mismatch")
        parameter_numbers = dsa.DSAParameterNumbers(p, q, g)
        public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers)
        self._validate(public_numbers)
        private_numbers = dsa.DSAPrivateNumbers(x, public_numbers)
        private_key = private_numbers.private_key()
        return private_key, data

    def encode_public(
        self, public_key: dsa.DSAPublicKey, f_pub: _FragList
    ) -> None:
        """Write DSA public key"""
        public_numbers = public_key.public_numbers()
        parameter_numbers = public_numbers.parameter_numbers
        self._validate(public_numbers)

        f_pub.put_mpint(parameter_numbers.p)
        f_pub.put_mpint(parameter_numbers.q)
        f_pub.put_mpint(parameter_numbers.g)
        f_pub.put_mpint(public_numbers.y)

    def encode_private(
        self, private_key: dsa.DSAPrivateKey, f_priv: _FragList
    ) -> None:
        """Write DSA private key"""
        self.encode_public(private_key.public_key(), f_priv)
        f_priv.put_mpint(private_key.private_numbers().x)

    def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None:
        parameter_numbers = public_numbers.parameter_numbers
        if parameter_numbers.p.bit_length() != 1024:
            raise ValueError("SSH supports only 1024 bit DSA keys")


class _SSHFormatECDSA:
    """Format for ECDSA keys.

    Public:
        str curve
        bytes point
    Private:
        str curve
        bytes point
        mpint secret
    """

    def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve):
        self.ssh_curve_name = ssh_curve_name
        self.curve = curve

    def get_public(
        self, data: memoryview
    ) -> typing.Tuple[typing.Tuple, memoryview]:
        """ECDSA public fields"""
        curve, data = _get_sshstr(data)
        point, data = _get_sshstr(data)
        if curve != self.ssh_curve_name:
            raise ValueError("Curve name mismatch")
        if point[0] != 4:
            raise NotImplementedError("Need uncompressed point")
        return (curve, point), data

    def load_public(
        self, data: memoryview
    ) -> typing.Tuple[ec.EllipticCurvePublicKey, memoryview]:
        """Make ECDSA public key from data."""
        (curve_name, point), data = self.get_public(data)
        public_key = ec.EllipticCurvePublicKey.from_encoded_point(
            self.curve, point.tobytes()
        )
        return public_key, data

    def load_private(
        self, data: memoryview, pubfields
    ) -> typing.Tuple[ec.EllipticCurvePrivateKey, memoryview]:
        """Make ECDSA private key from data."""
        (curve_name, point), data = self.get_public(data)
        secret, data = _get_mpint(data)

        if (curve_name, point) != pubfields:
            raise ValueError("Corrupt data: ecdsa field mismatch")
        private_key = ec.derive_private_key(secret, self.curve)
        return private_key, data

    def encode_public(
        self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList
    ) -> None:
        """Write ECDSA public key"""
        point = public_key.public_bytes(
            Encoding.X962, PublicFormat.UncompressedPoint
        )
        f_pub.put_sshstr(self.ssh_curve_name)
        f_pub.put_sshstr(point)

    def encode_private(
        self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList
    ) -> None:
        """Write ECDSA private key"""
        public_key = private_key.public_key()
        private_numbers = private_key.private_numbers()

        self.encode_public(public_key, f_priv)
        f_priv.put_mpint(private_numbers.private_value)


class _SSHFormatEd25519:
    """Format for Ed25519 keys.

    Public:
        bytes point
    Private:
        bytes point
        bytes secret_and_point
    """

    def get_public(
        self, data: memoryview
    ) -> typing.Tuple[typing.Tuple, memoryview]:
        """Ed25519 public fields"""
        point, data = _get_sshstr(data)
        return (point,), data

    def load_public(
        self, data: memoryview
    ) -> typing.Tuple[ed25519.Ed25519PublicKey, memoryview]:
        """Make Ed25519 public key from data."""
        (point,), data = self.get_public(data)
        public_key = ed25519.Ed25519PublicKey.from_public_bytes(
            point.tobytes()
        )
        return public_key, data

    def load_private(
        self, data: memoryview, pubfields
    ) -> typing.Tuple[ed25519.Ed25519PrivateKey, memoryview]:
        """Make Ed25519 private key from data."""
        (point,), data = self.get_public(data)
        keypair, data = _get_sshstr(data)

        secret = keypair[:32]
        point2 = keypair[32:]
        if point != point2 or (point,) != pubfields:
            raise ValueError("Corrupt data: ed25519 field mismatch")
        private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret)
        return private_key, data

    def encode_public(
        self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList
    ) -> None:
        """Write Ed25519 public key"""
        raw_public_key = public_key.public_bytes(
            Encoding.Raw, PublicFormat.Raw
        )
        f_pub.put_sshstr(raw_public_key)

    def encode_private(
        self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList
    ) -> None:
        """Write Ed25519 private key"""
        public_key = private_key.public_key()
        raw_private_key = private_key.private_bytes(
            Encoding.Raw, PrivateFormat.Raw, NoEncryption()
        )
        raw_public_key = public_key.public_bytes(
            Encoding.Raw, PublicFormat.Raw
        )
        f_keypair = _FragList([raw_private_key, raw_public_key])

        self.encode_public(public_key, f_priv)
        f_priv.put_sshstr(f_keypair)


_KEY_FORMATS = {
    _SSH_RSA: _SSHFormatRSA(),
    _SSH_DSA: _SSHFormatDSA(),
    _SSH_ED25519: _SSHFormatEd25519(),
    _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()),
    _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()),
    _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()),
}


def _lookup_kformat(key_type: bytes):
    """Return valid format or throw error"""
    if not isinstance(key_type, bytes):
        key_type = memoryview(key_type).tobytes()
    if key_type in _KEY_FORMATS:
        return _KEY_FORMATS[key_type]
    raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}")


SSHPrivateKeyTypes = typing.Union[
    ec.EllipticCurvePrivateKey,
    rsa.RSAPrivateKey,
    dsa.DSAPrivateKey,
    ed25519.Ed25519PrivateKey,
]


def load_ssh_private_key(
    data: bytes,
    password: typing.Optional[bytes],
    backend: typing.Any = None,
) -> SSHPrivateKeyTypes:
    """Load private key from OpenSSH custom encoding."""
    utils._check_byteslike("data", data)
    if password is not None:
        utils._check_bytes("password", password)

    m = _PEM_RC.search(data)
    if not m:
        raise ValueError("Not OpenSSH private key format")
    p1 = m.start(1)
    p2 = m.end(1)
    data = binascii.a2b_base64(memoryview(data)[p1:p2])
    if not data.startswith(_SK_MAGIC):
        raise ValueError("Not OpenSSH private key format")
    data = memoryview(data)[len(_SK_MAGIC) :]

    # parse header
    ciphername, data = _get_sshstr(data)
    kdfname, data = _get_sshstr(data)
    kdfoptions, data = _get_sshstr(data)
    nkeys, data = _get_u32(data)
    if nkeys != 1:
        raise ValueError("Only one key supported")

    # load public key data
    pubdata, data = _get_sshstr(data)
    pub_key_type, pubdata = _get_sshstr(pubdata)
    kformat = _lookup_kformat(pub_key_type)
    pubfields, pubdata = kformat.get_public(pubdata)
    _check_empty(pubdata)

    if (ciphername, kdfname) != (_NONE, _NONE):
        ciphername_bytes = ciphername.tobytes()
        if ciphername_bytes not in _SSH_CIPHERS:
            raise UnsupportedAlgorithm(
                f"Unsupported cipher: {ciphername_bytes!r}"
            )
        if kdfname != _BCRYPT:
            raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}")
        blklen = _SSH_CIPHERS[ciphername_bytes].block_len
        tag_len = _SSH_CIPHERS[ciphername_bytes].tag_len
        # load secret data
        edata, data = _get_sshstr(data)
        # see https://bugzilla.mindrot.org/show_bug.cgi?id=3553 for
        # information about how OpenSSH handles AEAD tags
        if _SSH_CIPHERS[ciphername_bytes].is_aead:
            tag = bytes(data)
            if len(tag) != tag_len:
                raise ValueError("Corrupt data: invalid tag length for cipher")
        else:
            _check_empty(data)
        _check_block_size(edata, blklen)
        salt, kbuf = _get_sshstr(kdfoptions)
        rounds, kbuf = _get_u32(kbuf)
        _check_empty(kbuf)
        ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds)
        dec = ciph.decryptor()
        edata = memoryview(dec.update(edata))
        if _SSH_CIPHERS[ciphername_bytes].is_aead:
            assert isinstance(dec, AEADDecryptionContext)
            _check_empty(dec.finalize_with_tag(tag))
        else:
            # _check_block_size requires data to be a full block so there
            # should be no output from finalize
            _check_empty(dec.finalize())
    else:
        # load secret data
        edata, data = _get_sshstr(data)
        _check_empty(data)
        blklen = 8
        _check_block_size(edata, blklen)
    ck1, edata = _get_u32(edata)
    ck2, edata = _get_u32(edata)
    if ck1 != ck2:
        raise ValueError("Corrupt data: broken checksum")

    # load per-key struct
    key_type, edata = _get_sshstr(edata)
    if key_type != pub_key_type:
        raise ValueError("Corrupt data: key type mismatch")
    private_key, edata = kformat.load_private(edata, pubfields)
    comment, edata = _get_sshstr(edata)

    # yes, SSH does padding check *after* all other parsing is done.
    # need to follow as it writes zero-byte padding too.
    if edata != _PADDING[: len(edata)]:
        raise ValueError("Corrupt data: invalid padding")

    if isinstance(private_key, dsa.DSAPrivateKey):
        warnings.warn(
            "SSH DSA keys are deprecated and will be removed in a future "
            "release.",
            utils.DeprecatedIn40,
            stacklevel=2,
        )

    return private_key


def _serialize_ssh_private_key(
    private_key: SSHPrivateKeyTypes,
    password: bytes,
    encryption_algorithm: KeySerializationEncryption,
) -> bytes:
    """Serialize private key with OpenSSH custom encoding."""
    utils._check_bytes("password", password)
    if isinstance(private_key, dsa.DSAPrivateKey):
        warnings.warn(
            "SSH DSA key support is deprecated and will be "
            "removed in a future release",
            utils.DeprecatedIn40,
            stacklevel=4,
        )

    key_type = _get_ssh_key_type(private_key)
    kformat = _lookup_kformat(key_type)

    # setup parameters
    f_kdfoptions = _FragList()
    if password:
        ciphername = _DEFAULT_CIPHER
        blklen = _SSH_CIPHERS[ciphername].block_len
        kdfname = _BCRYPT
        rounds = _DEFAULT_ROUNDS
        if (
            isinstance(encryption_algorithm, _KeySerializationEncryption)
            and encryption_algorithm._kdf_rounds is not None
        ):
            rounds = encryption_algorithm._kdf_rounds
        salt = os.urandom(16)
        f_kdfoptions.put_sshstr(salt)
        f_kdfoptions.put_u32(rounds)
        ciph = _init_cipher(ciphername, password, salt, rounds)
    else:
        ciphername = kdfname = _NONE
        blklen = 8
        ciph = None
    nkeys = 1
    checkval = os.urandom(4)
    comment = b""

    # encode public and private parts together
    f_public_key = _FragList()
    f_public_key.put_sshstr(key_type)
    kformat.encode_public(private_key.public_key(), f_public_key)

    f_secrets = _FragList([checkval, checkval])
    f_secrets.put_sshstr(key_type)
    kformat.encode_private(private_key, f_secrets)
    f_secrets.put_sshstr(comment)
    f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)])

    # top-level structure
    f_main = _FragList()
    f_main.put_raw(_SK_MAGIC)
    f_main.put_sshstr(ciphername)
    f_main.put_sshstr(kdfname)
    f_main.put_sshstr(f_kdfoptions)
    f_main.put_u32(nkeys)
    f_main.put_sshstr(f_public_key)
    f_main.put_sshstr(f_secrets)

    # copy result info bytearray
    slen = f_secrets.size()
    mlen = f_main.size()
    buf = memoryview(bytearray(mlen + blklen))
    f_main.render(buf)
    ofs = mlen - slen

    # encrypt in-place
    if ciph is not None:
        ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:])

    return _ssh_pem_encode(buf[:mlen])


SSHPublicKeyTypes = typing.Union[
    ec.EllipticCurvePublicKey,
    rsa.RSAPublicKey,
    dsa.DSAPublicKey,
    ed25519.Ed25519PublicKey,
]

SSHCertPublicKeyTypes = typing.Union[
    ec.EllipticCurvePublicKey,
    rsa.RSAPublicKey,
    ed25519.Ed25519PublicKey,
]


class SSHCertificateType(enum.Enum):
    USER = 1
    HOST = 2


class SSHCertificate:
    def __init__(
        self,
        _nonce: memoryview,
        _public_key: SSHPublicKeyTypes,
        _serial: int,
        _cctype: int,
        _key_id: memoryview,
        _valid_principals: typing.List[bytes],
        _valid_after: int,
        _valid_before: int,
        _critical_options: typing.Dict[bytes, bytes],
        _extensions: typing.Dict[bytes, bytes],
        _sig_type: memoryview,
        _sig_key: memoryview,
        _inner_sig_type: memoryview,
        _signature: memoryview,
        _tbs_cert_body: memoryview,
        _cert_key_type: bytes,
        _cert_body: memoryview,
    ):
        self._nonce = _nonce
        self._public_key = _public_key
        self._serial = _serial
        try:
            self._type = SSHCertificateType(_cctype)
        except ValueError:
            raise ValueError("Invalid certificate type")
        self._key_id = _key_id
        self._valid_principals = _valid_principals
        self._valid_after = _valid_after
        self._valid_before = _valid_before
        self._critical_options = _critical_options
        self._extensions = _extensions
        self._sig_type = _sig_type
        self._sig_key = _sig_key
        self._inner_sig_type = _inner_sig_type
        self._signature = _signature
        self._cert_key_type = _cert_key_type
        self._cert_body = _cert_body
        self._tbs_cert_body = _tbs_cert_body

    @property
    def nonce(self) -> bytes:
        return bytes(self._nonce)

    def public_key(self) -> SSHCertPublicKeyTypes:
        # make mypy happy until we remove DSA support entirely and
        # the underlying union won't have a disallowed type
        return typing.cast(SSHCertPublicKeyTypes, self._public_key)

    @property
    def serial(self) -> int:
        return self._serial

    @property
    def type(self) -> SSHCertificateType:
        return self._type

    @property
    def key_id(self) -> bytes:
        return bytes(self._key_id)

    @property
    def valid_principals(self) -> typing.List[bytes]:
        return self._valid_principals

    @property
    def valid_before(self) -> int:
        return self._valid_before

    @property
    def valid_after(self) -> int:
        return self._valid_after

    @property
    def critical_options(self) -> typing.Dict[bytes, bytes]:
        return self._critical_options

    @property
    def extensions(self) -> typing.Dict[bytes, bytes]:
        return self._extensions

    def signature_key(self) -> SSHCertPublicKeyTypes:
        sigformat = _lookup_kformat(self._sig_type)
        signature_key, sigkey_rest = sigformat.load_public(self._sig_key)
        _check_empty(sigkey_rest)
        return signature_key

    def public_bytes(self) -> bytes:
        return (
            bytes(self._cert_key_type)
            + b" "
            + binascii.b2a_base64(bytes(self._cert_body), newline=False)
        )

    def verify_cert_signature(self) -> None:
        signature_key = self.signature_key()
        if isinstance(signature_key, ed25519.Ed25519PublicKey):
            signature_key.verify(
                bytes(self._signature), bytes(self._tbs_cert_body)
            )
        elif isinstance(signature_key, ec.EllipticCurvePublicKey):
            # The signature is encoded as a pair of big-endian integers
            r, data = _get_mpint(self._signature)
            s, data = _get_mpint(data)
            _check_empty(data)
            computed_sig = asym_utils.encode_dss_signature(r, s)
            hash_alg = _get_ec_hash_alg(signature_key.curve)
            signature_key.verify(
                computed_sig, bytes(self._tbs_cert_body), ec.ECDSA(hash_alg)
            )
        else:
            assert isinstance(signature_key, rsa.RSAPublicKey)
            if self._inner_sig_type == _SSH_RSA:
                hash_alg = hashes.SHA1()
            elif self._inner_sig_type == _SSH_RSA_SHA256:
                hash_alg = hashes.SHA256()
            else:
                assert self._inner_sig_type == _SSH_RSA_SHA512
                hash_alg = hashes.SHA512()
            signature_key.verify(
                bytes(self._signature),
                bytes(self._tbs_cert_body),
                padding.PKCS1v15(),
                hash_alg,
            )


def _get_ec_hash_alg(curve: ec.EllipticCurve) -> hashes.HashAlgorithm:
    if isinstance(curve, ec.SECP256R1):
        return hashes.SHA256()
    elif isinstance(curve, ec.SECP384R1):
        return hashes.SHA384()
    else:
        assert isinstance(curve, ec.SECP521R1)
        return hashes.SHA512()


def _load_ssh_public_identity(
    data: bytes,
    _legacy_dsa_allowed=False,
) -> typing.Union[SSHCertificate, SSHPublicKeyTypes]:
    utils._check_byteslike("data", data)

    m = _SSH_PUBKEY_RC.match(data)
    if not m:
        raise ValueError("Invalid line format")
    key_type = orig_key_type = m.group(1)
    key_body = m.group(2)
    with_cert = False
    if key_type.endswith(_CERT_SUFFIX):
        with_cert = True
        key_type = key_type[: -len(_CERT_SUFFIX)]
    if key_type == _SSH_DSA and not _legacy_dsa_allowed:
        raise UnsupportedAlgorithm(
            "DSA keys aren't supported in SSH certificates"
        )
    kformat = _lookup_kformat(key_type)

    try:
        rest = memoryview(binascii.a2b_base64(key_body))
    except (TypeError, binascii.Error):
        raise ValueError("Invalid format")

    if with_cert:
        cert_body = rest
    inner_key_type, rest = _get_sshstr(rest)
    if inner_key_type != orig_key_type:
        raise ValueError("Invalid key format")
    if with_cert:
        nonce, rest = _get_sshstr(rest)
    public_key, rest = kformat.load_public(rest)
    if with_cert:
        serial, rest = _get_u64(rest)
        cctype, rest = _get_u32(rest)
        key_id, rest = _get_sshstr(rest)
        principals, rest = _get_sshstr(rest)
        valid_principals = []
        while principals:
            principal, principals = _get_sshstr(principals)
            valid_principals.append(bytes(principal))
        valid_after, rest = _get_u64(rest)
        valid_before, rest = _get_u64(rest)
        crit_options, rest = _get_sshstr(rest)
        critical_options = _parse_exts_opts(crit_options)
        exts, rest = _get_sshstr(rest)
        extensions = _parse_exts_opts(exts)
        # Get the reserved field, which is unused.
        _, rest = _get_sshstr(rest)
        sig_key_raw, rest = _get_sshstr(rest)
        sig_type, sig_key = _get_sshstr(sig_key_raw)
        if sig_type == _SSH_DSA and not _legacy_dsa_allowed:
            raise UnsupportedAlgorithm(
                "DSA signatures aren't supported in SSH certificates"
            )
        # Get the entire cert body and subtract the signature
        tbs_cert_body = cert_body[: -len(rest)]
        signature_raw, rest = _get_sshstr(rest)
        _check_empty(rest)
        inner_sig_type, sig_rest = _get_sshstr(signature_raw)
        # RSA certs can have multiple algorithm types
        if (
            sig_type == _SSH_RSA
            and inner_sig_type
            not in [_SSH_RSA_SHA256, _SSH_RSA_SHA512, _SSH_RSA]
        ) or (sig_type != _SSH_RSA and inner_sig_type != sig_type):
            raise ValueError("Signature key type does not match")
        signature, sig_rest = _get_sshstr(sig_rest)
        _check_empty(sig_rest)
        return SSHCertificate(
            nonce,
            public_key,
            serial,
            cctype,
            key_id,
            valid_principals,
            valid_after,
            valid_before,
            critical_options,
            extensions,
            sig_type,
            sig_key,
            inner_sig_type,
            signature,
            tbs_cert_body,
            orig_key_type,
            cert_body,
        )
    else:
        _check_empty(rest)
        return public_key


def load_ssh_public_identity(
    data: bytes,
) -> typing.Union[SSHCertificate, SSHPublicKeyTypes]:
    return _load_ssh_public_identity(data)


def _parse_exts_opts(exts_opts: memoryview) -> typing.Dict[bytes, bytes]:
    result: typing.Dict[bytes, bytes] = {}
    last_name = None
    while exts_opts:
        name, exts_opts = _get_sshstr(exts_opts)
        bname: bytes = bytes(name)
        if bname in result:
            raise ValueError("Duplicate name")
        if last_name is not None and bname < last_name:
            raise ValueError("Fields not lexically sorted")
        value, exts_opts = _get_sshstr(exts_opts)
        if len(value) > 0:
            try:
                value, extra = _get_sshstr(value)
            except ValueError:
                warnings.warn(
                    "This certificate has an incorrect encoding for critical "
                    "options or extensions. This will be an exception in "
                    "cryptography 42",
                    utils.DeprecatedIn41,
                    stacklevel=4,
                )
            else:
                if len(extra) > 0:
                    raise ValueError("Unexpected extra data after value")
        result[bname] = bytes(value)
        last_name = bname
    return result


def load_ssh_public_key(
    data: bytes, backend: typing.Any = None
) -> SSHPublicKeyTypes:
    cert_or_key = _load_ssh_public_identity(data, _legacy_dsa_allowed=True)
    public_key: SSHPublicKeyTypes
    if isinstance(cert_or_key, SSHCertificate):
        public_key = cert_or_key.public_key()
    else:
        public_key = cert_or_key

    if isinstance(public_key, dsa.DSAPublicKey):
        warnings.warn(
            "SSH DSA keys are deprecated and will be removed in a future "
            "release.",
            utils.DeprecatedIn40,
            stacklevel=2,
        )
    return public_key


def serialize_ssh_public_key(public_key: SSHPublicKeyTypes) -> bytes:
    """One-line public key format for OpenSSH"""
    if isinstance(public_key, dsa.DSAPublicKey):
        warnings.warn(
            "SSH DSA key support is deprecated and will be "
            "removed in a future release",
            utils.DeprecatedIn40,
            stacklevel=4,
        )
    key_type = _get_ssh_key_type(public_key)
    kformat = _lookup_kformat(key_type)

    f_pub = _FragList()
    f_pub.put_sshstr(key_type)
    kformat.encode_public(public_key, f_pub)

    pub = binascii.b2a_base64(f_pub.tobytes()).strip()
    return b"".join([key_type, b" ", pub])


SSHCertPrivateKeyTypes = typing.Union[
    ec.EllipticCurvePrivateKey,
    rsa.RSAPrivateKey,
    ed25519.Ed25519PrivateKey,
]


# This is an undocumented limit enforced in the openssh codebase for sshd and
# ssh-keygen, but it is undefined in the ssh certificates spec.
_SSHKEY_CERT_MAX_PRINCIPALS = 256


class SSHCertificateBuilder:
    def __init__(
        self,
        _public_key: typing.Optional[SSHCertPublicKeyTypes] = None,
        _serial: typing.Optional[int] = None,
        _type: typing.Optional[SSHCertificateType] = None,
        _key_id: typing.Optional[bytes] = None,
        _valid_principals: typing.List[bytes] = [],
        _valid_for_all_principals: bool = False,
        _valid_before: typing.Optional[int] = None,
        _valid_after: typing.Optional[int] = None,
        _critical_options: typing.List[typing.Tuple[bytes, bytes]] = [],
        _extensions: typing.List[typing.Tuple[bytes, bytes]] = [],
    ):
        self._public_key = _public_key
        self._serial = _serial
        self._type = _type
        self._key_id = _key_id
        self._valid_principals = _valid_principals
        self._valid_for_all_principals = _valid_for_all_principals
        self._valid_before = _valid_before
        self._valid_after = _valid_after
        self._critical_options = _critical_options
        self._extensions = _extensions

    def public_key(
        self, public_key: SSHCertPublicKeyTypes
    ) -> SSHCertificateBuilder:
        if not isinstance(
            public_key,
            (
                ec.EllipticCurvePublicKey,
                rsa.RSAPublicKey,
                ed25519.Ed25519PublicKey,
            ),
        ):
            raise TypeError("Unsupported key type")
        if self._public_key is not None:
            raise ValueError("public_key already set")

        return SSHCertificateBuilder(
            _public_key=public_key,
            _serial=self._serial,
            _type=self._type,
            _key_id=self._key_id,
            _valid_principals=self._valid_principals,
            _valid_for_all_principals=self._valid_for_all_principals,
            _valid_before=self._valid_before,
            _valid_after=self._valid_after,
            _critical_options=self._critical_options,
            _extensions=self._extensions,
        )

    def serial(self, serial: int) -> SSHCertificateBuilder:
        if not isinstance(serial, int):
            raise TypeError("serial must be an integer")
        if not 0 <= serial < 2**64:
            raise ValueError("serial must be between 0 and 2**64")
        if self._serial is not None:
            raise ValueError("serial already set")

        return SSHCertificateBuilder(
            _public_key=self._public_key,
            _serial=serial,
            _type=self._type,
            _key_id=self._key_id,
            _valid_principals=self._valid_principals,
            _valid_for_all_principals=self._valid_for_all_principals,
            _valid_before=self._valid_before,
            _valid_after=self._valid_after,
            _critical_options=self._critical_options,
            _extensions=self._extensions,
        )

    def type(self, type: SSHCertificateType) -> SSHCertificateBuilder:
        if not isinstance(type, SSHCertificateType):
            raise TypeError("type must be an SSHCertificateType")
        if self._type is not None:
            raise ValueError("type already set")

        return SSHCertificateBuilder(
            _public_key=self._public_key,
            _serial=self._serial,
            _type=type,
            _key_id=self._key_id,
            _valid_principals=self._valid_principals,
            _valid_for_all_principals=self._valid_for_all_principals,
            _valid_before=self._valid_before,
            _valid_after=self._valid_after,
            _critical_options=self._critical_options,
            _extensions=self._extensions,
        )

    def key_id(self, key_id: bytes) -> SSHCertificateBuilder:
        if not isinstance(key_id, bytes):
            raise TypeError("key_id must be bytes")
        if self._key_id is not None:
            raise ValueError("key_id already set")

        return SSHCertificateBuilder(
            _public_key=self._public_key,
            _serial=self._serial,
            _type=self._type,
            _key_id=key_id,
            _valid_principals=self._valid_principals,
            _valid_for_all_principals=self._valid_for_all_principals,
            _valid_before=self._valid_before,
            _valid_after=self._valid_after,
            _critical_options=self._critical_options,
            _extensions=self._extensions,
        )

    def valid_principals(
        self, valid_principals: typing.List[bytes]
    ) -> SSHCertificateBuilder:
        if self._valid_for_all_principals:
            raise ValueError(
                "Principals can't be set because the cert is valid "
                "for all principals"
            )
        if (
            not all(isinstance(x, bytes) for x in valid_principals)
            or not valid_principals
        ):
            raise TypeError(
                "principals must be a list of bytes and can't be empty"
            )
        if self._valid_principals:
            raise ValueError("valid_principals already set")

        if len(valid_principals) > _SSHKEY_CERT_MAX_PRINCIPALS:
            raise ValueError(
                "Reached or exceeded the maximum number of valid_principals"
            )

        return SSHCertificateBuilder(
            _public_key=self._public_key,
            _serial=self._serial,
            _type=self._type,
            _key_id=self._key_id,
            _valid_principals=valid_principals,
            _valid_for_all_principals=self._valid_for_all_principals,
            _valid_before=self._valid_before,
            _valid_after=self._valid_after,
            _critical_options=self._critical_options,
            _extensions=self._extensions,
        )

    def valid_for_all_principals(self):
        if self._valid_principals:
            raise ValueError(
                "valid_principals already set, can't set "
                "valid_for_all_principals"
            )
        if self._valid_for_all_principals:
            raise ValueError("valid_for_all_principals already set")

        return SSHCertificateBuilder(
            _public_key=self._public_key,
            _serial=self._serial,
            _type=self._type,
            _key_id=self._key_id,
            _valid_principals=self._valid_principals,
            _valid_for_all_principals=True,
            _valid_before=self._valid_before,
            _valid_after=self._valid_after,
            _critical_options=self._critical_options,
            _extensions=self._extensions,
        )

    def valid_before(
        self, valid_before: typing.Union[int, float]
    ) -> SSHCertificateBuilder:
        if not isinstance(valid_before, (int, float)):
            raise TypeError("valid_before must be an int or float")
        valid_before = int(valid_before)
        if valid_before < 0 or valid_before >= 2**64:
            raise ValueError("valid_before must [0, 2**64)")
        if self._valid_before is not None:
            raise ValueError("valid_before already set")

        return SSHCertificateBuilder(
            _public_key=self._public_key,
            _serial=self._serial,
            _type=self._type,
            _key_id=self._key_id,
            _valid_principals=self._valid_principals,
            _valid_for_all_principals=self._valid_for_all_principals,
            _valid_before=valid_before,
            _valid_after=self._valid_after,
            _critical_options=self._critical_options,
            _extensions=self._extensions,
        )

    def valid_after(
        self, valid_after: typing.Union[int, float]
    ) -> SSHCertificateBuilder:
        if not isinstance(valid_after, (int, float)):
            raise TypeError("valid_after must be an int or float")
        valid_after = int(valid_after)
        if valid_after < 0 or valid_after >= 2**64:
            raise ValueError("valid_after must [0, 2**64)")
        if self._valid_after is not None:
            raise ValueError("valid_after already set")

        return SSHCertificateBuilder(
            _public_key=self._public_key,
            _serial=self._serial,
            _type=self._type,
            _key_id=self._key_id,
            _valid_principals=self._valid_principals,
            _valid_for_all_principals=self._valid_for_all_principals,
            _valid_before=self._valid_before,
            _valid_after=valid_after,
            _critical_options=self._critical_options,
            _extensions=self._extensions,
        )

    def add_critical_option(
        self, name: bytes, value: bytes
    ) -> SSHCertificateBuilder:
        if not isinstance(name, bytes) or not isinstance(value, bytes):
            raise TypeError("name and value must be bytes")
        # This is O(n**2)
        if name in [name for name, _ in self._critical_options]:
            raise ValueError("Duplicate critical option name")

        return SSHCertificateBuilder(
            _public_key=self._public_key,
            _serial=self._serial,
            _type=self._type,
            _key_id=self._key_id,
            _valid_principals=self._valid_principals,
            _valid_for_all_principals=self._valid_for_all_principals,
            _valid_before=self._valid_before,
            _valid_after=self._valid_after,
            _critical_options=self._critical_options + [(name, value)],
            _extensions=self._extensions,
        )

    def add_extension(
        self, name: bytes, value: bytes
    ) -> SSHCertificateBuilder:
        if not isinstance(name, bytes) or not isinstance(value, bytes):
            raise TypeError("name and value must be bytes")
        # This is O(n**2)
        if name in [name for name, _ in self._extensions]:
            raise ValueError("Duplicate extension name")

        return SSHCertificateBuilder(
            _public_key=self._public_key,
            _serial=self._serial,
            _type=self._type,
            _key_id=self._key_id,
            _valid_principals=self._valid_principals,
            _valid_for_all_principals=self._valid_for_all_principals,
            _valid_before=self._valid_before,
            _valid_after=self._valid_after,
            _critical_options=self._critical_options,
            _extensions=self._extensions + [(name, value)],
        )

    def sign(self, private_key: SSHCertPrivateKeyTypes) -> SSHCertificate:
        if not isinstance(
            private_key,
            (
                ec.EllipticCurvePrivateKey,
                rsa.RSAPrivateKey,
                ed25519.Ed25519PrivateKey,
            ),
        ):
            raise TypeError("Unsupported private key type")

        if self._public_key is None:
            raise ValueError("public_key must be set")

        # Not required
        serial = 0 if self._serial is None else self._serial

        if self._type is None:
            raise ValueError("type must be set")

        # Not required
        key_id = b"" if self._key_id is None else self._key_id

        # A zero length list is valid, but means the certificate
        # is valid for any principal of the specified type. We require
        # the user to explicitly set valid_for_all_principals to get
        # that behavior.
        if not self._valid_principals and not self._valid_for_all_principals:
            raise ValueError(
                "valid_principals must be set if valid_for_all_principals "
                "is False"
            )

        if self._valid_before is None:
            raise ValueError("valid_before must be set")

        if self._valid_after is None:
            raise ValueError("valid_after must be set")

        if self._valid_after > self._valid_before:
            raise ValueError("valid_after must be earlier than valid_before")

        # lexically sort our byte strings
        self._critical_options.sort(key=lambda x: x[0])
        self._extensions.sort(key=lambda x: x[0])

        key_type = _get_ssh_key_type(self._public_key)
        cert_prefix = key_type + _CERT_SUFFIX

        # Marshal the bytes to be signed
        nonce = os.urandom(32)
        kformat = _lookup_kformat(key_type)
        f = _FragList()
        f.put_sshstr(cert_prefix)
        f.put_sshstr(nonce)
        kformat.encode_public(self._public_key, f)
        f.put_u64(serial)
        f.put_u32(self._type.value)
        f.put_sshstr(key_id)
        fprincipals = _FragList()
        for p in self._valid_principals:
            fprincipals.put_sshstr(p)
        f.put_sshstr(fprincipals.tobytes())
        f.put_u64(self._valid_after)
        f.put_u64(self._valid_before)
        fcrit = _FragList()
        for name, value in self._critical_options:
            fcrit.put_sshstr(name)
            if len(value) > 0:
                foptval = _FragList()
                foptval.put_sshstr(value)
                fcrit.put_sshstr(foptval.tobytes())
            else:
                fcrit.put_sshstr(value)
        f.put_sshstr(fcrit.tobytes())
        fext = _FragList()
        for name, value in self._extensions:
            fext.put_sshstr(name)
            if len(value) > 0:
                fextval = _FragList()
                fextval.put_sshstr(value)
                fext.put_sshstr(fextval.tobytes())
            else:
                fext.put_sshstr(value)
        f.put_sshstr(fext.tobytes())
        f.put_sshstr(b"")  # RESERVED FIELD
        # encode CA public key
        ca_type = _get_ssh_key_type(private_key)
        caformat = _lookup_kformat(ca_type)
        caf = _FragList()
        caf.put_sshstr(ca_type)
        caformat.encode_public(private_key.public_key(), caf)
        f.put_sshstr(caf.tobytes())
        # Sigs according to the rules defined for the CA's public key
        # (RFC4253 section 6.6 for ssh-rsa, RFC5656 for ECDSA,
        # and RFC8032 for Ed25519).
        if isinstance(private_key, ed25519.Ed25519PrivateKey):
            signature = private_key.sign(f.tobytes())
            fsig = _FragList()
            fsig.put_sshstr(ca_type)
            fsig.put_sshstr(signature)
            f.put_sshstr(fsig.tobytes())
        elif isinstance(private_key, ec.EllipticCurvePrivateKey):
            hash_alg = _get_ec_hash_alg(private_key.curve)
            signature = private_key.sign(f.tobytes(), ec.ECDSA(hash_alg))
            r, s = asym_utils.decode_dss_signature(signature)
            fsig = _FragList()
            fsig.put_sshstr(ca_type)
            fsigblob = _FragList()
            fsigblob.put_mpint(r)
            fsigblob.put_mpint(s)
            fsig.put_sshstr(fsigblob.tobytes())
            f.put_sshstr(fsig.tobytes())

        else:
            assert isinstance(private_key, rsa.RSAPrivateKey)
            # Just like Golang, we're going to use SHA512 for RSA
            # https://cs.opensource.google/go/x/crypto/+/refs/tags/
            # v0.4.0:ssh/certs.go;l=445
            # RFC 8332 defines SHA256 and 512 as options
            fsig = _FragList()
            fsig.put_sshstr(_SSH_RSA_SHA512)
            signature = private_key.sign(
                f.tobytes(), padding.PKCS1v15(), hashes.SHA512()
            )
            fsig.put_sshstr(signature)
            f.put_sshstr(fsig.tobytes())

        cert_data = binascii.b2a_base64(f.tobytes()).strip()
        # load_ssh_public_identity returns a union, but this is
        # guaranteed to be an SSHCertificate, so we cast to make
        # mypy happy.
        return typing.cast(
            SSHCertificate,
            load_ssh_public_identity(b"".join([cert_prefix, b" ", cert_data])),
        )

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import base64
import typing
from urllib.parse import quote, urlencode

from cryptography.hazmat.primitives import constant_time, hmac
from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512
from cryptography.hazmat.primitives.twofactor import InvalidToken

HOTPHashTypes = typing.Union[SHA1, SHA256, SHA512]


def _generate_uri(
    hotp: HOTP,
    type_name: str,
    account_name: str,
    issuer: typing.Optional[str],
    extra_parameters: typing.List[typing.Tuple[str, int]],
) -> str:
    parameters = [
        ("digits", hotp._length),
        ("secret", base64.b32encode(hotp._key)),
        ("algorithm", hotp._algorithm.name.upper()),
    ]

    if issuer is not None:
        parameters.append(("issuer", issuer))

    parameters.extend(extra_parameters)

    label = (
        f"{quote(issuer)}:{quote(account_name)}"
        if issuer
        else quote(account_name)
    )
    return f"otpauth://{type_name}/{label}?{urlencode(parameters)}"


class HOTP:
    def __init__(
        self,
        key: bytes,
        length: int,
        algorithm: HOTPHashTypes,
        backend: typing.Any = None,
        enforce_key_length: bool = True,
    ) -> None:
        if len(key) < 16 and enforce_key_length is True:
            raise ValueError("Key length has to be at least 128 bits.")

        if not isinstance(length, int):
            raise TypeError("Length parameter must be an integer type.")

        if length < 6 or length > 8:
            raise ValueError("Length of HOTP has to be between 6 and 8.")

        if not isinstance(algorithm, (SHA1, SHA256, SHA512)):
            raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.")

        self._key = key
        self._length = length
        self._algorithm = algorithm

    def generate(self, counter: int) -> bytes:
        truncated_value = self._dynamic_truncate(counter)
        hotp = truncated_value % (10**self._length)
        return "{0:0{1}}".format(hotp, self._length).encode()

    def verify(self, hotp: bytes, counter: int) -> None:
        if not constant_time.bytes_eq(self.generate(counter), hotp):
            raise InvalidToken("Supplied HOTP value does not match.")

    def _dynamic_truncate(self, counter: int) -> int:
        ctx = hmac.HMAC(self._key, self._algorithm)
        ctx.update(counter.to_bytes(length=8, byteorder="big"))
        hmac_value = ctx.finalize()

        offset = hmac_value[len(hmac_value) - 1] & 0b1111
        p = hmac_value[offset : offset + 4]
        return int.from_bytes(p, byteorder="big") & 0x7FFFFFFF

    def get_provisioning_uri(
        self, account_name: str, counter: int, issuer: typing.Optional[str]
    ) -> str:
        return _generate_uri(
            self, "hotp", account_name, issuer, [("counter", int(counter))]
        )

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import typing

from cryptography.hazmat.primitives import constant_time
from cryptography.hazmat.primitives.twofactor import InvalidToken
from cryptography.hazmat.primitives.twofactor.hotp import (
    HOTP,
    HOTPHashTypes,
    _generate_uri,
)


class TOTP:
    def __init__(
        self,
        key: bytes,
        length: int,
        algorithm: HOTPHashTypes,
        time_step: int,
        backend: typing.Any = None,
        enforce_key_length: bool = True,
    ):
        self._time_step = time_step
        self._hotp = HOTP(
            key, length, algorithm, enforce_key_length=enforce_key_length
        )

    def generate(self, time: typing.Union[int, float]) -> bytes:
        counter = int(time / self._time_step)
        return self._hotp.generate(counter)

    def verify(self, totp: bytes, time: int) -> None:
        if not constant_time.bytes_eq(self.generate(time), totp):
            raise InvalidToken("Supplied TOTP value does not match.")

    def get_provisioning_uri(
        self, account_name: str, issuer: typing.Optional[str]
    ) -> str:
        return _generate_uri(
            self._hotp,
            "totp",
            account_name,
            issuer,
            [("period", int(self._time_step))],
        )

back/venvmsg/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import enum
import sys
import types
import typing
import warnings


# We use a UserWarning subclass, instead of DeprecationWarning, because CPython
# decided deprecation warnings should be invisble by default.
class CryptographyDeprecationWarning(UserWarning):
    pass


# Several APIs were deprecated with no specific end-of-life date because of the
# ubiquity of their use. They should not be removed until we agree on when that
# cycle ends.
DeprecatedIn36 = CryptographyDeprecationWarning
DeprecatedIn37 = CryptographyDeprecationWarning
DeprecatedIn40 = CryptographyDeprecationWarning
DeprecatedIn41 = CryptographyDeprecationWarning


def _check_bytes(name: str, value: bytes) -> None:
    if not isinstance(value, bytes):
        raise TypeError(f"{name} must be bytes")


def _check_byteslike(name: str, value: bytes) -> None:
    try:
        memoryview(value)
    except TypeError:
        raise TypeError(f"{name} must be bytes-like")


def int_to_bytes(integer: int, length: typing.Optional[int] = None) -> bytes:
    return integer.to_bytes(
        length or (integer.bit_length() + 7) // 8 or 1, "big"
    )


def _extract_buffer_length(obj: typing.Any) -> typing.Tuple[typing.Any, int]:
    from cryptography.hazmat.bindings._rust import _openssl

    buf = _openssl.ffi.from_buffer(obj)
    return buf, int(_openssl.ffi.cast("uintptr_t", buf))


class InterfaceNotImplemented(Exception):
    pass


class _DeprecatedValue:
    def __init__(self, value: object, message: str, warning_class):
        self.value = value
        self.message = message
        self.warning_class = warning_class


class _ModuleWithDeprecations(types.ModuleType):
    def __init__(self, module: types.ModuleType):
        super().__init__(module.__name__)
        self.__dict__["_module"] = module

    def __getattr__(self, attr: str) -> object:
        obj = getattr(self._module, attr)
        if isinstance(obj, _DeprecatedValue):
            warnings.warn(obj.message, obj.warning_class, stacklevel=2)
            obj = obj.value
        return obj

    def __setattr__(self, attr: str, value: object) -> None:
        setattr(self._module, attr, value)

    def __delattr__(self, attr: str) -> None:
        obj = getattr(self._module, attr)
        if isinstance(obj, _DeprecatedValue):
            warnings.warn(obj.message, obj.warning_class, stacklevel=2)

        delattr(self._module, attr)

    def __dir__(self) -> typing.Sequence[str]:
        return ["_module"] + dir(self._module)


def deprecated(
    value: object,
    module_name: str,
    message: str,
    warning_class: typing.Type[Warning],
    name: typing.Optional[str] = None,
) -> _DeprecatedValue:
    module = sys.modules[module_name]
    if not isinstance(module, _ModuleWithDeprecations):
        sys.modules[module_name] = module = _ModuleWithDeprecations(module)
    dv = _DeprecatedValue(value, message, warning_class)
    # Maintain backwards compatibility with `name is None` for pyOpenSSL.
    if name is not None:
        setattr(module, name, dv)
    return dv


def cached_property(func: typing.Callable) -> property:
    cached_name = f"_cached_{func}"
    sentinel = object()

    def inner(instance: object):
        cache = getattr(instance, cached_name, sentinel)
        if cache is not sentinel:
            return cache
        result = func(instance)
        setattr(instance, cached_name, result)
        return result

    return property(inner)


# Python 3.10 changed representation of enums. We use well-defined object
# representation and string representation from Python 3.9.
class Enum(enum.Enum):
    def __repr__(self) -> str:
        return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>"

    def __str__(self) -> str:
        return f"{self.__class__.__name__}.{self._name_}"

back/venvmsg/Lib/site-packages/cryptography/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

from cryptography.x509 import certificate_transparency
from cryptography.x509.base import (
    Attribute,
    AttributeNotFound,
    Attributes,
    Certificate,
    CertificateBuilder,
    CertificateRevocationList,
    CertificateRevocationListBuilder,
    CertificateSigningRequest,
    CertificateSigningRequestBuilder,
    InvalidVersion,
    RevokedCertificate,
    RevokedCertificateBuilder,
    Version,
    load_der_x509_certificate,
    load_der_x509_crl,
    load_der_x509_csr,
    load_pem_x509_certificate,
    load_pem_x509_certificates,
    load_pem_x509_crl,
    load_pem_x509_csr,
    random_serial_number,
)
from cryptography.x509.extensions import (
    AccessDescription,
    AuthorityInformationAccess,
    AuthorityKeyIdentifier,
    BasicConstraints,
    CertificateIssuer,
    CertificatePolicies,
    CRLDistributionPoints,
    CRLNumber,
    CRLReason,
    DeltaCRLIndicator,
    DistributionPoint,
    DuplicateExtension,
    ExtendedKeyUsage,
    Extension,
    ExtensionNotFound,
    Extensions,
    ExtensionType,
    FreshestCRL,
    GeneralNames,
    InhibitAnyPolicy,
    InvalidityDate,
    IssuerAlternativeName,
    IssuingDistributionPoint,
    KeyUsage,
    MSCertificateTemplate,
    NameConstraints,
    NoticeReference,
    OCSPAcceptableResponses,
    OCSPNoCheck,
    OCSPNonce,
    PolicyConstraints,
    PolicyInformation,
    PrecertificateSignedCertificateTimestamps,
    PrecertPoison,
    ReasonFlags,
    SignedCertificateTimestamps,
    SubjectAlternativeName,
    SubjectInformationAccess,
    SubjectKeyIdentifier,
    TLSFeature,
    TLSFeatureType,
    UnrecognizedExtension,
    UserNotice,
)
from cryptography.x509.general_name import (
    DirectoryName,
    DNSName,
    GeneralName,
    IPAddress,
    OtherName,
    RegisteredID,
    RFC822Name,
    UniformResourceIdentifier,
    UnsupportedGeneralNameType,
)
from cryptography.x509.name import (
    Name,
    NameAttribute,
    RelativeDistinguishedName,
)
from cryptography.x509.oid import (
    AuthorityInformationAccessOID,
    CertificatePoliciesOID,
    CRLEntryExtensionOID,
    ExtendedKeyUsageOID,
    ExtensionOID,
    NameOID,
    ObjectIdentifier,
    SignatureAlgorithmOID,
)

OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS
OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER
OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS
OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES
OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS
OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE
OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL
OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY
OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME
OID_KEY_USAGE = ExtensionOID.KEY_USAGE
OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS
OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK
OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS
OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS
OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME
OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES
OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS
OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER

OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1
OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224
OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256
OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1
OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224
OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256
OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384
OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512
OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5
OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1
OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224
OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256
OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384
OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512
OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS

OID_COMMON_NAME = NameOID.COMMON_NAME
OID_COUNTRY_NAME = NameOID.COUNTRY_NAME
OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT
OID_DN_QUALIFIER = NameOID.DN_QUALIFIER
OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS
OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER
OID_GIVEN_NAME = NameOID.GIVEN_NAME
OID_LOCALITY_NAME = NameOID.LOCALITY_NAME
OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME
OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME
OID_PSEUDONYM = NameOID.PSEUDONYM
OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER
OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME
OID_SURNAME = NameOID.SURNAME
OID_TITLE = NameOID.TITLE

OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH
OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING
OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION
OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING
OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH
OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING

OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY
OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER
OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE

OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER
OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON
OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE

OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS
OID_OCSP = AuthorityInformationAccessOID.OCSP

__all__ = [
    "certificate_transparency",
    "load_pem_x509_certificate",
    "load_pem_x509_certificates",
    "load_der_x509_certificate",
    "load_pem_x509_csr",
    "load_der_x509_csr",
    "load_pem_x509_crl",
    "load_der_x509_crl",
    "random_serial_number",
    "Attribute",
    "AttributeNotFound",
    "Attributes",
    "InvalidVersion",
    "DeltaCRLIndicator",
    "DuplicateExtension",
    "ExtensionNotFound",
    "UnsupportedGeneralNameType",
    "NameAttribute",
    "Name",
    "RelativeDistinguishedName",
    "ObjectIdentifier",
    "ExtensionType",
    "Extensions",
    "Extension",
    "ExtendedKeyUsage",
    "FreshestCRL",
    "IssuingDistributionPoint",
    "TLSFeature",
    "TLSFeatureType",
    "OCSPAcceptableResponses",
    "OCSPNoCheck",
    "BasicConstraints",
    "CRLNumber",
    "KeyUsage",
    "AuthorityInformationAccess",
    "SubjectInformationAccess",
    "AccessDescription",
    "CertificatePolicies",
    "PolicyInformation",
    "UserNotice",
    "NoticeReference",
    "SubjectKeyIdentifier",
    "NameConstraints",
    "CRLDistributionPoints",
    "DistributionPoint",
    "ReasonFlags",
    "InhibitAnyPolicy",
    "SubjectAlternativeName",
    "IssuerAlternativeName",
    "AuthorityKeyIdentifier",
    "GeneralNames",
    "GeneralName",
    "RFC822Name",
    "DNSName",
    "UniformResourceIdentifier",
    "RegisteredID",
    "DirectoryName",
    "IPAddress",
    "OtherName",
    "Certificate",
    "CertificateRevocationList",
    "CertificateRevocationListBuilder",
    "CertificateSigningRequest",
    "RevokedCertificate",
    "RevokedCertificateBuilder",
    "CertificateSigningRequestBuilder",
    "CertificateBuilder",
    "Version",
    "OID_CA_ISSUERS",
    "OID_OCSP",
    "CertificateIssuer",
    "CRLReason",
    "InvalidityDate",
    "UnrecognizedExtension",
    "PolicyConstraints",
    "PrecertificateSignedCertificateTimestamps",
    "PrecertPoison",
    "OCSPNonce",
    "SignedCertificateTimestamps",
    "SignatureAlgorithmOID",
    "NameOID",
    "MSCertificateTemplate",
]

back/venvmsg/Lib/site-packages/cryptography/x509/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import datetime
import os
import typing

from cryptography import utils
from cryptography.hazmat.bindings._rust import x509 as rust_x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import (
    dsa,
    ec,
    ed448,
    ed25519,
    padding,
    rsa,
    x448,
    x25519,
)
from cryptography.hazmat.primitives.asymmetric.types import (
    CertificateIssuerPrivateKeyTypes,
    CertificateIssuerPublicKeyTypes,
    CertificatePublicKeyTypes,
)
from cryptography.x509.extensions import (
    Extension,
    Extensions,
    ExtensionType,
    _make_sequence_methods,
)
from cryptography.x509.name import Name, _ASN1Type
from cryptography.x509.oid import ObjectIdentifier

_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1)

# This must be kept in sync with sign.rs's list of allowable types in
# identify_hash_type
_AllowedHashTypes = typing.Union[
    hashes.SHA224,
    hashes.SHA256,
    hashes.SHA384,
    hashes.SHA512,
    hashes.SHA3_224,
    hashes.SHA3_256,
    hashes.SHA3_384,
    hashes.SHA3_512,
]


class AttributeNotFound(Exception):
    def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
        super().__init__(msg)
        self.oid = oid


def _reject_duplicate_extension(
    extension: Extension[ExtensionType],
    extensions: typing.List[Extension[ExtensionType]],
) -> None:
    # This is quadratic in the number of extensions
    for e in extensions:
        if e.oid == extension.oid:
            raise ValueError("This extension has already been set.")


def _reject_duplicate_attribute(
    oid: ObjectIdentifier,
    attributes: typing.List[
        typing.Tuple[ObjectIdentifier, bytes, typing.Optional[int]]
    ],
) -> None:
    # This is quadratic in the number of attributes
    for attr_oid, _, _ in attributes:
        if attr_oid == oid:
            raise ValueError("This attribute has already been set.")


def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime:
    """Normalizes a datetime to a naive datetime in UTC.

    time -- datetime to normalize. Assumed to be in UTC if not timezone
            aware.
    """
    if time.tzinfo is not None:
        offset = time.utcoffset()
        offset = offset if offset else datetime.timedelta()
        return time.replace(tzinfo=None) - offset
    else:
        return time


class Attribute:
    def __init__(
        self,
        oid: ObjectIdentifier,
        value: bytes,
        _type: int = _ASN1Type.UTF8String.value,
    ) -> None:
        self._oid = oid
        self._value = value
        self._type = _type

    @property
    def oid(self) -> ObjectIdentifier:
        return self._oid

    @property
    def value(self) -> bytes:
        return self._value

    def __repr__(self) -> str:
        return f"<Attribute(oid={self.oid}, value={self.value!r})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Attribute):
            return NotImplemented

        return (
            self.oid == other.oid
            and self.value == other.value
            and self._type == other._type
        )

    def __hash__(self) -> int:
        return hash((self.oid, self.value, self._type))


class Attributes:
    def __init__(
        self,
        attributes: typing.Iterable[Attribute],
    ) -> None:
        self._attributes = list(attributes)

    __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes")

    def __repr__(self) -> str:
        return f"<Attributes({self._attributes})>"

    def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute:
        for attr in self:
            if attr.oid == oid:
                return attr

        raise AttributeNotFound(f"No {oid} attribute was found", oid)


class Version(utils.Enum):
    v1 = 0
    v3 = 2


class InvalidVersion(Exception):
    def __init__(self, msg: str, parsed_version: int) -> None:
        super().__init__(msg)
        self.parsed_version = parsed_version


class Certificate(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes:
        """
        Returns bytes using digest passed.
        """

    @property
    @abc.abstractmethod
    def serial_number(self) -> int:
        """
        Returns certificate serial number
        """

    @property
    @abc.abstractmethod
    def version(self) -> Version:
        """
        Returns the certificate version
        """

    @abc.abstractmethod
    def public_key(self) -> CertificatePublicKeyTypes:
        """
        Returns the public key
        """

    @property
    @abc.abstractmethod
    def not_valid_before(self) -> datetime.datetime:
        """
        Not before time (represented as UTC datetime)
        """

    @property
    @abc.abstractmethod
    def not_valid_after(self) -> datetime.datetime:
        """
        Not after time (represented as UTC datetime)
        """

    @property
    @abc.abstractmethod
    def issuer(self) -> Name:
        """
        Returns the issuer name object.
        """

    @property
    @abc.abstractmethod
    def subject(self) -> Name:
        """
        Returns the subject name object.
        """

    @property
    @abc.abstractmethod
    def signature_hash_algorithm(
        self,
    ) -> typing.Optional[hashes.HashAlgorithm]:
        """
        Returns a HashAlgorithm corresponding to the type of the digest signed
        in the certificate.
        """

    @property
    @abc.abstractmethod
    def signature_algorithm_oid(self) -> ObjectIdentifier:
        """
        Returns the ObjectIdentifier of the signature algorithm.
        """

    @property
    @abc.abstractmethod
    def signature_algorithm_parameters(
        self,
    ) -> typing.Union[None, padding.PSS, padding.PKCS1v15, ec.ECDSA]:
        """
        Returns the signature algorithm parameters.
        """

    @property
    @abc.abstractmethod
    def extensions(self) -> Extensions:
        """
        Returns an Extensions object.
        """

    @property
    @abc.abstractmethod
    def signature(self) -> bytes:
        """
        Returns the signature bytes.
        """

    @property
    @abc.abstractmethod
    def tbs_certificate_bytes(self) -> bytes:
        """
        Returns the tbsCertificate payload bytes as defined in RFC 5280.
        """

    @property
    @abc.abstractmethod
    def tbs_precertificate_bytes(self) -> bytes:
        """
        Returns the tbsCertificate payload bytes with the SCT list extension
        stripped.
        """

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """

    @abc.abstractmethod
    def __hash__(self) -> int:
        """
        Computes a hash.
        """

    @abc.abstractmethod
    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
        """
        Serializes the certificate to PEM or DER format.
        """

    @abc.abstractmethod
    def verify_directly_issued_by(self, issuer: Certificate) -> None:
        """
        This method verifies that certificate issuer name matches the
        issuer subject name and that the certificate is signed by the
        issuer's private key. No other validation is performed.
        """


# Runtime isinstance checks need this since the rust class is not a subclass.
Certificate.register(rust_x509.Certificate)


class RevokedCertificate(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def serial_number(self) -> int:
        """
        Returns the serial number of the revoked certificate.
        """

    @property
    @abc.abstractmethod
    def revocation_date(self) -> datetime.datetime:
        """
        Returns the date of when this certificate was revoked.
        """

    @property
    @abc.abstractmethod
    def extensions(self) -> Extensions:
        """
        Returns an Extensions object containing a list of Revoked extensions.
        """


# Runtime isinstance checks need this since the rust class is not a subclass.
RevokedCertificate.register(rust_x509.RevokedCertificate)


class _RawRevokedCertificate(RevokedCertificate):
    def __init__(
        self,
        serial_number: int,
        revocation_date: datetime.datetime,
        extensions: Extensions,
    ):
        self._serial_number = serial_number
        self._revocation_date = revocation_date
        self._extensions = extensions

    @property
    def serial_number(self) -> int:
        return self._serial_number

    @property
    def revocation_date(self) -> datetime.datetime:
        return self._revocation_date

    @property
    def extensions(self) -> Extensions:
        return self._extensions


class CertificateRevocationList(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
        """
        Serializes the CRL to PEM or DER format.
        """

    @abc.abstractmethod
    def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes:
        """
        Returns bytes using digest passed.
        """

    @abc.abstractmethod
    def get_revoked_certificate_by_serial_number(
        self, serial_number: int
    ) -> typing.Optional[RevokedCertificate]:
        """
        Returns an instance of RevokedCertificate or None if the serial_number
        is not in the CRL.
        """

    @property
    @abc.abstractmethod
    def signature_hash_algorithm(
        self,
    ) -> typing.Optional[hashes.HashAlgorithm]:
        """
        Returns a HashAlgorithm corresponding to the type of the digest signed
        in the certificate.
        """

    @property
    @abc.abstractmethod
    def signature_algorithm_oid(self) -> ObjectIdentifier:
        """
        Returns the ObjectIdentifier of the signature algorithm.
        """

    @property
    @abc.abstractmethod
    def issuer(self) -> Name:
        """
        Returns the X509Name with the issuer of this CRL.
        """

    @property
    @abc.abstractmethod
    def next_update(self) -> typing.Optional[datetime.datetime]:
        """
        Returns the date of next update for this CRL.
        """

    @property
    @abc.abstractmethod
    def last_update(self) -> datetime.datetime:
        """
        Returns the date of last update for this CRL.
        """

    @property
    @abc.abstractmethod
    def extensions(self) -> Extensions:
        """
        Returns an Extensions object containing a list of CRL extensions.
        """

    @property
    @abc.abstractmethod
    def signature(self) -> bytes:
        """
        Returns the signature bytes.
        """

    @property
    @abc.abstractmethod
    def tbs_certlist_bytes(self) -> bytes:
        """
        Returns the tbsCertList payload bytes as defined in RFC 5280.
        """

    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """

    @abc.abstractmethod
    def __len__(self) -> int:
        """
        Number of revoked certificates in the CRL.
        """

    @typing.overload
    def __getitem__(self, idx: int) -> RevokedCertificate:
        ...

    @typing.overload
    def __getitem__(self, idx: slice) -> typing.List[RevokedCertificate]:
        ...

    @abc.abstractmethod
    def __getitem__(
        self, idx: typing.Union[int, slice]
    ) -> typing.Union[RevokedCertificate, typing.List[RevokedCertificate]]:
        """
        Returns a revoked certificate (or slice of revoked certificates).
        """

    @abc.abstractmethod
    def __iter__(self) -> typing.Iterator[RevokedCertificate]:
        """
        Iterator over the revoked certificates
        """

    @abc.abstractmethod
    def is_signature_valid(
        self, public_key: CertificateIssuerPublicKeyTypes
    ) -> bool:
        """
        Verifies signature of revocation list against given public key.
        """


CertificateRevocationList.register(rust_x509.CertificateRevocationList)


class CertificateSigningRequest(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def __eq__(self, other: object) -> bool:
        """
        Checks equality.
        """

    @abc.abstractmethod
    def __hash__(self) -> int:
        """
        Computes a hash.
        """

    @abc.abstractmethod
    def public_key(self) -> CertificatePublicKeyTypes:
        """
        Returns the public key
        """

    @property
    @abc.abstractmethod
    def subject(self) -> Name:
        """
        Returns the subject name object.
        """

    @property
    @abc.abstractmethod
    def signature_hash_algorithm(
        self,
    ) -> typing.Optional[hashes.HashAlgorithm]:
        """
        Returns a HashAlgorithm corresponding to the type of the digest signed
        in the certificate.
        """

    @property
    @abc.abstractmethod
    def signature_algorithm_oid(self) -> ObjectIdentifier:
        """
        Returns the ObjectIdentifier of the signature algorithm.
        """

    @property
    @abc.abstractmethod
    def extensions(self) -> Extensions:
        """
        Returns the extensions in the signing request.
        """

    @property
    @abc.abstractmethod
    def attributes(self) -> Attributes:
        """
        Returns an Attributes object.
        """

    @abc.abstractmethod
    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
        """
        Encodes the request to PEM or DER format.
        """

    @property
    @abc.abstractmethod
    def signature(self) -> bytes:
        """
        Returns the signature bytes.
        """

    @property
    @abc.abstractmethod
    def tbs_certrequest_bytes(self) -> bytes:
        """
        Returns the PKCS#10 CertificationRequestInfo bytes as defined in RFC
        2986.
        """

    @property
    @abc.abstractmethod
    def is_signature_valid(self) -> bool:
        """
        Verifies signature of signing request.
        """

    @abc.abstractmethod
    def get_attribute_for_oid(self, oid: ObjectIdentifier) -> bytes:
        """
        Get the attribute value for a given OID.
        """


# Runtime isinstance checks need this since the rust class is not a subclass.
CertificateSigningRequest.register(rust_x509.CertificateSigningRequest)


# Backend argument preserved for API compatibility, but ignored.
def load_pem_x509_certificate(
    data: bytes, backend: typing.Any = None
) -> Certificate:
    return rust_x509.load_pem_x509_certificate(data)


def load_pem_x509_certificates(data: bytes) -> typing.List[Certificate]:
    return rust_x509.load_pem_x509_certificates(data)


# Backend argument preserved for API compatibility, but ignored.
def load_der_x509_certificate(
    data: bytes, backend: typing.Any = None
) -> Certificate:
    return rust_x509.load_der_x509_certificate(data)


# Backend argument preserved for API compatibility, but ignored.
def load_pem_x509_csr(
    data: bytes, backend: typing.Any = None
) -> CertificateSigningRequest:
    return rust_x509.load_pem_x509_csr(data)


# Backend argument preserved for API compatibility, but ignored.
def load_der_x509_csr(
    data: bytes, backend: typing.Any = None
) -> CertificateSigningRequest:
    return rust_x509.load_der_x509_csr(data)


# Backend argument preserved for API compatibility, but ignored.
def load_pem_x509_crl(
    data: bytes, backend: typing.Any = None
) -> CertificateRevocationList:
    return rust_x509.load_pem_x509_crl(data)


# Backend argument preserved for API compatibility, but ignored.
def load_der_x509_crl(
    data: bytes, backend: typing.Any = None
) -> CertificateRevocationList:
    return rust_x509.load_der_x509_crl(data)


class CertificateSigningRequestBuilder:
    def __init__(
        self,
        subject_name: typing.Optional[Name] = None,
        extensions: typing.List[Extension[ExtensionType]] = [],
        attributes: typing.List[
            typing.Tuple[ObjectIdentifier, bytes, typing.Optional[int]]
        ] = [],
    ):
        """
        Creates an empty X.509 certificate request (v1).
        """
        self._subject_name = subject_name
        self._extensions = extensions
        self._attributes = attributes

    def subject_name(self, name: Name) -> CertificateSigningRequestBuilder:
        """
        Sets the certificate requestor's distinguished name.
        """
        if not isinstance(name, Name):
            raise TypeError("Expecting x509.Name object.")
        if self._subject_name is not None:
            raise ValueError("The subject name may only be set once.")
        return CertificateSigningRequestBuilder(
            name, self._extensions, self._attributes
        )

    def add_extension(
        self, extval: ExtensionType, critical: bool
    ) -> CertificateSigningRequestBuilder:
        """
        Adds an X.509 extension to the certificate request.
        """
        if not isinstance(extval, ExtensionType):
            raise TypeError("extension must be an ExtensionType")

        extension = Extension(extval.oid, critical, extval)
        _reject_duplicate_extension(extension, self._extensions)

        return CertificateSigningRequestBuilder(
            self._subject_name,
            self._extensions + [extension],
            self._attributes,
        )

    def add_attribute(
        self,
        oid: ObjectIdentifier,
        value: bytes,
        *,
        _tag: typing.Optional[_ASN1Type] = None,
    ) -> CertificateSigningRequestBuilder:
        """
        Adds an X.509 attribute with an OID and associated value.
        """
        if not isinstance(oid, ObjectIdentifier):
            raise TypeError("oid must be an ObjectIdentifier")

        if not isinstance(value, bytes):
            raise TypeError("value must be bytes")

        if _tag is not None and not isinstance(_tag, _ASN1Type):
            raise TypeError("tag must be _ASN1Type")

        _reject_duplicate_attribute(oid, self._attributes)

        if _tag is not None:
            tag = _tag.value
        else:
            tag = None

        return CertificateSigningRequestBuilder(
            self._subject_name,
            self._extensions,
            self._attributes + [(oid, value, tag)],
        )

    def sign(
        self,
        private_key: CertificateIssuerPrivateKeyTypes,
        algorithm: typing.Optional[_AllowedHashTypes],
        backend: typing.Any = None,
    ) -> CertificateSigningRequest:
        """
        Signs the request using the requestor's private key.
        """
        if self._subject_name is None:
            raise ValueError("A CertificateSigningRequest must have a subject")
        return rust_x509.create_x509_csr(self, private_key, algorithm)


class CertificateBuilder:
    _extensions: typing.List[Extension[ExtensionType]]

    def __init__(
        self,
        issuer_name: typing.Optional[Name] = None,
        subject_name: typing.Optional[Name] = None,
        public_key: typing.Optional[CertificatePublicKeyTypes] = None,
        serial_number: typing.Optional[int] = None,
        not_valid_before: typing.Optional[datetime.datetime] = None,
        not_valid_after: typing.Optional[datetime.datetime] = None,
        extensions: typing.List[Extension[ExtensionType]] = [],
    ) -> None:
        self._version = Version.v3
        self._issuer_name = issuer_name
        self._subject_name = subject_name
        self._public_key = public_key
        self._serial_number = serial_number
        self._not_valid_before = not_valid_before
        self._not_valid_after = not_valid_after
        self._extensions = extensions

    def issuer_name(self, name: Name) -> CertificateBuilder:
        """
        Sets the CA's distinguished name.
        """
        if not isinstance(name, Name):
            raise TypeError("Expecting x509.Name object.")
        if self._issuer_name is not None:
            raise ValueError("The issuer name may only be set once.")
        return CertificateBuilder(
            name,
            self._subject_name,
            self._public_key,
            self._serial_number,
            self._not_valid_before,
            self._not_valid_after,
            self._extensions,
        )

    def subject_name(self, name: Name) -> CertificateBuilder:
        """
        Sets the requestor's distinguished name.
        """
        if not isinstance(name, Name):
            raise TypeError("Expecting x509.Name object.")
        if self._subject_name is not None:
            raise ValueError("The subject name may only be set once.")
        return CertificateBuilder(
            self._issuer_name,
            name,
            self._public_key,
            self._serial_number,
            self._not_valid_before,
            self._not_valid_after,
            self._extensions,
        )

    def public_key(
        self,
        key: CertificatePublicKeyTypes,
    ) -> CertificateBuilder:
        """
        Sets the requestor's public key (as found in the signing request).
        """
        if not isinstance(
            key,
            (
                dsa.DSAPublicKey,
                rsa.RSAPublicKey,
                ec.EllipticCurvePublicKey,
                ed25519.Ed25519PublicKey,
                ed448.Ed448PublicKey,
                x25519.X25519PublicKey,
                x448.X448PublicKey,
            ),
        ):
            raise TypeError(
                "Expecting one of DSAPublicKey, RSAPublicKey,"
                " EllipticCurvePublicKey, Ed25519PublicKey,"
                " Ed448PublicKey, X25519PublicKey, or "
                "X448PublicKey."
            )
        if self._public_key is not None:
            raise ValueError("The public key may only be set once.")
        return CertificateBuilder(
            self._issuer_name,
            self._subject_name,
            key,
            self._serial_number,
            self._not_valid_before,
            self._not_valid_after,
            self._extensions,
        )

    def serial_number(self, number: int) -> CertificateBuilder:
        """
        Sets the certificate serial number.
        """
        if not isinstance(number, int):
            raise TypeError("Serial number must be of integral type.")
        if self._serial_number is not None:
            raise ValueError("The serial number may only be set once.")
        if number <= 0:
            raise ValueError("The serial number should be positive.")

        # ASN.1 integers are always signed, so most significant bit must be
        # zero.
        if number.bit_length() >= 160:  # As defined in RFC 5280
            raise ValueError(
                "The serial number should not be more than 159 " "bits."
            )
        return CertificateBuilder(
            self._issuer_name,
            self._subject_name,
            self._public_key,
            number,
            self._not_valid_before,
            self._not_valid_after,
            self._extensions,
        )

    def not_valid_before(self, time: datetime.datetime) -> CertificateBuilder:
        """
        Sets the certificate activation time.
        """
        if not isinstance(time, datetime.datetime):
            raise TypeError("Expecting datetime object.")
        if self._not_valid_before is not None:
            raise ValueError("The not valid before may only be set once.")
        time = _convert_to_naive_utc_time(time)
        if time < _EARLIEST_UTC_TIME:
            raise ValueError(
                "The not valid before date must be on or after"
                " 1950 January 1)."
            )
        if self._not_valid_after is not None and time > self._not_valid_after:
            raise ValueError(
                "The not valid before date must be before the not valid after "
                "date."
            )
        return CertificateBuilder(
            self._issuer_name,
            self._subject_name,
            self._public_key,
            self._serial_number,
            time,
            self._not_valid_after,
            self._extensions,
        )

    def not_valid_after(self, time: datetime.datetime) -> CertificateBuilder:
        """
        Sets the certificate expiration time.
        """
        if not isinstance(time, datetime.datetime):
            raise TypeError("Expecting datetime object.")
        if self._not_valid_after is not None:
            raise ValueError("The not valid after may only be set once.")
        time = _convert_to_naive_utc_time(time)
        if time < _EARLIEST_UTC_TIME:
            raise ValueError(
                "The not valid after date must be on or after"
                " 1950 January 1."
            )
        if (
            self._not_valid_before is not None
            and time < self._not_valid_before
        ):
            raise ValueError(
                "The not valid after date must be after the not valid before "
                "date."
            )
        return CertificateBuilder(
            self._issuer_name,
            self._subject_name,
            self._public_key,
            self._serial_number,
            self._not_valid_before,
            time,
            self._extensions,
        )

    def add_extension(
        self, extval: ExtensionType, critical: bool
    ) -> CertificateBuilder:
        """
        Adds an X.509 extension to the certificate.
        """
        if not isinstance(extval, ExtensionType):
            raise TypeError("extension must be an ExtensionType")

        extension = Extension(extval.oid, critical, extval)
        _reject_duplicate_extension(extension, self._extensions)

        return CertificateBuilder(
            self._issuer_name,
            self._subject_name,
            self._public_key,
            self._serial_number,
            self._not_valid_before,
            self._not_valid_after,
            self._extensions + [extension],
        )

    def sign(
        self,
        private_key: CertificateIssuerPrivateKeyTypes,
        algorithm: typing.Optional[_AllowedHashTypes],
        backend: typing.Any = None,
        *,
        rsa_padding: typing.Optional[
            typing.Union[padding.PSS, padding.PKCS1v15]
        ] = None,
    ) -> Certificate:
        """
        Signs the certificate using the CA's private key.
        """
        if self._subject_name is None:
            raise ValueError("A certificate must have a subject name")

        if self._issuer_name is None:
            raise ValueError("A certificate must have an issuer name")

        if self._serial_number is None:
            raise ValueError("A certificate must have a serial number")

        if self._not_valid_before is None:
            raise ValueError("A certificate must have a not valid before time")

        if self._not_valid_after is None:
            raise ValueError("A certificate must have a not valid after time")

        if self._public_key is None:
            raise ValueError("A certificate must have a public key")

        if rsa_padding is not None:
            if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)):
                raise TypeError("Padding must be PSS or PKCS1v15")
            if not isinstance(private_key, rsa.RSAPrivateKey):
                raise TypeError("Padding is only supported for RSA keys")

        return rust_x509.create_x509_certificate(
            self, private_key, algorithm, rsa_padding
        )


class CertificateRevocationListBuilder:
    _extensions: typing.List[Extension[ExtensionType]]
    _revoked_certificates: typing.List[RevokedCertificate]

    def __init__(
        self,
        issuer_name: typing.Optional[Name] = None,
        last_update: typing.Optional[datetime.datetime] = None,
        next_update: typing.Optional[datetime.datetime] = None,
        extensions: typing.List[Extension[ExtensionType]] = [],
        revoked_certificates: typing.List[RevokedCertificate] = [],
    ):
        self._issuer_name = issuer_name
        self._last_update = last_update
        self._next_update = next_update
        self._extensions = extensions
        self._revoked_certificates = revoked_certificates

    def issuer_name(
        self, issuer_name: Name
    ) -> CertificateRevocationListBuilder:
        if not isinstance(issuer_name, Name):
            raise TypeError("Expecting x509.Name object.")
        if self._issuer_name is not None:
            raise ValueError("The issuer name may only be set once.")
        return CertificateRevocationListBuilder(
            issuer_name,
            self._last_update,
            self._next_update,
            self._extensions,
            self._revoked_certificates,
        )

    def last_update(
        self, last_update: datetime.datetime
    ) -> CertificateRevocationListBuilder:
        if not isinstance(last_update, datetime.datetime):
            raise TypeError("Expecting datetime object.")
        if self._last_update is not None:
            raise ValueError("Last update may only be set once.")
        last_update = _convert_to_naive_utc_time(last_update)
        if last_update < _EARLIEST_UTC_TIME:
            raise ValueError(
                "The last update date must be on or after" " 1950 January 1."
            )
        if self._next_update is not None and last_update > self._next_update:
            raise ValueError(
                "The last update date must be before the next update date."
            )
        return CertificateRevocationListBuilder(
            self._issuer_name,
            last_update,
            self._next_update,
            self._extensions,
            self._revoked_certificates,
        )

    def next_update(
        self, next_update: datetime.datetime
    ) -> CertificateRevocationListBuilder:
        if not isinstance(next_update, datetime.datetime):
            raise TypeError("Expecting datetime object.")
        if self._next_update is not None:
            raise ValueError("Last update may only be set once.")
        next_update = _convert_to_naive_utc_time(next_update)
        if next_update < _EARLIEST_UTC_TIME:
            raise ValueError(
                "The last update date must be on or after" " 1950 January 1."
            )
        if self._last_update is not None and next_update < self._last_update:
            raise ValueError(
                "The next update date must be after the last update date."
            )
        return CertificateRevocationListBuilder(
            self._issuer_name,
            self._last_update,
            next_update,
            self._extensions,
            self._revoked_certificates,
        )

    def add_extension(
        self, extval: ExtensionType, critical: bool
    ) -> CertificateRevocationListBuilder:
        """
        Adds an X.509 extension to the certificate revocation list.
        """
        if not isinstance(extval, ExtensionType):
            raise TypeError("extension must be an ExtensionType")

        extension = Extension(extval.oid, critical, extval)
        _reject_duplicate_extension(extension, self._extensions)
        return CertificateRevocationListBuilder(
            self._issuer_name,
            self._last_update,
            self._next_update,
            self._extensions + [extension],
            self._revoked_certificates,
        )

    def add_revoked_certificate(
        self, revoked_certificate: RevokedCertificate
    ) -> CertificateRevocationListBuilder:
        """
        Adds a revoked certificate to the CRL.
        """
        if not isinstance(revoked_certificate, RevokedCertificate):
            raise TypeError("Must be an instance of RevokedCertificate")

        return CertificateRevocationListBuilder(
            self._issuer_name,
            self._last_update,
            self._next_update,
            self._extensions,
            self._revoked_certificates + [revoked_certificate],
        )

    def sign(
        self,
        private_key: CertificateIssuerPrivateKeyTypes,
        algorithm: typing.Optional[_AllowedHashTypes],
        backend: typing.Any = None,
    ) -> CertificateRevocationList:
        if self._issuer_name is None:
            raise ValueError("A CRL must have an issuer name")

        if self._last_update is None:
            raise ValueError("A CRL must have a last update time")

        if self._next_update is None:
            raise ValueError("A CRL must have a next update time")

        return rust_x509.create_x509_crl(self, private_key, algorithm)


class RevokedCertificateBuilder:
    def __init__(
        self,
        serial_number: typing.Optional[int] = None,
        revocation_date: typing.Optional[datetime.datetime] = None,
        extensions: typing.List[Extension[ExtensionType]] = [],
    ):
        self._serial_number = serial_number
        self._revocation_date = revocation_date
        self._extensions = extensions

    def serial_number(self, number: int) -> RevokedCertificateBuilder:
        if not isinstance(number, int):
            raise TypeError("Serial number must be of integral type.")
        if self._serial_number is not None:
            raise ValueError("The serial number may only be set once.")
        if number <= 0:
            raise ValueError("The serial number should be positive")

        # ASN.1 integers are always signed, so most significant bit must be
        # zero.
        if number.bit_length() >= 160:  # As defined in RFC 5280
            raise ValueError(
                "The serial number should not be more than 159 " "bits."
            )
        return RevokedCertificateBuilder(
            number, self._revocation_date, self._extensions
        )

    def revocation_date(
        self, time: datetime.datetime
    ) -> RevokedCertificateBuilder:
        if not isinstance(time, datetime.datetime):
            raise TypeError("Expecting datetime object.")
        if self._revocation_date is not None:
            raise ValueError("The revocation date may only be set once.")
        time = _convert_to_naive_utc_time(time)
        if time < _EARLIEST_UTC_TIME:
            raise ValueError(
                "The revocation date must be on or after" " 1950 January 1."
            )
        return RevokedCertificateBuilder(
            self._serial_number, time, self._extensions
        )

    def add_extension(
        self, extval: ExtensionType, critical: bool
    ) -> RevokedCertificateBuilder:
        if not isinstance(extval, ExtensionType):
            raise TypeError("extension must be an ExtensionType")

        extension = Extension(extval.oid, critical, extval)
        _reject_duplicate_extension(extension, self._extensions)
        return RevokedCertificateBuilder(
            self._serial_number,
            self._revocation_date,
            self._extensions + [extension],
        )

    def build(self, backend: typing.Any = None) -> RevokedCertificate:
        if self._serial_number is None:
            raise ValueError("A revoked certificate must have a serial number")
        if self._revocation_date is None:
            raise ValueError(
                "A revoked certificate must have a revocation date"
            )
        return _RawRevokedCertificate(
            self._serial_number,
            self._revocation_date,
            Extensions(self._extensions),
        )


def random_serial_number() -> int:
    return int.from_bytes(os.urandom(20), "big") >> 1

back/venvmsg/Lib/site-packages/cryptography/x509/base.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import datetime
import hashlib
import ipaddress
import typing

from cryptography import utils
from cryptography.hazmat.bindings._rust import asn1
from cryptography.hazmat.bindings._rust import x509 as rust_x509
from cryptography.hazmat.primitives import constant_time, serialization
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
from cryptography.hazmat.primitives.asymmetric.types import (
    CertificateIssuerPublicKeyTypes,
    CertificatePublicKeyTypes,
)
from cryptography.x509.certificate_transparency import (
    SignedCertificateTimestamp,
)
from cryptography.x509.general_name import (
    DirectoryName,
    DNSName,
    GeneralName,
    IPAddress,
    OtherName,
    RegisteredID,
    RFC822Name,
    UniformResourceIdentifier,
    _IPAddressTypes,
)
from cryptography.x509.name import Name, RelativeDistinguishedName
from cryptography.x509.oid import (
    CRLEntryExtensionOID,
    ExtensionOID,
    ObjectIdentifier,
    OCSPExtensionOID,
)

ExtensionTypeVar = typing.TypeVar(
    "ExtensionTypeVar", bound="ExtensionType", covariant=True
)


def _key_identifier_from_public_key(
    public_key: CertificatePublicKeyTypes,
) -> bytes:
    if isinstance(public_key, RSAPublicKey):
        data = public_key.public_bytes(
            serialization.Encoding.DER,
            serialization.PublicFormat.PKCS1,
        )
    elif isinstance(public_key, EllipticCurvePublicKey):
        data = public_key.public_bytes(
            serialization.Encoding.X962,
            serialization.PublicFormat.UncompressedPoint,
        )
    else:
        # This is a very slow way to do this.
        serialized = public_key.public_bytes(
            serialization.Encoding.DER,
            serialization.PublicFormat.SubjectPublicKeyInfo,
        )
        data = asn1.parse_spki_for_data(serialized)

    return hashlib.sha1(data).digest()


def _make_sequence_methods(field_name: str):
    def len_method(self) -> int:
        return len(getattr(self, field_name))

    def iter_method(self):
        return iter(getattr(self, field_name))

    def getitem_method(self, idx):
        return getattr(self, field_name)[idx]

    return len_method, iter_method, getitem_method


class DuplicateExtension(Exception):
    def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
        super().__init__(msg)
        self.oid = oid


class ExtensionNotFound(Exception):
    def __init__(self, msg: str, oid: ObjectIdentifier) -> None:
        super().__init__(msg)
        self.oid = oid


class ExtensionType(metaclass=abc.ABCMeta):
    oid: typing.ClassVar[ObjectIdentifier]

    def public_bytes(self) -> bytes:
        """
        Serializes the extension type to DER.
        """
        raise NotImplementedError(
            "public_bytes is not implemented for extension type {!r}".format(
                self
            )
        )


class Extensions:
    def __init__(
        self, extensions: typing.Iterable[Extension[ExtensionType]]
    ) -> None:
        self._extensions = list(extensions)

    def get_extension_for_oid(
        self, oid: ObjectIdentifier
    ) -> Extension[ExtensionType]:
        for ext in self:
            if ext.oid == oid:
                return ext

        raise ExtensionNotFound(f"No {oid} extension was found", oid)

    def get_extension_for_class(
        self, extclass: typing.Type[ExtensionTypeVar]
    ) -> Extension[ExtensionTypeVar]:
        if extclass is UnrecognizedExtension:
            raise TypeError(
                "UnrecognizedExtension can't be used with "
                "get_extension_for_class because more than one instance of the"
                " class may be present."
            )

        for ext in self:
            if isinstance(ext.value, extclass):
                return ext

        raise ExtensionNotFound(
            f"No {extclass} extension was found", extclass.oid
        )

    __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions")

    def __repr__(self) -> str:
        return f"<Extensions({self._extensions})>"


class CRLNumber(ExtensionType):
    oid = ExtensionOID.CRL_NUMBER

    def __init__(self, crl_number: int) -> None:
        if not isinstance(crl_number, int):
            raise TypeError("crl_number must be an integer")

        self._crl_number = crl_number

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, CRLNumber):
            return NotImplemented

        return self.crl_number == other.crl_number

    def __hash__(self) -> int:
        return hash(self.crl_number)

    def __repr__(self) -> str:
        return f"<CRLNumber({self.crl_number})>"

    @property
    def crl_number(self) -> int:
        return self._crl_number

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class AuthorityKeyIdentifier(ExtensionType):
    oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER

    def __init__(
        self,
        key_identifier: typing.Optional[bytes],
        authority_cert_issuer: typing.Optional[typing.Iterable[GeneralName]],
        authority_cert_serial_number: typing.Optional[int],
    ) -> None:
        if (authority_cert_issuer is None) != (
            authority_cert_serial_number is None
        ):
            raise ValueError(
                "authority_cert_issuer and authority_cert_serial_number "
                "must both be present or both None"
            )

        if authority_cert_issuer is not None:
            authority_cert_issuer = list(authority_cert_issuer)
            if not all(
                isinstance(x, GeneralName) for x in authority_cert_issuer
            ):
                raise TypeError(
                    "authority_cert_issuer must be a list of GeneralName "
                    "objects"
                )

        if authority_cert_serial_number is not None and not isinstance(
            authority_cert_serial_number, int
        ):
            raise TypeError("authority_cert_serial_number must be an integer")

        self._key_identifier = key_identifier
        self._authority_cert_issuer = authority_cert_issuer
        self._authority_cert_serial_number = authority_cert_serial_number

    # This takes a subset of CertificatePublicKeyTypes because an issuer
    # cannot have an X25519/X448 key. This introduces some unfortunate
    # asymmetry that requires typing users to explicitly
    # narrow their type, but we should make this accurate and not just
    # convenient.
    @classmethod
    def from_issuer_public_key(
        cls, public_key: CertificateIssuerPublicKeyTypes
    ) -> AuthorityKeyIdentifier:
        digest = _key_identifier_from_public_key(public_key)
        return cls(
            key_identifier=digest,
            authority_cert_issuer=None,
            authority_cert_serial_number=None,
        )

    @classmethod
    def from_issuer_subject_key_identifier(
        cls, ski: SubjectKeyIdentifier
    ) -> AuthorityKeyIdentifier:
        return cls(
            key_identifier=ski.digest,
            authority_cert_issuer=None,
            authority_cert_serial_number=None,
        )

    def __repr__(self) -> str:
        return (
            "<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, "
            "authority_cert_issuer={0.authority_cert_issuer}, "
            "authority_cert_serial_number={0.authority_cert_serial_number}"
            ")>".format(self)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, AuthorityKeyIdentifier):
            return NotImplemented

        return (
            self.key_identifier == other.key_identifier
            and self.authority_cert_issuer == other.authority_cert_issuer
            and self.authority_cert_serial_number
            == other.authority_cert_serial_number
        )

    def __hash__(self) -> int:
        if self.authority_cert_issuer is None:
            aci = None
        else:
            aci = tuple(self.authority_cert_issuer)
        return hash(
            (self.key_identifier, aci, self.authority_cert_serial_number)
        )

    @property
    def key_identifier(self) -> typing.Optional[bytes]:
        return self._key_identifier

    @property
    def authority_cert_issuer(
        self,
    ) -> typing.Optional[typing.List[GeneralName]]:
        return self._authority_cert_issuer

    @property
    def authority_cert_serial_number(self) -> typing.Optional[int]:
        return self._authority_cert_serial_number

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class SubjectKeyIdentifier(ExtensionType):
    oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER

    def __init__(self, digest: bytes) -> None:
        self._digest = digest

    @classmethod
    def from_public_key(
        cls, public_key: CertificatePublicKeyTypes
    ) -> SubjectKeyIdentifier:
        return cls(_key_identifier_from_public_key(public_key))

    @property
    def digest(self) -> bytes:
        return self._digest

    @property
    def key_identifier(self) -> bytes:
        return self._digest

    def __repr__(self) -> str:
        return f"<SubjectKeyIdentifier(digest={self.digest!r})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, SubjectKeyIdentifier):
            return NotImplemented

        return constant_time.bytes_eq(self.digest, other.digest)

    def __hash__(self) -> int:
        return hash(self.digest)

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class AuthorityInformationAccess(ExtensionType):
    oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS

    def __init__(
        self, descriptions: typing.Iterable[AccessDescription]
    ) -> None:
        descriptions = list(descriptions)
        if not all(isinstance(x, AccessDescription) for x in descriptions):
            raise TypeError(
                "Every item in the descriptions list must be an "
                "AccessDescription"
            )

        self._descriptions = descriptions

    __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")

    def __repr__(self) -> str:
        return f"<AuthorityInformationAccess({self._descriptions})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, AuthorityInformationAccess):
            return NotImplemented

        return self._descriptions == other._descriptions

    def __hash__(self) -> int:
        return hash(tuple(self._descriptions))

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class SubjectInformationAccess(ExtensionType):
    oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS

    def __init__(
        self, descriptions: typing.Iterable[AccessDescription]
    ) -> None:
        descriptions = list(descriptions)
        if not all(isinstance(x, AccessDescription) for x in descriptions):
            raise TypeError(
                "Every item in the descriptions list must be an "
                "AccessDescription"
            )

        self._descriptions = descriptions

    __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions")

    def __repr__(self) -> str:
        return f"<SubjectInformationAccess({self._descriptions})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, SubjectInformationAccess):
            return NotImplemented

        return self._descriptions == other._descriptions

    def __hash__(self) -> int:
        return hash(tuple(self._descriptions))

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class AccessDescription:
    def __init__(
        self, access_method: ObjectIdentifier, access_location: GeneralName
    ) -> None:
        if not isinstance(access_method, ObjectIdentifier):
            raise TypeError("access_method must be an ObjectIdentifier")

        if not isinstance(access_location, GeneralName):
            raise TypeError("access_location must be a GeneralName")

        self._access_method = access_method
        self._access_location = access_location

    def __repr__(self) -> str:
        return (
            "<AccessDescription(access_method={0.access_method}, access_locati"
            "on={0.access_location})>".format(self)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, AccessDescription):
            return NotImplemented

        return (
            self.access_method == other.access_method
            and self.access_location == other.access_location
        )

    def __hash__(self) -> int:
        return hash((self.access_method, self.access_location))

    @property
    def access_method(self) -> ObjectIdentifier:
        return self._access_method

    @property
    def access_location(self) -> GeneralName:
        return self._access_location


class BasicConstraints(ExtensionType):
    oid = ExtensionOID.BASIC_CONSTRAINTS

    def __init__(self, ca: bool, path_length: typing.Optional[int]) -> None:
        if not isinstance(ca, bool):
            raise TypeError("ca must be a boolean value")

        if path_length is not None and not ca:
            raise ValueError("path_length must be None when ca is False")

        if path_length is not None and (
            not isinstance(path_length, int) or path_length < 0
        ):
            raise TypeError(
                "path_length must be a non-negative integer or None"
            )

        self._ca = ca
        self._path_length = path_length

    @property
    def ca(self) -> bool:
        return self._ca

    @property
    def path_length(self) -> typing.Optional[int]:
        return self._path_length

    def __repr__(self) -> str:
        return (
            "<BasicConstraints(ca={0.ca}, " "path_length={0.path_length})>"
        ).format(self)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, BasicConstraints):
            return NotImplemented

        return self.ca == other.ca and self.path_length == other.path_length

    def __hash__(self) -> int:
        return hash((self.ca, self.path_length))

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class DeltaCRLIndicator(ExtensionType):
    oid = ExtensionOID.DELTA_CRL_INDICATOR

    def __init__(self, crl_number: int) -> None:
        if not isinstance(crl_number, int):
            raise TypeError("crl_number must be an integer")

        self._crl_number = crl_number

    @property
    def crl_number(self) -> int:
        return self._crl_number

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DeltaCRLIndicator):
            return NotImplemented

        return self.crl_number == other.crl_number

    def __hash__(self) -> int:
        return hash(self.crl_number)

    def __repr__(self) -> str:
        return f"<DeltaCRLIndicator(crl_number={self.crl_number})>"

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class CRLDistributionPoints(ExtensionType):
    oid = ExtensionOID.CRL_DISTRIBUTION_POINTS

    def __init__(
        self, distribution_points: typing.Iterable[DistributionPoint]
    ) -> None:
        distribution_points = list(distribution_points)
        if not all(
            isinstance(x, DistributionPoint) for x in distribution_points
        ):
            raise TypeError(
                "distribution_points must be a list of DistributionPoint "
                "objects"
            )

        self._distribution_points = distribution_points

    __len__, __iter__, __getitem__ = _make_sequence_methods(
        "_distribution_points"
    )

    def __repr__(self) -> str:
        return f"<CRLDistributionPoints({self._distribution_points})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, CRLDistributionPoints):
            return NotImplemented

        return self._distribution_points == other._distribution_points

    def __hash__(self) -> int:
        return hash(tuple(self._distribution_points))

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class FreshestCRL(ExtensionType):
    oid = ExtensionOID.FRESHEST_CRL

    def __init__(
        self, distribution_points: typing.Iterable[DistributionPoint]
    ) -> None:
        distribution_points = list(distribution_points)
        if not all(
            isinstance(x, DistributionPoint) for x in distribution_points
        ):
            raise TypeError(
                "distribution_points must be a list of DistributionPoint "
                "objects"
            )

        self._distribution_points = distribution_points

    __len__, __iter__, __getitem__ = _make_sequence_methods(
        "_distribution_points"
    )

    def __repr__(self) -> str:
        return f"<FreshestCRL({self._distribution_points})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, FreshestCRL):
            return NotImplemented

        return self._distribution_points == other._distribution_points

    def __hash__(self) -> int:
        return hash(tuple(self._distribution_points))

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class DistributionPoint:
    def __init__(
        self,
        full_name: typing.Optional[typing.Iterable[GeneralName]],
        relative_name: typing.Optional[RelativeDistinguishedName],
        reasons: typing.Optional[typing.FrozenSet[ReasonFlags]],
        crl_issuer: typing.Optional[typing.Iterable[GeneralName]],
    ) -> None:
        if full_name and relative_name:
            raise ValueError(
                "You cannot provide both full_name and relative_name, at "
                "least one must be None."
            )
        if not full_name and not relative_name and not crl_issuer:
            raise ValueError(
                "Either full_name, relative_name or crl_issuer must be "
                "provided."
            )

        if full_name is not None:
            full_name = list(full_name)
            if not all(isinstance(x, GeneralName) for x in full_name):
                raise TypeError(
                    "full_name must be a list of GeneralName objects"
                )

        if relative_name:
            if not isinstance(relative_name, RelativeDistinguishedName):
                raise TypeError(
                    "relative_name must be a RelativeDistinguishedName"
                )

        if crl_issuer is not None:
            crl_issuer = list(crl_issuer)
            if not all(isinstance(x, GeneralName) for x in crl_issuer):
                raise TypeError(
                    "crl_issuer must be None or a list of general names"
                )

        if reasons and (
            not isinstance(reasons, frozenset)
            or not all(isinstance(x, ReasonFlags) for x in reasons)
        ):
            raise TypeError("reasons must be None or frozenset of ReasonFlags")

        if reasons and (
            ReasonFlags.unspecified in reasons
            or ReasonFlags.remove_from_crl in reasons
        ):
            raise ValueError(
                "unspecified and remove_from_crl are not valid reasons in a "
                "DistributionPoint"
            )

        self._full_name = full_name
        self._relative_name = relative_name
        self._reasons = reasons
        self._crl_issuer = crl_issuer

    def __repr__(self) -> str:
        return (
            "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela"
            "tive_name}, reasons={0.reasons}, "
            "crl_issuer={0.crl_issuer})>".format(self)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DistributionPoint):
            return NotImplemented

        return (
            self.full_name == other.full_name
            and self.relative_name == other.relative_name
            and self.reasons == other.reasons
            and self.crl_issuer == other.crl_issuer
        )

    def __hash__(self) -> int:
        if self.full_name is not None:
            fn: typing.Optional[typing.Tuple[GeneralName, ...]] = tuple(
                self.full_name
            )
        else:
            fn = None

        if self.crl_issuer is not None:
            crl_issuer: typing.Optional[
                typing.Tuple[GeneralName, ...]
            ] = tuple(self.crl_issuer)
        else:
            crl_issuer = None

        return hash((fn, self.relative_name, self.reasons, crl_issuer))

    @property
    def full_name(self) -> typing.Optional[typing.List[GeneralName]]:
        return self._full_name

    @property
    def relative_name(self) -> typing.Optional[RelativeDistinguishedName]:
        return self._relative_name

    @property
    def reasons(self) -> typing.Optional[typing.FrozenSet[ReasonFlags]]:
        return self._reasons

    @property
    def crl_issuer(self) -> typing.Optional[typing.List[GeneralName]]:
        return self._crl_issuer


class ReasonFlags(utils.Enum):
    unspecified = "unspecified"
    key_compromise = "keyCompromise"
    ca_compromise = "cACompromise"
    affiliation_changed = "affiliationChanged"
    superseded = "superseded"
    cessation_of_operation = "cessationOfOperation"
    certificate_hold = "certificateHold"
    privilege_withdrawn = "privilegeWithdrawn"
    aa_compromise = "aACompromise"
    remove_from_crl = "removeFromCRL"


# These are distribution point bit string mappings. Not to be confused with
# CRLReason reason flags bit string mappings.
# ReasonFlags ::= BIT STRING {
#      unused                  (0),
#      keyCompromise           (1),
#      cACompromise            (2),
#      affiliationChanged      (3),
#      superseded              (4),
#      cessationOfOperation    (5),
#      certificateHold         (6),
#      privilegeWithdrawn      (7),
#      aACompromise            (8) }
_REASON_BIT_MAPPING = {
    1: ReasonFlags.key_compromise,
    2: ReasonFlags.ca_compromise,
    3: ReasonFlags.affiliation_changed,
    4: ReasonFlags.superseded,
    5: ReasonFlags.cessation_of_operation,
    6: ReasonFlags.certificate_hold,
    7: ReasonFlags.privilege_withdrawn,
    8: ReasonFlags.aa_compromise,
}

_CRLREASONFLAGS = {
    ReasonFlags.key_compromise: 1,
    ReasonFlags.ca_compromise: 2,
    ReasonFlags.affiliation_changed: 3,
    ReasonFlags.superseded: 4,
    ReasonFlags.cessation_of_operation: 5,
    ReasonFlags.certificate_hold: 6,
    ReasonFlags.privilege_withdrawn: 7,
    ReasonFlags.aa_compromise: 8,
}


class PolicyConstraints(ExtensionType):
    oid = ExtensionOID.POLICY_CONSTRAINTS

    def __init__(
        self,
        require_explicit_policy: typing.Optional[int],
        inhibit_policy_mapping: typing.Optional[int],
    ) -> None:
        if require_explicit_policy is not None and not isinstance(
            require_explicit_policy, int
        ):
            raise TypeError(
                "require_explicit_policy must be a non-negative integer or "
                "None"
            )

        if inhibit_policy_mapping is not None and not isinstance(
            inhibit_policy_mapping, int
        ):
            raise TypeError(
                "inhibit_policy_mapping must be a non-negative integer or None"
            )

        if inhibit_policy_mapping is None and require_explicit_policy is None:
            raise ValueError(
                "At least one of require_explicit_policy and "
                "inhibit_policy_mapping must not be None"
            )

        self._require_explicit_policy = require_explicit_policy
        self._inhibit_policy_mapping = inhibit_policy_mapping

    def __repr__(self) -> str:
        return (
            "<PolicyConstraints(require_explicit_policy={0.require_explicit"
            "_policy}, inhibit_policy_mapping={0.inhibit_policy_"
            "mapping})>".format(self)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, PolicyConstraints):
            return NotImplemented

        return (
            self.require_explicit_policy == other.require_explicit_policy
            and self.inhibit_policy_mapping == other.inhibit_policy_mapping
        )

    def __hash__(self) -> int:
        return hash(
            (self.require_explicit_policy, self.inhibit_policy_mapping)
        )

    @property
    def require_explicit_policy(self) -> typing.Optional[int]:
        return self._require_explicit_policy

    @property
    def inhibit_policy_mapping(self) -> typing.Optional[int]:
        return self._inhibit_policy_mapping

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class CertificatePolicies(ExtensionType):
    oid = ExtensionOID.CERTIFICATE_POLICIES

    def __init__(self, policies: typing.Iterable[PolicyInformation]) -> None:
        policies = list(policies)
        if not all(isinstance(x, PolicyInformation) for x in policies):
            raise TypeError(
                "Every item in the policies list must be a "
                "PolicyInformation"
            )

        self._policies = policies

    __len__, __iter__, __getitem__ = _make_sequence_methods("_policies")

    def __repr__(self) -> str:
        return f"<CertificatePolicies({self._policies})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, CertificatePolicies):
            return NotImplemented

        return self._policies == other._policies

    def __hash__(self) -> int:
        return hash(tuple(self._policies))

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class PolicyInformation:
    def __init__(
        self,
        policy_identifier: ObjectIdentifier,
        policy_qualifiers: typing.Optional[
            typing.Iterable[typing.Union[str, UserNotice]]
        ],
    ) -> None:
        if not isinstance(policy_identifier, ObjectIdentifier):
            raise TypeError("policy_identifier must be an ObjectIdentifier")

        self._policy_identifier = policy_identifier

        if policy_qualifiers is not None:
            policy_qualifiers = list(policy_qualifiers)
            if not all(
                isinstance(x, (str, UserNotice)) for x in policy_qualifiers
            ):
                raise TypeError(
                    "policy_qualifiers must be a list of strings and/or "
                    "UserNotice objects or None"
                )

        self._policy_qualifiers = policy_qualifiers

    def __repr__(self) -> str:
        return (
            "<PolicyInformation(policy_identifier={0.policy_identifier}, polic"
            "y_qualifiers={0.policy_qualifiers})>".format(self)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, PolicyInformation):
            return NotImplemented

        return (
            self.policy_identifier == other.policy_identifier
            and self.policy_qualifiers == other.policy_qualifiers
        )

    def __hash__(self) -> int:
        if self.policy_qualifiers is not None:
            pq: typing.Optional[
                typing.Tuple[typing.Union[str, UserNotice], ...]
            ] = tuple(self.policy_qualifiers)
        else:
            pq = None

        return hash((self.policy_identifier, pq))

    @property
    def policy_identifier(self) -> ObjectIdentifier:
        return self._policy_identifier

    @property
    def policy_qualifiers(
        self,
    ) -> typing.Optional[typing.List[typing.Union[str, UserNotice]]]:
        return self._policy_qualifiers


class UserNotice:
    def __init__(
        self,
        notice_reference: typing.Optional[NoticeReference],
        explicit_text: typing.Optional[str],
    ) -> None:
        if notice_reference and not isinstance(
            notice_reference, NoticeReference
        ):
            raise TypeError(
                "notice_reference must be None or a NoticeReference"
            )

        self._notice_reference = notice_reference
        self._explicit_text = explicit_text

    def __repr__(self) -> str:
        return (
            "<UserNotice(notice_reference={0.notice_reference}, explicit_text="
            "{0.explicit_text!r})>".format(self)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, UserNotice):
            return NotImplemented

        return (
            self.notice_reference == other.notice_reference
            and self.explicit_text == other.explicit_text
        )

    def __hash__(self) -> int:
        return hash((self.notice_reference, self.explicit_text))

    @property
    def notice_reference(self) -> typing.Optional[NoticeReference]:
        return self._notice_reference

    @property
    def explicit_text(self) -> typing.Optional[str]:
        return self._explicit_text


class NoticeReference:
    def __init__(
        self,
        organization: typing.Optional[str],
        notice_numbers: typing.Iterable[int],
    ) -> None:
        self._organization = organization
        notice_numbers = list(notice_numbers)
        if not all(isinstance(x, int) for x in notice_numbers):
            raise TypeError("notice_numbers must be a list of integers")

        self._notice_numbers = notice_numbers

    def __repr__(self) -> str:
        return (
            "<NoticeReference(organization={0.organization!r}, notice_numbers="
            "{0.notice_numbers})>".format(self)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, NoticeReference):
            return NotImplemented

        return (
            self.organization == other.organization
            and self.notice_numbers == other.notice_numbers
        )

    def __hash__(self) -> int:
        return hash((self.organization, tuple(self.notice_numbers)))

    @property
    def organization(self) -> typing.Optional[str]:
        return self._organization

    @property
    def notice_numbers(self) -> typing.List[int]:
        return self._notice_numbers


class ExtendedKeyUsage(ExtensionType):
    oid = ExtensionOID.EXTENDED_KEY_USAGE

    def __init__(self, usages: typing.Iterable[ObjectIdentifier]) -> None:
        usages = list(usages)
        if not all(isinstance(x, ObjectIdentifier) for x in usages):
            raise TypeError(
                "Every item in the usages list must be an ObjectIdentifier"
            )

        self._usages = usages

    __len__, __iter__, __getitem__ = _make_sequence_methods("_usages")

    def __repr__(self) -> str:
        return f"<ExtendedKeyUsage({self._usages})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, ExtendedKeyUsage):
            return NotImplemented

        return self._usages == other._usages

    def __hash__(self) -> int:
        return hash(tuple(self._usages))

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class OCSPNoCheck(ExtensionType):
    oid = ExtensionOID.OCSP_NO_CHECK

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, OCSPNoCheck):
            return NotImplemented

        return True

    def __hash__(self) -> int:
        return hash(OCSPNoCheck)

    def __repr__(self) -> str:
        return "<OCSPNoCheck()>"

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class PrecertPoison(ExtensionType):
    oid = ExtensionOID.PRECERT_POISON

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, PrecertPoison):
            return NotImplemented

        return True

    def __hash__(self) -> int:
        return hash(PrecertPoison)

    def __repr__(self) -> str:
        return "<PrecertPoison()>"

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class TLSFeature(ExtensionType):
    oid = ExtensionOID.TLS_FEATURE

    def __init__(self, features: typing.Iterable[TLSFeatureType]) -> None:
        features = list(features)
        if (
            not all(isinstance(x, TLSFeatureType) for x in features)
            or len(features) == 0
        ):
            raise TypeError(
                "features must be a list of elements from the TLSFeatureType "
                "enum"
            )

        self._features = features

    __len__, __iter__, __getitem__ = _make_sequence_methods("_features")

    def __repr__(self) -> str:
        return f"<TLSFeature(features={self._features})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, TLSFeature):
            return NotImplemented

        return self._features == other._features

    def __hash__(self) -> int:
        return hash(tuple(self._features))

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class TLSFeatureType(utils.Enum):
    # status_request is defined in RFC 6066 and is used for what is commonly
    # called OCSP Must-Staple when present in the TLS Feature extension in an
    # X.509 certificate.
    status_request = 5
    # status_request_v2 is defined in RFC 6961 and allows multiple OCSP
    # responses to be provided. It is not currently in use by clients or
    # servers.
    status_request_v2 = 17


_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType}


class InhibitAnyPolicy(ExtensionType):
    oid = ExtensionOID.INHIBIT_ANY_POLICY

    def __init__(self, skip_certs: int) -> None:
        if not isinstance(skip_certs, int):
            raise TypeError("skip_certs must be an integer")

        if skip_certs < 0:
            raise ValueError("skip_certs must be a non-negative integer")

        self._skip_certs = skip_certs

    def __repr__(self) -> str:
        return f"<InhibitAnyPolicy(skip_certs={self.skip_certs})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, InhibitAnyPolicy):
            return NotImplemented

        return self.skip_certs == other.skip_certs

    def __hash__(self) -> int:
        return hash(self.skip_certs)

    @property
    def skip_certs(self) -> int:
        return self._skip_certs

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class KeyUsage(ExtensionType):
    oid = ExtensionOID.KEY_USAGE

    def __init__(
        self,
        digital_signature: bool,
        content_commitment: bool,
        key_encipherment: bool,
        data_encipherment: bool,
        key_agreement: bool,
        key_cert_sign: bool,
        crl_sign: bool,
        encipher_only: bool,
        decipher_only: bool,
    ) -> None:
        if not key_agreement and (encipher_only or decipher_only):
            raise ValueError(
                "encipher_only and decipher_only can only be true when "
                "key_agreement is true"
            )

        self._digital_signature = digital_signature
        self._content_commitment = content_commitment
        self._key_encipherment = key_encipherment
        self._data_encipherment = data_encipherment
        self._key_agreement = key_agreement
        self._key_cert_sign = key_cert_sign
        self._crl_sign = crl_sign
        self._encipher_only = encipher_only
        self._decipher_only = decipher_only

    @property
    def digital_signature(self) -> bool:
        return self._digital_signature

    @property
    def content_commitment(self) -> bool:
        return self._content_commitment

    @property
    def key_encipherment(self) -> bool:
        return self._key_encipherment

    @property
    def data_encipherment(self) -> bool:
        return self._data_encipherment

    @property
    def key_agreement(self) -> bool:
        return self._key_agreement

    @property
    def key_cert_sign(self) -> bool:
        return self._key_cert_sign

    @property
    def crl_sign(self) -> bool:
        return self._crl_sign

    @property
    def encipher_only(self) -> bool:
        if not self.key_agreement:
            raise ValueError(
                "encipher_only is undefined unless key_agreement is true"
            )
        else:
            return self._encipher_only

    @property
    def decipher_only(self) -> bool:
        if not self.key_agreement:
            raise ValueError(
                "decipher_only is undefined unless key_agreement is true"
            )
        else:
            return self._decipher_only

    def __repr__(self) -> str:
        try:
            encipher_only = self.encipher_only
            decipher_only = self.decipher_only
        except ValueError:
            # Users found None confusing because even though encipher/decipher
            # have no meaning unless key_agreement is true, to construct an
            # instance of the class you still need to pass False.
            encipher_only = False
            decipher_only = False

        return (
            "<KeyUsage(digital_signature={0.digital_signature}, "
            "content_commitment={0.content_commitment}, "
            "key_encipherment={0.key_encipherment}, "
            "data_encipherment={0.data_encipherment}, "
            "key_agreement={0.key_agreement}, "
            "key_cert_sign={0.key_cert_sign}, crl_sign={0.crl_sign}, "
            "encipher_only={1}, decipher_only={2})>"
        ).format(self, encipher_only, decipher_only)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, KeyUsage):
            return NotImplemented

        return (
            self.digital_signature == other.digital_signature
            and self.content_commitment == other.content_commitment
            and self.key_encipherment == other.key_encipherment
            and self.data_encipherment == other.data_encipherment
            and self.key_agreement == other.key_agreement
            and self.key_cert_sign == other.key_cert_sign
            and self.crl_sign == other.crl_sign
            and self._encipher_only == other._encipher_only
            and self._decipher_only == other._decipher_only
        )

    def __hash__(self) -> int:
        return hash(
            (
                self.digital_signature,
                self.content_commitment,
                self.key_encipherment,
                self.data_encipherment,
                self.key_agreement,
                self.key_cert_sign,
                self.crl_sign,
                self._encipher_only,
                self._decipher_only,
            )
        )

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class NameConstraints(ExtensionType):
    oid = ExtensionOID.NAME_CONSTRAINTS

    def __init__(
        self,
        permitted_subtrees: typing.Optional[typing.Iterable[GeneralName]],
        excluded_subtrees: typing.Optional[typing.Iterable[GeneralName]],
    ) -> None:
        if permitted_subtrees is not None:
            permitted_subtrees = list(permitted_subtrees)
            if not permitted_subtrees:
                raise ValueError(
                    "permitted_subtrees must be a non-empty list or None"
                )
            if not all(isinstance(x, GeneralName) for x in permitted_subtrees):
                raise TypeError(
                    "permitted_subtrees must be a list of GeneralName objects "
                    "or None"
                )

            self._validate_tree(permitted_subtrees)

        if excluded_subtrees is not None:
            excluded_subtrees = list(excluded_subtrees)
            if not excluded_subtrees:
                raise ValueError(
                    "excluded_subtrees must be a non-empty list or None"
                )
            if not all(isinstance(x, GeneralName) for x in excluded_subtrees):
                raise TypeError(
                    "excluded_subtrees must be a list of GeneralName objects "
                    "or None"
                )

            self._validate_tree(excluded_subtrees)

        if permitted_subtrees is None and excluded_subtrees is None:
            raise ValueError(
                "At least one of permitted_subtrees and excluded_subtrees "
                "must not be None"
            )

        self._permitted_subtrees = permitted_subtrees
        self._excluded_subtrees = excluded_subtrees

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, NameConstraints):
            return NotImplemented

        return (
            self.excluded_subtrees == other.excluded_subtrees
            and self.permitted_subtrees == other.permitted_subtrees
        )

    def _validate_tree(self, tree: typing.Iterable[GeneralName]) -> None:
        self._validate_ip_name(tree)
        self._validate_dns_name(tree)

    def _validate_ip_name(self, tree: typing.Iterable[GeneralName]) -> None:
        if any(
            isinstance(name, IPAddress)
            and not isinstance(
                name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network)
            )
            for name in tree
        ):
            raise TypeError(
                "IPAddress name constraints must be an IPv4Network or"
                " IPv6Network object"
            )

    def _validate_dns_name(self, tree: typing.Iterable[GeneralName]) -> None:
        if any(
            isinstance(name, DNSName) and "*" in name.value for name in tree
        ):
            raise ValueError(
                "DNSName name constraints must not contain the '*' wildcard"
                " character"
            )

    def __repr__(self) -> str:
        return (
            "<NameConstraints(permitted_subtrees={0.permitted_subtrees}, "
            "excluded_subtrees={0.excluded_subtrees})>".format(self)
        )

    def __hash__(self) -> int:
        if self.permitted_subtrees is not None:
            ps: typing.Optional[typing.Tuple[GeneralName, ...]] = tuple(
                self.permitted_subtrees
            )
        else:
            ps = None

        if self.excluded_subtrees is not None:
            es: typing.Optional[typing.Tuple[GeneralName, ...]] = tuple(
                self.excluded_subtrees
            )
        else:
            es = None

        return hash((ps, es))

    @property
    def permitted_subtrees(
        self,
    ) -> typing.Optional[typing.List[GeneralName]]:
        return self._permitted_subtrees

    @property
    def excluded_subtrees(
        self,
    ) -> typing.Optional[typing.List[GeneralName]]:
        return self._excluded_subtrees

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class Extension(typing.Generic[ExtensionTypeVar]):
    def __init__(
        self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar
    ) -> None:
        if not isinstance(oid, ObjectIdentifier):
            raise TypeError(
                "oid argument must be an ObjectIdentifier instance."
            )

        if not isinstance(critical, bool):
            raise TypeError("critical must be a boolean value")

        self._oid = oid
        self._critical = critical
        self._value = value

    @property
    def oid(self) -> ObjectIdentifier:
        return self._oid

    @property
    def critical(self) -> bool:
        return self._critical

    @property
    def value(self) -> ExtensionTypeVar:
        return self._value

    def __repr__(self) -> str:
        return (
            "<Extension(oid={0.oid}, critical={0.critical}, "
            "value={0.value})>"
        ).format(self)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Extension):
            return NotImplemented

        return (
            self.oid == other.oid
            and self.critical == other.critical
            and self.value == other.value
        )

    def __hash__(self) -> int:
        return hash((self.oid, self.critical, self.value))


class GeneralNames:
    def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
        general_names = list(general_names)
        if not all(isinstance(x, GeneralName) for x in general_names):
            raise TypeError(
                "Every item in the general_names list must be an "
                "object conforming to the GeneralName interface"
            )

        self._general_names = general_names

    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Union[
            typing.Type[DNSName],
            typing.Type[UniformResourceIdentifier],
            typing.Type[RFC822Name],
        ],
    ) -> typing.List[str]:
        ...

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Type[DirectoryName],
    ) -> typing.List[Name]:
        ...

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Type[RegisteredID],
    ) -> typing.List[ObjectIdentifier]:
        ...

    @typing.overload
    def get_values_for_type(
        self, type: typing.Type[IPAddress]
    ) -> typing.List[_IPAddressTypes]:
        ...

    @typing.overload
    def get_values_for_type(
        self, type: typing.Type[OtherName]
    ) -> typing.List[OtherName]:
        ...

    def get_values_for_type(
        self,
        type: typing.Union[
            typing.Type[DNSName],
            typing.Type[DirectoryName],
            typing.Type[IPAddress],
            typing.Type[OtherName],
            typing.Type[RFC822Name],
            typing.Type[RegisteredID],
            typing.Type[UniformResourceIdentifier],
        ],
    ) -> typing.Union[
        typing.List[_IPAddressTypes],
        typing.List[str],
        typing.List[OtherName],
        typing.List[Name],
        typing.List[ObjectIdentifier],
    ]:
        # Return the value of each GeneralName, except for OtherName instances
        # which we return directly because it has two important properties not
        # just one value.
        objs = (i for i in self if isinstance(i, type))
        if type != OtherName:
            return [i.value for i in objs]
        return list(objs)

    def __repr__(self) -> str:
        return f"<GeneralNames({self._general_names})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, GeneralNames):
            return NotImplemented

        return self._general_names == other._general_names

    def __hash__(self) -> int:
        return hash(tuple(self._general_names))


class SubjectAlternativeName(ExtensionType):
    oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME

    def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
        self._general_names = GeneralNames(general_names)

    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Union[
            typing.Type[DNSName],
            typing.Type[UniformResourceIdentifier],
            typing.Type[RFC822Name],
        ],
    ) -> typing.List[str]:
        ...

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Type[DirectoryName],
    ) -> typing.List[Name]:
        ...

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Type[RegisteredID],
    ) -> typing.List[ObjectIdentifier]:
        ...

    @typing.overload
    def get_values_for_type(
        self, type: typing.Type[IPAddress]
    ) -> typing.List[_IPAddressTypes]:
        ...

    @typing.overload
    def get_values_for_type(
        self, type: typing.Type[OtherName]
    ) -> typing.List[OtherName]:
        ...

    def get_values_for_type(
        self,
        type: typing.Union[
            typing.Type[DNSName],
            typing.Type[DirectoryName],
            typing.Type[IPAddress],
            typing.Type[OtherName],
            typing.Type[RFC822Name],
            typing.Type[RegisteredID],
            typing.Type[UniformResourceIdentifier],
        ],
    ) -> typing.Union[
        typing.List[_IPAddressTypes],
        typing.List[str],
        typing.List[OtherName],
        typing.List[Name],
        typing.List[ObjectIdentifier],
    ]:
        return self._general_names.get_values_for_type(type)

    def __repr__(self) -> str:
        return f"<SubjectAlternativeName({self._general_names})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, SubjectAlternativeName):
            return NotImplemented

        return self._general_names == other._general_names

    def __hash__(self) -> int:
        return hash(self._general_names)

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class IssuerAlternativeName(ExtensionType):
    oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME

    def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
        self._general_names = GeneralNames(general_names)

    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Union[
            typing.Type[DNSName],
            typing.Type[UniformResourceIdentifier],
            typing.Type[RFC822Name],
        ],
    ) -> typing.List[str]:
        ...

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Type[DirectoryName],
    ) -> typing.List[Name]:
        ...

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Type[RegisteredID],
    ) -> typing.List[ObjectIdentifier]:
        ...

    @typing.overload
    def get_values_for_type(
        self, type: typing.Type[IPAddress]
    ) -> typing.List[_IPAddressTypes]:
        ...

    @typing.overload
    def get_values_for_type(
        self, type: typing.Type[OtherName]
    ) -> typing.List[OtherName]:
        ...

    def get_values_for_type(
        self,
        type: typing.Union[
            typing.Type[DNSName],
            typing.Type[DirectoryName],
            typing.Type[IPAddress],
            typing.Type[OtherName],
            typing.Type[RFC822Name],
            typing.Type[RegisteredID],
            typing.Type[UniformResourceIdentifier],
        ],
    ) -> typing.Union[
        typing.List[_IPAddressTypes],
        typing.List[str],
        typing.List[OtherName],
        typing.List[Name],
        typing.List[ObjectIdentifier],
    ]:
        return self._general_names.get_values_for_type(type)

    def __repr__(self) -> str:
        return f"<IssuerAlternativeName({self._general_names})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, IssuerAlternativeName):
            return NotImplemented

        return self._general_names == other._general_names

    def __hash__(self) -> int:
        return hash(self._general_names)

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class CertificateIssuer(ExtensionType):
    oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER

    def __init__(self, general_names: typing.Iterable[GeneralName]) -> None:
        self._general_names = GeneralNames(general_names)

    __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names")

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Union[
            typing.Type[DNSName],
            typing.Type[UniformResourceIdentifier],
            typing.Type[RFC822Name],
        ],
    ) -> typing.List[str]:
        ...

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Type[DirectoryName],
    ) -> typing.List[Name]:
        ...

    @typing.overload
    def get_values_for_type(
        self,
        type: typing.Type[RegisteredID],
    ) -> typing.List[ObjectIdentifier]:
        ...

    @typing.overload
    def get_values_for_type(
        self, type: typing.Type[IPAddress]
    ) -> typing.List[_IPAddressTypes]:
        ...

    @typing.overload
    def get_values_for_type(
        self, type: typing.Type[OtherName]
    ) -> typing.List[OtherName]:
        ...

    def get_values_for_type(
        self,
        type: typing.Union[
            typing.Type[DNSName],
            typing.Type[DirectoryName],
            typing.Type[IPAddress],
            typing.Type[OtherName],
            typing.Type[RFC822Name],
            typing.Type[RegisteredID],
            typing.Type[UniformResourceIdentifier],
        ],
    ) -> typing.Union[
        typing.List[_IPAddressTypes],
        typing.List[str],
        typing.List[OtherName],
        typing.List[Name],
        typing.List[ObjectIdentifier],
    ]:
        return self._general_names.get_values_for_type(type)

    def __repr__(self) -> str:
        return f"<CertificateIssuer({self._general_names})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, CertificateIssuer):
            return NotImplemented

        return self._general_names == other._general_names

    def __hash__(self) -> int:
        return hash(self._general_names)

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class CRLReason(ExtensionType):
    oid = CRLEntryExtensionOID.CRL_REASON

    def __init__(self, reason: ReasonFlags) -> None:
        if not isinstance(reason, ReasonFlags):
            raise TypeError("reason must be an element from ReasonFlags")

        self._reason = reason

    def __repr__(self) -> str:
        return f"<CRLReason(reason={self._reason})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, CRLReason):
            return NotImplemented

        return self.reason == other.reason

    def __hash__(self) -> int:
        return hash(self.reason)

    @property
    def reason(self) -> ReasonFlags:
        return self._reason

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class InvalidityDate(ExtensionType):
    oid = CRLEntryExtensionOID.INVALIDITY_DATE

    def __init__(self, invalidity_date: datetime.datetime) -> None:
        if not isinstance(invalidity_date, datetime.datetime):
            raise TypeError("invalidity_date must be a datetime.datetime")

        self._invalidity_date = invalidity_date

    def __repr__(self) -> str:
        return "<InvalidityDate(invalidity_date={})>".format(
            self._invalidity_date
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, InvalidityDate):
            return NotImplemented

        return self.invalidity_date == other.invalidity_date

    def __hash__(self) -> int:
        return hash(self.invalidity_date)

    @property
    def invalidity_date(self) -> datetime.datetime:
        return self._invalidity_date

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class PrecertificateSignedCertificateTimestamps(ExtensionType):
    oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS

    def __init__(
        self,
        signed_certificate_timestamps: typing.Iterable[
            SignedCertificateTimestamp
        ],
    ) -> None:
        signed_certificate_timestamps = list(signed_certificate_timestamps)
        if not all(
            isinstance(sct, SignedCertificateTimestamp)
            for sct in signed_certificate_timestamps
        ):
            raise TypeError(
                "Every item in the signed_certificate_timestamps list must be "
                "a SignedCertificateTimestamp"
            )
        self._signed_certificate_timestamps = signed_certificate_timestamps

    __len__, __iter__, __getitem__ = _make_sequence_methods(
        "_signed_certificate_timestamps"
    )

    def __repr__(self) -> str:
        return "<PrecertificateSignedCertificateTimestamps({})>".format(
            list(self)
        )

    def __hash__(self) -> int:
        return hash(tuple(self._signed_certificate_timestamps))

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, PrecertificateSignedCertificateTimestamps):
            return NotImplemented

        return (
            self._signed_certificate_timestamps
            == other._signed_certificate_timestamps
        )

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class SignedCertificateTimestamps(ExtensionType):
    oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS

    def __init__(
        self,
        signed_certificate_timestamps: typing.Iterable[
            SignedCertificateTimestamp
        ],
    ) -> None:
        signed_certificate_timestamps = list(signed_certificate_timestamps)
        if not all(
            isinstance(sct, SignedCertificateTimestamp)
            for sct in signed_certificate_timestamps
        ):
            raise TypeError(
                "Every item in the signed_certificate_timestamps list must be "
                "a SignedCertificateTimestamp"
            )
        self._signed_certificate_timestamps = signed_certificate_timestamps

    __len__, __iter__, __getitem__ = _make_sequence_methods(
        "_signed_certificate_timestamps"
    )

    def __repr__(self) -> str:
        return f"<SignedCertificateTimestamps({list(self)})>"

    def __hash__(self) -> int:
        return hash(tuple(self._signed_certificate_timestamps))

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, SignedCertificateTimestamps):
            return NotImplemented

        return (
            self._signed_certificate_timestamps
            == other._signed_certificate_timestamps
        )

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class OCSPNonce(ExtensionType):
    oid = OCSPExtensionOID.NONCE

    def __init__(self, nonce: bytes) -> None:
        if not isinstance(nonce, bytes):
            raise TypeError("nonce must be bytes")

        self._nonce = nonce

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, OCSPNonce):
            return NotImplemented

        return self.nonce == other.nonce

    def __hash__(self) -> int:
        return hash(self.nonce)

    def __repr__(self) -> str:
        return f"<OCSPNonce(nonce={self.nonce!r})>"

    @property
    def nonce(self) -> bytes:
        return self._nonce

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class OCSPAcceptableResponses(ExtensionType):
    oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES

    def __init__(self, responses: typing.Iterable[ObjectIdentifier]) -> None:
        responses = list(responses)
        if any(not isinstance(r, ObjectIdentifier) for r in responses):
            raise TypeError("All responses must be ObjectIdentifiers")

        self._responses = responses

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, OCSPAcceptableResponses):
            return NotImplemented

        return self._responses == other._responses

    def __hash__(self) -> int:
        return hash(tuple(self._responses))

    def __repr__(self) -> str:
        return f"<OCSPAcceptableResponses(responses={self._responses})>"

    def __iter__(self) -> typing.Iterator[ObjectIdentifier]:
        return iter(self._responses)

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class IssuingDistributionPoint(ExtensionType):
    oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT

    def __init__(
        self,
        full_name: typing.Optional[typing.Iterable[GeneralName]],
        relative_name: typing.Optional[RelativeDistinguishedName],
        only_contains_user_certs: bool,
        only_contains_ca_certs: bool,
        only_some_reasons: typing.Optional[typing.FrozenSet[ReasonFlags]],
        indirect_crl: bool,
        only_contains_attribute_certs: bool,
    ) -> None:
        if full_name is not None:
            full_name = list(full_name)

        if only_some_reasons and (
            not isinstance(only_some_reasons, frozenset)
            or not all(isinstance(x, ReasonFlags) for x in only_some_reasons)
        ):
            raise TypeError(
                "only_some_reasons must be None or frozenset of ReasonFlags"
            )

        if only_some_reasons and (
            ReasonFlags.unspecified in only_some_reasons
            or ReasonFlags.remove_from_crl in only_some_reasons
        ):
            raise ValueError(
                "unspecified and remove_from_crl are not valid reasons in an "
                "IssuingDistributionPoint"
            )

        if not (
            isinstance(only_contains_user_certs, bool)
            and isinstance(only_contains_ca_certs, bool)
            and isinstance(indirect_crl, bool)
            and isinstance(only_contains_attribute_certs, bool)
        ):
            raise TypeError(
                "only_contains_user_certs, only_contains_ca_certs, "
                "indirect_crl and only_contains_attribute_certs "
                "must all be boolean."
            )

        crl_constraints = [
            only_contains_user_certs,
            only_contains_ca_certs,
            indirect_crl,
            only_contains_attribute_certs,
        ]

        if len([x for x in crl_constraints if x]) > 1:
            raise ValueError(
                "Only one of the following can be set to True: "
                "only_contains_user_certs, only_contains_ca_certs, "
                "indirect_crl, only_contains_attribute_certs"
            )

        if not any(
            [
                only_contains_user_certs,
                only_contains_ca_certs,
                indirect_crl,
                only_contains_attribute_certs,
                full_name,
                relative_name,
                only_some_reasons,
            ]
        ):
            raise ValueError(
                "Cannot create empty extension: "
                "if only_contains_user_certs, only_contains_ca_certs, "
                "indirect_crl, and only_contains_attribute_certs are all False"
                ", then either full_name, relative_name, or only_some_reasons "
                "must have a value."
            )

        self._only_contains_user_certs = only_contains_user_certs
        self._only_contains_ca_certs = only_contains_ca_certs
        self._indirect_crl = indirect_crl
        self._only_contains_attribute_certs = only_contains_attribute_certs
        self._only_some_reasons = only_some_reasons
        self._full_name = full_name
        self._relative_name = relative_name

    def __repr__(self) -> str:
        return (
            "<IssuingDistributionPoint(full_name={0.full_name}, "
            "relative_name={0.relative_name}, "
            "only_contains_user_certs={0.only_contains_user_certs}, "
            "only_contains_ca_certs={0.only_contains_ca_certs}, "
            "only_some_reasons={0.only_some_reasons}, "
            "indirect_crl={0.indirect_crl}, "
            "only_contains_attribute_certs="
            "{0.only_contains_attribute_certs})>".format(self)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, IssuingDistributionPoint):
            return NotImplemented

        return (
            self.full_name == other.full_name
            and self.relative_name == other.relative_name
            and self.only_contains_user_certs == other.only_contains_user_certs
            and self.only_contains_ca_certs == other.only_contains_ca_certs
            and self.only_some_reasons == other.only_some_reasons
            and self.indirect_crl == other.indirect_crl
            and self.only_contains_attribute_certs
            == other.only_contains_attribute_certs
        )

    def __hash__(self) -> int:
        return hash(
            (
                self.full_name,
                self.relative_name,
                self.only_contains_user_certs,
                self.only_contains_ca_certs,
                self.only_some_reasons,
                self.indirect_crl,
                self.only_contains_attribute_certs,
            )
        )

    @property
    def full_name(self) -> typing.Optional[typing.List[GeneralName]]:
        return self._full_name

    @property
    def relative_name(self) -> typing.Optional[RelativeDistinguishedName]:
        return self._relative_name

    @property
    def only_contains_user_certs(self) -> bool:
        return self._only_contains_user_certs

    @property
    def only_contains_ca_certs(self) -> bool:
        return self._only_contains_ca_certs

    @property
    def only_some_reasons(
        self,
    ) -> typing.Optional[typing.FrozenSet[ReasonFlags]]:
        return self._only_some_reasons

    @property
    def indirect_crl(self) -> bool:
        return self._indirect_crl

    @property
    def only_contains_attribute_certs(self) -> bool:
        return self._only_contains_attribute_certs

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class MSCertificateTemplate(ExtensionType):
    oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE

    def __init__(
        self,
        template_id: ObjectIdentifier,
        major_version: typing.Optional[int],
        minor_version: typing.Optional[int],
    ) -> None:
        if not isinstance(template_id, ObjectIdentifier):
            raise TypeError("oid must be an ObjectIdentifier")
        self._template_id = template_id
        if (
            major_version is not None and not isinstance(major_version, int)
        ) or (
            minor_version is not None and not isinstance(minor_version, int)
        ):
            raise TypeError(
                "major_version and minor_version must be integers or None"
            )
        self._major_version = major_version
        self._minor_version = minor_version

    @property
    def template_id(self) -> ObjectIdentifier:
        return self._template_id

    @property
    def major_version(self) -> typing.Optional[int]:
        return self._major_version

    @property
    def minor_version(self) -> typing.Optional[int]:
        return self._minor_version

    def __repr__(self) -> str:
        return (
            f"<MSCertificateTemplate(template_id={self.template_id}, "
            f"major_version={self.major_version}, "
            f"minor_version={self.minor_version})>"
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, MSCertificateTemplate):
            return NotImplemented

        return (
            self.template_id == other.template_id
            and self.major_version == other.major_version
            and self.minor_version == other.minor_version
        )

    def __hash__(self) -> int:
        return hash((self.template_id, self.major_version, self.minor_version))

    def public_bytes(self) -> bytes:
        return rust_x509.encode_extension_value(self)


class UnrecognizedExtension(ExtensionType):
    def __init__(self, oid: ObjectIdentifier, value: bytes) -> None:
        if not isinstance(oid, ObjectIdentifier):
            raise TypeError("oid must be an ObjectIdentifier")
        self._oid = oid
        self._value = value

    @property
    def oid(self) -> ObjectIdentifier:  # type: ignore[override]
        return self._oid

    @property
    def value(self) -> bytes:
        return self._value

    def __repr__(self) -> str:
        return (
            "<UnrecognizedExtension(oid={0.oid}, "
            "value={0.value!r})>".format(self)
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, UnrecognizedExtension):
            return NotImplemented

        return self.oid == other.oid and self.value == other.value

    def __hash__(self) -> int:
        return hash((self.oid, self.value))

    def public_bytes(self) -> bytes:
        return self.value

back/venvmsg/Lib/site-packages/cryptography/x509/extensions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import ipaddress
import typing
from email.utils import parseaddr

from cryptography.x509.name import Name
from cryptography.x509.oid import ObjectIdentifier

_IPAddressTypes = typing.Union[
    ipaddress.IPv4Address,
    ipaddress.IPv6Address,
    ipaddress.IPv4Network,
    ipaddress.IPv6Network,
]


class UnsupportedGeneralNameType(Exception):
    pass


class GeneralName(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def value(self) -> typing.Any:
        """
        Return the value of the object
        """


class RFC822Name(GeneralName):
    def __init__(self, value: str) -> None:
        if isinstance(value, str):
            try:
                value.encode("ascii")
            except UnicodeEncodeError:
                raise ValueError(
                    "RFC822Name values should be passed as an A-label string. "
                    "This means unicode characters should be encoded via "
                    "a library like idna."
                )
        else:
            raise TypeError("value must be string")

        name, address = parseaddr(value)
        if name or not address:
            # parseaddr has found a name (e.g. Name <email>) or the entire
            # value is an empty string.
            raise ValueError("Invalid rfc822name value")

        self._value = value

    @property
    def value(self) -> str:
        return self._value

    @classmethod
    def _init_without_validation(cls, value: str) -> RFC822Name:
        instance = cls.__new__(cls)
        instance._value = value
        return instance

    def __repr__(self) -> str:
        return f"<RFC822Name(value={self.value!r})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, RFC822Name):
            return NotImplemented

        return self.value == other.value

    def __hash__(self) -> int:
        return hash(self.value)


class DNSName(GeneralName):
    def __init__(self, value: str) -> None:
        if isinstance(value, str):
            try:
                value.encode("ascii")
            except UnicodeEncodeError:
                raise ValueError(
                    "DNSName values should be passed as an A-label string. "
                    "This means unicode characters should be encoded via "
                    "a library like idna."
                )
        else:
            raise TypeError("value must be string")

        self._value = value

    @property
    def value(self) -> str:
        return self._value

    @classmethod
    def _init_without_validation(cls, value: str) -> DNSName:
        instance = cls.__new__(cls)
        instance._value = value
        return instance

    def __repr__(self) -> str:
        return f"<DNSName(value={self.value!r})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DNSName):
            return NotImplemented

        return self.value == other.value

    def __hash__(self) -> int:
        return hash(self.value)


class UniformResourceIdentifier(GeneralName):
    def __init__(self, value: str) -> None:
        if isinstance(value, str):
            try:
                value.encode("ascii")
            except UnicodeEncodeError:
                raise ValueError(
                    "URI values should be passed as an A-label string. "
                    "This means unicode characters should be encoded via "
                    "a library like idna."
                )
        else:
            raise TypeError("value must be string")

        self._value = value

    @property
    def value(self) -> str:
        return self._value

    @classmethod
    def _init_without_validation(cls, value: str) -> UniformResourceIdentifier:
        instance = cls.__new__(cls)
        instance._value = value
        return instance

    def __repr__(self) -> str:
        return f"<UniformResourceIdentifier(value={self.value!r})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, UniformResourceIdentifier):
            return NotImplemented

        return self.value == other.value

    def __hash__(self) -> int:
        return hash(self.value)


class DirectoryName(GeneralName):
    def __init__(self, value: Name) -> None:
        if not isinstance(value, Name):
            raise TypeError("value must be a Name")

        self._value = value

    @property
    def value(self) -> Name:
        return self._value

    def __repr__(self) -> str:
        return f"<DirectoryName(value={self.value})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, DirectoryName):
            return NotImplemented

        return self.value == other.value

    def __hash__(self) -> int:
        return hash(self.value)


class RegisteredID(GeneralName):
    def __init__(self, value: ObjectIdentifier) -> None:
        if not isinstance(value, ObjectIdentifier):
            raise TypeError("value must be an ObjectIdentifier")

        self._value = value

    @property
    def value(self) -> ObjectIdentifier:
        return self._value

    def __repr__(self) -> str:
        return f"<RegisteredID(value={self.value})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, RegisteredID):
            return NotImplemented

        return self.value == other.value

    def __hash__(self) -> int:
        return hash(self.value)


class IPAddress(GeneralName):
    def __init__(self, value: _IPAddressTypes) -> None:
        if not isinstance(
            value,
            (
                ipaddress.IPv4Address,
                ipaddress.IPv6Address,
                ipaddress.IPv4Network,
                ipaddress.IPv6Network,
            ),
        ):
            raise TypeError(
                "value must be an instance of ipaddress.IPv4Address, "
                "ipaddress.IPv6Address, ipaddress.IPv4Network, or "
                "ipaddress.IPv6Network"
            )

        self._value = value

    @property
    def value(self) -> _IPAddressTypes:
        return self._value

    def _packed(self) -> bytes:
        if isinstance(
            self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address)
        ):
            return self.value.packed
        else:
            return (
                self.value.network_address.packed + self.value.netmask.packed
            )

    def __repr__(self) -> str:
        return f"<IPAddress(value={self.value})>"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, IPAddress):
            return NotImplemented

        return self.value == other.value

    def __hash__(self) -> int:
        return hash(self.value)


class OtherName(GeneralName):
    def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None:
        if not isinstance(type_id, ObjectIdentifier):
            raise TypeError("type_id must be an ObjectIdentifier")
        if not isinstance(value, bytes):
            raise TypeError("value must be a binary string")

        self._type_id = type_id
        self._value = value

    @property
    def type_id(self) -> ObjectIdentifier:
        return self._type_id

    @property
    def value(self) -> bytes:
        return self._value

    def __repr__(self) -> str:
        return "<OtherName(type_id={}, value={!r})>".format(
            self.type_id, self.value
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, OtherName):
            return NotImplemented

        return self.type_id == other.type_id and self.value == other.value

    def __hash__(self) -> int:
        return hash((self.type_id, self.value))

back/venvmsg/Lib/site-packages/cryptography/x509/general_name.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import binascii
import re
import sys
import typing
import warnings

from cryptography import utils
from cryptography.hazmat.bindings._rust import x509 as rust_x509
from cryptography.x509.oid import NameOID, ObjectIdentifier


class _ASN1Type(utils.Enum):
    BitString = 3
    OctetString = 4
    UTF8String = 12
    NumericString = 18
    PrintableString = 19
    T61String = 20
    IA5String = 22
    UTCTime = 23
    GeneralizedTime = 24
    VisibleString = 26
    UniversalString = 28
    BMPString = 30


_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type}
_NAMEOID_DEFAULT_TYPE: typing.Dict[ObjectIdentifier, _ASN1Type] = {
    NameOID.COUNTRY_NAME: _ASN1Type.PrintableString,
    NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString,
    NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString,
    NameOID.DN_QUALIFIER: _ASN1Type.PrintableString,
    NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String,
    NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String,
}

# Type alias
_OidNameMap = typing.Mapping[ObjectIdentifier, str]
_NameOidMap = typing.Mapping[str, ObjectIdentifier]

#: Short attribute names from RFC 4514:
#: https://tools.ietf.org/html/rfc4514#page-7
_NAMEOID_TO_NAME: _OidNameMap = {
    NameOID.COMMON_NAME: "CN",
    NameOID.LOCALITY_NAME: "L",
    NameOID.STATE_OR_PROVINCE_NAME: "ST",
    NameOID.ORGANIZATION_NAME: "O",
    NameOID.ORGANIZATIONAL_UNIT_NAME: "OU",
    NameOID.COUNTRY_NAME: "C",
    NameOID.STREET_ADDRESS: "STREET",
    NameOID.DOMAIN_COMPONENT: "DC",
    NameOID.USER_ID: "UID",
}
_NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()}


def _escape_dn_value(val: typing.Union[str, bytes]) -> str:
    """Escape special characters in RFC4514 Distinguished Name value."""

    if not val:
        return ""

    # RFC 4514 Section 2.4 defines the value as being the # (U+0023) character
    # followed by the hexadecimal encoding of the octets.
    if isinstance(val, bytes):
        return "#" + binascii.hexlify(val).decode("utf8")

    # See https://tools.ietf.org/html/rfc4514#section-2.4
    val = val.replace("\\", "\\\\")
    val = val.replace('"', '\\"')
    val = val.replace("+", "\\+")
    val = val.replace(",", "\\,")
    val = val.replace(";", "\\;")
    val = val.replace("<", "\\<")
    val = val.replace(">", "\\>")
    val = val.replace("\0", "\\00")

    if val[0] in ("#", " "):
        val = "\\" + val
    if val[-1] == " ":
        val = val[:-1] + "\\ "

    return val


def _unescape_dn_value(val: str) -> str:
    if not val:
        return ""

    # See https://tools.ietf.org/html/rfc4514#section-3

    # special = escaped / SPACE / SHARP / EQUALS
    # escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE
    def sub(m):
        val = m.group(1)
        # Regular escape
        if len(val) == 1:
            return val
        # Hex-value scape
        return chr(int(val, 16))

    return _RFC4514NameParser._PAIR_RE.sub(sub, val)


class NameAttribute:
    def __init__(
        self,
        oid: ObjectIdentifier,
        value: typing.Union[str, bytes],
        _type: typing.Optional[_ASN1Type] = None,
        *,
        _validate: bool = True,
    ) -> None:
        if not isinstance(oid, ObjectIdentifier):
            raise TypeError(
                "oid argument must be an ObjectIdentifier instance."
            )
        if _type == _ASN1Type.BitString:
            if oid != NameOID.X500_UNIQUE_IDENTIFIER:
                raise TypeError(
                    "oid must be X500_UNIQUE_IDENTIFIER for BitString type."
                )
            if not isinstance(value, bytes):
                raise TypeError("value must be bytes for BitString")
        else:
            if not isinstance(value, str):
                raise TypeError("value argument must be a str")

        if (
            oid == NameOID.COUNTRY_NAME
            or oid == NameOID.JURISDICTION_COUNTRY_NAME
        ):
            assert isinstance(value, str)
            c_len = len(value.encode("utf8"))
            if c_len != 2 and _validate is True:
                raise ValueError(
                    "Country name must be a 2 character country code"
                )
            elif c_len != 2:
                warnings.warn(
                    "Country names should be two characters, but the "
                    "attribute is {} characters in length.".format(c_len),
                    stacklevel=2,
                )

        # The appropriate ASN1 string type varies by OID and is defined across
        # multiple RFCs including 2459, 3280, and 5280. In general UTF8String
        # is preferred (2459), but 3280 and 5280 specify several OIDs with
        # alternate types. This means when we see the sentinel value we need
        # to look up whether the OID has a non-UTF8 type. If it does, set it
        # to that. Otherwise, UTF8!
        if _type is None:
            _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String)

        if not isinstance(_type, _ASN1Type):
            raise TypeError("_type must be from the _ASN1Type enum")

        self._oid = oid
        self._value = value
        self._type = _type

    @property
    def oid(self) -> ObjectIdentifier:
        return self._oid

    @property
    def value(self) -> typing.Union[str, bytes]:
        return self._value

    @property
    def rfc4514_attribute_name(self) -> str:
        """
        The short attribute name (for example "CN") if available,
        otherwise the OID dotted string.
        """
        return _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string)

    def rfc4514_string(
        self, attr_name_overrides: typing.Optional[_OidNameMap] = None
    ) -> str:
        """
        Format as RFC4514 Distinguished Name string.

        Use short attribute name if available, otherwise fall back to OID
        dotted string.
        """
        attr_name = (
            attr_name_overrides.get(self.oid) if attr_name_overrides else None
        )
        if attr_name is None:
            attr_name = self.rfc4514_attribute_name

        return f"{attr_name}={_escape_dn_value(self.value)}"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, NameAttribute):
            return NotImplemented

        return self.oid == other.oid and self.value == other.value

    def __hash__(self) -> int:
        return hash((self.oid, self.value))

    def __repr__(self) -> str:
        return "<NameAttribute(oid={0.oid}, value={0.value!r})>".format(self)


class RelativeDistinguishedName:
    def __init__(self, attributes: typing.Iterable[NameAttribute]):
        attributes = list(attributes)
        if not attributes:
            raise ValueError("a relative distinguished name cannot be empty")
        if not all(isinstance(x, NameAttribute) for x in attributes):
            raise TypeError("attributes must be an iterable of NameAttribute")

        # Keep list and frozenset to preserve attribute order where it matters
        self._attributes = attributes
        self._attribute_set = frozenset(attributes)

        if len(self._attribute_set) != len(attributes):
            raise ValueError("duplicate attributes are not allowed")

    def get_attributes_for_oid(
        self, oid: ObjectIdentifier
    ) -> typing.List[NameAttribute]:
        return [i for i in self if i.oid == oid]

    def rfc4514_string(
        self, attr_name_overrides: typing.Optional[_OidNameMap] = None
    ) -> str:
        """
        Format as RFC4514 Distinguished Name string.

        Within each RDN, attributes are joined by '+', although that is rarely
        used in certificates.
        """
        return "+".join(
            attr.rfc4514_string(attr_name_overrides)
            for attr in self._attributes
        )

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, RelativeDistinguishedName):
            return NotImplemented

        return self._attribute_set == other._attribute_set

    def __hash__(self) -> int:
        return hash(self._attribute_set)

    def __iter__(self) -> typing.Iterator[NameAttribute]:
        return iter(self._attributes)

    def __len__(self) -> int:
        return len(self._attributes)

    def __repr__(self) -> str:
        return f"<RelativeDistinguishedName({self.rfc4514_string()})>"


class Name:
    @typing.overload
    def __init__(self, attributes: typing.Iterable[NameAttribute]) -> None:
        ...

    @typing.overload
    def __init__(
        self, attributes: typing.Iterable[RelativeDistinguishedName]
    ) -> None:
        ...

    def __init__(
        self,
        attributes: typing.Iterable[
            typing.Union[NameAttribute, RelativeDistinguishedName]
        ],
    ) -> None:
        attributes = list(attributes)
        if all(isinstance(x, NameAttribute) for x in attributes):
            self._attributes = [
                RelativeDistinguishedName([typing.cast(NameAttribute, x)])
                for x in attributes
            ]
        elif all(isinstance(x, RelativeDistinguishedName) for x in attributes):
            self._attributes = typing.cast(
                typing.List[RelativeDistinguishedName], attributes
            )
        else:
            raise TypeError(
                "attributes must be a list of NameAttribute"
                " or a list RelativeDistinguishedName"
            )

    @classmethod
    def from_rfc4514_string(
        cls,
        data: str,
        attr_name_overrides: typing.Optional[_NameOidMap] = None,
    ) -> Name:
        return _RFC4514NameParser(data, attr_name_overrides or {}).parse()

    def rfc4514_string(
        self, attr_name_overrides: typing.Optional[_OidNameMap] = None
    ) -> str:
        """
        Format as RFC4514 Distinguished Name string.
        For example 'CN=foobar.com,O=Foo Corp,C=US'

        An X.509 name is a two-level structure: a list of sets of attributes.
        Each list element is separated by ',' and within each list element, set
        elements are separated by '+'. The latter is almost never used in
        real world certificates. According to RFC4514 section 2.1 the
        RDNSequence must be reversed when converting to string representation.
        """
        return ",".join(
            attr.rfc4514_string(attr_name_overrides)
            for attr in reversed(self._attributes)
        )

    def get_attributes_for_oid(
        self, oid: ObjectIdentifier
    ) -> typing.List[NameAttribute]:
        return [i for i in self if i.oid == oid]

    @property
    def rdns(self) -> typing.List[RelativeDistinguishedName]:
        return self._attributes

    def public_bytes(self, backend: typing.Any = None) -> bytes:
        return rust_x509.encode_name_bytes(self)

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Name):
            return NotImplemented

        return self._attributes == other._attributes

    def __hash__(self) -> int:
        # TODO: this is relatively expensive, if this looks like a bottleneck
        # for you, consider optimizing!
        return hash(tuple(self._attributes))

    def __iter__(self) -> typing.Iterator[NameAttribute]:
        for rdn in self._attributes:
            for ava in rdn:
                yield ava

    def __len__(self) -> int:
        return sum(len(rdn) for rdn in self._attributes)

    def __repr__(self) -> str:
        rdns = ",".join(attr.rfc4514_string() for attr in self._attributes)
        return f"<Name({rdns})>"


class _RFC4514NameParser:
    _OID_RE = re.compile(r"(0|([1-9]\d*))(\.(0|([1-9]\d*)))+")
    _DESCR_RE = re.compile(r"[a-zA-Z][a-zA-Z\d-]*")

    _PAIR = r"\\([\\ #=\"\+,;<>]|[\da-zA-Z]{2})"
    _PAIR_RE = re.compile(_PAIR)
    _LUTF1 = r"[\x01-\x1f\x21\x24-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]"
    _SUTF1 = r"[\x01-\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]"
    _TUTF1 = r"[\x01-\x1F\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]"
    _UTFMB = rf"[\x80-{chr(sys.maxunicode)}]"
    _LEADCHAR = rf"{_LUTF1}|{_UTFMB}"
    _STRINGCHAR = rf"{_SUTF1}|{_UTFMB}"
    _TRAILCHAR = rf"{_TUTF1}|{_UTFMB}"
    _STRING_RE = re.compile(
        rf"""
        (
            ({_LEADCHAR}|{_PAIR})
            (
                ({_STRINGCHAR}|{_PAIR})*
                ({_TRAILCHAR}|{_PAIR})
            )?
        )?
        """,
        re.VERBOSE,
    )
    _HEXSTRING_RE = re.compile(r"#([\da-zA-Z]{2})+")

    def __init__(self, data: str, attr_name_overrides: _NameOidMap) -> None:
        self._data = data
        self._idx = 0

        self._attr_name_overrides = attr_name_overrides

    def _has_data(self) -> bool:
        return self._idx < len(self._data)

    def _peek(self) -> typing.Optional[str]:
        if self._has_data():
            return self._data[self._idx]
        return None

    def _read_char(self, ch: str) -> None:
        if self._peek() != ch:
            raise ValueError
        self._idx += 1

    def _read_re(self, pat) -> str:
        match = pat.match(self._data, pos=self._idx)
        if match is None:
            raise ValueError
        val = match.group()
        self._idx += len(val)
        return val

    def parse(self) -> Name:
        """
        Parses the `data` string and converts it to a Name.

        According to RFC4514 section 2.1 the RDNSequence must be
        reversed when converting to string representation. So, when
        we parse it, we need to reverse again to get the RDNs on the
        correct order.
        """
        rdns = [self._parse_rdn()]

        while self._has_data():
            self._read_char(",")
            rdns.append(self._parse_rdn())

        return Name(reversed(rdns))

    def _parse_rdn(self) -> RelativeDistinguishedName:
        nas = [self._parse_na()]
        while self._peek() == "+":
            self._read_char("+")
            nas.append(self._parse_na())

        return RelativeDistinguishedName(nas)

    def _parse_na(self) -> NameAttribute:
        try:
            oid_value = self._read_re(self._OID_RE)
        except ValueError:
            name = self._read_re(self._DESCR_RE)
            oid = self._attr_name_overrides.get(
                name, _NAME_TO_NAMEOID.get(name)
            )
            if oid is None:
                raise ValueError
        else:
            oid = ObjectIdentifier(oid_value)

        self._read_char("=")
        if self._peek() == "#":
            value = self._read_re(self._HEXSTRING_RE)
            value = binascii.unhexlify(value[1:]).decode()
        else:
            raw_value = self._read_re(self._STRING_RE)
            value = _unescape_dn_value(raw_value)

        return NameAttribute(oid, value)

back/venvmsg/Lib/site-packages/cryptography/x509/name.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

from __future__ import annotations

import abc
import datetime
import typing

from cryptography import utils, x509
from cryptography.hazmat.bindings._rust import ocsp
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric.types import (
    CertificateIssuerPrivateKeyTypes,
)
from cryptography.x509.base import (
    _EARLIEST_UTC_TIME,
    _convert_to_naive_utc_time,
    _reject_duplicate_extension,
)


class OCSPResponderEncoding(utils.Enum):
    HASH = "By Hash"
    NAME = "By Name"


class OCSPResponseStatus(utils.Enum):
    SUCCESSFUL = 0
    MALFORMED_REQUEST = 1
    INTERNAL_ERROR = 2
    TRY_LATER = 3
    SIG_REQUIRED = 5
    UNAUTHORIZED = 6


_ALLOWED_HASHES = (
    hashes.SHA1,
    hashes.SHA224,
    hashes.SHA256,
    hashes.SHA384,
    hashes.SHA512,
)


def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None:
    if not isinstance(algorithm, _ALLOWED_HASHES):
        raise ValueError(
            "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512"
        )


class OCSPCertStatus(utils.Enum):
    GOOD = 0
    REVOKED = 1
    UNKNOWN = 2


class _SingleResponse:
    def __init__(
        self,
        cert: x509.Certificate,
        issuer: x509.Certificate,
        algorithm: hashes.HashAlgorithm,
        cert_status: OCSPCertStatus,
        this_update: datetime.datetime,
        next_update: typing.Optional[datetime.datetime],
        revocation_time: typing.Optional[datetime.datetime],
        revocation_reason: typing.Optional[x509.ReasonFlags],
    ):
        if not isinstance(cert, x509.Certificate) or not isinstance(
            issuer, x509.Certificate
        ):
            raise TypeError("cert and issuer must be a Certificate")

        _verify_algorithm(algorithm)
        if not isinstance(this_update, datetime.datetime):
            raise TypeError("this_update must be a datetime object")
        if next_update is not None and not isinstance(
            next_update, datetime.datetime
        ):
            raise TypeError("next_update must be a datetime object or None")

        self._cert = cert
        self._issuer = issuer
        self._algorithm = algorithm
        self._this_update = this_update
        self._next_update = next_update

        if not isinstance(cert_status, OCSPCertStatus):
            raise TypeError(
                "cert_status must be an item from the OCSPCertStatus enum"
            )
        if cert_status is not OCSPCertStatus.REVOKED:
            if revocation_time is not None:
                raise ValueError(
                    "revocation_time can only be provided if the certificate "
                    "is revoked"
                )
            if revocation_reason is not None:
                raise ValueError(
                    "revocation_reason can only be provided if the certificate"
                    " is revoked"
                )
        else:
            if not isinstance(revocation_time, datetime.datetime):
                raise TypeError("revocation_time must be a datetime object")

            revocation_time = _convert_to_naive_utc_time(revocation_time)
            if revocation_time < _EARLIEST_UTC_TIME:
                raise ValueError(
                    "The revocation_time must be on or after"
                    " 1950 January 1."
                )

            if revocation_reason is not None and not isinstance(
                revocation_reason, x509.ReasonFlags
            ):
                raise TypeError(
                    "revocation_reason must be an item from the ReasonFlags "
                    "enum or None"
                )

        self._cert_status = cert_status
        self._revocation_time = revocation_time
        self._revocation_reason = revocation_reason


class OCSPRequest(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def issuer_key_hash(self) -> bytes:
        """
        The hash of the issuer public key
        """

    @property
    @abc.abstractmethod
    def issuer_name_hash(self) -> bytes:
        """
        The hash of the issuer name
        """

    @property
    @abc.abstractmethod
    def hash_algorithm(self) -> hashes.HashAlgorithm:
        """
        The hash algorithm used in the issuer name and key hashes
        """

    @property
    @abc.abstractmethod
    def serial_number(self) -> int:
        """
        The serial number of the cert whose status is being checked
        """

    @abc.abstractmethod
    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
        """
        Serializes the request to DER
        """

    @property
    @abc.abstractmethod
    def extensions(self) -> x509.Extensions:
        """
        The list of request extensions. Not single request extensions.
        """


class OCSPSingleResponse(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def certificate_status(self) -> OCSPCertStatus:
        """
        The status of the certificate (an element from the OCSPCertStatus enum)
        """

    @property
    @abc.abstractmethod
    def revocation_time(self) -> typing.Optional[datetime.datetime]:
        """
        The date of when the certificate was revoked or None if not
        revoked.
        """

    @property
    @abc.abstractmethod
    def revocation_reason(self) -> typing.Optional[x509.ReasonFlags]:
        """
        The reason the certificate was revoked or None if not specified or
        not revoked.
        """

    @property
    @abc.abstractmethod
    def this_update(self) -> datetime.datetime:
        """
        The most recent time at which the status being indicated is known by
        the responder to have been correct
        """

    @property
    @abc.abstractmethod
    def next_update(self) -> typing.Optional[datetime.datetime]:
        """
        The time when newer information will be available
        """

    @property
    @abc.abstractmethod
    def issuer_key_hash(self) -> bytes:
        """
        The hash of the issuer public key
        """

    @property
    @abc.abstractmethod
    def issuer_name_hash(self) -> bytes:
        """
        The hash of the issuer name
        """

    @property
    @abc.abstractmethod
    def hash_algorithm(self) -> hashes.HashAlgorithm:
        """
        The hash algorithm used in the issuer name and key hashes
        """

    @property
    @abc.abstractmethod
    def serial_number(self) -> int:
        """
        The serial number of the cert whose status is being checked
        """


class OCSPResponse(metaclass=abc.ABCMeta):
    @property
    @abc.abstractmethod
    def responses(self) -> typing.Iterator[OCSPSingleResponse]:
        """
        An iterator over the individual SINGLERESP structures in the
        response
        """

    @property
    @abc.abstractmethod
    def response_status(self) -> OCSPResponseStatus:
        """
        The status of the response. This is a value from the OCSPResponseStatus
        enumeration
        """

    @property
    @abc.abstractmethod
    def signature_algorithm_oid(self) -> x509.ObjectIdentifier:
        """
        The ObjectIdentifier of the signature algorithm
        """

    @property
    @abc.abstractmethod
    def signature_hash_algorithm(
        self,
    ) -> typing.Optional[hashes.HashAlgorithm]:
        """
        Returns a HashAlgorithm corresponding to the type of the digest signed
        """

    @property
    @abc.abstractmethod
    def signature(self) -> bytes:
        """
        The signature bytes
        """

    @property
    @abc.abstractmethod
    def tbs_response_bytes(self) -> bytes:
        """
        The tbsResponseData bytes
        """

    @property
    @abc.abstractmethod
    def certificates(self) -> typing.List[x509.Certificate]:
        """
        A list of certificates used to help build a chain to verify the OCSP
        response. This situation occurs when the OCSP responder uses a delegate
        certificate.
        """

    @property
    @abc.abstractmethod
    def responder_key_hash(self) -> typing.Optional[bytes]:
        """
        The responder's key hash or None
        """

    @property
    @abc.abstractmethod
    def responder_name(self) -> typing.Optional[x509.Name]:
        """
        The responder's Name or None
        """

    @property
    @abc.abstractmethod
    def produced_at(self) -> datetime.datetime:
        """
        The time the response was produced
        """

    @property
    @abc.abstractmethod
    def certificate_status(self) -> OCSPCertStatus:
        """
        The status of the certificate (an element from the OCSPCertStatus enum)
        """

    @property
    @abc.abstractmethod
    def revocation_time(self) -> typing.Optional[datetime.datetime]:
        """
        The date of when the certificate was revoked or None if not
        revoked.
        """

    @property
    @abc.abstractmethod
    def revocation_reason(self) -> typing.Optional[x509.ReasonFlags]:
        """
        The reason the certificate was revoked or None if not specified or
        not revoked.
        """

    @property
    @abc.abstractmethod
    def this_update(self) -> datetime.datetime:
        """
        The most recent time at which the status being indicated is known by
        the responder to have been correct
        """

    @property
    @abc.abstractmethod
    def next_update(self) -> typing.Optional[datetime.datetime]:
        """
        The time when newer information will be available
        """

    @property
    @abc.abstractmethod
    def issuer_key_hash(self) -> bytes:
        """
        The hash of the issuer public key
        """

    @property
    @abc.abstractmethod
    def issuer_name_hash(self) -> bytes:
        """
        The hash of the issuer name
        """

    @property
    @abc.abstractmethod
    def hash_algorithm(self) -> hashes.HashAlgorithm:
        """
        The hash algorithm used in the issuer name and key hashes
        """

    @property
    @abc.abstractmethod
    def serial_number(self) -> int:
        """
        The serial number of the cert whose status is being checked
        """

    @property
    @abc.abstractmethod
    def extensions(self) -> x509.Extensions:
        """
        The list of response extensions. Not single response extensions.
        """

    @property
    @abc.abstractmethod
    def single_extensions(self) -> x509.Extensions:
        """
        The list of single response extensions. Not response extensions.
        """

    @abc.abstractmethod
    def public_bytes(self, encoding: serialization.Encoding) -> bytes:
        """
        Serializes the response to DER
        """


class OCSPRequestBuilder:
    def __init__(
        self,
        request: typing.Optional[
            typing.Tuple[
                x509.Certificate, x509.Certificate, hashes.HashAlgorithm
            ]
        ] = None,
        request_hash: typing.Optional[
            typing.Tuple[bytes, bytes, int, hashes.HashAlgorithm]
        ] = None,
        extensions: typing.List[x509.Extension[x509.ExtensionType]] = [],
    ) -> None:
        self._request = request
        self._request_hash = request_hash
        self._extensions = extensions

    def add_certificate(
        self,
        cert: x509.Certificate,
        issuer: x509.Certificate,
        algorithm: hashes.HashAlgorithm,
    ) -> OCSPRequestBuilder:
        if self._request is not None or self._request_hash is not None:
            raise ValueError("Only one certificate can be added to a request")

        _verify_algorithm(algorithm)
        if not isinstance(cert, x509.Certificate) or not isinstance(
            issuer, x509.Certificate
        ):
            raise TypeError("cert and issuer must be a Certificate")

        return OCSPRequestBuilder(
            (cert, issuer, algorithm), self._request_hash, self._extensions
        )

    def add_certificate_by_hash(
        self,
        issuer_name_hash: bytes,
        issuer_key_hash: bytes,
        serial_number: int,
        algorithm: hashes.HashAlgorithm,
    ) -> OCSPRequestBuilder:
        if self._request is not None or self._request_hash is not None:
            raise ValueError("Only one certificate can be added to a request")

        if not isinstance(serial_number, int):
            raise TypeError("serial_number must be an integer")

        _verify_algorithm(algorithm)
        utils._check_bytes("issuer_name_hash", issuer_name_hash)
        utils._check_bytes("issuer_key_hash", issuer_key_hash)
        if algorithm.digest_size != len(
            issuer_name_hash
        ) or algorithm.digest_size != len(issuer_key_hash):
            raise ValueError(
                "issuer_name_hash and issuer_key_hash must be the same length "
                "as the digest size of the algorithm"
            )

        return OCSPRequestBuilder(
            self._request,
            (issuer_name_hash, issuer_key_hash, serial_number, algorithm),
            self._extensions,
        )

    def add_extension(
        self, extval: x509.ExtensionType, critical: bool
    ) -> OCSPRequestBuilder:
        if not isinstance(extval, x509.ExtensionType):
            raise TypeError("extension must be an ExtensionType")

        extension = x509.Extension(extval.oid, critical, extval)
        _reject_duplicate_extension(extension, self._extensions)

        return OCSPRequestBuilder(
            self._request, self._request_hash, self._extensions + [extension]
        )

    def build(self) -> OCSPRequest:
        if self._request is None and self._request_hash is None:
            raise ValueError("You must add a certificate before building")

        return ocsp.create_ocsp_request(self)


class OCSPResponseBuilder:
    def __init__(
        self,
        response: typing.Optional[_SingleResponse] = None,
        responder_id: typing.Optional[
            typing.Tuple[x509.Certificate, OCSPResponderEncoding]
        ] = None,
        certs: typing.Optional[typing.List[x509.Certificate]] = None,
        extensions: typing.List[x509.Extension[x509.ExtensionType]] = [],
    ):
        self._response = response
        self._responder_id = responder_id
        self._certs = certs
        self._extensions = extensions

    def add_response(
        self,
        cert: x509.Certificate,
        issuer: x509.Certificate,
        algorithm: hashes.HashAlgorithm,
        cert_status: OCSPCertStatus,
        this_update: datetime.datetime,
        next_update: typing.Optional[datetime.datetime],
        revocation_time: typing.Optional[datetime.datetime],
        revocation_reason: typing.Optional[x509.ReasonFlags],
    ) -> OCSPResponseBuilder:
        if self._response is not None:
            raise ValueError("Only one response per OCSPResponse.")

        singleresp = _SingleResponse(
            cert,
            issuer,
            algorithm,
            cert_status,
            this_update,
            next_update,
            revocation_time,
            revocation_reason,
        )
        return OCSPResponseBuilder(
            singleresp,
            self._responder_id,
            self._certs,
            self._extensions,
        )

    def responder_id(
        self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate
    ) -> OCSPResponseBuilder:
        if self._responder_id is not None:
            raise ValueError("responder_id can only be set once")
        if not isinstance(responder_cert, x509.Certificate):
            raise TypeError("responder_cert must be a Certificate")
        if not isinstance(encoding, OCSPResponderEncoding):
            raise TypeError(
                "encoding must be an element from OCSPResponderEncoding"
            )

        return OCSPResponseBuilder(
            self._response,
            (responder_cert, encoding),
            self._certs,
            self._extensions,
        )

    def certificates(
        self, certs: typing.Iterable[x509.Certificate]
    ) -> OCSPResponseBuilder:
        if self._certs is not None:
            raise ValueError("certificates may only be set once")
        certs = list(certs)
        if len(certs) == 0:
            raise ValueError("certs must not be an empty list")
        if not all(isinstance(x, x509.Certificate) for x in certs):
            raise TypeError("certs must be a list of Certificates")
        return OCSPResponseBuilder(
            self._response,
            self._responder_id,
            certs,
            self._extensions,
        )

    def add_extension(
        self, extval: x509.ExtensionType, critical: bool
    ) -> OCSPResponseBuilder:
        if not isinstance(extval, x509.ExtensionType):
            raise TypeError("extension must be an ExtensionType")

        extension = x509.Extension(extval.oid, critical, extval)
        _reject_duplicate_extension(extension, self._extensions)

        return OCSPResponseBuilder(
            self._response,
            self._responder_id,
            self._certs,
            self._extensions + [extension],
        )

    def sign(
        self,
        private_key: CertificateIssuerPrivateKeyTypes,
        algorithm: typing.Optional[hashes.HashAlgorithm],
    ) -> OCSPResponse:
        if self._response is None:
            raise ValueError("You must add a response before signing")
        if self._responder_id is None:
            raise ValueError("You must add a responder_id before signing")

        return ocsp.create_ocsp_response(
            OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm
        )

    @classmethod
    def build_unsuccessful(
        cls, response_status: OCSPResponseStatus
    ) -> OCSPResponse:
        if not isinstance(response_status, OCSPResponseStatus):
            raise TypeError(
                "response_status must be an item from OCSPResponseStatus"
            )
        if response_status is OCSPResponseStatus.SUCCESSFUL:
            raise ValueError("response_status cannot be SUCCESSFUL")

        return ocsp.create_ocsp_response(response_status, None, None, None)


def load_der_ocsp_request(data: bytes) -> OCSPRequest:
    return ocsp.load_der_ocsp_request(data)


def load_der_ocsp_response(data: bytes) -> OCSPResponse:
    return ocsp.load_der_ocsp_response(data)

back/venvmsg/Lib/site-packages/cryptography/x509/ocsp.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
#!/usr/bin/env python
#
# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
# Copyright (c) 2023 The Uncertainty Quantification Foundation.
# License: 3-clause BSD.  The full license text is available at:
#  - https://github.com/uqfoundation/dill/blob/master/LICENSE
'''
-----------------------------
dill: serialize all of Python
-----------------------------

About Dill
==========

``dill`` extends Python's ``pickle`` module for serializing and de-serializing
Python objects to the majority of the built-in Python types. Serialization
is the process of converting an object to a byte stream, and the inverse
of which is converting a byte stream back to a Python object hierarchy.

``dill`` provides the user the same interface as the ``pickle`` module, and
also includes some additional features. In addition to pickling Python
objects, ``dill`` provides the ability to save the state of an interpreter
session in a single command.  Hence, it would be feasible to save an
interpreter session, close the interpreter, ship the pickled file to
another computer, open a new interpreter, unpickle the session and
thus continue from the 'saved' state of the original interpreter
session.

``dill`` can be used to store Python objects to a file, but the primary
usage is to send Python objects across the network as a byte stream.
``dill`` is quite flexible, and allows arbitrary user defined classes
and functions to be serialized.  Thus ``dill`` is not intended to be
secure against erroneously or maliciously constructed data. It is
left to the user to decide whether the data they unpickle is from
a trustworthy source.

``dill`` is part of ``pathos``, a Python framework for heterogeneous computing.
``dill`` is in active development, so any user feedback, bug reports, comments,
or suggestions are highly appreciated.  A list of issues is located at
https://github.com/uqfoundation/dill/issues, with a legacy list maintained at
https://uqfoundation.github.io/project/pathos/query.


Major Features
==============

``dill`` can pickle the following standard types:

    - none, type, bool, int, float, complex, bytes, str,
    - tuple, list, dict, file, buffer, builtin,
    - Python classes, namedtuples, dataclasses, metaclasses,
    - instances of classes,
    - set, frozenset, array, functions, exceptions

``dill`` can also pickle more 'exotic' standard types:

    - functions with yields, nested functions, lambdas,
    - cell, method, unboundmethod, module, code, methodwrapper,
    - methoddescriptor, getsetdescriptor, memberdescriptor, wrapperdescriptor,
    - dictproxy, slice, notimplemented, ellipsis, quit

``dill`` cannot yet pickle these standard types:

    - frame, generator, traceback

``dill`` also provides the capability to:

    - save and load Python interpreter sessions
    - save and extract the source code from functions and classes
    - interactively diagnose pickling errors


Current Release
===============

The latest released version of ``dill`` is available from:

    https://pypi.org/project/dill

``dill`` is distributed under a 3-clause BSD license.


Development Version
===================

You can get the latest development version with all the shiny new features at:

    https://github.com/uqfoundation

If you have a new contribution, please submit a pull request.


Installation
============

``dill`` can be installed with ``pip``::

    $ pip install dill

To optionally include the ``objgraph`` diagnostic tool in the install::

    $ pip install dill[graph]

For windows users, to optionally install session history tools::

    $ pip install dill[readline]


Requirements
============

``dill`` requires:

    - ``python`` (or ``pypy``), **>=3.7**
    - ``setuptools``, **>=42**

Optional requirements:

    - ``objgraph``, **>=1.7.2**
    - ``pyreadline``, **>=1.7.1** (on windows)


Basic Usage
===========

``dill`` is a drop-in replacement for ``pickle``. Existing code can be
updated to allow complete pickling using::

    >>> import dill as pickle

or::

    >>> from dill import dumps, loads

``dumps`` converts the object to a unique byte string, and ``loads`` performs
the inverse operation::

    >>> squared = lambda x: x**2
    >>> loads(dumps(squared))(3)
    9

There are a number of options to control serialization which are provided
as keyword arguments to several ``dill`` functions:

* with *protocol*, the pickle protocol level can be set. This uses the
  same value as the ``pickle`` module, *DEFAULT_PROTOCOL*.
* with *byref=True*, ``dill`` to behave a lot more like pickle with
  certain objects (like modules) pickled by reference as opposed to
  attempting to pickle the object itself.
* with *recurse=True*, objects referred to in the global dictionary are
  recursively traced and pickled, instead of the default behavior of
  attempting to store the entire global dictionary.
* with *fmode*, the contents of the file can be pickled along with the file
  handle, which is useful if the object is being sent over the wire to a
  remote system which does not have the original file on disk. Options are
  *HANDLE_FMODE* for just the handle, *CONTENTS_FMODE* for the file content
  and *FILE_FMODE* for content and handle.
* with *ignore=False*, objects reconstructed with types defined in the
  top-level script environment use the existing type in the environment
  rather than a possibly different reconstructed type.

The default serialization can also be set globally in *dill.settings*.
Thus, we can modify how ``dill`` handles references to the global dictionary
locally or globally::

    >>> import dill.settings
    >>> dumps(absolute) == dumps(absolute, recurse=True)
    False
    >>> dill.settings['recurse'] = True
    >>> dumps(absolute) == dumps(absolute, recurse=True)
    True

``dill`` also includes source code inspection, as an alternate to pickling::

    >>> import dill.source
    >>> print(dill.source.getsource(squared))
    squared = lambda x:x**2

To aid in debugging pickling issues, use *dill.detect* which provides
tools like pickle tracing::

    >>> import dill.detect
    >>> with dill.detect.trace():
    >>>     dumps(squared)
    ┬ F1: <function <lambda> at 0x7fe074f8c280>
    ├┬ F2: <function _create_function at 0x7fe074c49c10>
    │└ # F2 [34 B]
    ├┬ Co: <code object <lambda> at 0x7fe07501eb30, file "<stdin>", line 1>
    │├┬ F2: <function _create_code at 0x7fe074c49ca0>
    ││└ # F2 [19 B]
    │└ # Co [87 B]
    ├┬ D1: <dict object at 0x7fe0750d4680>
    │└ # D1 [22 B]
    ├┬ D2: <dict object at 0x7fe074c5a1c0>
    │└ # D2 [2 B]
    ├┬ D2: <dict object at 0x7fe074f903c0>
    │├┬ D2: <dict object at 0x7fe074f8ebc0>
    ││└ # D2 [2 B]
    │└ # D2 [23 B]
    └ # F1 [180 B]

With trace, we see how ``dill`` stored the lambda (``F1``) by first storing
``_create_function``, the underlying code object (``Co``) and ``_create_code``
(which is used to handle code objects), then we handle the reference to
the global dict (``D2``) plus other dictionaries (``D1`` and ``D2``) that
save the lambda object's state. A ``#`` marks when the object is actually stored.


More Information
================

Probably the best way to get started is to look at the documentation at
http://dill.rtfd.io. Also see ``dill.tests`` for a set of scripts that
demonstrate how ``dill`` can serialize different Python objects. You can
run the test suite with ``python -m dill.tests``. The contents of any
pickle file can be examined with ``undill``.  As ``dill`` conforms to
the ``pickle`` interface, the examples and documentation found at
http://docs.python.org/library/pickle.html also apply to ``dill``
if one will ``import dill as pickle``. The source code is also generally
well documented, so further questions may be resolved by inspecting the
code itself. Please feel free to submit a ticket on github, or ask a
question on stackoverflow (**@Mike McKerns**).
If you would like to share how you use ``dill`` in your work, please send
an email (to **mmckerns at uqfoundation dot org**).


Citation
========

If you use ``dill`` to do research that leads to publication, we ask that you
acknowledge use of ``dill`` by citing the following in your publication::

    M.M. McKerns, L. Strand, T. Sullivan, A. Fang, M.A.G. Aivazis,
    "Building a framework for predictive science", Proceedings of
    the 10th Python in Science Conference, 2011;
    http://arxiv.org/pdf/1202.1056

    Michael McKerns and Michael Aivazis,
    "pathos: a framework for heterogeneous computing", 2010- ;
    https://uqfoundation.github.io/project/pathos

Please see https://uqfoundation.github.io/project/pathos or
http://arxiv.org/pdf/1202.1056 for further information.

'''

__version__ = '0.3.7'
__author__ = 'Mike McKerns'

__license__ = '''
Copyright (c) 2004-2016 California Institute of Technology.
Copyright (c) 2016-2023 The Uncertainty Quantification Foundation.
All rights reserved.

This software is available subject to the conditions and terms laid
out below. By downloading and using this software you are agreeing
to the following conditions.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    - Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    - Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

    - Neither the names of the copyright holders nor the names of any of
      the contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

'''

back/venvmsg/Lib/site-packages/dill/__info__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# -*- coding: utf-8 -*-
#
# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
# Copyright (c) 2008-2015 California Institute of Technology.
# Copyright (c) 2016-2023 The Uncertainty Quantification Foundation.
# License: 3-clause BSD.  The full license text is available at:
#  - https://github.com/uqfoundation/dill/blob/master/LICENSE
"""
dill: a utility for serialization of python objects

The primary functions in `dill` are :func:`dump` and
:func:`dumps` for serialization ("pickling") to a
file or to a string, respectively, and :func:`load`
and :func:`loads` for deserialization ("unpickling"),
similarly, from a file or from a string. Other notable
functions are :func:`~dill.dump_module` and
:func:`~dill.load_module`, which are used to save and
restore module objects, including an intepreter session.

Based on code written by Oren Tirosh and Armin Ronacher.
Extended to a (near) full set of the builtin types (in types module),
and coded to the pickle interface, by <mmckerns@caltech.edu>.
Initial port to python3 by Jonathan Dobson, continued by mmckerns.
Tested against "all" python types (Std. Lib. CH 1-15 @ 2.7) by mmckerns.
Tested against CH16+ Std. Lib. ... TBD.
"""

from __future__ import annotations

__all__ = [
    'dump','dumps','load','loads','copy',
    'Pickler','Unpickler','register','pickle','pickles','check',
    'DEFAULT_PROTOCOL','HIGHEST_PROTOCOL','HANDLE_FMODE','CONTENTS_FMODE','FILE_FMODE',
    'PickleError','PickleWarning','PicklingError','PicklingWarning','UnpicklingError',
    'UnpicklingWarning',
]

__module__ = 'dill'

import warnings
from .logger import adapter as logger
from .logger import trace as _trace
log = logger # backward compatibility (see issue #582)

import os
import sys
diff = None
_use_diff = False
OLD38 = (sys.hexversion < 0x3080000)
OLD39 = (sys.hexversion < 0x3090000)
OLD310 = (sys.hexversion < 0x30a0000)
OLD312a7 = (sys.hexversion < 0x30c00a7)
#XXX: get types from .objtypes ?
import builtins as __builtin__
from pickle import _Pickler as StockPickler, Unpickler as StockUnpickler
from pickle import GLOBAL, POP
from _thread import LockType
from _thread import RLock as RLockType
#from io import IOBase
from types import CodeType, FunctionType, MethodType, GeneratorType, \
    TracebackType, FrameType, ModuleType, BuiltinMethodType
BufferType = memoryview #XXX: unregistered
ClassType = type # no 'old-style' classes
EllipsisType = type(Ellipsis)
#FileType = IOBase
NotImplementedType = type(NotImplemented)
SliceType = slice
TypeType = type # 'new-style' classes #XXX: unregistered
XRangeType = range
from types import MappingProxyType as DictProxyType, new_class
from pickle import DEFAULT_PROTOCOL, HIGHEST_PROTOCOL, PickleError, PicklingError, UnpicklingError
import __main__ as _main_module
import marshal
import gc
# import zlib
import abc
import dataclasses
from weakref import ReferenceType, ProxyType, CallableProxyType
from collections import OrderedDict
from enum import Enum, EnumMeta
from functools import partial
from operator import itemgetter, attrgetter
GENERATOR_FAIL = False
import importlib.machinery
EXTENSION_SUFFIXES = tuple(importlib.machinery.EXTENSION_SUFFIXES)
try:
    import ctypes
    HAS_CTYPES = True
    # if using `pypy`, pythonapi is not found
    IS_PYPY = not hasattr(ctypes, 'pythonapi')
except ImportError:
    HAS_CTYPES = False
    IS_PYPY = False
NumpyUfuncType = None
NumpyDType = None
NumpyArrayType = None
try:
    if not importlib.machinery.PathFinder().find_spec('numpy'):
        raise ImportError("No module named 'numpy'")
    NumpyUfuncType = True
    NumpyDType = True
    NumpyArrayType = True
except ImportError:
    pass
def __hook__():
    global NumpyArrayType, NumpyDType, NumpyUfuncType
    from numpy import ufunc as NumpyUfuncType
    from numpy import ndarray as NumpyArrayType
    from numpy import dtype as NumpyDType
    return True
if NumpyArrayType: # then has numpy
    def ndarraysubclassinstance(obj_type):
        if all((c.__module__, c.__name__) != ('numpy', 'ndarray') for c in obj_type.__mro__):
            return False
        # anything below here is a numpy array (or subclass) instance
        __hook__() # import numpy (so the following works!!!)
        # verify that __reduce__ has not been overridden
        if obj_type.__reduce_ex__ is not NumpyArrayType.__reduce_ex__ \
                or obj_type.__reduce__ is not NumpyArrayType.__reduce__:
            return False
        return True
    def numpyufunc(obj_type):
        return any((c.__module__, c.__name__) == ('numpy', 'ufunc') for c in obj_type.__mro__)
    def numpydtype(obj_type):
        if all((c.__module__, c.__name__) != ('numpy', 'dtype') for c in obj_type.__mro__):
            return False
        # anything below here is a numpy dtype
        __hook__() # import numpy (so the following works!!!)
        return obj_type is type(NumpyDType) # handles subclasses
else:
    def ndarraysubclassinstance(obj): return False
    def numpyufunc(obj): return False
    def numpydtype(obj): return False

from types import GetSetDescriptorType, ClassMethodDescriptorType, \
     WrapperDescriptorType,  MethodDescriptorType, MemberDescriptorType, \
     MethodWrapperType #XXX: unused

# make sure to add these 'hand-built' types to _typemap
CellType = type((lambda x: lambda y: x)(0).__closure__[0])
PartialType = type(partial(int, base=2))
SuperType = type(super(Exception, TypeError()))
ItemGetterType = type(itemgetter(0))
AttrGetterType = type(attrgetter('__repr__'))

try:
    from functools import _lru_cache_wrapper as LRUCacheType
except ImportError:
    LRUCacheType = None

if not isinstance(LRUCacheType, type):
    LRUCacheType = None

def get_file_type(*args, **kwargs):
    open = kwargs.pop("open", __builtin__.open)
    f = open(os.devnull, *args, **kwargs)
    t = type(f)
    f.close()
    return t

FileType = get_file_type('rb', buffering=0)
TextWrapperType = get_file_type('r', buffering=-1)
BufferedRandomType = get_file_type('r+b', buffering=-1)
BufferedReaderType = get_file_type('rb', buffering=-1)
BufferedWriterType = get_file_type('wb', buffering=-1)
try:
    from _pyio import open as _open
    PyTextWrapperType = get_file_type('r', buffering=-1, open=_open)
    PyBufferedRandomType = get_file_type('r+b', buffering=-1, open=_open)
    PyBufferedReaderType = get_file_type('rb', buffering=-1, open=_open)
    PyBufferedWriterType = get_file_type('wb', buffering=-1, open=_open)
except ImportError:
    PyTextWrapperType = PyBufferedRandomType = PyBufferedReaderType = PyBufferedWriterType = None
from io import BytesIO as StringIO
InputType = OutputType = None
from socket import socket as SocketType
#FIXME: additionally calls ForkingPickler.register several times
from multiprocessing.reduction import _reduce_socket as reduce_socket
try: #pragma: no cover
    IS_IPYTHON = __IPYTHON__  # is True
    ExitType = None # IPython.core.autocall.ExitAutocall
    IPYTHON_SINGLETONS = ('exit', 'quit', 'get_ipython')
except NameError:
    IS_IPYTHON = False
    try: ExitType = type(exit) # apparently 'exit' can be removed
    except NameError: ExitType = None
    IPYTHON_SINGLETONS = ()

import inspect
import typing


### Shims for different versions of Python and dill
class Sentinel(object):
    """
    Create a unique sentinel object that is pickled as a constant.
    """
    def __init__(self, name, module_name=None):
        self.name = name
        if module_name is None:
            # Use the calling frame's module
            self.__module__ = inspect.currentframe().f_back.f_globals['__name__']
        else:
            self.__module__ = module_name # pragma: no cover
    def __repr__(self):
        return self.__module__ + '.' + self.name # pragma: no cover
    def __copy__(self):
        return self # pragma: no cover
    def __deepcopy__(self, memo):
        return self # pragma: no cover
    def __reduce__(self):
        return self.name
    def __reduce_ex__(self, protocol):
        return self.name

from . import _shims
from ._shims import Reduce, Getattr

### File modes
#: Pickles the file handle, preserving mode. The position of the unpickled
#: object is as for a new file handle.
HANDLE_FMODE = 0
#: Pickles the file contents, creating a new file if on load the file does
#: not exist. The position = min(pickled position, EOF) and mode is chosen
#: as such that "best" preserves behavior of the original file.
CONTENTS_FMODE = 1
#: Pickles the entire file (handle and contents), preserving mode and position.
FILE_FMODE = 2

### Shorthands (modified from python2.5/lib/pickle.py)
def copy(obj, *args, **kwds):
    """
    Use pickling to 'copy' an object (i.e. `loads(dumps(obj))`).

    See :func:`dumps` and :func:`loads` for keyword arguments.
    """
    ignore = kwds.pop('ignore', Unpickler.settings['ignore'])
    return loads(dumps(obj, *args, **kwds), ignore=ignore)

def dump(obj, file, protocol=None, byref=None, fmode=None, recurse=None, **kwds):#, strictio=None):
    """
    Pickle an object to a file.

    See :func:`dumps` for keyword arguments.
    """
    from .settings import settings
    protocol = settings['protocol'] if protocol is None else int(protocol)
    _kwds = kwds.copy()
    _kwds.update(dict(byref=byref, fmode=fmode, recurse=recurse))
    Pickler(file, protocol, **_kwds).dump(obj)
    return

def dumps(obj, protocol=None, byref=None, fmode=None, recurse=None, **kwds):#, strictio=None):
    """
    Pickle an object to a string.

    *protocol* is the pickler protocol, as defined for Python *pickle*.

    If *byref=True*, then dill behaves a lot more like pickle as certain
    objects (like modules) are pickled by reference as opposed to attempting
    to pickle the object itself.

    If *recurse=True*, then objects referred to in the global dictionary
    are recursively traced and pickled, instead of the default behavior
    of attempting to store the entire global dictionary. This is needed for
    functions defined via *exec()*.

    *fmode* (:const:`HANDLE_FMODE`, :const:`CONTENTS_FMODE`,
    or :const:`FILE_FMODE`) indicates how file handles will be pickled.
    For example, when pickling a data file handle for transfer to a remote
    compute service, *FILE_FMODE* will include the file contents in the
    pickle and cursor position so that a remote method can operate
    transparently on an object with an open file handle.

    Default values for keyword arguments can be set in :mod:`dill.settings`.
    """
    file = StringIO()
    dump(obj, file, protocol, byref, fmode, recurse, **kwds)#, strictio)
    return file.getvalue()

def load(file, ignore=None, **kwds):
    """
    Unpickle an object from a file.

    See :func:`loads` for keyword arguments.
    """
    return Unpickler(file, ignore=ignore, **kwds).load()

def loads(str, ignore=None, **kwds):
    """
    Unpickle an object from a string.

    If *ignore=False* then objects whose class is defined in the module
    *__main__* are updated to reference the existing class in *__main__*,
    otherwise they are left to refer to the reconstructed type, which may
    be different.

    Default values for keyword arguments can be set in :mod:`dill.settings`.
    """
    file = StringIO(str)
    return load(file, ignore, **kwds)

# def dumpzs(obj, protocol=None):
#     """pickle an object to a compressed string"""
#     return zlib.compress(dumps(obj, protocol))

# def loadzs(str):
#     """unpickle an object from a compressed string"""
#     return loads(zlib.decompress(str))

### End: Shorthands ###

class MetaCatchingDict(dict):
    def get(self, key, default=None):
        try:
            return self[key]
        except KeyError:
            return default

    def __missing__(self, key):
        if issubclass(key, type):
            return save_type
        else:
            raise KeyError()

class PickleWarning(Warning, PickleError):
    pass

class PicklingWarning(PickleWarning, PicklingError):
    pass

class UnpicklingWarning(PickleWarning, UnpicklingError):
    pass

### Extend the Picklers
class Pickler(StockPickler):
    """python's Pickler extended to interpreter sessions"""
    dispatch: typing.Dict[type, typing.Callable[[Pickler, typing.Any], None]] \
            = MetaCatchingDict(StockPickler.dispatch.copy())
    """The dispatch table, a dictionary of serializing functions used
    by Pickler to save objects of specific types.  Use :func:`pickle`
    or :func:`register` to associate types to custom functions.

    :meta hide-value:
    """
    _session = False
    from .settings import settings

    def __init__(self, file, *args, **kwds):
        settings = Pickler.settings
        _byref = kwds.pop('byref', None)
       #_strictio = kwds.pop('strictio', None)
        _fmode = kwds.pop('fmode', None)
        _recurse = kwds.pop('recurse', None)
        StockPickler.__init__(self, file, *args, **kwds)
        self._main = _main_module
        self._diff_cache = {}
        self._byref = settings['byref'] if _byref is None else _byref
        self._strictio = False #_strictio
        self._fmode = settings['fmode'] if _fmode is None else _fmode
        self._recurse = settings['recurse'] if _recurse is None else _recurse
        self._postproc = OrderedDict()
        self._file = file

    def save(self, obj, save_persistent_id=True):
        # numpy hack
        obj_type = type(obj)
        if NumpyArrayType and not (obj_type is type or obj_type in Pickler.dispatch):
            # register if the object is a numpy ufunc
            # thanks to Paul Kienzle for pointing out ufuncs didn't pickle
            if numpyufunc(obj_type):
                @register(obj_type)
                def save_numpy_ufunc(pickler, obj):
                    logger.trace(pickler, "Nu: %s", obj)
                    name = getattr(obj, '__qualname__', getattr(obj, '__name__', None))
                    StockPickler.save_global(pickler, obj, name=name)
                    logger.trace(pickler, "# Nu")
                    return
                # NOTE: the above 'save' performs like:
                #   import copy_reg
                #   def udump(f): return f.__name__
                #   def uload(name): return getattr(numpy, name)
                #   copy_reg.pickle(NumpyUfuncType, udump, uload)
            # register if the object is a numpy dtype
            if numpydtype(obj_type):
                @register(obj_type)
                def save_numpy_dtype(pickler, obj):
                    logger.trace(pickler, "Dt: %s", obj)
                    pickler.save_reduce(_create_dtypemeta, (obj.type,), obj=obj)
                    logger.trace(pickler, "# Dt")
                    return
                # NOTE: the above 'save' performs like:
                #   import copy_reg
                #   def uload(name): return type(NumpyDType(name))
                #   def udump(f): return uload, (f.type,)
                #   copy_reg.pickle(NumpyDTypeType, udump, uload)
            # register if the object is a subclassed numpy array instance
            if ndarraysubclassinstance(obj_type):
                @register(obj_type)
                def save_numpy_array(pickler, obj):
                    logger.trace(pickler, "Nu: (%s, %s)", obj.shape, obj.dtype)
                    npdict = getattr(obj, '__dict__', None)
                    f, args, state = obj.__reduce__()
                    pickler.save_reduce(_create_array, (f,args,state,npdict), obj=obj)
                    logger.trace(pickler, "# Nu")
                    return
        # end numpy hack

        if GENERATOR_FAIL and obj_type is GeneratorType:
            msg = "Can't pickle %s: attribute lookup builtins.generator failed" % GeneratorType
            raise PicklingError(msg)
        StockPickler.save(self, obj, save_persistent_id)

    save.__doc__ = StockPickler.save.__doc__

    def dump(self, obj): #NOTE: if settings change, need to update attributes
        logger.trace_setup(self)
        StockPickler.dump(self, obj)
    dump.__doc__ = StockPickler.dump.__doc__

class Unpickler(StockUnpickler):
    """python's Unpickler extended to interpreter sessions and more types"""
    from .settings import settings
    _session = False

    def find_class(self, module, name):
        if (module, name) == ('__builtin__', '__main__'):
            return self._main.__dict__ #XXX: above set w/save_module_dict
        elif (module, name) == ('__builtin__', 'NoneType'):
            return type(None) #XXX: special case: NoneType missing
        if module == 'dill.dill': module = 'dill._dill'
        return StockUnpickler.find_class(self, module, name)

    def __init__(self, *args, **kwds):
        settings = Pickler.settings
        _ignore = kwds.pop('ignore', None)
        StockUnpickler.__init__(self, *args, **kwds)
        self._main = _main_module
        self._ignore = settings['ignore'] if _ignore is None else _ignore

    def load(self): #NOTE: if settings change, need to update attributes
        obj = StockUnpickler.load(self)
        if type(obj).__module__ == getattr(_main_module, '__name__', '__main__'):
            if not self._ignore:
                # point obj class to main
                try: obj.__class__ = getattr(self._main, type(obj).__name__)
                except (AttributeError,TypeError): pass # defined in a file
       #_main_module.__dict__.update(obj.__dict__) #XXX: should update globals ?
        return obj
    load.__doc__ = StockUnpickler.load.__doc__
    pass

'''
def dispatch_table():
    """get the dispatch table of registered types"""
    return Pickler.dispatch
'''

pickle_dispatch_copy = StockPickler.dispatch.copy()

def pickle(t, func):
    """expose :attr:`~Pickler.dispatch` table for user-created extensions"""
    Pickler.dispatch[t] = func
    return

def register(t):
    """decorator to register types to Pickler's :attr:`~Pickler.dispatch` table"""
    def proxy(func):
        Pickler.dispatch[t] = func
        return func
    return proxy

def _revert_extension():
    """drop dill-registered types from pickle's dispatch table"""
    for type, func in list(StockPickler.dispatch.items()):
        if func.__module__ == __name__:
            del StockPickler.dispatch[type]
            if type in pickle_dispatch_copy:
                StockPickler.dispatch[type] = pickle_dispatch_copy[type]

def use_diff(on=True):
    """
    Reduces size of pickles by only including object which have changed.

    Decreases pickle size but increases CPU time needed.
    Also helps avoid some unpickleable objects.
    MUST be called at start of script, otherwise changes will not be recorded.
    """
    global _use_diff, diff
    _use_diff = on
    if _use_diff and diff is None:
        try:
            from . import diff as d
        except ImportError:
            import diff as d
        diff = d

def _create_typemap():
    import types
    d = dict(list(__builtin__.__dict__.items()) + \
             list(types.__dict__.items())).items()
    for key, value in d:
        if getattr(value, '__module__', None) == 'builtins' \
                and type(value) is type:
            yield key, value
    return
_reverse_typemap = dict(_create_typemap())
_reverse_typemap.update({
    'PartialType': PartialType,
    'SuperType': SuperType,
    'ItemGetterType': ItemGetterType,
    'AttrGetterType': AttrGetterType,
})
if sys.hexversion < 0x30800a2:
    _reverse_typemap.update({
        'CellType': CellType,
    })

# "Incidental" implementation specific types. Unpickling these types in another
# implementation of Python (PyPy -> CPython) is not guaranteed to work

# This dictionary should contain all types that appear in Python implementations
# but are not defined in https://docs.python.org/3/library/types.html#standard-interpreter-types
x=OrderedDict()
_incedental_reverse_typemap = {
    'FileType': FileType,
    'BufferedRandomType': BufferedRandomType,
    'BufferedReaderType': BufferedReaderType,
    'BufferedWriterType': BufferedWriterType,
    'TextWrapperType': TextWrapperType,
    'PyBufferedRandomType': PyBufferedRandomType,
    'PyBufferedReaderType': PyBufferedReaderType,
    'PyBufferedWriterType': PyBufferedWriterType,
    'PyTextWrapperType': PyTextWrapperType,
}

_incedental_reverse_typemap.update({
    "DictKeysType": type({}.keys()),
    "DictValuesType": type({}.values()),
    "DictItemsType": type({}.items()),

    "OdictKeysType": type(x.keys()),
    "OdictValuesType": type(x.values()),
    "OdictItemsType": type(x.items()),
})

if ExitType:
    _incedental_reverse_typemap['ExitType'] = ExitType
if InputType:
    _incedental_reverse_typemap['InputType'] = InputType
    _incedental_reverse_typemap['OutputType'] = OutputType

'''
try:
    import symtable
    _incedental_reverse_typemap["SymtableEntryType"] = type(symtable.symtable("", "string", "exec")._table)
except: #FIXME: fails to pickle
    pass

if sys.hexversion >= 0x30a00a0:
    _incedental_reverse_typemap['LineIteratorType'] = type(compile('3', '', 'eval').co_lines())
'''

if sys.hexversion >= 0x30b00b0:
    from types import GenericAlias
    _incedental_reverse_typemap["GenericAliasIteratorType"] = type(iter(GenericAlias(list, (int,))))
    '''
    _incedental_reverse_typemap['PositionsIteratorType'] = type(compile('3', '', 'eval').co_positions())
    '''

try:
    import winreg
    _incedental_reverse_typemap["HKEYType"] = winreg.HKEYType
except ImportError:
    pass

_reverse_typemap.update(_incedental_reverse_typemap)
_incedental_types = set(_incedental_reverse_typemap.values())

del x

_typemap = dict((v, k) for k, v in _reverse_typemap.items())

def _unmarshal(string):
    return marshal.loads(string)

def _load_type(name):
    return _reverse_typemap[name]

def _create_type(typeobj, *args):
    return typeobj(*args)

def _create_function(fcode, fglobals, fname=None, fdefaults=None,
                     fclosure=None, fdict=None, fkwdefaults=None):
    # same as FunctionType, but enable passing __dict__ to new function,
    # __dict__ is the storehouse for attributes added after function creation
    func = FunctionType(fcode, fglobals or dict(), fname, fdefaults, fclosure)
    if fdict is not None:
        func.__dict__.update(fdict) #XXX: better copy? option to copy?
    if fkwdefaults is not None:
        func.__kwdefaults__ = fkwdefaults
    # 'recurse' only stores referenced modules/objects in fglobals,
    # thus we need to make sure that we have __builtins__ as well
    if "__builtins__" not in func.__globals__:
        func.__globals__["__builtins__"] = globals()["__builtins__"]
    # assert id(fglobals) == id(func.__globals__)
    return func

class match:
    """
    Make avaialable a limited structural pattern matching-like syntax for Python < 3.10

    Patterns can be only tuples (without types) currently.
    Inspired by the package pattern-matching-PEP634.

    Usage:
    >>> with match(args) as m:
    >>>     if   m.case(('x', 'y')):
    >>>         # use m.x and m.y
    >>>     elif m.case(('x', 'y', 'z')):
    >>>         # use m.x, m.y and m.z

    Equivalent native code for Python >= 3.10:
    >>> match args:
    >>>     case (x, y):
    >>>         # use x and y
    >>>     case (x, y, z):
    >>>         # use x, y and z
    """
    def __init__(self, value):
        self.value = value
        self._fields = None
    def __enter__(self):
        return self
    def __exit__(self, *exc_info):
        return False
    def case(self, args): # *args, **kwargs):
        """just handles tuple patterns"""
        if len(self.value) != len(args): # + len(kwargs):
            return False
        #if not all(isinstance(arg, pat) for arg, pat in zip(self.value[len(args):], kwargs.values())):
        #    return False
        self.args = args # (*args, *kwargs)
        return True
    @property
    def fields(self):
        # Only bind names to values if necessary.
        if self._fields is None:
            self._fields = dict(zip(self.args, self.value))
        return self._fields
    def __getattr__(self, item):
        return self.fields[item]

ALL_CODE_PARAMS = [
    # Version     New attribute         CodeType parameters
    ((3,11,'a'), 'co_endlinetable',    'argcount posonlyargcount kwonlyargcount nlocals stacksize flags code consts names varnames filename name qualname firstlineno linetable endlinetable columntable exceptiontable freevars cellvars'),
    ((3,11),     'co_exceptiontable',  'argcount posonlyargcount kwonlyargcount nlocals stacksize flags code consts names varnames filename name qualname firstlineno linetable                          exceptiontable freevars cellvars'),
    ((3,10),     'co_linetable',       'argcount posonlyargcount kwonlyargcount nlocals stacksize flags code consts names varnames filename name          firstlineno linetable                                         freevars cellvars'),
    ((3,8),      'co_posonlyargcount', 'argcount posonlyargcount kwonlyargcount nlocals stacksize flags code consts names varnames filename name          firstlineno lnotab                                            freevars cellvars'),
    ((3,7),      'co_kwonlyargcount',  'argcount                 kwonlyargcount nlocals stacksize flags code consts names varnames filename name          firstlineno lnotab                                            freevars cellvars'),
    ]
for version, new_attr, params in ALL_CODE_PARAMS:
    if hasattr(CodeType, new_attr):
        CODE_VERSION = version
        CODE_PARAMS = params.split()
        break
ENCODE_PARAMS = set(CODE_PARAMS).intersection(
        ['code', 'lnotab', 'linetable', 'endlinetable', 'columntable', 'exceptiontable'])

def _create_code(*args):
    if not isinstance(args[0], int): # co_lnotab stored from >= 3.10
        LNOTAB, *args = args
    else: # from < 3.10 (or pre-LNOTAB storage)
        LNOTAB = b''

    with match(args) as m:
        # Python 3.11/3.12a (18 members)
        if m.case((
            'argcount', 'posonlyargcount', 'kwonlyargcount', 'nlocals', 'stacksize', 'flags',     # args[0:6]
            'code', 'consts', 'names', 'varnames', 'filename', 'name', 'qualname', 'firstlineno', # args[6:14]
            'linetable', 'exceptiontable', 'freevars', 'cellvars'                                 # args[14:]
        )):
            if CODE_VERSION == (3,11):
                return CodeType(
                    *args[:6],
                    args[6].encode() if hasattr(args[6], 'encode') else args[6], # code
                    *args[7:14],
                    args[14].encode() if hasattr(args[14], 'encode') else args[14], # linetable
                    args[15].encode() if hasattr(args[15], 'encode') else args[15], # exceptiontable
                    args[16],
                    args[17],
                )
            fields = m.fields
        # Python 3.10 or 3.8/3.9 (16 members)
        elif m.case((
            'argcount', 'posonlyargcount', 'kwonlyargcount', 'nlocals', 'stacksize', 'flags', # args[0:6]
            'code', 'consts', 'names', 'varnames', 'filename', 'name', 'firstlineno',         # args[6:13]
            'LNOTAB_OR_LINETABLE', 'freevars', 'cellvars'                                     # args[13:]
        )):
            if CODE_VERSION == (3,10) or CODE_VERSION == (3,8):
                return CodeType(
                    *args[:6],
                    args[6].encode() if hasattr(args[6], 'encode') else args[6], # code
                    *args[7:13],
                    args[13].encode() if hasattr(args[13], 'encode') else args[13], # lnotab/linetable
                    args[14],
                    args[15],
                )
            fields = m.fields
            if CODE_VERSION >= (3,10):
                fields['linetable'] = m.LNOTAB_OR_LINETABLE
            else:
                fields['lnotab'] = LNOTAB if LNOTAB else m.LNOTAB_OR_LINETABLE
        # Python 3.7 (15 args)
        elif m.case((
            'argcount', 'kwonlyargcount', 'nlocals', 'stacksize', 'flags',            # args[0:5]
            'code', 'consts', 'names', 'varnames', 'filename', 'name', 'firstlineno', # args[5:12]
            'lnotab', 'freevars', 'cellvars'                                          # args[12:]
        )):
            if CODE_VERSION == (3,7):
                return CodeType(
                    *args[:5],
                    args[5].encode() if hasattr(args[5], 'encode') else args[5], # code
                    *args[6:12],
                    args[12].encode() if hasattr(args[12], 'encode') else args[12], # lnotab
                    args[13],
                    args[14],
                )
            fields = m.fields
        # Python 3.11a (20 members)
        elif m.case((
            'argcount', 'posonlyargcount', 'kwonlyargcount', 'nlocals', 'stacksize', 'flags',     # args[0:6]
            'code', 'consts', 'names', 'varnames', 'filename', 'name', 'qualname', 'firstlineno', # args[6:14]
            'linetable', 'endlinetable', 'columntable', 'exceptiontable', 'freevars', 'cellvars'  # args[14:]
        )):
            if CODE_VERSION == (3,11,'a'):
                return CodeType(
                    *args[:6],
                    args[6].encode() if hasattr(args[6], 'encode') else args[6], # code
                    *args[7:14],
                    *(a.encode() if hasattr(a, 'encode') else a for a in args[14:18]), # linetable-exceptiontable
                    args[18],
                    args[19],
                )
            fields = m.fields
        else:
            raise UnpicklingError("pattern match for code object failed")

    # The args format doesn't match this version.
    fields.setdefault('posonlyargcount', 0)         # from python <= 3.7
    fields.setdefault('lnotab', LNOTAB)             # from python >= 3.10
    fields.setdefault('linetable', b'')             # from python <= 3.9
    fields.setdefault('qualname', fields['name'])   # from python <= 3.10
    fields.setdefault('exceptiontable', b'')        # from python <= 3.10
    fields.setdefault('endlinetable', None)         # from python != 3.11a
    fields.setdefault('columntable', None)          # from python != 3.11a

    args = (fields[k].encode() if k in ENCODE_PARAMS and hasattr(fields[k], 'encode') else fields[k]
            for k in CODE_PARAMS)
    return CodeType(*args)

def _create_ftype(ftypeobj, func, args, kwds):
    if kwds is None:
        kwds = {}
    if args is None:
        args = ()
    return ftypeobj(func, *args, **kwds)

def _create_typing_tuple(argz, *args): #NOTE: workaround python/cpython#94245
    if not argz:
        return typing.Tuple[()].copy_with(())
    if argz == ((),):
        return typing.Tuple[()]
    return typing.Tuple[argz]

def _create_lock(locked, *args): #XXX: ignores 'blocking'
    from threading import Lock
    lock = Lock()
    if locked:
        if not lock.acquire(False):
            raise UnpicklingError("Cannot acquire lock")
    return lock

def _create_rlock(count, owner, *args): #XXX: ignores 'blocking'
    lock = RLockType()
    if owner is not None:
        lock._acquire_restore((count, owner))
    if owner and not lock._is_owned():
        raise UnpicklingError("Cannot acquire lock")
    return lock

# thanks to matsjoyce for adding all the different file modes
def _create_filehandle(name, mode, position, closed, open, strictio, fmode, fdata): # buffering=0
    # only pickles the handle, not the file contents... good? or StringIO(data)?
    # (for file contents see: http://effbot.org/librarybook/copy-reg.htm)
    # NOTE: handle special cases first (are there more special cases?)
    names = {'<stdin>':sys.__stdin__, '<stdout>':sys.__stdout__,
             '<stderr>':sys.__stderr__} #XXX: better fileno=(0,1,2) ?
    if name in list(names.keys()):
        f = names[name] #XXX: safer "f=sys.stdin"
    elif name == '<tmpfile>':
        f = os.tmpfile()
    elif name == '<fdopen>':
        import tempfile
        f = tempfile.TemporaryFile(mode)
    else:
        try:
            exists = os.path.exists(name)
        except Exception:
            exists = False
        if not exists:
            if strictio:
                raise FileNotFoundError("[Errno 2] No such file or directory: '%s'" % name)
            elif "r" in mode and fmode != FILE_FMODE:
                name = '<fdopen>' # or os.devnull?
            current_size = 0 # or maintain position?
        else:
            current_size = os.path.getsize(name)

        if position > current_size:
            if strictio:
                raise ValueError("invalid buffer size")
            elif fmode == CONTENTS_FMODE:
                position = current_size
        # try to open the file by name
        # NOTE: has different fileno
        try:
            #FIXME: missing: *buffering*, encoding, softspace
            if fmode == FILE_FMODE:
                f = open(name, mode if "w" in mode else "w")
                f.write(fdata)
                if "w" not in mode:
                    f.close()
                    f = open(name, mode)
            elif name == '<fdopen>': # file did not exist
                import tempfile
                f = tempfile.TemporaryFile(mode)
            # treat x mode as w mode
            elif fmode == CONTENTS_FMODE \
               and ("w" in mode or "x" in mode):
                # stop truncation when opening
                flags = os.O_CREAT
                if "+" in mode:
                    flags |= os.O_RDWR
                else:
                    flags |= os.O_WRONLY
                f = os.fdopen(os.open(name, flags), mode)
                # set name to the correct value
                r = getattr(f, "buffer", f)
                r = getattr(r, "raw", r)
                r.name = name
                assert f.name == name
            else:
                f = open(name, mode)
        except (IOError, FileNotFoundError):
            err = sys.exc_info()[1]
            raise UnpicklingError(err)
    if closed:
        f.close()
    elif position >= 0 and fmode != HANDLE_FMODE:
        f.seek(position)
    return f

def _create_stringi(value, position, closed):
    f = StringIO(value)
    if closed: f.close()
    else: f.seek(position)
    return f

def _create_stringo(value, position, closed):
    f = StringIO()
    if closed: f.close()
    else:
       f.write(value)
       f.seek(position)
    return f

class _itemgetter_helper(object):
    def __init__(self):
        self.items = []
    def __getitem__(self, item):
        self.items.append(item)
        return

class _attrgetter_helper(object):
    def __init__(self, attrs, index=None):
        self.attrs = attrs
        self.index = index
    def __getattribute__(self, attr):
        attrs = object.__getattribute__(self, "attrs")
        index = object.__getattribute__(self, "index")
        if index is None:
            index = len(attrs)
            attrs.append(attr)
        else:
            attrs[index] = ".".join([attrs[index], attr])
        return type(self)(attrs, index)

class _dictproxy_helper(dict):
   def __ror__(self, a):
        return a

_dictproxy_helper_instance = _dictproxy_helper()

__d = {}
try:
    # In CPython 3.9 and later, this trick can be used to exploit the
    # implementation of the __or__ function of MappingProxyType to get the true
    # mapping referenced by the proxy. It may work for other implementations,
    # but is not guaranteed.
    MAPPING_PROXY_TRICK = __d is (DictProxyType(__d) | _dictproxy_helper_instance)
except Exception:
    MAPPING_PROXY_TRICK = False
del __d

# _CELL_REF and _CELL_EMPTY are used to stay compatible with versions of dill
# whose _create_cell functions do not have a default value.
# _CELL_REF can be safely removed entirely (replaced by empty tuples for calls
# to _create_cell) once breaking changes are allowed.
_CELL_REF = None
_CELL_EMPTY = Sentinel('_CELL_EMPTY')

def _create_cell(contents=None):
    if contents is not _CELL_EMPTY:
        value = contents
    return (lambda: value).__closure__[0]

def _create_weakref(obj, *args):
    from weakref import ref
    if obj is None: # it's dead
        from collections import UserDict
        return ref(UserDict(), *args)
    return ref(obj, *args)

def _create_weakproxy(obj, callable=False, *args):
    from weakref import proxy
    if obj is None: # it's dead
        if callable: return proxy(lambda x:x, *args)
        from collections import UserDict
        return proxy(UserDict(), *args)
    return proxy(obj, *args)

def _eval_repr(repr_str):
    return eval(repr_str)

def _create_array(f, args, state, npdict=None):
   #array = numpy.core.multiarray._reconstruct(*args)
    array = f(*args)
    array.__setstate__(state)
    if npdict is not None: # we also have saved state in __dict__
        array.__dict__.update(npdict)
    return array

def _create_dtypemeta(scalar_type):
    if NumpyDType is True: __hook__() # a bit hacky I think
    if scalar_type is None:
        return NumpyDType
    return type(NumpyDType(scalar_type))

def _create_namedtuple(name, fieldnames, modulename, defaults=None):
    class_ = _import_module(modulename + '.' + name, safe=True)
    if class_ is not None:
        return class_
    import collections
    t = collections.namedtuple(name, fieldnames, defaults=defaults, module=modulename)
    return t

def _create_capsule(pointer, name, context, destructor):
    attr_found = False
    try:
        # based on https://github.com/python/cpython/blob/f4095e53ab708d95e019c909d5928502775ba68f/Objects/capsule.c#L209-L231
        uname = name.decode('utf8')
        for i in range(1, uname.count('.')+1):
            names = uname.rsplit('.', i)
            try:
                module = __import__(names[0])
            except ImportError:
                pass
            obj = module
            for attr in names[1:]:
                obj = getattr(obj, attr)
            capsule = obj
            attr_found = True
            break
    except Exception:
        pass

    if attr_found:
        if _PyCapsule_IsValid(capsule, name):
            return capsule
        raise UnpicklingError("%s object exists at %s but a PyCapsule object was expected." % (type(capsule), name))
    else:
        #warnings.warn('Creating a new PyCapsule %s for a C data structure that may not be present in memory. Segmentation faults or other memory errors are possible.' % (name,), UnpicklingWarning)
        capsule = _PyCapsule_New(pointer, name, destructor)
        _PyCapsule_SetContext(capsule, context)
        return capsule

def _getattr(objclass, name, repr_str):
    # hack to grab the reference directly
    try: #XXX: works only for __builtin__ ?
        attr = repr_str.split("'")[3]
        return eval(attr+'.__dict__["'+name+'"]')
    except Exception:
        try:
            attr = objclass.__dict__
            if type(attr) is DictProxyType:
                attr = attr[name]
            else:
                attr = getattr(objclass,name)
        except (AttributeError, KeyError):
            attr = getattr(objclass,name)
        return attr

def _get_attr(self, name):
    # stop recursive pickling
    return getattr(self, name, None) or getattr(__builtin__, name)

def _import_module(import_name, safe=False):
    try:
        if import_name.startswith('__runtime__.'):
            return sys.modules[import_name]
        elif '.' in import_name:
            items = import_name.split('.')
            module = '.'.join(items[:-1])
            obj = items[-1]
        else:
            return __import__(import_name)
        return getattr(__import__(module, None, None, [obj]), obj)
    except (ImportError, AttributeError, KeyError):
        if safe:
            return None
        raise

# https://github.com/python/cpython/blob/a8912a0f8d9eba6d502c37d522221f9933e976db/Lib/pickle.py#L322-L333
def _getattribute(obj, name):
    for subpath in name.split('.'):
        if subpath == '<locals>':
            raise AttributeError("Can't get local attribute {!r} on {!r}"
                                 .format(name, obj))
        try:
            parent = obj
            obj = getattr(obj, subpath)
        except AttributeError:
            raise AttributeError("Can't get attribute {!r} on {!r}"
                                 .format(name, obj))
    return obj, parent

def _locate_function(obj, pickler=None):
    module_name = getattr(obj, '__module__', None)
    if module_name in ['__main__', None] or \
            pickler and is_dill(pickler, child=False) and pickler._session and module_name == pickler._main.__name__:
        return False
    if hasattr(obj, '__qualname__'):
        module = _import_module(module_name, safe=True)
        try:
            found, _ = _getattribute(module, obj.__qualname__)
            return found is obj
        except AttributeError:
            return False
    else:
        found = _import_module(module_name + '.' + obj.__name__, safe=True)
        return found is obj


def _setitems(dest, source):
    for k, v in source.items():
        dest[k] = v


def _save_with_postproc(pickler, reduction, is_pickler_dill=None, obj=Getattr.NO_DEFAULT, postproc_list=None):
    if obj is Getattr.NO_DEFAULT:
        obj = Reduce(reduction) # pragma: no cover

    if is_pickler_dill is None:
        is_pickler_dill = is_dill(pickler, child=True)
    if is_pickler_dill:
        # assert id(obj) not in pickler._postproc, str(obj) + ' already pushed on stack!'
        # if not hasattr(pickler, 'x'): pickler.x = 0
        # print(pickler.x*' ', 'push', obj, id(obj), pickler._recurse)
        # pickler.x += 1
        if postproc_list is None:
            postproc_list = []

        # Recursive object not supported. Default to a global instead.
        if id(obj) in pickler._postproc:
            name = '%s.%s ' % (obj.__module__, getattr(obj, '__qualname__', obj.__name__)) if hasattr(obj, '__module__') else ''
            warnings.warn('Cannot pickle %r: %shas recursive self-references that trigger a RecursionError.' % (obj, name), PicklingWarning)
            pickler.save_global(obj)
            return
        pickler._postproc[id(obj)] = postproc_list

    # TODO: Use state_setter in Python 3.8 to allow for faster cPickle implementations
    pickler.save_reduce(*reduction, obj=obj)

    if is_pickler_dill:
        # pickler.x -= 1
        # print(pickler.x*' ', 'pop', obj, id(obj))
        postproc = pickler._postproc.pop(id(obj))
        # assert postproc_list == postproc, 'Stack tampered!'
        for reduction in reversed(postproc):
            if reduction[0] is _setitems:
                # use the internal machinery of pickle.py to speedup when
                # updating a dictionary in postproc
                dest, source = reduction[1]
                if source:
                    pickler.write(pickler.get(pickler.memo[id(dest)][0]))
                    pickler._batch_setitems(iter(source.items()))
                else:
                    # Updating with an empty dictionary. Same as doing nothing.
                    continue
            else:
                pickler.save_reduce(*reduction)
            # pop None created by calling preprocessing step off stack
            pickler.write(POP)

#@register(CodeType)
#def save_code(pickler, obj):
#    logger.trace(pickler, "Co: %s", obj)
#    pickler.save_reduce(_unmarshal, (marshal.dumps(obj),), obj=obj)
#    logger.trace(pickler, "# Co")
#    return

# The following function is based on 'save_codeobject' from 'cloudpickle'
# Copyright (c) 2012, Regents of the University of California.
# Copyright (c) 2009 `PiCloud, Inc. <http://www.picloud.com>`_.
# License: https://github.com/cloudpipe/cloudpickle/blob/master/LICENSE
@register(CodeType)
def save_code(pickler, obj):
    logger.trace(pickler, "Co: %s", obj)
    if hasattr(obj, "co_endlinetable"): # python 3.11a (20 args)
        args = (
            obj.co_lnotab, # for < python 3.10 [not counted in args]
            obj.co_argcount, obj.co_posonlyargcount,
            obj.co_kwonlyargcount, obj.co_nlocals, obj.co_stacksize,
            obj.co_flags, obj.co_code, obj.co_consts, obj.co_names,
            obj.co_varnames, obj.co_filename, obj.co_name, obj.co_qualname,
            obj.co_firstlineno, obj.co_linetable, obj.co_endlinetable,
            obj.co_columntable, obj.co_exceptiontable, obj.co_freevars,
            obj.co_cellvars
        )
    elif hasattr(obj, "co_exceptiontable"): # python 3.11 (18 args)
        with warnings.catch_warnings():
            if not OLD312a7: # issue 597
                warnings.filterwarnings('ignore', category=DeprecationWarning)
            args = (
                obj.co_lnotab, # for < python 3.10 [not counted in args]
                obj.co_argcount, obj.co_posonlyargcount,
                obj.co_kwonlyargcount, obj.co_nlocals, obj.co_stacksize,
                obj.co_flags, obj.co_code, obj.co_consts, obj.co_names,
                obj.co_varnames, obj.co_filename, obj.co_name, obj.co_qualname,
                obj.co_firstlineno, obj.co_linetable, obj.co_exceptiontable,
                obj.co_freevars, obj.co_cellvars
            )
    elif hasattr(obj, "co_linetable"): # python 3.10 (16 args)
        args = (
            obj.co_lnotab, # for < python 3.10 [not counted in args]
            obj.co_argcount, obj.co_posonlyargcount,
            obj.co_kwonlyargcount, obj.co_nlocals, obj.co_stacksize,
            obj.co_flags, obj.co_code, obj.co_consts, obj.co_names,
            obj.co_varnames, obj.co_filename, obj.co_name,
            obj.co_firstlineno, obj.co_linetable, obj.co_freevars,
            obj.co_cellvars
        )
    elif hasattr(obj, "co_posonlyargcount"): # python 3.8 (16 args)
        args = (
            obj.co_argcount, obj.co_posonlyargcount,
            obj.co_kwonlyargcount, obj.co_nlocals, obj.co_stacksize,
            obj.co_flags, obj.co_code, obj.co_consts, obj.co_names,
            obj.co_varnames, obj.co_filename, obj.co_name,
            obj.co_firstlineno, obj.co_lnotab, obj.co_freevars,
            obj.co_cellvars
        )
    else: # python 3.7 (15 args)
        args = (
            obj.co_argcount, obj.co_kwonlyargcount, obj.co_nlocals,
            obj.co_stacksize, obj.co_flags, obj.co_code, obj.co_consts,
            obj.co_names, obj.co_varnames, obj.co_filename,
            obj.co_name, obj.co_firstlineno, obj.co_lnotab,
            obj.co_freevars, obj.co_cellvars
        )

    pickler.save_reduce(_create_code, args, obj=obj)
    logger.trace(pickler, "# Co")
    return

def _repr_dict(obj):
    """Make a short string representation of a dictionary."""
    return "<%s object at %#012x>" % (type(obj).__name__, id(obj))

@register(dict)
def save_module_dict(pickler, obj):
    if is_dill(pickler, child=False) and obj == pickler._main.__dict__ and \
            not (pickler._session and pickler._first_pass):
        logger.trace(pickler, "D1: %s", _repr_dict(obj)) # obj
        pickler.write(bytes('c__builtin__\n__main__\n', 'UTF-8'))
        logger.trace(pickler, "# D1")
    elif (not is_dill(pickler, child=False)) and (obj == _main_module.__dict__):
        logger.trace(pickler, "D3: %s", _repr_dict(obj)) # obj
        pickler.write(bytes('c__main__\n__dict__\n', 'UTF-8'))  #XXX: works in general?
        logger.trace(pickler, "# D3")
    elif '__name__' in obj and obj != _main_module.__dict__ \
            and type(obj['__name__']) is str \
            and obj is getattr(_import_module(obj['__name__'],True), '__dict__', None):
        logger.trace(pickler, "D4: %s", _repr_dict(obj)) # obj
        pickler.write(bytes('c%s\n__dict__\n' % obj['__name__'], 'UTF-8'))
        logger.trace(pickler, "# D4")
    else:
        logger.trace(pickler, "D2: %s", _repr_dict(obj)) # obj
        if is_dill(pickler, child=False) and pickler._session:
            # we only care about session the first pass thru
            pickler._first_pass = False
        StockPickler.save_dict(pickler, obj)
        logger.trace(pickler, "# D2")
    return


if not OLD310 and MAPPING_PROXY_TRICK:
    def save_dict_view(dicttype):
        def save_dict_view_for_function(func):
            def _save_dict_view(pickler, obj):
                logger.trace(pickler, "Dkvi: <%s>", obj)
                mapping = obj.mapping | _dictproxy_helper_instance
                pickler.save_reduce(func, (mapping,), obj=obj)
                logger.trace(pickler, "# Dkvi")
            return _save_dict_view
        return [
            (funcname, save_dict_view_for_function(getattr(dicttype, funcname)))
            for funcname in ('keys', 'values', 'items')
        ]
else:
    # The following functions are based on 'cloudpickle'
    # https://github.com/cloudpipe/cloudpickle/blob/5d89947288a18029672596a4d719093cc6d5a412/cloudpickle/cloudpickle.py#L922-L940
    # Copyright (c) 2012, Regents of the University of California.
    # Copyright (c) 2009 `PiCloud, Inc. <http://www.picloud.com>`_.
    # License: https://github.com/cloudpipe/cloudpickle/blob/master/LICENSE
    def save_dict_view(dicttype):
        def save_dict_keys(pickler, obj):
            logger.trace(pickler, "Dk: <%s>", obj)
            dict_constructor = _shims.Reduce(dicttype.fromkeys, (list(obj),))
            pickler.save_reduce(dicttype.keys, (dict_constructor,), obj=obj)
            logger.trace(pickler, "# Dk")

        def save_dict_values(pickler, obj):
            logger.trace(pickler, "Dv: <%s>", obj)
            dict_constructor = _shims.Reduce(dicttype, (enumerate(obj),))
            pickler.save_reduce(dicttype.values, (dict_constructor,), obj=obj)
            logger.trace(pickler, "# Dv")

        def save_dict_items(pickler, obj):
            logger.trace(pickler, "Di: <%s>", obj)
            pickler.save_reduce(dicttype.items, (dicttype(obj),), obj=obj)
            logger.trace(pickler, "# Di")

        return (
            ('keys', save_dict_keys),
            ('values', save_dict_values),
            ('items', save_dict_items)
        )

for __dicttype in (
      dict,
      OrderedDict
):
    __obj = __dicttype()
    for __funcname, __savefunc in save_dict_view(__dicttype):
        __tview = type(getattr(__obj, __funcname)())
        if __tview not in Pickler.dispatch:
            Pickler.dispatch[__tview] = __savefunc
del __dicttype, __obj, __funcname, __tview, __savefunc


@register(ClassType)
def save_classobj(pickler, obj): #FIXME: enable pickler._byref
    if not _locate_function(obj, pickler):
        logger.trace(pickler, "C1: %s", obj)
        pickler.save_reduce(ClassType, (obj.__name__, obj.__bases__,
                                        obj.__dict__), obj=obj)
                                       #XXX: or obj.__dict__.copy()), obj=obj) ?
        logger.trace(pickler, "# C1")
    else:
        logger.trace(pickler, "C2: %s", obj)
        name = getattr(obj, '__qualname__', getattr(obj, '__name__', None))
        StockPickler.save_global(pickler, obj, name=name)
        logger.trace(pickler, "# C2")
    return

@register(typing._GenericAlias)
def save_generic_alias(pickler, obj):
    args = obj.__args__
    if type(obj.__reduce__()) is str:
        logger.trace(pickler, "Ga0: %s", obj)
        StockPickler.save_global(pickler, obj, name=obj.__reduce__())
        logger.trace(pickler, "# Ga0")
    elif obj.__origin__ is tuple and (not args or args == ((),)):
        logger.trace(pickler, "Ga1: %s", obj)
        pickler.save_reduce(_create_typing_tuple, (args,), obj=obj)
        logger.trace(pickler, "# Ga1")
    else:
        logger.trace(pickler, "Ga2: %s", obj)
        StockPickler.save_reduce(pickler, *obj.__reduce__(), obj=obj)
        logger.trace(pickler, "# Ga2")
    return

@register(LockType)
def save_lock(pickler, obj):
    logger.trace(pickler, "Lo: %s", obj)
    pickler.save_reduce(_create_lock, (obj.locked(),), obj=obj)
    logger.trace(pickler, "# Lo")
    return

@register(RLockType)
def save_rlock(pickler, obj):
    logger.trace(pickler, "RL: %s", obj)
    r = obj.__repr__() # don't use _release_save as it unlocks the lock
    count = int(r.split('count=')[1].split()[0].rstrip('>'))
    owner = int(r.split('owner=')[1].split()[0])
    pickler.save_reduce(_create_rlock, (count,owner,), obj=obj)
    logger.trace(pickler, "# RL")
    return

#@register(SocketType) #FIXME: causes multiprocess test_pickling FAIL
def save_socket(pickler, obj):
    logger.trace(pickler, "So: %s", obj)
    pickler.save_reduce(*reduce_socket(obj))
    logger.trace(pickler, "# So")
    return

def _save_file(pickler, obj, open_):
    if obj.closed:
        position = 0
    else:
        obj.flush()
        if obj in (sys.__stdout__, sys.__stderr__, sys.__stdin__):
            position = -1
        else:
            position = obj.tell()
    if is_dill(pickler, child=True) and pickler._fmode == FILE_FMODE:
        f = open_(obj.name, "r")
        fdata = f.read()
        f.close()
    else:
        fdata = ""
    if is_dill(pickler, child=True):
        strictio = pickler._strictio
        fmode = pickler._fmode
    else:
        strictio = False
        fmode = 0 # HANDLE_FMODE
    pickler.save_reduce(_create_filehandle, (obj.name, obj.mode, position,
                                             obj.closed, open_, strictio,
                                             fmode, fdata), obj=obj)
    return


@register(FileType) #XXX: in 3.x has buffer=0, needs different _create?
@register(BufferedRandomType)
@register(BufferedReaderType)
@register(BufferedWriterType)
@register(TextWrapperType)
def save_file(pickler, obj):
    logger.trace(pickler, "Fi: %s", obj)
    f = _save_file(pickler, obj, open)
    logger.trace(pickler, "# Fi")
    return f

if PyTextWrapperType:
    @register(PyBufferedRandomType)
    @register(PyBufferedReaderType)
    @register(PyBufferedWriterType)
    @register(PyTextWrapperType)
    def save_file(pickler, obj):
        logger.trace(pickler, "Fi: %s", obj)
        f = _save_file(pickler, obj, _open)
        logger.trace(pickler, "# Fi")
        return f

# The following two functions are based on 'saveCStringIoInput'
# and 'saveCStringIoOutput' from spickle
# Copyright (c) 2011 by science+computing ag
# License: http://www.apache.org/licenses/LICENSE-2.0
if InputType:
    @register(InputType)
    def save_stringi(pickler, obj):
        logger.trace(pickler, "Io: %s", obj)
        if obj.closed:
            value = ''; position = 0
        else:
            value = obj.getvalue(); position = obj.tell()
        pickler.save_reduce(_create_stringi, (value, position, \
                                              obj.closed), obj=obj)
        logger.trace(pickler, "# Io")
        return

    @register(OutputType)
    def save_stringo(pickler, obj):
        logger.trace(pickler, "Io: %s", obj)
        if obj.closed:
            value = ''; position = 0
        else:
            value = obj.getvalue(); position = obj.tell()
        pickler.save_reduce(_create_stringo, (value, position, \
                                              obj.closed), obj=obj)
        logger.trace(pickler, "# Io")
        return

if LRUCacheType is not None:
    from functools import lru_cache
    @register(LRUCacheType)
    def save_lru_cache(pickler, obj):
        logger.trace(pickler, "LRU: %s", obj)
        if OLD39:
            kwargs = obj.cache_info()
            args = (kwargs.maxsize,)
        else:
            kwargs = obj.cache_parameters()
            args = (kwargs['maxsize'], kwargs['typed'])
        if args != lru_cache.__defaults__:
            wrapper = Reduce(lru_cache, args, is_callable=True)
        else:
            wrapper = lru_cache
        pickler.save_reduce(wrapper, (obj.__wrapped__,), obj=obj)
        logger.trace(pickler, "# LRU")
        return

@register(SuperType)
def save_super(pickler, obj):
    logger.trace(pickler, "Su: %s", obj)
    pickler.save_reduce(super, (obj.__thisclass__, obj.__self__), obj=obj)
    logger.trace(pickler, "# Su")
    return

if IS_PYPY:
    @register(MethodType)
    def save_instancemethod0(pickler, obj):
        code = getattr(obj.__func__, '__code__', None)
        if code is not None and type(code) is not CodeType \
              and getattr(obj.__self__, obj.__name__) == obj:
            # Some PyPy builtin functions have no module name
            logger.trace(pickler, "Me2: %s", obj)
            # TODO: verify that this works for all PyPy builtin methods
            pickler.save_reduce(getattr, (obj.__self__, obj.__name__), obj=obj)
            logger.trace(pickler, "# Me2")
            return

        logger.trace(pickler, "Me1: %s", obj)
        pickler.save_reduce(MethodType, (obj.__func__, obj.__self__), obj=obj)
        logger.trace(pickler, "# Me1")
        return
else:
    @register(MethodType)
    def save_instancemethod0(pickler, obj):
        logger.trace(pickler, "Me1: %s", obj)
        pickler.save_reduce(MethodType, (obj.__func__, obj.__self__), obj=obj)
        logger.trace(pickler, "# Me1")
        return

if not IS_PYPY:
    @register(MemberDescriptorType)
    @register(GetSetDescriptorType)
    @register(MethodDescriptorType)
    @register(WrapperDescriptorType)
    @register(ClassMethodDescriptorType)
    def save_wrapper_descriptor(pickler, obj):
        logger.trace(pickler, "Wr: %s", obj)
        pickler.save_reduce(_getattr, (obj.__objclass__, obj.__name__,
                                       obj.__repr__()), obj=obj)
        logger.trace(pickler, "# Wr")
        return
else:
    @register(MemberDescriptorType)
    @register(GetSetDescriptorType)
    def save_wrapper_descriptor(pickler, obj):
        logger.trace(pickler, "Wr: %s", obj)
        pickler.save_reduce(_getattr, (obj.__objclass__, obj.__name__,
                                       obj.__repr__()), obj=obj)
        logger.trace(pickler, "# Wr")
        return

@register(CellType)
def save_cell(pickler, obj):
    try:
        f = obj.cell_contents
    except ValueError: # cell is empty
        logger.trace(pickler, "Ce3: %s", obj)
        # _shims._CELL_EMPTY is defined in _shims.py to support PyPy 2.7.
        # It unpickles to a sentinel object _dill._CELL_EMPTY, also created in
        # _shims.py. This object is not present in Python 3 because the cell's
        # contents can be deleted in newer versions of Python. The reduce object
        # will instead unpickle to None if unpickled in Python 3.

        # When breaking changes are made to dill, (_shims._CELL_EMPTY,) can
        # be replaced by () OR the delattr function can be removed repending on
        # whichever is more convienient.
        pickler.save_reduce(_create_cell, (_shims._CELL_EMPTY,), obj=obj)
        # Call the function _delattr on the cell's cell_contents attribute
        # The result of this function call will be None
        pickler.save_reduce(_shims._delattr, (obj, 'cell_contents'))
        # pop None created by calling _delattr off stack
        pickler.write(POP)
        logger.trace(pickler, "# Ce3")
        return
    if is_dill(pickler, child=True):
        if id(f) in pickler._postproc:
            # Already seen. Add to its postprocessing.
            postproc = pickler._postproc[id(f)]
        else:
            # Haven't seen it. Add to the highest possible object and set its
            # value as late as possible to prevent cycle.
            postproc = next(iter(pickler._postproc.values()), None)
        if postproc is not None:
            logger.trace(pickler, "Ce2: %s", obj)
            # _CELL_REF is defined in _shims.py to support older versions of
            # dill. When breaking changes are made to dill, (_CELL_REF,) can
            # be replaced by ()
            pickler.save_reduce(_create_cell, (_CELL_REF,), obj=obj)
            postproc.append((_shims._setattr, (obj, 'cell_contents', f)))
            logger.trace(pickler, "# Ce2")
            return
    logger.trace(pickler, "Ce1: %s", obj)
    pickler.save_reduce(_create_cell, (f,), obj=obj)
    logger.trace(pickler, "# Ce1")
    return

if MAPPING_PROXY_TRICK:
    @register(DictProxyType)
    def save_dictproxy(pickler, obj):
        logger.trace(pickler, "Mp: %s", _repr_dict(obj)) # obj
        mapping = obj | _dictproxy_helper_instance
        pickler.save_reduce(DictProxyType, (mapping,), obj=obj)
        logger.trace(pickler, "# Mp")
        return
else:
    @register(DictProxyType)
    def save_dictproxy(pickler, obj):
        logger.trace(pickler, "Mp: %s", _repr_dict(obj)) # obj
        pickler.save_reduce(DictProxyType, (obj.copy(),), obj=obj)
        logger.trace(pickler, "# Mp")
        return

@register(SliceType)
def save_slice(pickler, obj):
    logger.trace(pickler, "Sl: %s", obj)
    pickler.save_reduce(slice, (obj.start, obj.stop, obj.step), obj=obj)
    logger.trace(pickler, "# Sl")
    return

@register(XRangeType)
@register(EllipsisType)
@register(NotImplementedType)
def save_singleton(pickler, obj):
    logger.trace(pickler, "Si: %s", obj)
    pickler.save_reduce(_eval_repr, (obj.__repr__(),), obj=obj)
    logger.trace(pickler, "# Si")
    return

def _proxy_helper(obj): # a dead proxy returns a reference to None
    """get memory address of proxy's reference object"""
    _repr = repr(obj)
    try: _str = str(obj)
    except ReferenceError: # it's a dead proxy
        return id(None)
    if _str == _repr: return id(obj) # it's a repr
    try: # either way, it's a proxy from here
        address = int(_str.rstrip('>').split(' at ')[-1], base=16)
    except ValueError: # special case: proxy of a 'type'
        if not IS_PYPY:
            address = int(_repr.rstrip('>').split(' at ')[-1], base=16)
        else:
            objects = iter(gc.get_objects())
            for _obj in objects:
                if repr(_obj) == _str: return id(_obj)
            # all bad below... nothing found so throw ReferenceError
            msg = "Cannot reference object for proxy at '%s'" % id(obj)
            raise ReferenceError(msg)
    return address

def _locate_object(address, module=None):
    """get object located at the given memory address (inverse of id(obj))"""
    special = [None, True, False] #XXX: more...?
    for obj in special:
        if address == id(obj): return obj
    if module:
        objects = iter(module.__dict__.values())
    else: objects = iter(gc.get_objects())
    for obj in objects:
        if address == id(obj): return obj
    # all bad below... nothing found so throw ReferenceError or TypeError
    try: address = hex(address)
    except TypeError:
        raise TypeError("'%s' is not a valid memory address" % str(address))
    raise ReferenceError("Cannot reference object at '%s'" % address)

@register(ReferenceType)
def save_weakref(pickler, obj):
    refobj = obj()
    logger.trace(pickler, "R1: %s", obj)
   #refobj = ctypes.pythonapi.PyWeakref_GetObject(obj) # dead returns "None"
    pickler.save_reduce(_create_weakref, (refobj,), obj=obj)
    logger.trace(pickler, "# R1")
    return

@register(ProxyType)
@register(CallableProxyType)
def save_weakproxy(pickler, obj):
    # Must do string substitution here and use %r to avoid ReferenceError.
    logger.trace(pickler, "R2: %r" % obj)
    refobj = _locate_object(_proxy_helper(obj))
    pickler.save_reduce(_create_weakproxy, (refobj, callable(obj)), obj=obj)
    logger.trace(pickler, "# R2")
    return

def _is_builtin_module(module):
    if not hasattr(module, "__file__"): return True
    if module.__file__ is None: return False
    # If a module file name starts with prefix, it should be a builtin
    # module, so should always be pickled as a reference.
    names = ["base_prefix", "base_exec_prefix", "exec_prefix", "prefix", "real_prefix"]
    rp = os.path.realpath
    # See https://github.com/uqfoundation/dill/issues/566
    return (
        any(
            module.__file__.startswith(getattr(sys, name))
            or rp(module.__file__).startswith(rp(getattr(sys, name)))
            for name in names
            if hasattr(sys, name)
        )
        or module.__file__.endswith(EXTENSION_SUFFIXES)
        or 'site-packages' in module.__file__
    )

def _is_imported_module(module):
    return getattr(module, '__loader__', None) is not None or module in sys.modules.values()

@register(ModuleType)
def save_module(pickler, obj):
    if False: #_use_diff:
        if obj.__name__.split('.', 1)[0] != "dill":
            try:
                changed = diff.whats_changed(obj, seen=pickler._diff_cache)[0]
            except RuntimeError:  # not memorised module, probably part of dill
                pass
            else:
                logger.trace(pickler, "M2: %s with diff", obj)
                logger.info("Diff: %s", changed.keys())
                pickler.save_reduce(_import_module, (obj.__name__,), obj=obj,
                                    state=changed)
                logger.trace(pickler, "# M2")
                return

        logger.trace(pickler, "M1: %s", obj)
        pickler.save_reduce(_import_module, (obj.__name__,), obj=obj)
        logger.trace(pickler, "# M1")
    else:
        builtin_mod = _is_builtin_module(obj)
        is_session_main = is_dill(pickler, child=True) and obj is pickler._main
        if (obj.__name__ not in ("builtins", "dill", "dill._dill") and not builtin_mod
                or is_session_main):
            logger.trace(pickler, "M1: %s", obj)
            # Hack for handling module-type objects in load_module().
            mod_name = obj.__name__ if _is_imported_module(obj) else '__runtime__.%s' % obj.__name__
            # Second references are saved as __builtin__.__main__ in save_module_dict().
            main_dict = obj.__dict__.copy()
            for item in ('__builtins__', '__loader__'):
                main_dict.pop(item, None)
            for item in IPYTHON_SINGLETONS: #pragma: no cover
                if getattr(main_dict.get(item), '__module__', '').startswith('IPython'):
                    del main_dict[item]
            pickler.save_reduce(_import_module, (mod_name,), obj=obj, state=main_dict)
            logger.trace(pickler, "# M1")
        elif obj.__name__ == "dill._dill":
            logger.trace(pickler, "M2: %s", obj)
            pickler.save_global(obj, name="_dill")
            logger.trace(pickler, "# M2")
        else:
            logger.trace(pickler, "M2: %s", obj)
            pickler.save_reduce(_import_module, (obj.__name__,), obj=obj)
            logger.trace(pickler, "# M2")
    return

# The following function is based on '_extract_class_dict' from 'cloudpickle'
# Copyright (c) 2012, Regents of the University of California.
# Copyright (c) 2009 `PiCloud, Inc. <http://www.picloud.com>`_.
# License: https://github.com/cloudpipe/cloudpickle/blob/master/LICENSE
def _get_typedict_type(cls, clsdict, attrs, postproc_list):
    """Retrieve a copy of the dict of a class without the inherited methods"""
    if len(cls.__bases__) == 1:
        inherited_dict = cls.__bases__[0].__dict__
    else:
        inherited_dict = {}
        for base in reversed(cls.__bases__):
            inherited_dict.update(base.__dict__)
    to_remove = []
    for name, value in dict.items(clsdict):
        try:
            base_value = inherited_dict[name]
            if value is base_value:
                to_remove.append(name)
        except KeyError:
            pass
    for name in to_remove:
        dict.pop(clsdict, name)

    if issubclass(type(cls), type):
        clsdict.pop('__dict__', None)
        clsdict.pop('__weakref__', None)
        # clsdict.pop('__prepare__', None)
    return clsdict, attrs

def _get_typedict_abc(obj, _dict, attrs, postproc_list):
    if hasattr(abc, '_get_dump'):
        (registry, _, _, _) = abc._get_dump(obj)
        register = obj.register
        postproc_list.extend((register, (reg(),)) for reg in registry)
    elif hasattr(obj, '_abc_registry'):
        registry = obj._abc_registry
        register = obj.register
        postproc_list.extend((register, (reg,)) for reg in registry)
    else:
        raise PicklingError("Cannot find registry of ABC %s", obj)

    if '_abc_registry' in _dict:
        _dict.pop('_abc_registry', None)
        _dict.pop('_abc_cache', None)
        _dict.pop('_abc_negative_cache', None)
        # _dict.pop('_abc_negative_cache_version', None)
    else:
        _dict.pop('_abc_impl', None)
    return _dict, attrs

@register(TypeType)
def save_type(pickler, obj, postproc_list=None):
    if obj in _typemap:
        logger.trace(pickler, "T1: %s", obj)
        # if obj in _incedental_types:
        #     warnings.warn('Type %r may only exist on this implementation of Python and cannot be unpickled in other implementations.' % (obj,), PicklingWarning)
        pickler.save_reduce(_load_type, (_typemap[obj],), obj=obj)
        logger.trace(pickler, "# T1")
    elif obj.__bases__ == (tuple,) and all([hasattr(obj, attr) for attr in ('_fields','_asdict','_make','_replace')]):
        # special case: namedtuples
        logger.trace(pickler, "T6: %s", obj)

        obj_name = getattr(obj, '__qualname__', getattr(obj, '__name__', None))
        if obj.__name__ != obj_name:
            if postproc_list is None:
                postproc_list = []
            postproc_list.append((setattr, (obj, '__qualname__', obj_name)))

        if not obj._field_defaults:
            _save_with_postproc(pickler, (_create_namedtuple, (obj.__name__, obj._fields, obj.__module__)), obj=obj, postproc_list=postproc_list)
        else:
            defaults = [obj._field_defaults[field] for field in obj._fields if field in obj._field_defaults]
            _save_with_postproc(pickler, (_create_namedtuple, (obj.__name__, obj._fields, obj.__module__, defaults)), obj=obj, postproc_list=postproc_list)
        logger.trace(pickler, "# T6")
        return

    # special cases: NoneType, NotImplementedType, EllipsisType, EnumMeta
    elif obj is type(None):
        logger.trace(pickler, "T7: %s", obj)
        #XXX: pickler.save_reduce(type, (None,), obj=obj)
        pickler.write(GLOBAL + b'__builtin__\nNoneType\n')
        logger.trace(pickler, "# T7")
    elif obj is NotImplementedType:
        logger.trace(pickler, "T7: %s", obj)
        pickler.save_reduce(type, (NotImplemented,), obj=obj)
        logger.trace(pickler, "# T7")
    elif obj is EllipsisType:
        logger.trace(pickler, "T7: %s", obj)
        pickler.save_reduce(type, (Ellipsis,), obj=obj)
        logger.trace(pickler, "# T7")
    elif obj is EnumMeta:
        logger.trace(pickler, "T7: %s", obj)
        pickler.write(GLOBAL + b'enum\nEnumMeta\n')
        logger.trace(pickler, "# T7")

    else:
        _byref = getattr(pickler, '_byref', None)
        obj_recursive = id(obj) in getattr(pickler, '_postproc', ())
        incorrectly_named = not _locate_function(obj, pickler)
        if not _byref and not obj_recursive and incorrectly_named: # not a function, but the name was held over
            if postproc_list is None:
                postproc_list = []

            # thanks to Tom Stepleton pointing out pickler._session unneeded
            logger.trace(pickler, "T2: %s", obj)
            _dict, attrs = _get_typedict_type(obj, obj.__dict__.copy(), None, postproc_list) # copy dict proxy to a dict

           #print (_dict)
           #print ("%s\n%s" % (type(obj), obj.__name__))
           #print ("%s\n%s" % (obj.__bases__, obj.__dict__))
            slots = _dict.get('__slots__', ())
            if type(slots) == str:
                # __slots__ accepts a single string
                slots = (slots,)

            for name in slots:
                _dict.pop(name, None)

            if isinstance(obj, abc.ABCMeta):
                logger.trace(pickler, "ABC: %s", obj)
                _dict, attrs = _get_typedict_abc(obj, _dict, attrs, postproc_list)
                logger.trace(pickler, "# ABC")

            qualname = getattr(obj, '__qualname__', None)
            if attrs is not None:
                for k, v in attrs.items():
                    postproc_list.append((setattr, (obj, k, v)))
                # TODO: Consider using the state argument to save_reduce?
            if qualname is not None:
                postproc_list.append((setattr, (obj, '__qualname__', qualname)))

            if not hasattr(obj, '__orig_bases__'):
                _save_with_postproc(pickler, (_create_type, (
                    type(obj), obj.__name__, obj.__bases__, _dict
                )), obj=obj, postproc_list=postproc_list)
            else:
                # This case will always work, but might be overkill.
                _metadict = {
                    'metaclass': type(obj)
                }

                if _dict:
                    _dict_update = PartialType(_setitems, source=_dict)
                else:
                    _dict_update = None

                _save_with_postproc(pickler, (new_class, (
                    obj.__name__, obj.__orig_bases__, _metadict, _dict_update
                )), obj=obj, postproc_list=postproc_list)
            logger.trace(pickler, "# T2")
        else:
            obj_name = getattr(obj, '__qualname__', getattr(obj, '__name__', None))
            logger.trace(pickler, "T4: %s", obj)
            if incorrectly_named:
                warnings.warn(
                    "Cannot locate reference to %r." % (obj,),
                    PicklingWarning,
                    stacklevel=3,
                )
            if obj_recursive:
                warnings.warn(
                    "Cannot pickle %r: %s.%s has recursive self-references that "
                    "trigger a RecursionError." % (obj, obj.__module__, obj_name),
                    PicklingWarning,
                    stacklevel=3,
                )
           #print (obj.__dict__)
           #print ("%s\n%s" % (type(obj), obj.__name__))
           #print ("%s\n%s" % (obj.__bases__, obj.__dict__))
            StockPickler.save_global(pickler, obj, name=obj_name)
            logger.trace(pickler, "# T4")
    return

@register(property)
@register(abc.abstractproperty)
def save_property(pickler, obj):
    logger.trace(pickler, "Pr: %s", obj)
    pickler.save_reduce(type(obj), (obj.fget, obj.fset, obj.fdel, obj.__doc__),
                        obj=obj)
    logger.trace(pickler, "# Pr")

@register(staticmethod)
@register(classmethod)
@register(abc.abstractstaticmethod)
@register(abc.abstractclassmethod)
def save_classmethod(pickler, obj):
    logger.trace(pickler, "Cm: %s", obj)
    orig_func = obj.__func__

    # if type(obj.__dict__) is dict:
    #     if obj.__dict__:
    #         state = obj.__dict__
    #     else:
    #         state = None
    # else:
    #     state = (None, {'__dict__', obj.__dict__})

    pickler.save_reduce(type(obj), (orig_func,), obj=obj)
    logger.trace(pickler, "# Cm")

@register(FunctionType)
def save_function(pickler, obj):
    if not _locate_function(obj, pickler):
        if type(obj.__code__) is not CodeType:
            # Some PyPy builtin functions have no module name, and thus are not
            # able to be located
            module_name = getattr(obj, '__module__', None)
            if module_name is None:
                module_name = __builtin__.__name__
            module = _import_module(module_name, safe=True)
            _pypy_builtin = False
            try:
                found, _ = _getattribute(module, obj.__qualname__)
                if getattr(found, '__func__', None) is obj:
                    _pypy_builtin = True
            except AttributeError:
                pass

            if _pypy_builtin:
                logger.trace(pickler, "F3: %s", obj)
                pickler.save_reduce(getattr, (found, '__func__'), obj=obj)
                logger.trace(pickler, "# F3")
                return

        logger.trace(pickler, "F1: %s", obj)
        _recurse = getattr(pickler, '_recurse', None)
        _postproc = getattr(pickler, '_postproc', None)
        _main_modified = getattr(pickler, '_main_modified', None)
        _original_main = getattr(pickler, '_original_main', __builtin__)#'None'
        postproc_list = []
        if _recurse:
            # recurse to get all globals referred to by obj
            from .detect import globalvars
            globs_copy = globalvars(obj, recurse=True, builtin=True)

            # Add the name of the module to the globs dictionary to prevent
            # the duplication of the dictionary. Pickle the unpopulated
            # globals dictionary and set the remaining items after the function
            # is created to correctly handle recursion.
            globs = {'__name__': obj.__module__}
        else:
            globs_copy = obj.__globals__

            # If the globals is the __dict__ from the module being saved as a
            # session, substitute it by the dictionary being actually saved.
            if _main_modified and globs_copy is _original_main.__dict__:
                globs_copy = getattr(pickler, '_main', _original_main).__dict__
                globs = globs_copy
            # If the globals is a module __dict__, do not save it in the pickle.
            elif globs_copy is not None and obj.__module__ is not None and \
                    getattr(_import_module(obj.__module__, True), '__dict__', None) is globs_copy:
                globs = globs_copy
            else:
                globs = {'__name__': obj.__module__}

        if globs_copy is not None and globs is not globs_copy:
            # In the case that the globals are copied, we need to ensure that
            # the globals dictionary is updated when all objects in the
            # dictionary are already created.
            glob_ids = {id(g) for g in globs_copy.values()}
            for stack_element in _postproc:
                if stack_element in glob_ids:
                    _postproc[stack_element].append((_setitems, (globs, globs_copy)))
                    break
            else:
                postproc_list.append((_setitems, (globs, globs_copy)))

        closure = obj.__closure__
        state_dict = {}
        for fattrname in ('__doc__', '__kwdefaults__', '__annotations__'):
            fattr = getattr(obj, fattrname, None)
            if fattr is not None:
                state_dict[fattrname] = fattr
        if obj.__qualname__ != obj.__name__:
            state_dict['__qualname__'] = obj.__qualname__
        if '__name__' not in globs or obj.__module__ != globs['__name__']:
            state_dict['__module__'] = obj.__module__

        state = obj.__dict__
        if type(state) is not dict:
            state_dict['__dict__'] = state
            state = None
        if state_dict:
            state = state, state_dict

        _save_with_postproc(pickler, (_create_function, (
                obj.__code__, globs, obj.__name__, obj.__defaults__,
                closure
        ), state), obj=obj, postproc_list=postproc_list)

        # Lift closure cell update to earliest function (#458)
        if _postproc:
            topmost_postproc = next(iter(_postproc.values()), None)
            if closure and topmost_postproc:
                for cell in closure:
                    possible_postproc = (setattr, (cell, 'cell_contents', obj))
                    try:
                        topmost_postproc.remove(possible_postproc)
                    except ValueError:
                        continue

                    # Change the value of the cell
                    pickler.save_reduce(*possible_postproc)
                    # pop None created by calling preprocessing step off stack
                    pickler.write(POP)

        logger.trace(pickler, "# F1")
    else:
        logger.trace(pickler, "F2: %s", obj)
        name = getattr(obj, '__qualname__', getattr(obj, '__name__', None))
        StockPickler.save_global(pickler, obj, name=name)
        logger.trace(pickler, "# F2")
    return

if HAS_CTYPES and hasattr(ctypes, 'pythonapi'):
    _PyCapsule_New = ctypes.pythonapi.PyCapsule_New
    _PyCapsule_New.argtypes = (ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p)
    _PyCapsule_New.restype = ctypes.py_object
    _PyCapsule_GetPointer = ctypes.pythonapi.PyCapsule_GetPointer
    _PyCapsule_GetPointer.argtypes = (ctypes.py_object, ctypes.c_char_p)
    _PyCapsule_GetPointer.restype = ctypes.c_void_p
    _PyCapsule_GetDestructor = ctypes.pythonapi.PyCapsule_GetDestructor
    _PyCapsule_GetDestructor.argtypes = (ctypes.py_object,)
    _PyCapsule_GetDestructor.restype = ctypes.c_void_p
    _PyCapsule_GetContext = ctypes.pythonapi.PyCapsule_GetContext
    _PyCapsule_GetContext.argtypes = (ctypes.py_object,)
    _PyCapsule_GetContext.restype = ctypes.c_void_p
    _PyCapsule_GetName = ctypes.pythonapi.PyCapsule_GetName
    _PyCapsule_GetName.argtypes = (ctypes.py_object,)
    _PyCapsule_GetName.restype = ctypes.c_char_p
    _PyCapsule_IsValid = ctypes.pythonapi.PyCapsule_IsValid
    _PyCapsule_IsValid.argtypes = (ctypes.py_object, ctypes.c_char_p)
    _PyCapsule_IsValid.restype = ctypes.c_bool
    _PyCapsule_SetContext = ctypes.pythonapi.PyCapsule_SetContext
    _PyCapsule_SetContext.argtypes = (ctypes.py_object, ctypes.c_void_p)
    _PyCapsule_SetDestructor = ctypes.pythonapi.PyCapsule_SetDestructor
    _PyCapsule_SetDestructor.argtypes = (ctypes.py_object, ctypes.c_void_p)
    _PyCapsule_SetName = ctypes.pythonapi.PyCapsule_SetName
    _PyCapsule_SetName.argtypes = (ctypes.py_object, ctypes.c_char_p)
    _PyCapsule_SetPointer = ctypes.pythonapi.PyCapsule_SetPointer
    _PyCapsule_SetPointer.argtypes = (ctypes.py_object, ctypes.c_void_p)
    _testcapsule = _PyCapsule_New(
        ctypes.cast(_PyCapsule_New, ctypes.c_void_p),
        ctypes.create_string_buffer(b'dill._dill._testcapsule'),
        None
    )
    PyCapsuleType = type(_testcapsule)
    @register(PyCapsuleType)
    def save_capsule(pickler, obj):
        logger.trace(pickler, "Cap: %s", obj)
        name = _PyCapsule_GetName(obj)
        #warnings.warn('Pickling a PyCapsule (%s) does not pickle any C data structures and could cause segmentation faults or other memory errors when unpickling.' % (name,), PicklingWarning)
        pointer = _PyCapsule_GetPointer(obj, name)
        context = _PyCapsule_GetContext(obj)
        destructor = _PyCapsule_GetDestructor(obj)
        pickler.save_reduce(_create_capsule, (pointer, name, context, destructor), obj=obj)
        logger.trace(pickler, "# Cap")
    _incedental_reverse_typemap['PyCapsuleType'] = PyCapsuleType
    _reverse_typemap['PyCapsuleType'] = PyCapsuleType
    _incedental_types.add(PyCapsuleType)
else:
    _testcapsule = None


#############################
# A quick fix for issue #500
# This should be removed when a better solution is found.

if hasattr(dataclasses, "_HAS_DEFAULT_FACTORY_CLASS"):
    @register(dataclasses._HAS_DEFAULT_FACTORY_CLASS)
    def save_dataclasses_HAS_DEFAULT_FACTORY_CLASS(pickler, obj):
        logger.trace(pickler, "DcHDF: %s", obj)
        pickler.write(GLOBAL + b"dataclasses\n_HAS_DEFAULT_FACTORY\n")
        logger.trace(pickler, "# DcHDF")

if hasattr(dataclasses, "MISSING"):
    @register(type(dataclasses.MISSING))
    def save_dataclasses_MISSING_TYPE(pickler, obj):
        logger.trace(pickler, "DcM: %s", obj)
        pickler.write(GLOBAL + b"dataclasses\nMISSING\n")
        logger.trace(pickler, "# DcM")

if hasattr(dataclasses, "KW_ONLY"):
    @register(type(dataclasses.KW_ONLY))
    def save_dataclasses_KW_ONLY_TYPE(pickler, obj):
        logger.trace(pickler, "DcKWO: %s", obj)
        pickler.write(GLOBAL + b"dataclasses\nKW_ONLY\n")
        logger.trace(pickler, "# DcKWO")

if hasattr(dataclasses, "_FIELD_BASE"):
    @register(dataclasses._FIELD_BASE)
    def save_dataclasses_FIELD_BASE(pickler, obj):
        logger.trace(pickler, "DcFB: %s", obj)
        pickler.write(GLOBAL + b"dataclasses\n" + obj.name.encode() + b"\n")
        logger.trace(pickler, "# DcFB")

#############################

# quick sanity checking
def pickles(obj,exact=False,safe=False,**kwds):
    """
    Quick check if object pickles with dill.

    If *exact=True* then an equality test is done to check if the reconstructed
    object matches the original object.

    If *safe=True* then any exception will raised in copy signal that the
    object is not picklable, otherwise only pickling errors will be trapped.

    Additional keyword arguments are as :func:`dumps` and :func:`loads`.
    """
    if safe: exceptions = (Exception,) # RuntimeError, ValueError
    else:
        exceptions = (TypeError, AssertionError, NotImplementedError, PicklingError, UnpicklingError)
    try:
        pik = copy(obj, **kwds)
        #FIXME: should check types match first, then check content if "exact"
        try:
            #FIXME: should be "(pik == obj).all()" for numpy comparison, though that'll fail if shapes differ
            result = bool(pik.all() == obj.all())
        except (AttributeError, TypeError):
            warnings.filterwarnings('ignore') #FIXME: be specific
            result = pik == obj
            if warnings.filters: del warnings.filters[0]
        if hasattr(result, 'toarray'): # for unusual types like sparse matrix
            result = result.toarray().all()
        if result: return True
        if not exact:
            result = type(pik) == type(obj)
            if result: return result
            # class instances might have been dumped with byref=False
            return repr(type(pik)) == repr(type(obj)) #XXX: InstanceType?
        return False
    except exceptions:
        return False

def check(obj, *args, **kwds):
    """
    Check pickling of an object across another process.

    *python* is the path to the python interpreter (defaults to sys.executable)

    Set *verbose=True* to print the unpickled object in the other process.

    Additional keyword arguments are as :func:`dumps` and :func:`loads`.
    """
   # == undocumented ==
   # python -- the string path or executable name of the selected python
   # verbose -- if True, be verbose about printing warning messages
   # all other args and kwds are passed to dill.dumps #FIXME: ignore on load
    verbose = kwds.pop('verbose', False)
    python = kwds.pop('python', None)
    if python is None:
        import sys
        python = sys.executable
    # type check
    isinstance(python, str)
    import subprocess
    fail = True
    try:
        _obj = dumps(obj, *args, **kwds)
        fail = False
    finally:
        if fail and verbose:
            print("DUMP FAILED")
    #FIXME: fails if python interpreter path contains spaces
    # Use the following instead (which also processes the 'ignore' keyword):
    #    ignore = kwds.pop('ignore', None)
    #    unpickle = "dill.loads(%s, ignore=%s)"%(repr(_obj), repr(ignore))
    #    cmd = [python, "-c", "import dill; print(%s)"%unpickle]
    #    msg = "SUCCESS" if not subprocess.call(cmd) else "LOAD FAILED"
    msg = "%s -c import dill; print(dill.loads(%s))" % (python, repr(_obj))
    msg = "SUCCESS" if not subprocess.call(msg.split(None,2)) else "LOAD FAILED"
    if verbose:
        print(msg)
    return

# use to protect against missing attributes
def is_dill(pickler, child=None):
    "check the dill-ness of your pickler"
    if child is False or not hasattr(pickler.__class__, 'mro'):
        return 'dill' in pickler.__module__
    return Pickler in pickler.__class__.mro()

def _extend():
    """extend pickle with all of dill's registered types"""
    # need to have pickle not choke on _main_module?  use is_dill(pickler)
    for t,func in Pickler.dispatch.items():
        try:
            StockPickler.dispatch[t] = func
        except Exception: #TypeError, PicklingError, UnpicklingError
            logger.trace(pickler, "skip: %s", t)
    return

del diff, _use_diff, use_diff

# EOF

back/venvmsg/Lib/site-packages/dill/_dill.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
#!/usr/bin/env python
"""
test dill's ability to pickle abstract base class objects
"""
import dill
import abc
from abc import ABC
import warnings

from types import FunctionType

dill.settings['recurse'] = True

class OneTwoThree(ABC):
    @abc.abstractmethod
    def foo(self):
        """A method"""
        pass

    @property
    @abc.abstractmethod
    def bar(self):
        """Property getter"""
        pass

    @bar.setter
    @abc.abstractmethod
    def bar(self, value):
        """Property setter"""
        pass

    @classmethod
    @abc.abstractmethod
    def cfoo(cls):
        """Class method"""
        pass

    @staticmethod
    @abc.abstractmethod
    def sfoo():
        """Static method"""
        pass

class EasyAsAbc(OneTwoThree):
    def __init__(self):
        self._bar = None

    def foo(self):
        return "Instance Method FOO"

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    @classmethod
    def cfoo(cls):
        return "Class Method CFOO"

    @staticmethod
    def sfoo():
        return "Static Method SFOO"

def test_abc_non_local():
    assert dill.copy(OneTwoThree) is not OneTwoThree
    assert dill.copy(EasyAsAbc) is not EasyAsAbc

    with warnings.catch_warnings():
        warnings.simplefilter("ignore", dill.PicklingWarning)
        assert dill.copy(OneTwoThree, byref=True) is OneTwoThree
        assert dill.copy(EasyAsAbc, byref=True) is EasyAsAbc

    instance = EasyAsAbc()
    # Set a property that StockPickle can't preserve
    instance.bar = lambda x: x**2
    depickled = dill.copy(instance)
    assert type(depickled) is not type(instance)
    assert type(depickled.bar) is FunctionType
    assert depickled.bar(3) == 9
    assert depickled.sfoo() == "Static Method SFOO"
    assert depickled.cfoo() == "Class Method CFOO"
    assert depickled.foo() == "Instance Method FOO"

def test_abc_local():
    """
    Test using locally scoped ABC class
    """
    class LocalABC(ABC):
        @abc.abstractmethod
        def foo(self):
            pass

        def baz(self):
            return repr(self)

    labc = dill.copy(LocalABC)
    assert labc is not LocalABC
    assert type(labc) is type(LocalABC)
    # TODO should work like it does for non local classes
    # <class '__main__.LocalABC'>
    # <class '__main__.test_abc_local.<locals>.LocalABC'>

    class Real(labc):
        def foo(self):
            return "True!"

        def baz(self):
            return "My " + super(Real, self).baz()

    real = Real()
    assert real.foo() == "True!"

    try:
        labc()
    except TypeError as e:
        # Expected error
        pass
    else:
        print('Failed to raise type error')
        assert False

    labc2, pik = dill.copy((labc, Real()))
    assert 'Real' == type(pik).__name__
    assert '.Real' in type(pik).__qualname__
    assert type(pik) is not Real
    assert labc2 is not LocalABC
    assert labc2 is not labc
    assert isinstance(pik, labc2)
    assert not isinstance(pik, labc)
    assert not isinstance(pik, LocalABC)
    assert pik.baz() == "My " + repr(pik)

def test_meta_local_no_cache():
    """
    Test calling metaclass and cache registration
    """
    LocalMetaABC = abc.ABCMeta('LocalMetaABC', (), {})

    class ClassyClass:
        pass

    class KlassyClass:
      pass

    LocalMetaABC.register(ClassyClass)

    assert not issubclass(KlassyClass, LocalMetaABC)
    assert issubclass(ClassyClass, LocalMetaABC)

    res = dill.dumps((LocalMetaABC, ClassyClass, KlassyClass))

    lmabc, cc, kc = dill.loads(res)
    assert type(lmabc) == type(LocalMetaABC)
    assert not issubclass(kc, lmabc)
    assert issubclass(cc, lmabc)

if __name__ == '__main__':
    test_abc_non_local()
    test_abc_local()
    test_meta_local_no_cache()

back/venvmsg/Lib/site-packages/dill/tests/test_abc.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
#!/usr/bin/env python
#
# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
# Copyright (c) 2008-2016 California Institute of Technology.
# Copyright (c) 2016-2023 The Uncertainty Quantification Foundation.
# License: 3-clause BSD.  The full license text is available at:
#  - https://github.com/uqfoundation/dill/blob/master/LICENSE

import dill
from enum import EnumMeta
import sys
dill.settings['recurse'] = True

# test classdefs
class _class:
    def _method(self):
        pass
    def ok(self):
        return True

class _class2:
    def __call__(self):
        pass
    def ok(self):
        return True

class _newclass(object):
    def _method(self):
        pass
    def ok(self):
        return True

class _newclass2(object):
    def __call__(self):
        pass
    def ok(self):
        return True

class _meta(type):
    pass

def __call__(self):
    pass
def ok(self):
    return True

_mclass = _meta("_mclass", (object,), {"__call__": __call__, "ok": ok})

del __call__
del ok

o = _class()
oc = _class2()
n = _newclass()
nc = _newclass2()
m = _mclass()

if sys.hexversion < 0x03090000:
    import typing
    class customIntList(typing.List[int]):
        pass
else:
    class customIntList(list[int]):
        pass

# test pickles for class instances
def test_class_instances():
    assert dill.pickles(o)
    assert dill.pickles(oc)
    assert dill.pickles(n)
    assert dill.pickles(nc)
    assert dill.pickles(m)

def test_class_objects():
    clslist = [_class,_class2,_newclass,_newclass2,_mclass]
    objlist = [o,oc,n,nc,m]
    _clslist = [dill.dumps(obj) for obj in clslist]
    _objlist = [dill.dumps(obj) for obj in objlist]

    for obj in clslist:
        globals().pop(obj.__name__)
    del clslist
    for obj in ['o','oc','n','nc']:
        globals().pop(obj)
    del objlist
    del obj

    for obj,cls in zip(_objlist,_clslist):
        _cls = dill.loads(cls)
        _obj = dill.loads(obj)
        assert _obj.ok()
        assert _cls.ok(_cls())
        if _cls.__name__ == "_mclass":
            assert type(_cls).__name__ == "_meta"

# test NoneType
def test_specialtypes():
    assert dill.pickles(type(None))
    assert dill.pickles(type(NotImplemented))
    assert dill.pickles(type(Ellipsis))
    assert dill.pickles(type(EnumMeta))

from collections import namedtuple
Z = namedtuple("Z", ['a','b'])
Zi = Z(0,1)
X = namedtuple("Y", ['a','b'])
X.__name__ = "X"
X.__qualname__ = "X" #XXX: name must 'match' or fails to pickle
Xi = X(0,1)
Bad = namedtuple("FakeName", ['a','b'])
Badi = Bad(0,1)
Defaults = namedtuple('Defaults', ['x', 'y'], defaults=[1])
Defaultsi = Defaults(2)

# test namedtuple
def test_namedtuple():
    assert Z is dill.loads(dill.dumps(Z))
    assert Zi == dill.loads(dill.dumps(Zi))
    assert X is dill.loads(dill.dumps(X))
    assert Xi == dill.loads(dill.dumps(Xi))
    assert Defaults is dill.loads(dill.dumps(Defaults))
    assert Defaultsi == dill.loads(dill.dumps(Defaultsi))
    assert Bad is not dill.loads(dill.dumps(Bad))
    assert Bad._fields == dill.loads(dill.dumps(Bad))._fields
    assert tuple(Badi) == tuple(dill.loads(dill.dumps(Badi)))

    class A:
        class B(namedtuple("C", ["one", "two"])):
            '''docstring'''
        B.__module__ = 'testing'

    a = A()
    assert dill.copy(a)

    assert dill.copy(A.B).__name__ == 'B'
    assert dill.copy(A.B).__qualname__.endswith('.<locals>.A.B')
    assert dill.copy(A.B).__doc__ == 'docstring'
    assert dill.copy(A.B).__module__ == 'testing'

    from typing import NamedTuple

    def A():
        class B(NamedTuple):
            x: int
        return B

    assert type(dill.copy(A()(8))).__qualname__ == type(A()(8)).__qualname__

def test_dtype():
    try:
        import numpy as np

        dti = np.dtype('int')
        assert np.dtype == dill.copy(np.dtype)
        assert dti == dill.copy(dti)
    except ImportError: pass


def test_array_nested():
    try:
        import numpy as np

        x = np.array([1])
        y = (x,)
        assert y == dill.copy(y)

    except ImportError: pass


def test_array_subclass():
    try:
        import numpy as np

        class TestArray(np.ndarray):
            def __new__(cls, input_array, color):
                obj = np.asarray(input_array).view(cls)
                obj.color = color
                return obj
            def __array_finalize__(self, obj):
                if obj is None:
                    return
                if isinstance(obj, type(self)):
                    self.color = obj.color
            def __getnewargs__(self):
                return np.asarray(self), self.color

        a1 = TestArray(np.zeros(100), color='green')
        if not dill._dill.IS_PYPY:
            assert dill.pickles(a1)
            assert a1.__dict__ == dill.copy(a1).__dict__

        a2 = a1[0:9]
        if not dill._dill.IS_PYPY:
            assert dill.pickles(a2)
            assert a2.__dict__ == dill.copy(a2).__dict__

        class TestArray2(np.ndarray):
            color = 'blue'

        a3 = TestArray2([1,2,3,4,5])
        a3.color = 'green'
        if not dill._dill.IS_PYPY:
            assert dill.pickles(a3)
            assert a3.__dict__ == dill.copy(a3).__dict__

    except ImportError: pass


def test_method_decorator():
    class A(object):
      @classmethod
      def test(cls):
        pass

    a = A()

    res = dill.dumps(a)
    new_obj = dill.loads(res)
    new_obj.__class__.test()

# test slots
class Y(object):
  __slots__ = ('y', '__weakref__')
  def __init__(self, y):
    self.y = y

value = 123
y = Y(value)

class Y2(object):
  __slots__ = 'y'
  def __init__(self, y):
    self.y = y

def test_slots():
    assert dill.pickles(Y)
    assert dill.pickles(y)
    assert dill.pickles(Y.y)
    assert dill.copy(y).y == value
    assert dill.copy(Y2(value)).y == value

def test_origbases():
    assert dill.copy(customIntList).__orig_bases__ == customIntList.__orig_bases__

def test_attr():
    import attr
    @attr.s
    class A:
        a = attr.ib()

    v = A(1)
    assert dill.copy(v) == v

def test_metaclass():
    class metaclass_with_new(type):
        def __new__(mcls, name, bases, ns, **kwds):
            cls = super().__new__(mcls, name, bases, ns, **kwds)
            assert mcls is not None
            assert cls.method(mcls)
            return cls
        def method(cls, mcls):
            return isinstance(cls, mcls)

    l = locals()
    exec("""class subclass_with_new(metaclass=metaclass_with_new):
        def __new__(cls):
            self = super().__new__(cls)
            return self""", None, l)
    subclass_with_new = l['subclass_with_new']

    assert dill.copy(subclass_with_new())

def test_enummeta():
    from http import HTTPStatus
    import enum
    assert dill.copy(HTTPStatus.OK) is HTTPStatus.OK
    assert dill.copy(enum.EnumMeta) is enum.EnumMeta

if __name__ == '__main__':
    test_class_instances()
    test_class_objects()
    test_specialtypes()
    test_namedtuple()
    test_dtype()
    test_array_nested()
    test_array_subclass()
    test_method_decorator()
    test_slots()
    test_origbases()
    test_metaclass()
    test_enummeta()

back/venvmsg/Lib/site-packages/dill/tests/test_classdef.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""
Settings and configuration for Django.

Read values from the module specified by the DJANGO_SETTINGS_MODULE environment
variable, and then from django.conf.global_settings; see the global_settings.py
for a list of all possible variables.
"""

import importlib
import os
import time
import traceback
import warnings
from pathlib import Path

import django
from django.conf import global_settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.deprecation import RemovedInDjango50Warning, RemovedInDjango51Warning
from django.utils.functional import LazyObject, empty

ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
DEFAULT_STORAGE_ALIAS = "default"
STATICFILES_STORAGE_ALIAS = "staticfiles"

# RemovedInDjango50Warning
USE_DEPRECATED_PYTZ_DEPRECATED_MSG = (
    "The USE_DEPRECATED_PYTZ setting, and support for pytz timezones is "
    "deprecated in favor of the stdlib zoneinfo module. Please update your "
    "code to use zoneinfo and remove the USE_DEPRECATED_PYTZ setting."
)

USE_L10N_DEPRECATED_MSG = (
    "The USE_L10N setting is deprecated. Starting with Django 5.0, localized "
    "formatting of data will always be enabled. For example Django will "
    "display numbers and dates using the format of the current locale."
)

CSRF_COOKIE_MASKED_DEPRECATED_MSG = (
    "The CSRF_COOKIE_MASKED transitional setting is deprecated. Support for "
    "it will be removed in Django 5.0."
)

DEFAULT_FILE_STORAGE_DEPRECATED_MSG = (
    "The DEFAULT_FILE_STORAGE setting is deprecated. Use STORAGES instead."
)

STATICFILES_STORAGE_DEPRECATED_MSG = (
    "The STATICFILES_STORAGE setting is deprecated. Use STORAGES instead."
)


class SettingsReference(str):
    """
    String subclass which references a current settings value. It's treated as
    the value in memory but serializes to a settings.NAME attribute reference.
    """

    def __new__(self, value, setting_name):
        return str.__new__(self, value)

    def __init__(self, value, setting_name):
        self.setting_name = setting_name


class LazySettings(LazyObject):
    """
    A lazy proxy for either global Django settings or a custom settings object.
    The user can manually configure settings prior to using them. Otherwise,
    Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE.
    """

    def _setup(self, name=None):
        """
        Load the settings module pointed to by the environment variable. This
        is used the first time settings are needed, if the user hasn't
        configured settings manually.
        """
        settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
        if not settings_module:
            desc = ("setting %s" % name) if name else "settings"
            raise ImproperlyConfigured(
                "Requested %s, but settings are not configured. "
                "You must either define the environment variable %s "
                "or call settings.configure() before accessing settings."
                % (desc, ENVIRONMENT_VARIABLE)
            )

        self._wrapped = Settings(settings_module)

    def __repr__(self):
        # Hardcode the class name as otherwise it yields 'Settings'.
        if self._wrapped is empty:
            return "<LazySettings [Unevaluated]>"
        return '<LazySettings "%(settings_module)s">' % {
            "settings_module": self._wrapped.SETTINGS_MODULE,
        }

    def __getattr__(self, name):
        """Return the value of a setting and cache it in self.__dict__."""
        if (_wrapped := self._wrapped) is empty:
            self._setup(name)
            _wrapped = self._wrapped
        val = getattr(_wrapped, name)

        # Special case some settings which require further modification.
        # This is done here for performance reasons so the modified value is cached.
        if name in {"MEDIA_URL", "STATIC_URL"} and val is not None:
            val = self._add_script_prefix(val)
        elif name == "SECRET_KEY" and not val:
            raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.")

        self.__dict__[name] = val
        return val

    def __setattr__(self, name, value):
        """
        Set the value of setting. Clear all cached values if _wrapped changes
        (@override_settings does this) or clear single values when set.
        """
        if name == "_wrapped":
            self.__dict__.clear()
        else:
            self.__dict__.pop(name, None)
        super().__setattr__(name, value)

    def __delattr__(self, name):
        """Delete a setting and clear it from cache if needed."""
        super().__delattr__(name)
        self.__dict__.pop(name, None)

    def configure(self, default_settings=global_settings, **options):
        """
        Called to manually configure the settings. The 'default_settings'
        parameter sets where to retrieve any unspecified values from (its
        argument must support attribute access (__getattr__)).
        """
        if self._wrapped is not empty:
            raise RuntimeError("Settings already configured.")
        holder = UserSettingsHolder(default_settings)
        for name, value in options.items():
            if not name.isupper():
                raise TypeError("Setting %r must be uppercase." % name)
            setattr(holder, name, value)
        self._wrapped = holder

    @staticmethod
    def _add_script_prefix(value):
        """
        Add SCRIPT_NAME prefix to relative paths.

        Useful when the app is being served at a subpath and manually prefixing
        subpath to STATIC_URL and MEDIA_URL in settings is inconvenient.
        """
        # Don't apply prefix to absolute paths and URLs.
        if value.startswith(("http://", "https://", "/")):
            return value
        from django.urls import get_script_prefix

        return "%s%s" % (get_script_prefix(), value)

    @property
    def configured(self):
        """Return True if the settings have already been configured."""
        return self._wrapped is not empty

    def _show_deprecation_warning(self, message, category):
        stack = traceback.extract_stack()
        # Show a warning if the setting is used outside of Django.
        # Stack index: -1 this line, -2 the property, -3 the
        # LazyObject __getattribute__(), -4 the caller.
        filename, _, _, _ = stack[-4]
        if not filename.startswith(os.path.dirname(django.__file__)):
            warnings.warn(message, category, stacklevel=2)

    @property
    def USE_L10N(self):
        self._show_deprecation_warning(
            USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning
        )
        return self.__getattr__("USE_L10N")

    # RemovedInDjango50Warning.
    @property
    def _USE_L10N_INTERNAL(self):
        # Special hook to avoid checking a traceback in internal use on hot
        # paths.
        return self.__getattr__("USE_L10N")

    # RemovedInDjango51Warning.
    @property
    def DEFAULT_FILE_STORAGE(self):
        self._show_deprecation_warning(
            DEFAULT_FILE_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning
        )
        return self.__getattr__("DEFAULT_FILE_STORAGE")

    # RemovedInDjango51Warning.
    @property
    def STATICFILES_STORAGE(self):
        self._show_deprecation_warning(
            STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning
        )
        return self.__getattr__("STATICFILES_STORAGE")


class Settings:
    def __init__(self, settings_module):
        # update this dict from global settings (but only for ALL_CAPS settings)
        for setting in dir(global_settings):
            if setting.isupper():
                setattr(self, setting, getattr(global_settings, setting))

        # store the settings module in case someone later cares
        self.SETTINGS_MODULE = settings_module

        mod = importlib.import_module(self.SETTINGS_MODULE)

        tuple_settings = (
            "ALLOWED_HOSTS",
            "INSTALLED_APPS",
            "TEMPLATE_DIRS",
            "LOCALE_PATHS",
            "SECRET_KEY_FALLBACKS",
        )
        self._explicit_settings = set()
        for setting in dir(mod):
            if setting.isupper():
                setting_value = getattr(mod, setting)

                if setting in tuple_settings and not isinstance(
                    setting_value, (list, tuple)
                ):
                    raise ImproperlyConfigured(
                        "The %s setting must be a list or a tuple." % setting
                    )
                setattr(self, setting, setting_value)
                self._explicit_settings.add(setting)

        if self.USE_TZ is False and not self.is_overridden("USE_TZ"):
            warnings.warn(
                "The default value of USE_TZ will change from False to True "
                "in Django 5.0. Set USE_TZ to False in your project settings "
                "if you want to keep the current default behavior.",
                category=RemovedInDjango50Warning,
            )

        if self.is_overridden("USE_DEPRECATED_PYTZ"):
            warnings.warn(USE_DEPRECATED_PYTZ_DEPRECATED_MSG, RemovedInDjango50Warning)

        if self.is_overridden("CSRF_COOKIE_MASKED"):
            warnings.warn(CSRF_COOKIE_MASKED_DEPRECATED_MSG, RemovedInDjango50Warning)

        if hasattr(time, "tzset") and self.TIME_ZONE:
            # When we can, attempt to validate the timezone. If we can't find
            # this file, no check happens and it's harmless.
            zoneinfo_root = Path("/usr/share/zoneinfo")
            zone_info_file = zoneinfo_root.joinpath(*self.TIME_ZONE.split("/"))
            if zoneinfo_root.exists() and not zone_info_file.exists():
                raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE)
            # Move the time zone info into os.environ. See ticket #2315 for why
            # we don't do this unconditionally (breaks Windows).
            os.environ["TZ"] = self.TIME_ZONE
            time.tzset()

        if self.is_overridden("USE_L10N"):
            warnings.warn(USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning)

        if self.is_overridden("DEFAULT_FILE_STORAGE"):
            if self.is_overridden("STORAGES"):
                raise ImproperlyConfigured(
                    "DEFAULT_FILE_STORAGE/STORAGES are mutually exclusive."
                )
            self.STORAGES[DEFAULT_STORAGE_ALIAS] = {
                "BACKEND": self.DEFAULT_FILE_STORAGE
            }
            warnings.warn(DEFAULT_FILE_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning)

        if self.is_overridden("STATICFILES_STORAGE"):
            if self.is_overridden("STORAGES"):
                raise ImproperlyConfigured(
                    "STATICFILES_STORAGE/STORAGES are mutually exclusive."
                )
            self.STORAGES[STATICFILES_STORAGE_ALIAS] = {
                "BACKEND": self.STATICFILES_STORAGE
            }
            warnings.warn(STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning)
        # RemovedInDjango51Warning.
        if self.is_overridden("STORAGES"):
            setattr(
                self,
                "DEFAULT_FILE_STORAGE",
                self.STORAGES.get(DEFAULT_STORAGE_ALIAS, {}).get("BACKEND"),
            )
            setattr(
                self,
                "STATICFILES_STORAGE",
                self.STORAGES.get(STATICFILES_STORAGE_ALIAS, {}).get("BACKEND"),
            )

    def is_overridden(self, setting):
        return setting in self._explicit_settings

    def __repr__(self):
        return '<%(cls)s "%(settings_module)s">' % {
            "cls": self.__class__.__name__,
            "settings_module": self.SETTINGS_MODULE,
        }


class UserSettingsHolder:
    """Holder for user configured settings."""

    # SETTINGS_MODULE doesn't make much sense in the manually configured
    # (standalone) case.
    SETTINGS_MODULE = None

    def __init__(self, default_settings):
        """
        Requests for configuration variables not in this class are satisfied
        from the module specified in default_settings (if possible).
        """
        self.__dict__["_deleted"] = set()
        self.default_settings = default_settings

    def __getattr__(self, name):
        if not name.isupper() or name in self._deleted:
            raise AttributeError
        return getattr(self.default_settings, name)

    def __setattr__(self, name, value):
        self._deleted.discard(name)
        if name == "USE_L10N":
            warnings.warn(USE_L10N_DEPRECATED_MSG, RemovedInDjango50Warning)
        if name == "CSRF_COOKIE_MASKED":
            warnings.warn(CSRF_COOKIE_MASKED_DEPRECATED_MSG, RemovedInDjango50Warning)
        if name == "DEFAULT_FILE_STORAGE":
            self.STORAGES[DEFAULT_STORAGE_ALIAS] = {
                "BACKEND": self.DEFAULT_FILE_STORAGE
            }
            warnings.warn(DEFAULT_FILE_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning)
        if name == "STATICFILES_STORAGE":
            self.STORAGES[STATICFILES_STORAGE_ALIAS] = {
                "BACKEND": self.STATICFILES_STORAGE
            }
            warnings.warn(STATICFILES_STORAGE_DEPRECATED_MSG, RemovedInDjango51Warning)
        super().__setattr__(name, value)
        if name == "USE_DEPRECATED_PYTZ":
            warnings.warn(USE_DEPRECATED_PYTZ_DEPRECATED_MSG, RemovedInDjango50Warning)
        # RemovedInDjango51Warning.
        if name == "STORAGES":
            if default_file_storage := self.STORAGES.get(DEFAULT_STORAGE_ALIAS):
                super().__setattr__(
                    "DEFAULT_FILE_STORAGE", default_file_storage.get("BACKEND")
                )
            else:
                self.STORAGES.setdefault(
                    DEFAULT_STORAGE_ALIAS,
                    {"BACKEND": "django.core.files.storage.FileSystemStorage"},
                )
            if staticfiles_storage := self.STORAGES.get(STATICFILES_STORAGE_ALIAS):
                super().__setattr__(
                    "STATICFILES_STORAGE", staticfiles_storage.get("BACKEND")
                )
            else:
                self.STORAGES.setdefault(
                    STATICFILES_STORAGE_ALIAS,
                    {
                        "BACKEND": (
                            "django.contrib.staticfiles.storage.StaticFilesStorage"
                        ),
                    },
                )

    def __delattr__(self, name):
        self._deleted.add(name)
        if hasattr(self, name):
            super().__delattr__(name)

    def __dir__(self):
        return sorted(
            s
            for s in [*self.__dict__, *dir(self.default_settings)]
            if s not in self._deleted
        )

    def is_overridden(self, setting):
        deleted = setting in self._deleted
        set_locally = setting in self.__dict__
        set_on_default = getattr(
            self.default_settings, "is_overridden", lambda s: False
        )(setting)
        return deleted or set_locally or set_on_default

    def __repr__(self):
        return "<%(cls)s>" % {
            "cls": self.__class__.__name__,
        }


settings = LazySettings()

back/venvmsg/Lib/site-packages/django/conf/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import logging
import sys
from functools import wraps

from asgiref.sync import iscoroutinefunction, sync_to_async

from django.conf import settings
from django.core import signals
from django.core.exceptions import (
    BadRequest,
    PermissionDenied,
    RequestDataTooBig,
    SuspiciousOperation,
    TooManyFieldsSent,
    TooManyFilesSent,
)
from django.http import Http404
from django.http.multipartparser import MultiPartParserError
from django.urls import get_resolver, get_urlconf
from django.utils.log import log_response
from django.views import debug


def convert_exception_to_response(get_response):
    """
    Wrap the given get_response callable in exception-to-response conversion.

    All exceptions will be converted. All known 4xx exceptions (Http404,
    PermissionDenied, MultiPartParserError, SuspiciousOperation) will be
    converted to the appropriate response, and all other exceptions will be
    converted to 500 responses.

    This decorator is automatically applied to all middleware to ensure that
    no middleware leaks an exception and that the next middleware in the stack
    can rely on getting a response instead of an exception.
    """
    if iscoroutinefunction(get_response):

        @wraps(get_response)
        async def inner(request):
            try:
                response = await get_response(request)
            except Exception as exc:
                response = await sync_to_async(
                    response_for_exception, thread_sensitive=False
                )(request, exc)
            return response

        return inner
    else:

        @wraps(get_response)
        def inner(request):
            try:
                response = get_response(request)
            except Exception as exc:
                response = response_for_exception(request, exc)
            return response

        return inner


def response_for_exception(request, exc):
    if isinstance(exc, Http404):
        if settings.DEBUG:
            response = debug.technical_404_response(request, exc)
        else:
            response = get_exception_response(
                request, get_resolver(get_urlconf()), 404, exc
            )

    elif isinstance(exc, PermissionDenied):
        response = get_exception_response(
            request, get_resolver(get_urlconf()), 403, exc
        )
        log_response(
            "Forbidden (Permission denied): %s",
            request.path,
            response=response,
            request=request,
            exception=exc,
        )

    elif isinstance(exc, MultiPartParserError):
        response = get_exception_response(
            request, get_resolver(get_urlconf()), 400, exc
        )
        log_response(
            "Bad request (Unable to parse request body): %s",
            request.path,
            response=response,
            request=request,
            exception=exc,
        )

    elif isinstance(exc, BadRequest):
        if settings.DEBUG:
            response = debug.technical_500_response(
                request, *sys.exc_info(), status_code=400
            )
        else:
            response = get_exception_response(
                request, get_resolver(get_urlconf()), 400, exc
            )
        log_response(
            "%s: %s",
            str(exc),
            request.path,
            response=response,
            request=request,
            exception=exc,
        )
    elif isinstance(exc, SuspiciousOperation):
        if isinstance(exc, (RequestDataTooBig, TooManyFieldsSent, TooManyFilesSent)):
            # POST data can't be accessed again, otherwise the original
            # exception would be raised.
            request._mark_post_parse_error()

        # The request logger receives events for any problematic request
        # The security logger receives events for all SuspiciousOperations
        security_logger = logging.getLogger(
            "django.security.%s" % exc.__class__.__name__
        )
        security_logger.error(
            str(exc),
            exc_info=exc,
            extra={"status_code": 400, "request": request},
        )
        if settings.DEBUG:
            response = debug.technical_500_response(
                request, *sys.exc_info(), status_code=400
            )
        else:
            response = get_exception_response(
                request, get_resolver(get_urlconf()), 400, exc
            )

    else:
        signals.got_request_exception.send(sender=None, request=request)
        response = handle_uncaught_exception(
            request, get_resolver(get_urlconf()), sys.exc_info()
        )
        log_response(
            "%s: %s",
            response.reason_phrase,
            request.path,
            response=response,
            request=request,
            exception=exc,
        )

    # Force a TemplateResponse to be rendered.
    if not getattr(response, "is_rendered", True) and callable(
        getattr(response, "render", None)
    ):
        response = response.render()

    return response


def get_exception_response(request, resolver, status_code, exception):
    try:
        callback = resolver.resolve_error_handler(status_code)
        response = callback(request, exception=exception)
    except Exception:
        signals.got_request_exception.send(sender=None, request=request)
        response = handle_uncaught_exception(request, resolver, sys.exc_info())

    return response


def handle_uncaught_exception(request, resolver, exc_info):
    """
    Processing for any otherwise uncaught exceptions (those that will
    generate HTTP 500 responses).
    """
    if settings.DEBUG_PROPAGATE_EXCEPTIONS:
        raise

    if settings.DEBUG:
        return debug.technical_500_response(request, *exc_info)

    # Return an HttpResponse that displays a friendly error message.
    callback = resolver.resolve_error_handler(500)
    return callback(request)

back/venvmsg/Lib/site-packages/django/core/handlers/exception.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import keyword
import re

from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS, connections
from django.db.models.constants import LOOKUP_SEP


class Command(BaseCommand):
    help = (
        "Introspects the database tables in the given database and outputs a Django "
        "model module."
    )
    requires_system_checks = []
    stealth_options = ("table_name_filter",)
    db_module = "django.db"

    def add_arguments(self, parser):
        parser.add_argument(
            "table",
            nargs="*",
            type=str,
            help="Selects what tables or views should be introspected.",
        )
        parser.add_argument(
            "--database",
            default=DEFAULT_DB_ALIAS,
            help=(
                'Nominates a database to introspect. Defaults to using the "default" '
                "database."
            ),
        )
        parser.add_argument(
            "--include-partitions",
            action="store_true",
            help="Also output models for partition tables.",
        )
        parser.add_argument(
            "--include-views",
            action="store_true",
            help="Also output models for database views.",
        )

    def handle(self, **options):
        try:
            for line in self.handle_inspection(options):
                self.stdout.write(line)
        except NotImplementedError:
            raise CommandError(
                "Database inspection isn't supported for the currently selected "
                "database backend."
            )

    def handle_inspection(self, options):
        connection = connections[options["database"]]
        # 'table_name_filter' is a stealth option
        table_name_filter = options.get("table_name_filter")

        def table2model(table_name):
            return re.sub(r"[^a-zA-Z0-9]", "", table_name.title())

        with connection.cursor() as cursor:
            yield "# This is an auto-generated Django model module."
            yield "# You'll have to do the following manually to clean this up:"
            yield "#   * Rearrange models' order"
            yield "#   * Make sure each model has one field with primary_key=True"
            yield (
                "#   * Make sure each ForeignKey and OneToOneField has `on_delete` set "
                "to the desired behavior"
            )
            yield (
                "#   * Remove `managed = False` lines if you wish to allow "
                "Django to create, modify, and delete the table"
            )
            yield (
                "# Feel free to rename the models, but don't rename db_table values or "
                "field names."
            )
            yield "from %s import models" % self.db_module
            known_models = []
            # Determine types of tables and/or views to be introspected.
            types = {"t"}
            if options["include_partitions"]:
                types.add("p")
            if options["include_views"]:
                types.add("v")
            table_info = connection.introspection.get_table_list(cursor)
            table_info = {info.name: info for info in table_info if info.type in types}

            for table_name in options["table"] or sorted(name for name in table_info):
                if table_name_filter is not None and callable(table_name_filter):
                    if not table_name_filter(table_name):
                        continue
                try:
                    try:
                        relations = connection.introspection.get_relations(
                            cursor, table_name
                        )
                    except NotImplementedError:
                        relations = {}
                    try:
                        constraints = connection.introspection.get_constraints(
                            cursor, table_name
                        )
                    except NotImplementedError:
                        constraints = {}
                    primary_key_columns = (
                        connection.introspection.get_primary_key_columns(
                            cursor, table_name
                        )
                    )
                    primary_key_column = (
                        primary_key_columns[0] if primary_key_columns else None
                    )
                    unique_columns = [
                        c["columns"][0]
                        for c in constraints.values()
                        if c["unique"] and len(c["columns"]) == 1
                    ]
                    table_description = connection.introspection.get_table_description(
                        cursor, table_name
                    )
                except Exception as e:
                    yield "# Unable to inspect table '%s'" % table_name
                    yield "# The error was: %s" % e
                    continue

                model_name = table2model(table_name)
                yield ""
                yield ""
                yield "class %s(models.Model):" % model_name
                known_models.append(model_name)
                used_column_names = []  # Holds column names used in the table so far
                column_to_field_name = {}  # Maps column names to names of model fields
                used_relations = set()  # Holds foreign relations used in the table.
                for row in table_description:
                    comment_notes = (
                        []
                    )  # Holds Field notes, to be displayed in a Python comment.
                    extra_params = {}  # Holds Field parameters such as 'db_column'.
                    column_name = row.name
                    is_relation = column_name in relations

                    att_name, params, notes = self.normalize_col_name(
                        column_name, used_column_names, is_relation
                    )
                    extra_params.update(params)
                    comment_notes.extend(notes)

                    used_column_names.append(att_name)
                    column_to_field_name[column_name] = att_name

                    # Add primary_key and unique, if necessary.
                    if column_name == primary_key_column:
                        extra_params["primary_key"] = True
                        if len(primary_key_columns) > 1:
                            comment_notes.append(
                                "The composite primary key (%s) found, that is not "
                                "supported. The first column is selected."
                                % ", ".join(primary_key_columns)
                            )
                    elif column_name in unique_columns:
                        extra_params["unique"] = True

                    if is_relation:
                        ref_db_column, ref_db_table = relations[column_name]
                        if extra_params.pop("unique", False) or extra_params.get(
                            "primary_key"
                        ):
                            rel_type = "OneToOneField"
                        else:
                            rel_type = "ForeignKey"
                            ref_pk_column = (
                                connection.introspection.get_primary_key_column(
                                    cursor, ref_db_table
                                )
                            )
                            if ref_pk_column and ref_pk_column != ref_db_column:
                                extra_params["to_field"] = ref_db_column
                        rel_to = (
                            "self"
                            if ref_db_table == table_name
                            else table2model(ref_db_table)
                        )
                        if rel_to in known_models:
                            field_type = "%s(%s" % (rel_type, rel_to)
                        else:
                            field_type = "%s('%s'" % (rel_type, rel_to)
                        if rel_to in used_relations:
                            extra_params["related_name"] = "%s_%s_set" % (
                                model_name.lower(),
                                att_name,
                            )
                        used_relations.add(rel_to)
                    else:
                        # Calling `get_field_type` to get the field type string and any
                        # additional parameters and notes.
                        field_type, field_params, field_notes = self.get_field_type(
                            connection, table_name, row
                        )
                        extra_params.update(field_params)
                        comment_notes.extend(field_notes)

                        field_type += "("

                    # Don't output 'id = meta.AutoField(primary_key=True)', because
                    # that's assumed if it doesn't exist.
                    if att_name == "id" and extra_params == {"primary_key": True}:
                        if field_type == "AutoField(":
                            continue
                        elif (
                            field_type
                            == connection.features.introspected_field_types["AutoField"]
                            + "("
                        ):
                            comment_notes.append("AutoField?")

                    # Add 'null' and 'blank', if the 'null_ok' flag was present in the
                    # table description.
                    if row.null_ok:  # If it's NULL...
                        extra_params["blank"] = True
                        extra_params["null"] = True

                    field_desc = "%s = %s%s" % (
                        att_name,
                        # Custom fields will have a dotted path
                        "" if "." in field_type else "models.",
                        field_type,
                    )
                    if field_type.startswith(("ForeignKey(", "OneToOneField(")):
                        field_desc += ", models.DO_NOTHING"

                    # Add comment.
                    if connection.features.supports_comments and row.comment:
                        extra_params["db_comment"] = row.comment

                    if extra_params:
                        if not field_desc.endswith("("):
                            field_desc += ", "
                        field_desc += ", ".join(
                            "%s=%r" % (k, v) for k, v in extra_params.items()
                        )
                    field_desc += ")"
                    if comment_notes:
                        field_desc += "  # " + " ".join(comment_notes)
                    yield "    %s" % field_desc
                comment = None
                if info := table_info.get(table_name):
                    is_view = info.type == "v"
                    is_partition = info.type == "p"
                    if connection.features.supports_comments:
                        comment = info.comment
                else:
                    is_view = False
                    is_partition = False
                yield from self.get_meta(
                    table_name,
                    constraints,
                    column_to_field_name,
                    is_view,
                    is_partition,
                    comment,
                )

    def normalize_col_name(self, col_name, used_column_names, is_relation):
        """
        Modify the column name to make it Python-compatible as a field name
        """
        field_params = {}
        field_notes = []

        new_name = col_name.lower()
        if new_name != col_name:
            field_notes.append("Field name made lowercase.")

        if is_relation:
            if new_name.endswith("_id"):
                new_name = new_name[:-3]
            else:
                field_params["db_column"] = col_name

        new_name, num_repl = re.subn(r"\W", "_", new_name)
        if num_repl > 0:
            field_notes.append("Field renamed to remove unsuitable characters.")

        if new_name.find(LOOKUP_SEP) >= 0:
            while new_name.find(LOOKUP_SEP) >= 0:
                new_name = new_name.replace(LOOKUP_SEP, "_")
            if col_name.lower().find(LOOKUP_SEP) >= 0:
                # Only add the comment if the double underscore was in the original name
                field_notes.append(
                    "Field renamed because it contained more than one '_' in a row."
                )

        if new_name.startswith("_"):
            new_name = "field%s" % new_name
            field_notes.append("Field renamed because it started with '_'.")

        if new_name.endswith("_"):
            new_name = "%sfield" % new_name
            field_notes.append("Field renamed because it ended with '_'.")

        if keyword.iskeyword(new_name):
            new_name += "_field"
            field_notes.append("Field renamed because it was a Python reserved word.")

        if new_name[0].isdigit():
            new_name = "number_%s" % new_name
            field_notes.append(
                "Field renamed because it wasn't a valid Python identifier."
            )

        if new_name in used_column_names:
            num = 0
            while "%s_%d" % (new_name, num) in used_column_names:
                num += 1
            new_name = "%s_%d" % (new_name, num)
            field_notes.append("Field renamed because of name conflict.")

        if col_name != new_name and field_notes:
            field_params["db_column"] = col_name

        return new_name, field_params, field_notes

    def get_field_type(self, connection, table_name, row):
        """
        Given the database connection, the table name, and the cursor row
        description, this routine will return the given field type name, as
        well as any additional keyword parameters and notes for the field.
        """
        field_params = {}
        field_notes = []

        try:
            field_type = connection.introspection.get_field_type(row.type_code, row)
        except KeyError:
            field_type = "TextField"
            field_notes.append("This field type is a guess.")

        # Add max_length for all CharFields.
        if field_type == "CharField" and row.display_size:
            if (size := int(row.display_size)) and size > 0:
                field_params["max_length"] = size

        if field_type in {"CharField", "TextField"} and row.collation:
            field_params["db_collation"] = row.collation

        if field_type == "DecimalField":
            if row.precision is None or row.scale is None:
                field_notes.append(
                    "max_digits and decimal_places have been guessed, as this "
                    "database handles decimal fields as float"
                )
                field_params["max_digits"] = (
                    row.precision if row.precision is not None else 10
                )
                field_params["decimal_places"] = (
                    row.scale if row.scale is not None else 5
                )
            else:
                field_params["max_digits"] = row.precision
                field_params["decimal_places"] = row.scale

        return field_type, field_params, field_notes

    def get_meta(
        self,
        table_name,
        constraints,
        column_to_field_name,
        is_view,
        is_partition,
        comment,
    ):
        """
        Return a sequence comprising the lines of code necessary
        to construct the inner Meta class for the model corresponding
        to the given database table name.
        """
        unique_together = []
        has_unsupported_constraint = False
        for params in constraints.values():
            if params["unique"]:
                columns = params["columns"]
                if None in columns:
                    has_unsupported_constraint = True
                columns = [
                    x for x in columns if x is not None and x in column_to_field_name
                ]
                if len(columns) > 1:
                    unique_together.append(
                        str(tuple(column_to_field_name[c] for c in columns))
                    )
        if is_view:
            managed_comment = "  # Created from a view. Don't remove."
        elif is_partition:
            managed_comment = "  # Created from a partition. Don't remove."
        else:
            managed_comment = ""
        meta = [""]
        if has_unsupported_constraint:
            meta.append("    # A unique constraint could not be introspected.")
        meta += [
            "    class Meta:",
            "        managed = False%s" % managed_comment,
            "        db_table = %r" % table_name,
        ]
        if unique_together:
            tup = "(" + ", ".join(unique_together) + ",)"
            meta += ["        unique_together = %s" % tup]
        if comment:
            meta += [f"        db_table_comment = {comment!r}"]
        return meta

back/venvmsg/Lib/site-packages/django/core/management/commands/inspectdb.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import operator

from django.db import DataError, InterfaceError
from django.db.backends.base.features import BaseDatabaseFeatures
from django.db.backends.postgresql.psycopg_any import is_psycopg3
from django.utils.functional import cached_property


class DatabaseFeatures(BaseDatabaseFeatures):
    minimum_database_version = (12,)
    allows_group_by_selected_pks = True
    can_return_columns_from_insert = True
    can_return_rows_from_bulk_insert = True
    has_real_datatype = True
    has_native_uuid_field = True
    has_native_duration_field = True
    has_native_json_field = True
    can_defer_constraint_checks = True
    has_select_for_update = True
    has_select_for_update_nowait = True
    has_select_for_update_of = True
    has_select_for_update_skip_locked = True
    has_select_for_no_key_update = True
    can_release_savepoints = True
    supports_comments = True
    supports_tablespaces = True
    supports_transactions = True
    can_introspect_materialized_views = True
    can_distinct_on_fields = True
    can_rollback_ddl = True
    schema_editor_uses_clientside_param_binding = True
    supports_combined_alters = True
    nulls_order_largest = True
    closed_cursor_error_class = InterfaceError
    greatest_least_ignores_nulls = True
    can_clone_databases = True
    supports_temporal_subtraction = True
    supports_slicing_ordering_in_compound = True
    create_test_procedure_without_params_sql = """
        CREATE FUNCTION test_procedure () RETURNS void AS $$
        DECLARE
            V_I INTEGER;
        BEGIN
            V_I := 1;
        END;
    $$ LANGUAGE plpgsql;"""
    create_test_procedure_with_int_param_sql = """
        CREATE FUNCTION test_procedure (P_I INTEGER) RETURNS void AS $$
        DECLARE
            V_I INTEGER;
        BEGIN
            V_I := P_I;
        END;
    $$ LANGUAGE plpgsql;"""
    create_test_table_with_composite_primary_key = """
        CREATE TABLE test_table_composite_pk (
            column_1 INTEGER NOT NULL,
            column_2 INTEGER NOT NULL,
            PRIMARY KEY(column_1, column_2)
        )
    """
    requires_casted_case_in_updates = True
    supports_over_clause = True
    only_supports_unbounded_with_preceding_and_following = True
    supports_aggregate_filter_clause = True
    supported_explain_formats = {"JSON", "TEXT", "XML", "YAML"}
    supports_deferrable_unique_constraints = True
    has_json_operators = True
    json_key_contains_list_matching_requires_list = True
    supports_update_conflicts = True
    supports_update_conflicts_with_target = True
    supports_covering_indexes = True
    can_rename_index = True
    test_collations = {
        "non_default": "sv-x-icu",
        "swedish_ci": "sv-x-icu",
    }
    test_now_utc_template = "STATEMENT_TIMESTAMP() AT TIME ZONE 'UTC'"

    django_test_skips = {
        "opclasses are PostgreSQL only.": {
            "indexes.tests.SchemaIndexesNotPostgreSQLTests."
            "test_create_index_ignores_opclasses",
        },
        "PostgreSQL requires casting to text.": {
            "lookup.tests.LookupTests.test_textfield_exact_null",
        },
    }

    @cached_property
    def django_test_expected_failures(self):
        expected_failures = set()
        if self.uses_server_side_binding:
            expected_failures.update(
                {
                    # Parameters passed to expressions in SELECT and GROUP BY
                    # clauses are not recognized as the same values when using
                    # server-side binding cursors (#34255).
                    "aggregation.tests.AggregateTestCase."
                    "test_group_by_nested_expression_with_params",
                }
            )
        return expected_failures

    @cached_property
    def uses_server_side_binding(self):
        options = self.connection.settings_dict["OPTIONS"]
        return is_psycopg3 and options.get("server_side_binding") is True

    @cached_property
    def prohibits_null_characters_in_text_exception(self):
        if is_psycopg3:
            return DataError, "PostgreSQL text fields cannot contain NUL (0x00) bytes"
        else:
            return ValueError, "A string literal cannot contain NUL (0x00) characters."

    @cached_property
    def introspected_field_types(self):
        return {
            **super().introspected_field_types,
            "PositiveBigIntegerField": "BigIntegerField",
            "PositiveIntegerField": "IntegerField",
            "PositiveSmallIntegerField": "SmallIntegerField",
        }

    @cached_property
    def is_postgresql_13(self):
        return self.connection.pg_version >= 130000

    @cached_property
    def is_postgresql_14(self):
        return self.connection.pg_version >= 140000

    has_bit_xor = property(operator.attrgetter("is_postgresql_14"))
    supports_covering_spgist_indexes = property(operator.attrgetter("is_postgresql_14"))
    supports_unlimited_charfield = True

back/venvmsg/Lib/site-packages/django/db/backends/postgresql/features.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import functools
import re
from collections import defaultdict
from itertools import chain

from django.conf import settings
from django.db import models
from django.db.migrations import operations
from django.db.migrations.migration import Migration
from django.db.migrations.operations.models import AlterModelOptions
from django.db.migrations.optimizer import MigrationOptimizer
from django.db.migrations.questioner import MigrationQuestioner
from django.db.migrations.utils import (
    COMPILED_REGEX_TYPE,
    RegexObject,
    resolve_relation,
)
from django.utils.topological_sort import stable_topological_sort


class MigrationAutodetector:
    """
    Take a pair of ProjectStates and compare them to see what the first would
    need doing to make it match the second (the second usually being the
    project's current state).

    Note that this naturally operates on entire projects at a time,
    as it's likely that changes interact (for example, you can't
    add a ForeignKey without having a migration to add the table it
    depends on first). A user interface may offer single-app usage
    if it wishes, with the caveat that it may not always be possible.
    """

    def __init__(self, from_state, to_state, questioner=None):
        self.from_state = from_state
        self.to_state = to_state
        self.questioner = questioner or MigrationQuestioner()
        self.existing_apps = {app for app, model in from_state.models}

    def changes(self, graph, trim_to_apps=None, convert_apps=None, migration_name=None):
        """
        Main entry point to produce a list of applicable changes.
        Take a graph to base names on and an optional set of apps
        to try and restrict to (restriction is not guaranteed)
        """
        changes = self._detect_changes(convert_apps, graph)
        changes = self.arrange_for_graph(changes, graph, migration_name)
        if trim_to_apps:
            changes = self._trim_to_apps(changes, trim_to_apps)
        return changes

    def deep_deconstruct(self, obj):
        """
        Recursive deconstruction for a field and its arguments.
        Used for full comparison for rename/alter; sometimes a single-level
        deconstruction will not compare correctly.
        """
        if isinstance(obj, list):
            return [self.deep_deconstruct(value) for value in obj]
        elif isinstance(obj, tuple):
            return tuple(self.deep_deconstruct(value) for value in obj)
        elif isinstance(obj, dict):
            return {key: self.deep_deconstruct(value) for key, value in obj.items()}
        elif isinstance(obj, functools.partial):
            return (
                obj.func,
                self.deep_deconstruct(obj.args),
                self.deep_deconstruct(obj.keywords),
            )
        elif isinstance(obj, COMPILED_REGEX_TYPE):
            return RegexObject(obj)
        elif isinstance(obj, type):
            # If this is a type that implements 'deconstruct' as an instance method,
            # avoid treating this as being deconstructible itself - see #22951
            return obj
        elif hasattr(obj, "deconstruct"):
            deconstructed = obj.deconstruct()
            if isinstance(obj, models.Field):
                # we have a field which also returns a name
                deconstructed = deconstructed[1:]
            path, args, kwargs = deconstructed
            return (
                path,
                [self.deep_deconstruct(value) for value in args],
                {key: self.deep_deconstruct(value) for key, value in kwargs.items()},
            )
        else:
            return obj

    def only_relation_agnostic_fields(self, fields):
        """
        Return a definition of the fields that ignores field names and
        what related fields actually relate to. Used for detecting renames (as
        the related fields change during renames).
        """
        fields_def = []
        for name, field in sorted(fields.items()):
            deconstruction = self.deep_deconstruct(field)
            if field.remote_field and field.remote_field.model:
                deconstruction[2].pop("to", None)
            fields_def.append(deconstruction)
        return fields_def

    def _detect_changes(self, convert_apps=None, graph=None):
        """
        Return a dict of migration plans which will achieve the
        change from from_state to to_state. The dict has app labels
        as keys and a list of migrations as values.

        The resulting migrations aren't specially named, but the names
        do matter for dependencies inside the set.

        convert_apps is the list of apps to convert to use migrations
        (i.e. to make initial migrations for, in the usual case)

        graph is an optional argument that, if provided, can help improve
        dependency generation and avoid potential circular dependencies.
        """
        # The first phase is generating all the operations for each app
        # and gathering them into a big per-app list.
        # Then go through that list, order it, and split into migrations to
        # resolve dependencies caused by M2Ms and FKs.
        self.generated_operations = {}
        self.altered_indexes = {}
        self.altered_constraints = {}
        self.renamed_fields = {}

        # Prepare some old/new state and model lists, separating
        # proxy models and ignoring unmigrated apps.
        self.old_model_keys = set()
        self.old_proxy_keys = set()
        self.old_unmanaged_keys = set()
        self.new_model_keys = set()
        self.new_proxy_keys = set()
        self.new_unmanaged_keys = set()
        for (app_label, model_name), model_state in self.from_state.models.items():
            if not model_state.options.get("managed", True):
                self.old_unmanaged_keys.add((app_label, model_name))
            elif app_label not in self.from_state.real_apps:
                if model_state.options.get("proxy"):
                    self.old_proxy_keys.add((app_label, model_name))
                else:
                    self.old_model_keys.add((app_label, model_name))

        for (app_label, model_name), model_state in self.to_state.models.items():
            if not model_state.options.get("managed", True):
                self.new_unmanaged_keys.add((app_label, model_name))
            elif app_label not in self.from_state.real_apps or (
                convert_apps and app_label in convert_apps
            ):
                if model_state.options.get("proxy"):
                    self.new_proxy_keys.add((app_label, model_name))
                else:
                    self.new_model_keys.add((app_label, model_name))

        self.from_state.resolve_fields_and_relations()
        self.to_state.resolve_fields_and_relations()

        # Renames have to come first
        self.generate_renamed_models()

        # Prepare lists of fields and generate through model map
        self._prepare_field_lists()
        self._generate_through_model_map()

        # Generate non-rename model operations
        self.generate_deleted_models()
        self.generate_created_models()
        self.generate_deleted_proxies()
        self.generate_created_proxies()
        self.generate_altered_options()
        self.generate_altered_managers()
        self.generate_altered_db_table_comment()

        # Create the renamed fields and store them in self.renamed_fields.
        # They are used by create_altered_indexes(), generate_altered_fields(),
        # generate_removed_altered_index/unique_together(), and
        # generate_altered_index/unique_together().
        self.create_renamed_fields()
        # Create the altered indexes and store them in self.altered_indexes.
        # This avoids the same computation in generate_removed_indexes()
        # and generate_added_indexes().
        self.create_altered_indexes()
        self.create_altered_constraints()
        # Generate index removal operations before field is removed
        self.generate_removed_constraints()
        self.generate_removed_indexes()
        # Generate field renaming operations.
        self.generate_renamed_fields()
        self.generate_renamed_indexes()
        # Generate removal of foo together.
        self.generate_removed_altered_unique_together()
        self.generate_removed_altered_index_together()  # RemovedInDjango51Warning.
        # Generate field operations.
        self.generate_removed_fields()
        self.generate_added_fields()
        self.generate_altered_fields()
        self.generate_altered_order_with_respect_to()
        self.generate_altered_unique_together()
        self.generate_altered_index_together()  # RemovedInDjango51Warning.
        self.generate_added_indexes()
        self.generate_added_constraints()
        self.generate_altered_db_table()

        self._sort_migrations()
        self._build_migration_list(graph)
        self._optimize_migrations()

        return self.migrations

    def _prepare_field_lists(self):
        """
        Prepare field lists and a list of the fields that used through models
        in the old state so dependencies can be made from the through model
        deletion to the field that uses it.
        """
        self.kept_model_keys = self.old_model_keys & self.new_model_keys
        self.kept_proxy_keys = self.old_proxy_keys & self.new_proxy_keys
        self.kept_unmanaged_keys = self.old_unmanaged_keys & self.new_unmanaged_keys
        self.through_users = {}
        self.old_field_keys = {
            (app_label, model_name, field_name)
            for app_label, model_name in self.kept_model_keys
            for field_name in self.from_state.models[
                app_label, self.renamed_models.get((app_label, model_name), model_name)
            ].fields
        }
        self.new_field_keys = {
            (app_label, model_name, field_name)
            for app_label, model_name in self.kept_model_keys
            for field_name in self.to_state.models[app_label, model_name].fields
        }

    def _generate_through_model_map(self):
        """Through model map generation."""
        for app_label, model_name in sorted(self.old_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            for field_name, field in old_model_state.fields.items():
                if hasattr(field, "remote_field") and getattr(
                    field.remote_field, "through", None
                ):
                    through_key = resolve_relation(
                        field.remote_field.through, app_label, model_name
                    )
                    self.through_users[through_key] = (
                        app_label,
                        old_model_name,
                        field_name,
                    )

    @staticmethod
    def _resolve_dependency(dependency):
        """
        Return the resolved dependency and a boolean denoting whether or not
        it was swappable.
        """
        if dependency[0] != "__setting__":
            return dependency, False
        resolved_app_label, resolved_object_name = getattr(
            settings, dependency[1]
        ).split(".")
        return (resolved_app_label, resolved_object_name.lower()) + dependency[2:], True

    def _build_migration_list(self, graph=None):
        """
        Chop the lists of operations up into migrations with dependencies on
        each other. Do this by going through an app's list of operations until
        one is found that has an outgoing dependency that isn't in another
        app's migration yet (hasn't been chopped off its list). Then chop off
        the operations before it into a migration and move onto the next app.
        If the loops completes without doing anything, there's a circular
        dependency (which _should_ be impossible as the operations are
        all split at this point so they can't depend and be depended on).
        """
        self.migrations = {}
        num_ops = sum(len(x) for x in self.generated_operations.values())
        chop_mode = False
        while num_ops:
            # On every iteration, we step through all the apps and see if there
            # is a completed set of operations.
            # If we find that a subset of the operations are complete we can
            # try to chop it off from the rest and continue, but we only
            # do this if we've already been through the list once before
            # without any chopping and nothing has changed.
            for app_label in sorted(self.generated_operations):
                chopped = []
                dependencies = set()
                for operation in list(self.generated_operations[app_label]):
                    deps_satisfied = True
                    operation_dependencies = set()
                    for dep in operation._auto_deps:
                        # Temporarily resolve the swappable dependency to
                        # prevent circular references. While keeping the
                        # dependency checks on the resolved model, add the
                        # swappable dependencies.
                        original_dep = dep
                        dep, is_swappable_dep = self._resolve_dependency(dep)
                        if dep[0] != app_label:
                            # External app dependency. See if it's not yet
                            # satisfied.
                            for other_operation in self.generated_operations.get(
                                dep[0], []
                            ):
                                if self.check_dependency(other_operation, dep):
                                    deps_satisfied = False
                                    break
                            if not deps_satisfied:
                                break
                            else:
                                if is_swappable_dep:
                                    operation_dependencies.add(
                                        (original_dep[0], original_dep[1])
                                    )
                                elif dep[0] in self.migrations:
                                    operation_dependencies.add(
                                        (dep[0], self.migrations[dep[0]][-1].name)
                                    )
                                else:
                                    # If we can't find the other app, we add a
                                    # first/last dependency, but only if we've
                                    # already been through once and checked
                                    # everything.
                                    if chop_mode:
                                        # If the app already exists, we add a
                                        # dependency on the last migration, as
                                        # we don't know which migration
                                        # contains the target field. If it's
                                        # not yet migrated or has no
                                        # migrations, we use __first__.
                                        if graph and graph.leaf_nodes(dep[0]):
                                            operation_dependencies.add(
                                                graph.leaf_nodes(dep[0])[0]
                                            )
                                        else:
                                            operation_dependencies.add(
                                                (dep[0], "__first__")
                                            )
                                    else:
                                        deps_satisfied = False
                    if deps_satisfied:
                        chopped.append(operation)
                        dependencies.update(operation_dependencies)
                        del self.generated_operations[app_label][0]
                    else:
                        break
                # Make a migration! Well, only if there's stuff to put in it
                if dependencies or chopped:
                    if not self.generated_operations[app_label] or chop_mode:
                        subclass = type(
                            "Migration",
                            (Migration,),
                            {"operations": [], "dependencies": []},
                        )
                        instance = subclass(
                            "auto_%i" % (len(self.migrations.get(app_label, [])) + 1),
                            app_label,
                        )
                        instance.dependencies = list(dependencies)
                        instance.operations = chopped
                        instance.initial = app_label not in self.existing_apps
                        self.migrations.setdefault(app_label, []).append(instance)
                        chop_mode = False
                    else:
                        self.generated_operations[app_label] = (
                            chopped + self.generated_operations[app_label]
                        )
            new_num_ops = sum(len(x) for x in self.generated_operations.values())
            if new_num_ops == num_ops:
                if not chop_mode:
                    chop_mode = True
                else:
                    raise ValueError(
                        "Cannot resolve operation dependencies: %r"
                        % self.generated_operations
                    )
            num_ops = new_num_ops

    def _sort_migrations(self):
        """
        Reorder to make things possible. Reordering may be needed so FKs work
        nicely inside the same app.
        """
        for app_label, ops in sorted(self.generated_operations.items()):
            # construct a dependency graph for intra-app dependencies
            dependency_graph = {op: set() for op in ops}
            for op in ops:
                for dep in op._auto_deps:
                    # Resolve intra-app dependencies to handle circular
                    # references involving a swappable model.
                    dep = self._resolve_dependency(dep)[0]
                    if dep[0] == app_label:
                        for op2 in ops:
                            if self.check_dependency(op2, dep):
                                dependency_graph[op].add(op2)

            # we use a stable sort for deterministic tests & general behavior
            self.generated_operations[app_label] = stable_topological_sort(
                ops, dependency_graph
            )

    def _optimize_migrations(self):
        # Add in internal dependencies among the migrations
        for app_label, migrations in self.migrations.items():
            for m1, m2 in zip(migrations, migrations[1:]):
                m2.dependencies.append((app_label, m1.name))

        # De-dupe dependencies
        for migrations in self.migrations.values():
            for migration in migrations:
                migration.dependencies = list(set(migration.dependencies))

        # Optimize migrations
        for app_label, migrations in self.migrations.items():
            for migration in migrations:
                migration.operations = MigrationOptimizer().optimize(
                    migration.operations, app_label
                )

    def check_dependency(self, operation, dependency):
        """
        Return True if the given operation depends on the given dependency,
        False otherwise.
        """
        # Created model
        if dependency[2] is None and dependency[3] is True:
            return (
                isinstance(operation, operations.CreateModel)
                and operation.name_lower == dependency[1].lower()
            )
        # Created field
        elif dependency[2] is not None and dependency[3] is True:
            return (
                isinstance(operation, operations.CreateModel)
                and operation.name_lower == dependency[1].lower()
                and any(dependency[2] == x for x, y in operation.fields)
            ) or (
                isinstance(operation, operations.AddField)
                and operation.model_name_lower == dependency[1].lower()
                and operation.name_lower == dependency[2].lower()
            )
        # Removed field
        elif dependency[2] is not None and dependency[3] is False:
            return (
                isinstance(operation, operations.RemoveField)
                and operation.model_name_lower == dependency[1].lower()
                and operation.name_lower == dependency[2].lower()
            )
        # Removed model
        elif dependency[2] is None and dependency[3] is False:
            return (
                isinstance(operation, operations.DeleteModel)
                and operation.name_lower == dependency[1].lower()
            )
        # Field being altered
        elif dependency[2] is not None and dependency[3] == "alter":
            return (
                isinstance(operation, operations.AlterField)
                and operation.model_name_lower == dependency[1].lower()
                and operation.name_lower == dependency[2].lower()
            )
        # order_with_respect_to being unset for a field
        elif dependency[2] is not None and dependency[3] == "order_wrt_unset":
            return (
                isinstance(operation, operations.AlterOrderWithRespectTo)
                and operation.name_lower == dependency[1].lower()
                and (operation.order_with_respect_to or "").lower()
                != dependency[2].lower()
            )
        # Field is removed and part of an index/unique_together
        elif dependency[2] is not None and dependency[3] == "foo_together_change":
            return (
                isinstance(
                    operation,
                    (operations.AlterUniqueTogether, operations.AlterIndexTogether),
                )
                and operation.name_lower == dependency[1].lower()
            )
        # Unknown dependency. Raise an error.
        else:
            raise ValueError("Can't handle dependency %r" % (dependency,))

    def add_operation(self, app_label, operation, dependencies=None, beginning=False):
        # Dependencies are
        # (app_label, model_name, field_name, create/delete as True/False)
        operation._auto_deps = dependencies or []
        if beginning:
            self.generated_operations.setdefault(app_label, []).insert(0, operation)
        else:
            self.generated_operations.setdefault(app_label, []).append(operation)

    def swappable_first_key(self, item):
        """
        Place potential swappable models first in lists of created models (only
        real way to solve #22783).
        """
        try:
            model_state = self.to_state.models[item]
            base_names = {
                base if isinstance(base, str) else base.__name__
                for base in model_state.bases
            }
            string_version = "%s.%s" % (item[0], item[1])
            if (
                model_state.options.get("swappable")
                or "AbstractUser" in base_names
                or "AbstractBaseUser" in base_names
                or settings.AUTH_USER_MODEL.lower() == string_version.lower()
            ):
                return ("___" + item[0], "___" + item[1])
        except LookupError:
            pass
        return item

    def generate_renamed_models(self):
        """
        Find any renamed models, generate the operations for them, and remove
        the old entry from the model lists. Must be run before other
        model-level generation.
        """
        self.renamed_models = {}
        self.renamed_models_rel = {}
        added_models = self.new_model_keys - self.old_model_keys
        for app_label, model_name in sorted(added_models):
            model_state = self.to_state.models[app_label, model_name]
            model_fields_def = self.only_relation_agnostic_fields(model_state.fields)

            removed_models = self.old_model_keys - self.new_model_keys
            for rem_app_label, rem_model_name in removed_models:
                if rem_app_label == app_label:
                    rem_model_state = self.from_state.models[
                        rem_app_label, rem_model_name
                    ]
                    rem_model_fields_def = self.only_relation_agnostic_fields(
                        rem_model_state.fields
                    )
                    if model_fields_def == rem_model_fields_def:
                        if self.questioner.ask_rename_model(
                            rem_model_state, model_state
                        ):
                            dependencies = []
                            fields = list(model_state.fields.values()) + [
                                field.remote_field
                                for relations in self.to_state.relations[
                                    app_label, model_name
                                ].values()
                                for field in relations.values()
                            ]
                            for field in fields:
                                if field.is_relation:
                                    dependencies.extend(
                                        self._get_dependencies_for_foreign_key(
                                            app_label,
                                            model_name,
                                            field,
                                            self.to_state,
                                        )
                                    )
                            self.add_operation(
                                app_label,
                                operations.RenameModel(
                                    old_name=rem_model_state.name,
                                    new_name=model_state.name,
                                ),
                                dependencies=dependencies,
                            )
                            self.renamed_models[app_label, model_name] = rem_model_name
                            renamed_models_rel_key = "%s.%s" % (
                                rem_model_state.app_label,
                                rem_model_state.name_lower,
                            )
                            self.renamed_models_rel[
                                renamed_models_rel_key
                            ] = "%s.%s" % (
                                model_state.app_label,
                                model_state.name_lower,
                            )
                            self.old_model_keys.remove((rem_app_label, rem_model_name))
                            self.old_model_keys.add((app_label, model_name))
                            break

    def generate_created_models(self):
        """
        Find all new models (both managed and unmanaged) and make create
        operations for them as well as separate operations to create any
        foreign key or M2M relationships (these are optimized later, if
        possible).

        Defer any model options that refer to collections of fields that might
        be deferred (e.g. unique_together, index_together).
        """
        old_keys = self.old_model_keys | self.old_unmanaged_keys
        added_models = self.new_model_keys - old_keys
        added_unmanaged_models = self.new_unmanaged_keys - old_keys
        all_added_models = chain(
            sorted(added_models, key=self.swappable_first_key, reverse=True),
            sorted(added_unmanaged_models, key=self.swappable_first_key, reverse=True),
        )
        for app_label, model_name in all_added_models:
            model_state = self.to_state.models[app_label, model_name]
            # Gather related fields
            related_fields = {}
            primary_key_rel = None
            for field_name, field in model_state.fields.items():
                if field.remote_field:
                    if field.remote_field.model:
                        if field.primary_key:
                            primary_key_rel = field.remote_field.model
                        elif not field.remote_field.parent_link:
                            related_fields[field_name] = field
                    if getattr(field.remote_field, "through", None):
                        related_fields[field_name] = field

            # Are there indexes/unique|index_together to defer?
            indexes = model_state.options.pop("indexes")
            constraints = model_state.options.pop("constraints")
            unique_together = model_state.options.pop("unique_together", None)
            # RemovedInDjango51Warning.
            index_together = model_state.options.pop("index_together", None)
            order_with_respect_to = model_state.options.pop(
                "order_with_respect_to", None
            )
            # Depend on the deletion of any possible proxy version of us
            dependencies = [
                (app_label, model_name, None, False),
            ]
            # Depend on all bases
            for base in model_state.bases:
                if isinstance(base, str) and "." in base:
                    base_app_label, base_name = base.split(".", 1)
                    dependencies.append((base_app_label, base_name, None, True))
                    # Depend on the removal of base fields if the new model has
                    # a field with the same name.
                    old_base_model_state = self.from_state.models.get(
                        (base_app_label, base_name)
                    )
                    new_base_model_state = self.to_state.models.get(
                        (base_app_label, base_name)
                    )
                    if old_base_model_state and new_base_model_state:
                        removed_base_fields = (
                            set(old_base_model_state.fields)
                            .difference(
                                new_base_model_state.fields,
                            )
                            .intersection(model_state.fields)
                        )
                        for removed_base_field in removed_base_fields:
                            dependencies.append(
                                (base_app_label, base_name, removed_base_field, False)
                            )
            # Depend on the other end of the primary key if it's a relation
            if primary_key_rel:
                dependencies.append(
                    resolve_relation(
                        primary_key_rel,
                        app_label,
                        model_name,
                    )
                    + (None, True)
                )
            # Generate creation operation
            self.add_operation(
                app_label,
                operations.CreateModel(
                    name=model_state.name,
                    fields=[
                        d
                        for d in model_state.fields.items()
                        if d[0] not in related_fields
                    ],
                    options=model_state.options,
                    bases=model_state.bases,
                    managers=model_state.managers,
                ),
                dependencies=dependencies,
                beginning=True,
            )

            # Don't add operations which modify the database for unmanaged models
            if not model_state.options.get("managed", True):
                continue

            # Generate operations for each related field
            for name, field in sorted(related_fields.items()):
                dependencies = self._get_dependencies_for_foreign_key(
                    app_label,
                    model_name,
                    field,
                    self.to_state,
                )
                # Depend on our own model being created
                dependencies.append((app_label, model_name, None, True))
                # Make operation
                self.add_operation(
                    app_label,
                    operations.AddField(
                        model_name=model_name,
                        name=name,
                        field=field,
                    ),
                    dependencies=list(set(dependencies)),
                )
            # Generate other opns
            if order_with_respect_to:
                self.add_operation(
                    app_label,
                    operations.AlterOrderWithRespectTo(
                        name=model_name,
                        order_with_respect_to=order_with_respect_to,
                    ),
                    dependencies=[
                        (app_label, model_name, order_with_respect_to, True),
                        (app_label, model_name, None, True),
                    ],
                )
            related_dependencies = [
                (app_label, model_name, name, True) for name in sorted(related_fields)
            ]
            related_dependencies.append((app_label, model_name, None, True))
            for index in indexes:
                self.add_operation(
                    app_label,
                    operations.AddIndex(
                        model_name=model_name,
                        index=index,
                    ),
                    dependencies=related_dependencies,
                )
            for constraint in constraints:
                self.add_operation(
                    app_label,
                    operations.AddConstraint(
                        model_name=model_name,
                        constraint=constraint,
                    ),
                    dependencies=related_dependencies,
                )
            if unique_together:
                self.add_operation(
                    app_label,
                    operations.AlterUniqueTogether(
                        name=model_name,
                        unique_together=unique_together,
                    ),
                    dependencies=related_dependencies,
                )
            # RemovedInDjango51Warning.
            if index_together:
                self.add_operation(
                    app_label,
                    operations.AlterIndexTogether(
                        name=model_name,
                        index_together=index_together,
                    ),
                    dependencies=related_dependencies,
                )
            # Fix relationships if the model changed from a proxy model to a
            # concrete model.
            relations = self.to_state.relations
            if (app_label, model_name) in self.old_proxy_keys:
                for related_model_key, related_fields in relations[
                    app_label, model_name
                ].items():
                    related_model_state = self.to_state.models[related_model_key]
                    for related_field_name, related_field in related_fields.items():
                        self.add_operation(
                            related_model_state.app_label,
                            operations.AlterField(
                                model_name=related_model_state.name,
                                name=related_field_name,
                                field=related_field,
                            ),
                            dependencies=[(app_label, model_name, None, True)],
                        )

    def generate_created_proxies(self):
        """
        Make CreateModel statements for proxy models. Use the same statements
        as that way there's less code duplication, but for proxy models it's
        safe to skip all the pointless field stuff and chuck out an operation.
        """
        added = self.new_proxy_keys - self.old_proxy_keys
        for app_label, model_name in sorted(added):
            model_state = self.to_state.models[app_label, model_name]
            assert model_state.options.get("proxy")
            # Depend on the deletion of any possible non-proxy version of us
            dependencies = [
                (app_label, model_name, None, False),
            ]
            # Depend on all bases
            for base in model_state.bases:
                if isinstance(base, str) and "." in base:
                    base_app_label, base_name = base.split(".", 1)
                    dependencies.append((base_app_label, base_name, None, True))
            # Generate creation operation
            self.add_operation(
                app_label,
                operations.CreateModel(
                    name=model_state.name,
                    fields=[],
                    options=model_state.options,
                    bases=model_state.bases,
                    managers=model_state.managers,
                ),
                # Depend on the deletion of any possible non-proxy version of us
                dependencies=dependencies,
            )

    def generate_deleted_models(self):
        """
        Find all deleted models (managed and unmanaged) and make delete
        operations for them as well as separate operations to delete any
        foreign key or M2M relationships (these are optimized later, if
        possible).

        Also bring forward removal of any model options that refer to
        collections of fields - the inverse of generate_created_models().
        """
        new_keys = self.new_model_keys | self.new_unmanaged_keys
        deleted_models = self.old_model_keys - new_keys
        deleted_unmanaged_models = self.old_unmanaged_keys - new_keys
        all_deleted_models = chain(
            sorted(deleted_models), sorted(deleted_unmanaged_models)
        )
        for app_label, model_name in all_deleted_models:
            model_state = self.from_state.models[app_label, model_name]
            # Gather related fields
            related_fields = {}
            for field_name, field in model_state.fields.items():
                if field.remote_field:
                    if field.remote_field.model:
                        related_fields[field_name] = field
                    if getattr(field.remote_field, "through", None):
                        related_fields[field_name] = field
            # Generate option removal first
            unique_together = model_state.options.pop("unique_together", None)
            # RemovedInDjango51Warning.
            index_together = model_state.options.pop("index_together", None)
            if unique_together:
                self.add_operation(
                    app_label,
                    operations.AlterUniqueTogether(
                        name=model_name,
                        unique_together=None,
                    ),
                )
            # RemovedInDjango51Warning.
            if index_together:
                self.add_operation(
                    app_label,
                    operations.AlterIndexTogether(
                        name=model_name,
                        index_together=None,
                    ),
                )
            # Then remove each related field
            for name in sorted(related_fields):
                self.add_operation(
                    app_label,
                    operations.RemoveField(
                        model_name=model_name,
                        name=name,
                    ),
                )
            # Finally, remove the model.
            # This depends on both the removal/alteration of all incoming fields
            # and the removal of all its own related fields, and if it's
            # a through model the field that references it.
            dependencies = []
            relations = self.from_state.relations
            for (
                related_object_app_label,
                object_name,
            ), relation_related_fields in relations[app_label, model_name].items():
                for field_name, field in relation_related_fields.items():
                    dependencies.append(
                        (related_object_app_label, object_name, field_name, False),
                    )
                    if not field.many_to_many:
                        dependencies.append(
                            (
                                related_object_app_label,
                                object_name,
                                field_name,
                                "alter",
                            ),
                        )

            for name in sorted(related_fields):
                dependencies.append((app_label, model_name, name, False))
            # We're referenced in another field's through=
            through_user = self.through_users.get((app_label, model_state.name_lower))
            if through_user:
                dependencies.append(
                    (through_user[0], through_user[1], through_user[2], False)
                )
            # Finally, make the operation, deduping any dependencies
            self.add_operation(
                app_label,
                operations.DeleteModel(
                    name=model_state.name,
                ),
                dependencies=list(set(dependencies)),
            )

    def generate_deleted_proxies(self):
        """Make DeleteModel options for proxy models."""
        deleted = self.old_proxy_keys - self.new_proxy_keys
        for app_label, model_name in sorted(deleted):
            model_state = self.from_state.models[app_label, model_name]
            assert model_state.options.get("proxy")
            self.add_operation(
                app_label,
                operations.DeleteModel(
                    name=model_state.name,
                ),
            )

    def create_renamed_fields(self):
        """Work out renamed fields."""
        self.renamed_operations = []
        old_field_keys = self.old_field_keys.copy()
        for app_label, model_name, field_name in sorted(
            self.new_field_keys - old_field_keys
        ):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            field = new_model_state.get_field(field_name)
            # Scan to see if this is actually a rename!
            field_dec = self.deep_deconstruct(field)
            for rem_app_label, rem_model_name, rem_field_name in sorted(
                old_field_keys - self.new_field_keys
            ):
                if rem_app_label == app_label and rem_model_name == model_name:
                    old_field = old_model_state.get_field(rem_field_name)
                    old_field_dec = self.deep_deconstruct(old_field)
                    if (
                        field.remote_field
                        and field.remote_field.model
                        and "to" in old_field_dec[2]
                    ):
                        old_rel_to = old_field_dec[2]["to"]
                        if old_rel_to in self.renamed_models_rel:
                            old_field_dec[2]["to"] = self.renamed_models_rel[old_rel_to]
                    old_field.set_attributes_from_name(rem_field_name)
                    old_db_column = old_field.get_attname_column()[1]
                    if old_field_dec == field_dec or (
                        # Was the field renamed and db_column equal to the
                        # old field's column added?
                        old_field_dec[0:2] == field_dec[0:2]
                        and dict(old_field_dec[2], db_column=old_db_column)
                        == field_dec[2]
                    ):
                        if self.questioner.ask_rename(
                            model_name, rem_field_name, field_name, field
                        ):
                            self.renamed_operations.append(
                                (
                                    rem_app_label,
                                    rem_model_name,
                                    old_field.db_column,
                                    rem_field_name,
                                    app_label,
                                    model_name,
                                    field,
                                    field_name,
                                )
                            )
                            old_field_keys.remove(
                                (rem_app_label, rem_model_name, rem_field_name)
                            )
                            old_field_keys.add((app_label, model_name, field_name))
                            self.renamed_fields[
                                app_label, model_name, field_name
                            ] = rem_field_name
                            break

    def generate_renamed_fields(self):
        """Generate RenameField operations."""
        for (
            rem_app_label,
            rem_model_name,
            rem_db_column,
            rem_field_name,
            app_label,
            model_name,
            field,
            field_name,
        ) in self.renamed_operations:
            # A db_column mismatch requires a prior noop AlterField for the
            # subsequent RenameField to be a noop on attempts at preserving the
            # old name.
            if rem_db_column != field.db_column:
                altered_field = field.clone()
                altered_field.name = rem_field_name
                self.add_operation(
                    app_label,
                    operations.AlterField(
                        model_name=model_name,
                        name=rem_field_name,
                        field=altered_field,
                    ),
                )
            self.add_operation(
                app_label,
                operations.RenameField(
                    model_name=model_name,
                    old_name=rem_field_name,
                    new_name=field_name,
                ),
            )
            self.old_field_keys.remove((rem_app_label, rem_model_name, rem_field_name))
            self.old_field_keys.add((app_label, model_name, field_name))

    def generate_added_fields(self):
        """Make AddField operations."""
        for app_label, model_name, field_name in sorted(
            self.new_field_keys - self.old_field_keys
        ):
            self._generate_added_field(app_label, model_name, field_name)

    def _generate_added_field(self, app_label, model_name, field_name):
        field = self.to_state.models[app_label, model_name].get_field(field_name)
        # Adding a field always depends at least on its removal.
        dependencies = [(app_label, model_name, field_name, False)]
        # Fields that are foreignkeys/m2ms depend on stuff.
        if field.remote_field and field.remote_field.model:
            dependencies.extend(
                self._get_dependencies_for_foreign_key(
                    app_label,
                    model_name,
                    field,
                    self.to_state,
                )
            )
        # You can't just add NOT NULL fields with no default or fields
        # which don't allow empty strings as default.
        time_fields = (models.DateField, models.DateTimeField, models.TimeField)
        preserve_default = (
            field.null
            or field.has_default()
            or field.many_to_many
            or (field.blank and field.empty_strings_allowed)
            or (isinstance(field, time_fields) and field.auto_now)
        )
        if not preserve_default:
            field = field.clone()
            if isinstance(field, time_fields) and field.auto_now_add:
                field.default = self.questioner.ask_auto_now_add_addition(
                    field_name, model_name
                )
            else:
                field.default = self.questioner.ask_not_null_addition(
                    field_name, model_name
                )
        if (
            field.unique
            and field.default is not models.NOT_PROVIDED
            and callable(field.default)
        ):
            self.questioner.ask_unique_callable_default_addition(field_name, model_name)
        self.add_operation(
            app_label,
            operations.AddField(
                model_name=model_name,
                name=field_name,
                field=field,
                preserve_default=preserve_default,
            ),
            dependencies=dependencies,
        )

    def generate_removed_fields(self):
        """Make RemoveField operations."""
        for app_label, model_name, field_name in sorted(
            self.old_field_keys - self.new_field_keys
        ):
            self._generate_removed_field(app_label, model_name, field_name)

    def _generate_removed_field(self, app_label, model_name, field_name):
        self.add_operation(
            app_label,
            operations.RemoveField(
                model_name=model_name,
                name=field_name,
            ),
            # We might need to depend on the removal of an
            # order_with_respect_to or index/unique_together operation;
            # this is safely ignored if there isn't one
            dependencies=[
                (app_label, model_name, field_name, "order_wrt_unset"),
                (app_label, model_name, field_name, "foo_together_change"),
            ],
        )

    def generate_altered_fields(self):
        """
        Make AlterField operations, or possibly RemovedField/AddField if alter
        isn't possible.
        """
        for app_label, model_name, field_name in sorted(
            self.old_field_keys & self.new_field_keys
        ):
            # Did the field change?
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_field_name = self.renamed_fields.get(
                (app_label, model_name, field_name), field_name
            )
            old_field = self.from_state.models[app_label, old_model_name].get_field(
                old_field_name
            )
            new_field = self.to_state.models[app_label, model_name].get_field(
                field_name
            )
            dependencies = []
            # Implement any model renames on relations; these are handled by RenameModel
            # so we need to exclude them from the comparison
            if hasattr(new_field, "remote_field") and getattr(
                new_field.remote_field, "model", None
            ):
                rename_key = resolve_relation(
                    new_field.remote_field.model, app_label, model_name
                )
                if rename_key in self.renamed_models:
                    new_field.remote_field.model = old_field.remote_field.model
                # Handle ForeignKey which can only have a single to_field.
                remote_field_name = getattr(new_field.remote_field, "field_name", None)
                if remote_field_name:
                    to_field_rename_key = rename_key + (remote_field_name,)
                    if to_field_rename_key in self.renamed_fields:
                        # Repoint both model and field name because to_field
                        # inclusion in ForeignKey.deconstruct() is based on
                        # both.
                        new_field.remote_field.model = old_field.remote_field.model
                        new_field.remote_field.field_name = (
                            old_field.remote_field.field_name
                        )
                # Handle ForeignObjects which can have multiple from_fields/to_fields.
                from_fields = getattr(new_field, "from_fields", None)
                if from_fields:
                    from_rename_key = (app_label, model_name)
                    new_field.from_fields = tuple(
                        [
                            self.renamed_fields.get(
                                from_rename_key + (from_field,), from_field
                            )
                            for from_field in from_fields
                        ]
                    )
                    new_field.to_fields = tuple(
                        [
                            self.renamed_fields.get(rename_key + (to_field,), to_field)
                            for to_field in new_field.to_fields
                        ]
                    )
                dependencies.extend(
                    self._get_dependencies_for_foreign_key(
                        app_label,
                        model_name,
                        new_field,
                        self.to_state,
                    )
                )
            if hasattr(new_field, "remote_field") and getattr(
                new_field.remote_field, "through", None
            ):
                rename_key = resolve_relation(
                    new_field.remote_field.through, app_label, model_name
                )
                if rename_key in self.renamed_models:
                    new_field.remote_field.through = old_field.remote_field.through
            old_field_dec = self.deep_deconstruct(old_field)
            new_field_dec = self.deep_deconstruct(new_field)
            # If the field was confirmed to be renamed it means that only
            # db_column was allowed to change which generate_renamed_fields()
            # already accounts for by adding an AlterField operation.
            if old_field_dec != new_field_dec and old_field_name == field_name:
                both_m2m = old_field.many_to_many and new_field.many_to_many
                neither_m2m = not old_field.many_to_many and not new_field.many_to_many
                if both_m2m or neither_m2m:
                    # Either both fields are m2m or neither is
                    preserve_default = True
                    if (
                        old_field.null
                        and not new_field.null
                        and not new_field.has_default()
                        and not new_field.many_to_many
                    ):
                        field = new_field.clone()
                        new_default = self.questioner.ask_not_null_alteration(
                            field_name, model_name
                        )
                        if new_default is not models.NOT_PROVIDED:
                            field.default = new_default
                            preserve_default = False
                    else:
                        field = new_field
                    self.add_operation(
                        app_label,
                        operations.AlterField(
                            model_name=model_name,
                            name=field_name,
                            field=field,
                            preserve_default=preserve_default,
                        ),
                        dependencies=dependencies,
                    )
                else:
                    # We cannot alter between m2m and concrete fields
                    self._generate_removed_field(app_label, model_name, field_name)
                    self._generate_added_field(app_label, model_name, field_name)

    def create_altered_indexes(self):
        option_name = operations.AddIndex.option_name
        self.renamed_index_together_values = defaultdict(list)

        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]

            old_indexes = old_model_state.options[option_name]
            new_indexes = new_model_state.options[option_name]
            added_indexes = [idx for idx in new_indexes if idx not in old_indexes]
            removed_indexes = [idx for idx in old_indexes if idx not in new_indexes]
            renamed_indexes = []
            # Find renamed indexes.
            remove_from_added = []
            remove_from_removed = []
            for new_index in added_indexes:
                new_index_dec = new_index.deconstruct()
                new_index_name = new_index_dec[2].pop("name")
                for old_index in removed_indexes:
                    old_index_dec = old_index.deconstruct()
                    old_index_name = old_index_dec[2].pop("name")
                    # Indexes are the same except for the names.
                    if (
                        new_index_dec == old_index_dec
                        and new_index_name != old_index_name
                    ):
                        renamed_indexes.append((old_index_name, new_index_name, None))
                        remove_from_added.append(new_index)
                        remove_from_removed.append(old_index)
            # Find index_together changed to indexes.
            for (
                old_value,
                new_value,
                index_together_app_label,
                index_together_model_name,
                dependencies,
            ) in self._get_altered_foo_together_operations(
                operations.AlterIndexTogether.option_name
            ):
                if (
                    app_label != index_together_app_label
                    or model_name != index_together_model_name
                ):
                    continue
                removed_values = old_value.difference(new_value)
                for removed_index_together in removed_values:
                    renamed_index_together_indexes = []
                    for new_index in added_indexes:
                        _, args, kwargs = new_index.deconstruct()
                        # Ensure only 'fields' are defined in the Index.
                        if (
                            not args
                            and new_index.fields == list(removed_index_together)
                            and set(kwargs) == {"name", "fields"}
                        ):
                            renamed_index_together_indexes.append(new_index)

                    if len(renamed_index_together_indexes) == 1:
                        renamed_index = renamed_index_together_indexes[0]
                        remove_from_added.append(renamed_index)
                        renamed_indexes.append(
                            (None, renamed_index.name, removed_index_together)
                        )
                        self.renamed_index_together_values[
                            index_together_app_label, index_together_model_name
                        ].append(removed_index_together)
            # Remove renamed indexes from the lists of added and removed
            # indexes.
            added_indexes = [
                idx for idx in added_indexes if idx not in remove_from_added
            ]
            removed_indexes = [
                idx for idx in removed_indexes if idx not in remove_from_removed
            ]

            self.altered_indexes.update(
                {
                    (app_label, model_name): {
                        "added_indexes": added_indexes,
                        "removed_indexes": removed_indexes,
                        "renamed_indexes": renamed_indexes,
                    }
                }
            )

    def generate_added_indexes(self):
        for (app_label, model_name), alt_indexes in self.altered_indexes.items():
            for index in alt_indexes["added_indexes"]:
                self.add_operation(
                    app_label,
                    operations.AddIndex(
                        model_name=model_name,
                        index=index,
                    ),
                )

    def generate_removed_indexes(self):
        for (app_label, model_name), alt_indexes in self.altered_indexes.items():
            for index in alt_indexes["removed_indexes"]:
                self.add_operation(
                    app_label,
                    operations.RemoveIndex(
                        model_name=model_name,
                        name=index.name,
                    ),
                )

    def generate_renamed_indexes(self):
        for (app_label, model_name), alt_indexes in self.altered_indexes.items():
            for old_index_name, new_index_name, old_fields in alt_indexes[
                "renamed_indexes"
            ]:
                self.add_operation(
                    app_label,
                    operations.RenameIndex(
                        model_name=model_name,
                        new_name=new_index_name,
                        old_name=old_index_name,
                        old_fields=old_fields,
                    ),
                )

    def create_altered_constraints(self):
        option_name = operations.AddConstraint.option_name
        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]

            old_constraints = old_model_state.options[option_name]
            new_constraints = new_model_state.options[option_name]
            add_constraints = [c for c in new_constraints if c not in old_constraints]
            rem_constraints = [c for c in old_constraints if c not in new_constraints]

            self.altered_constraints.update(
                {
                    (app_label, model_name): {
                        "added_constraints": add_constraints,
                        "removed_constraints": rem_constraints,
                    }
                }
            )

    def generate_added_constraints(self):
        for (
            app_label,
            model_name,
        ), alt_constraints in self.altered_constraints.items():
            for constraint in alt_constraints["added_constraints"]:
                self.add_operation(
                    app_label,
                    operations.AddConstraint(
                        model_name=model_name,
                        constraint=constraint,
                    ),
                )

    def generate_removed_constraints(self):
        for (
            app_label,
            model_name,
        ), alt_constraints in self.altered_constraints.items():
            for constraint in alt_constraints["removed_constraints"]:
                self.add_operation(
                    app_label,
                    operations.RemoveConstraint(
                        model_name=model_name,
                        name=constraint.name,
                    ),
                )

    @staticmethod
    def _get_dependencies_for_foreign_key(app_label, model_name, field, project_state):
        remote_field_model = None
        if hasattr(field.remote_field, "model"):
            remote_field_model = field.remote_field.model
        else:
            relations = project_state.relations[app_label, model_name]
            for (remote_app_label, remote_model_name), fields in relations.items():
                if any(
                    field == related_field.remote_field
                    for related_field in fields.values()
                ):
                    remote_field_model = f"{remote_app_label}.{remote_model_name}"
                    break
        # Account for FKs to swappable models
        swappable_setting = getattr(field, "swappable_setting", None)
        if swappable_setting is not None:
            dep_app_label = "__setting__"
            dep_object_name = swappable_setting
        else:
            dep_app_label, dep_object_name = resolve_relation(
                remote_field_model,
                app_label,
                model_name,
            )
        dependencies = [(dep_app_label, dep_object_name, None, True)]
        if getattr(field.remote_field, "through", None):
            through_app_label, through_object_name = resolve_relation(
                field.remote_field.through,
                app_label,
                model_name,
            )
            dependencies.append((through_app_label, through_object_name, None, True))
        return dependencies

    def _get_altered_foo_together_operations(self, option_name):
        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]

            # We run the old version through the field renames to account for those
            old_value = old_model_state.options.get(option_name)
            old_value = (
                {
                    tuple(
                        self.renamed_fields.get((app_label, model_name, n), n)
                        for n in unique
                    )
                    for unique in old_value
                }
                if old_value
                else set()
            )

            new_value = new_model_state.options.get(option_name)
            new_value = set(new_value) if new_value else set()

            if old_value != new_value:
                dependencies = []
                for foo_togethers in new_value:
                    for field_name in foo_togethers:
                        field = new_model_state.get_field(field_name)
                        if field.remote_field and field.remote_field.model:
                            dependencies.extend(
                                self._get_dependencies_for_foreign_key(
                                    app_label,
                                    model_name,
                                    field,
                                    self.to_state,
                                )
                            )
                yield (
                    old_value,
                    new_value,
                    app_label,
                    model_name,
                    dependencies,
                )

    def _generate_removed_altered_foo_together(self, operation):
        for (
            old_value,
            new_value,
            app_label,
            model_name,
            dependencies,
        ) in self._get_altered_foo_together_operations(operation.option_name):
            if operation == operations.AlterIndexTogether:
                old_value = {
                    value
                    for value in old_value
                    if value
                    not in self.renamed_index_together_values[app_label, model_name]
                }
            removal_value = new_value.intersection(old_value)
            if removal_value or old_value:
                self.add_operation(
                    app_label,
                    operation(
                        name=model_name, **{operation.option_name: removal_value}
                    ),
                    dependencies=dependencies,
                )

    def generate_removed_altered_unique_together(self):
        self._generate_removed_altered_foo_together(operations.AlterUniqueTogether)

    # RemovedInDjango51Warning.
    def generate_removed_altered_index_together(self):
        self._generate_removed_altered_foo_together(operations.AlterIndexTogether)

    def _generate_altered_foo_together(self, operation):
        for (
            old_value,
            new_value,
            app_label,
            model_name,
            dependencies,
        ) in self._get_altered_foo_together_operations(operation.option_name):
            removal_value = new_value.intersection(old_value)
            if new_value != removal_value:
                self.add_operation(
                    app_label,
                    operation(name=model_name, **{operation.option_name: new_value}),
                    dependencies=dependencies,
                )

    def generate_altered_unique_together(self):
        self._generate_altered_foo_together(operations.AlterUniqueTogether)

    # RemovedInDjango51Warning.
    def generate_altered_index_together(self):
        self._generate_altered_foo_together(operations.AlterIndexTogether)

    def generate_altered_db_table(self):
        models_to_check = self.kept_model_keys.union(
            self.kept_proxy_keys, self.kept_unmanaged_keys
        )
        for app_label, model_name in sorted(models_to_check):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            old_db_table_name = old_model_state.options.get("db_table")
            new_db_table_name = new_model_state.options.get("db_table")
            if old_db_table_name != new_db_table_name:
                self.add_operation(
                    app_label,
                    operations.AlterModelTable(
                        name=model_name,
                        table=new_db_table_name,
                    ),
                )

    def generate_altered_db_table_comment(self):
        models_to_check = self.kept_model_keys.union(
            self.kept_proxy_keys, self.kept_unmanaged_keys
        )
        for app_label, model_name in sorted(models_to_check):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]

            old_db_table_comment = old_model_state.options.get("db_table_comment")
            new_db_table_comment = new_model_state.options.get("db_table_comment")
            if old_db_table_comment != new_db_table_comment:
                self.add_operation(
                    app_label,
                    operations.AlterModelTableComment(
                        name=model_name,
                        table_comment=new_db_table_comment,
                    ),
                )

    def generate_altered_options(self):
        """
        Work out if any non-schema-affecting options have changed and make an
        operation to represent them in state changes (in case Python code in
        migrations needs them).
        """
        models_to_check = self.kept_model_keys.union(
            self.kept_proxy_keys,
            self.kept_unmanaged_keys,
            # unmanaged converted to managed
            self.old_unmanaged_keys & self.new_model_keys,
            # managed converted to unmanaged
            self.old_model_keys & self.new_unmanaged_keys,
        )

        for app_label, model_name in sorted(models_to_check):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            old_options = {
                key: value
                for key, value in old_model_state.options.items()
                if key in AlterModelOptions.ALTER_OPTION_KEYS
            }
            new_options = {
                key: value
                for key, value in new_model_state.options.items()
                if key in AlterModelOptions.ALTER_OPTION_KEYS
            }
            if old_options != new_options:
                self.add_operation(
                    app_label,
                    operations.AlterModelOptions(
                        name=model_name,
                        options=new_options,
                    ),
                )

    def generate_altered_order_with_respect_to(self):
        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            if old_model_state.options.get(
                "order_with_respect_to"
            ) != new_model_state.options.get("order_with_respect_to"):
                # Make sure it comes second if we're adding
                # (removal dependency is part of RemoveField)
                dependencies = []
                if new_model_state.options.get("order_with_respect_to"):
                    dependencies.append(
                        (
                            app_label,
                            model_name,
                            new_model_state.options["order_with_respect_to"],
                            True,
                        )
                    )
                # Actually generate the operation
                self.add_operation(
                    app_label,
                    operations.AlterOrderWithRespectTo(
                        name=model_name,
                        order_with_respect_to=new_model_state.options.get(
                            "order_with_respect_to"
                        ),
                    ),
                    dependencies=dependencies,
                )

    def generate_altered_managers(self):
        for app_label, model_name in sorted(self.kept_model_keys):
            old_model_name = self.renamed_models.get(
                (app_label, model_name), model_name
            )
            old_model_state = self.from_state.models[app_label, old_model_name]
            new_model_state = self.to_state.models[app_label, model_name]
            if old_model_state.managers != new_model_state.managers:
                self.add_operation(
                    app_label,
                    operations.AlterModelManagers(
                        name=model_name,
                        managers=new_model_state.managers,
                    ),
                )

    def arrange_for_graph(self, changes, graph, migration_name=None):
        """
        Take a result from changes() and a MigrationGraph, and fix the names
        and dependencies of the changes so they extend the graph from the leaf
        nodes for each app.
        """
        leaves = graph.leaf_nodes()
        name_map = {}
        for app_label, migrations in list(changes.items()):
            if not migrations:
                continue
            # Find the app label's current leaf node
            app_leaf = None
            for leaf in leaves:
                if leaf[0] == app_label:
                    app_leaf = leaf
                    break
            # Do they want an initial migration for this app?
            if app_leaf is None and not self.questioner.ask_initial(app_label):
                # They don't.
                for migration in migrations:
                    name_map[(app_label, migration.name)] = (app_label, "__first__")
                del changes[app_label]
                continue
            # Work out the next number in the sequence
            if app_leaf is None:
                next_number = 1
            else:
                next_number = (self.parse_number(app_leaf[1]) or 0) + 1
            # Name each migration
            for i, migration in enumerate(migrations):
                if i == 0 and app_leaf:
                    migration.dependencies.append(app_leaf)
                new_name_parts = ["%04i" % next_number]
                if migration_name:
                    new_name_parts.append(migration_name)
                elif i == 0 and not app_leaf:
                    new_name_parts.append("initial")
                else:
                    new_name_parts.append(migration.suggest_name()[:100])
                new_name = "_".join(new_name_parts)
                name_map[(app_label, migration.name)] = (app_label, new_name)
                next_number += 1
                migration.name = new_name
        # Now fix dependencies
        for migrations in changes.values():
            for migration in migrations:
                migration.dependencies = [
                    name_map.get(d, d) for d in migration.dependencies
                ]
        return changes

    def _trim_to_apps(self, changes, app_labels):
        """
        Take changes from arrange_for_graph() and set of app labels, and return
        a modified set of changes which trims out as many migrations that are
        not in app_labels as possible. Note that some other migrations may
        still be present as they may be required dependencies.
        """
        # Gather other app dependencies in a first pass
        app_dependencies = {}
        for app_label, migrations in changes.items():
            for migration in migrations:
                for dep_app_label, name in migration.dependencies:
                    app_dependencies.setdefault(app_label, set()).add(dep_app_label)
        required_apps = set(app_labels)
        # Keep resolving till there's no change
        old_required_apps = None
        while old_required_apps != required_apps:
            old_required_apps = set(required_apps)
            required_apps.update(
                *[app_dependencies.get(app_label, ()) for app_label in required_apps]
            )
        # Remove all migrations that aren't needed
        for app_label in list(changes):
            if app_label not in required_apps:
                del changes[app_label]
        return changes

    @classmethod
    def parse_number(cls, name):
        """
        Given a migration name, try to extract a number from the beginning of
        it. For a squashed migration such as '0001_squashed_0004…', return the
        second number. If no number is found, return None.
        """
        if squashed_match := re.search(r".*_squashed_(\d+)", name):
            return int(squashed_match[1])
        match = re.match(r"^\d+", name)
        if match:
            return int(match[0])
        return None

back/venvmsg/Lib/site-packages/django/db/migrations/autodetector.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import copy
import datetime
import functools
import inspect
import warnings
from collections import defaultdict
from decimal import Decimal
from uuid import UUID

from django.core.exceptions import EmptyResultSet, FieldError, FullResultSet
from django.db import DatabaseError, NotSupportedError, connection
from django.db.models import fields
from django.db.models.constants import LOOKUP_SEP
from django.db.models.query_utils import Q
from django.utils.deconstruct import deconstructible
from django.utils.deprecation import RemovedInDjango50Warning
from django.utils.functional import cached_property
from django.utils.hashable import make_hashable


class SQLiteNumericMixin:
    """
    Some expressions with output_field=DecimalField() must be cast to
    numeric to be properly filtered.
    """

    def as_sqlite(self, compiler, connection, **extra_context):
        sql, params = self.as_sql(compiler, connection, **extra_context)
        try:
            if self.output_field.get_internal_type() == "DecimalField":
                sql = "CAST(%s AS NUMERIC)" % sql
        except FieldError:
            pass
        return sql, params


class Combinable:
    """
    Provide the ability to combine one or two objects with
    some connector. For example F('foo') + F('bar').
    """

    # Arithmetic connectors
    ADD = "+"
    SUB = "-"
    MUL = "*"
    DIV = "/"
    POW = "^"
    # The following is a quoted % operator - it is quoted because it can be
    # used in strings that also have parameter substitution.
    MOD = "%%"

    # Bitwise operators - note that these are generated by .bitand()
    # and .bitor(), the '&' and '|' are reserved for boolean operator
    # usage.
    BITAND = "&"
    BITOR = "|"
    BITLEFTSHIFT = "<<"
    BITRIGHTSHIFT = ">>"
    BITXOR = "#"

    def _combine(self, other, connector, reversed):
        if not hasattr(other, "resolve_expression"):
            # everything must be resolvable to an expression
            other = Value(other)

        if reversed:
            return CombinedExpression(other, connector, self)
        return CombinedExpression(self, connector, other)

    #############
    # OPERATORS #
    #############

    def __neg__(self):
        return self._combine(-1, self.MUL, False)

    def __add__(self, other):
        return self._combine(other, self.ADD, False)

    def __sub__(self, other):
        return self._combine(other, self.SUB, False)

    def __mul__(self, other):
        return self._combine(other, self.MUL, False)

    def __truediv__(self, other):
        return self._combine(other, self.DIV, False)

    def __mod__(self, other):
        return self._combine(other, self.MOD, False)

    def __pow__(self, other):
        return self._combine(other, self.POW, False)

    def __and__(self, other):
        if getattr(self, "conditional", False) and getattr(other, "conditional", False):
            return Q(self) & Q(other)
        raise NotImplementedError(
            "Use .bitand(), .bitor(), and .bitxor() for bitwise logical operations."
        )

    def bitand(self, other):
        return self._combine(other, self.BITAND, False)

    def bitleftshift(self, other):
        return self._combine(other, self.BITLEFTSHIFT, False)

    def bitrightshift(self, other):
        return self._combine(other, self.BITRIGHTSHIFT, False)

    def __xor__(self, other):
        if getattr(self, "conditional", False) and getattr(other, "conditional", False):
            return Q(self) ^ Q(other)
        raise NotImplementedError(
            "Use .bitand(), .bitor(), and .bitxor() for bitwise logical operations."
        )

    def bitxor(self, other):
        return self._combine(other, self.BITXOR, False)

    def __or__(self, other):
        if getattr(self, "conditional", False) and getattr(other, "conditional", False):
            return Q(self) | Q(other)
        raise NotImplementedError(
            "Use .bitand(), .bitor(), and .bitxor() for bitwise logical operations."
        )

    def bitor(self, other):
        return self._combine(other, self.BITOR, False)

    def __radd__(self, other):
        return self._combine(other, self.ADD, True)

    def __rsub__(self, other):
        return self._combine(other, self.SUB, True)

    def __rmul__(self, other):
        return self._combine(other, self.MUL, True)

    def __rtruediv__(self, other):
        return self._combine(other, self.DIV, True)

    def __rmod__(self, other):
        return self._combine(other, self.MOD, True)

    def __rpow__(self, other):
        return self._combine(other, self.POW, True)

    def __rand__(self, other):
        raise NotImplementedError(
            "Use .bitand(), .bitor(), and .bitxor() for bitwise logical operations."
        )

    def __ror__(self, other):
        raise NotImplementedError(
            "Use .bitand(), .bitor(), and .bitxor() for bitwise logical operations."
        )

    def __rxor__(self, other):
        raise NotImplementedError(
            "Use .bitand(), .bitor(), and .bitxor() for bitwise logical operations."
        )

    def __invert__(self):
        return NegatedExpression(self)


class BaseExpression:
    """Base class for all query expressions."""

    empty_result_set_value = NotImplemented
    # aggregate specific fields
    is_summary = False
    _output_field_resolved_to_none = False
    # Can the expression be used in a WHERE clause?
    filterable = True
    # Can the expression can be used as a source expression in Window?
    window_compatible = False

    def __init__(self, output_field=None):
        if output_field is not None:
            self.output_field = output_field

    def __getstate__(self):
        state = self.__dict__.copy()
        state.pop("convert_value", None)
        return state

    def get_db_converters(self, connection):
        return (
            []
            if self.convert_value is self._convert_value_noop
            else [self.convert_value]
        ) + self.output_field.get_db_converters(connection)

    def get_source_expressions(self):
        return []

    def set_source_expressions(self, exprs):
        assert not exprs

    def _parse_expressions(self, *expressions):
        return [
            arg
            if hasattr(arg, "resolve_expression")
            else (F(arg) if isinstance(arg, str) else Value(arg))
            for arg in expressions
        ]

    def as_sql(self, compiler, connection):
        """
        Responsible for returning a (sql, [params]) tuple to be included
        in the current query.

        Different backends can provide their own implementation, by
        providing an `as_{vendor}` method and patching the Expression:

        ```
        def override_as_sql(self, compiler, connection):
            # custom logic
            return super().as_sql(compiler, connection)
        setattr(Expression, 'as_' + connection.vendor, override_as_sql)
        ```

        Arguments:
         * compiler: the query compiler responsible for generating the query.
           Must have a compile method, returning a (sql, [params]) tuple.
           Calling compiler(value) will return a quoted `value`.

         * connection: the database connection used for the current query.

        Return: (sql, params)
          Where `sql` is a string containing ordered sql parameters to be
          replaced with the elements of the list `params`.
        """
        raise NotImplementedError("Subclasses must implement as_sql()")

    @cached_property
    def contains_aggregate(self):
        return any(
            expr and expr.contains_aggregate for expr in self.get_source_expressions()
        )

    @cached_property
    def contains_over_clause(self):
        return any(
            expr and expr.contains_over_clause for expr in self.get_source_expressions()
        )

    @cached_property
    def contains_column_references(self):
        return any(
            expr and expr.contains_column_references
            for expr in self.get_source_expressions()
        )

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        """
        Provide the chance to do any preprocessing or validation before being
        added to the query.

        Arguments:
         * query: the backend query implementation
         * allow_joins: boolean allowing or denying use of joins
           in this query
         * reuse: a set of reusable joins for multijoins
         * summarize: a terminal aggregate clause
         * for_save: whether this expression about to be used in a save or update

        Return: an Expression to be added to the query.
        """
        c = self.copy()
        c.is_summary = summarize
        c.set_source_expressions(
            [
                expr.resolve_expression(query, allow_joins, reuse, summarize)
                if expr
                else None
                for expr in c.get_source_expressions()
            ]
        )
        return c

    @property
    def conditional(self):
        return isinstance(self.output_field, fields.BooleanField)

    @property
    def field(self):
        return self.output_field

    @cached_property
    def output_field(self):
        """Return the output type of this expressions."""
        output_field = self._resolve_output_field()
        if output_field is None:
            self._output_field_resolved_to_none = True
            raise FieldError("Cannot resolve expression type, unknown output_field")
        return output_field

    @cached_property
    def _output_field_or_none(self):
        """
        Return the output field of this expression, or None if
        _resolve_output_field() didn't return an output type.
        """
        try:
            return self.output_field
        except FieldError:
            if not self._output_field_resolved_to_none:
                raise

    def _resolve_output_field(self):
        """
        Attempt to infer the output type of the expression.

        As a guess, if the output fields of all source fields match then simply
        infer the same type here.

        If a source's output field resolves to None, exclude it from this check.
        If all sources are None, then an error is raised higher up the stack in
        the output_field property.
        """
        # This guess is mostly a bad idea, but there is quite a lot of code
        # (especially 3rd party Func subclasses) that depend on it, we'd need a
        # deprecation path to fix it.
        sources_iter = (
            source for source in self.get_source_fields() if source is not None
        )
        for output_field in sources_iter:
            for source in sources_iter:
                if not isinstance(output_field, source.__class__):
                    raise FieldError(
                        "Expression contains mixed types: %s, %s. You must "
                        "set output_field."
                        % (
                            output_field.__class__.__name__,
                            source.__class__.__name__,
                        )
                    )
            return output_field

    @staticmethod
    def _convert_value_noop(value, expression, connection):
        return value

    @cached_property
    def convert_value(self):
        """
        Expressions provide their own converters because users have the option
        of manually specifying the output_field which may be a different type
        from the one the database returns.
        """
        field = self.output_field
        internal_type = field.get_internal_type()
        if internal_type == "FloatField":
            return (
                lambda value, expression, connection: None
                if value is None
                else float(value)
            )
        elif internal_type.endswith("IntegerField"):
            return (
                lambda value, expression, connection: None
                if value is None
                else int(value)
            )
        elif internal_type == "DecimalField":
            return (
                lambda value, expression, connection: None
                if value is None
                else Decimal(value)
            )
        return self._convert_value_noop

    def get_lookup(self, lookup):
        return self.output_field.get_lookup(lookup)

    def get_transform(self, name):
        return self.output_field.get_transform(name)

    def relabeled_clone(self, change_map):
        clone = self.copy()
        clone.set_source_expressions(
            [
                e.relabeled_clone(change_map) if e is not None else None
                for e in self.get_source_expressions()
            ]
        )
        return clone

    def replace_expressions(self, replacements):
        if replacement := replacements.get(self):
            return replacement
        clone = self.copy()
        source_expressions = clone.get_source_expressions()
        clone.set_source_expressions(
            [
                expr.replace_expressions(replacements) if expr else None
                for expr in source_expressions
            ]
        )
        return clone

    def get_refs(self):
        refs = set()
        for expr in self.get_source_expressions():
            refs |= expr.get_refs()
        return refs

    def copy(self):
        return copy.copy(self)

    def prefix_references(self, prefix):
        clone = self.copy()
        clone.set_source_expressions(
            [
                F(f"{prefix}{expr.name}")
                if isinstance(expr, F)
                else expr.prefix_references(prefix)
                for expr in self.get_source_expressions()
            ]
        )
        return clone

    def get_group_by_cols(self):
        if not self.contains_aggregate:
            return [self]
        cols = []
        for source in self.get_source_expressions():
            cols.extend(source.get_group_by_cols())
        return cols

    def get_source_fields(self):
        """Return the underlying field types used by this aggregate."""
        return [e._output_field_or_none for e in self.get_source_expressions()]

    def asc(self, **kwargs):
        return OrderBy(self, **kwargs)

    def desc(self, **kwargs):
        return OrderBy(self, descending=True, **kwargs)

    def reverse_ordering(self):
        return self

    def flatten(self):
        """
        Recursively yield this expression and all subexpressions, in
        depth-first order.
        """
        yield self
        for expr in self.get_source_expressions():
            if expr:
                if hasattr(expr, "flatten"):
                    yield from expr.flatten()
                else:
                    yield expr

    def select_format(self, compiler, sql, params):
        """
        Custom format for select clauses. For example, EXISTS expressions need
        to be wrapped in CASE WHEN on Oracle.
        """
        if hasattr(self.output_field, "select_format"):
            return self.output_field.select_format(compiler, sql, params)
        return sql, params


@deconstructible
class Expression(BaseExpression, Combinable):
    """An expression that can be combined with other expressions."""

    @cached_property
    def identity(self):
        constructor_signature = inspect.signature(self.__init__)
        args, kwargs = self._constructor_args
        signature = constructor_signature.bind_partial(*args, **kwargs)
        signature.apply_defaults()
        arguments = signature.arguments.items()
        identity = [self.__class__]
        for arg, value in arguments:
            if isinstance(value, fields.Field):
                if value.name and value.model:
                    value = (value.model._meta.label, value.name)
                else:
                    value = type(value)
            else:
                value = make_hashable(value)
            identity.append((arg, value))
        return tuple(identity)

    def __eq__(self, other):
        if not isinstance(other, Expression):
            return NotImplemented
        return other.identity == self.identity

    def __hash__(self):
        return hash(self.identity)


# Type inference for CombinedExpression.output_field.
# Missing items will result in FieldError, by design.
#
# The current approach for NULL is based on lowest common denominator behavior
# i.e. if one of the supported databases is raising an error (rather than
# return NULL) for `val <op> NULL`, then Django raises FieldError.
NoneType = type(None)

_connector_combinations = [
    # Numeric operations - operands of same type.
    {
        connector: [
            (fields.IntegerField, fields.IntegerField, fields.IntegerField),
            (fields.FloatField, fields.FloatField, fields.FloatField),
            (fields.DecimalField, fields.DecimalField, fields.DecimalField),
        ]
        for connector in (
            Combinable.ADD,
            Combinable.SUB,
            Combinable.MUL,
            # Behavior for DIV with integer arguments follows Postgres/SQLite,
            # not MySQL/Oracle.
            Combinable.DIV,
            Combinable.MOD,
            Combinable.POW,
        )
    },
    # Numeric operations - operands of different type.
    {
        connector: [
            (fields.IntegerField, fields.DecimalField, fields.DecimalField),
            (fields.DecimalField, fields.IntegerField, fields.DecimalField),
            (fields.IntegerField, fields.FloatField, fields.FloatField),
            (fields.FloatField, fields.IntegerField, fields.FloatField),
        ]
        for connector in (
            Combinable.ADD,
            Combinable.SUB,
            Combinable.MUL,
            Combinable.DIV,
            Combinable.MOD,
        )
    },
    # Bitwise operators.
    {
        connector: [
            (fields.IntegerField, fields.IntegerField, fields.IntegerField),
        ]
        for connector in (
            Combinable.BITAND,
            Combinable.BITOR,
            Combinable.BITLEFTSHIFT,
            Combinable.BITRIGHTSHIFT,
            Combinable.BITXOR,
        )
    },
    # Numeric with NULL.
    {
        connector: [
            (field_type, NoneType, field_type),
            (NoneType, field_type, field_type),
        ]
        for connector in (
            Combinable.ADD,
            Combinable.SUB,
            Combinable.MUL,
            Combinable.DIV,
            Combinable.MOD,
            Combinable.POW,
        )
        for field_type in (fields.IntegerField, fields.DecimalField, fields.FloatField)
    },
    # Date/DateTimeField/DurationField/TimeField.
    {
        Combinable.ADD: [
            # Date/DateTimeField.
            (fields.DateField, fields.DurationField, fields.DateTimeField),
            (fields.DateTimeField, fields.DurationField, fields.DateTimeField),
            (fields.DurationField, fields.DateField, fields.DateTimeField),
            (fields.DurationField, fields.DateTimeField, fields.DateTimeField),
            # DurationField.
            (fields.DurationField, fields.DurationField, fields.DurationField),
            # TimeField.
            (fields.TimeField, fields.DurationField, fields.TimeField),
            (fields.DurationField, fields.TimeField, fields.TimeField),
        ],
    },
    {
        Combinable.SUB: [
            # Date/DateTimeField.
            (fields.DateField, fields.DurationField, fields.DateTimeField),
            (fields.DateTimeField, fields.DurationField, fields.DateTimeField),
            (fields.DateField, fields.DateField, fields.DurationField),
            (fields.DateField, fields.DateTimeField, fields.DurationField),
            (fields.DateTimeField, fields.DateField, fields.DurationField),
            (fields.DateTimeField, fields.DateTimeField, fields.DurationField),
            # DurationField.
            (fields.DurationField, fields.DurationField, fields.DurationField),
            # TimeField.
            (fields.TimeField, fields.DurationField, fields.TimeField),
            (fields.TimeField, fields.TimeField, fields.DurationField),
        ],
    },
]

_connector_combinators = defaultdict(list)


def register_combinable_fields(lhs, connector, rhs, result):
    """
    Register combinable types:
        lhs <connector> rhs -> result
    e.g.
        register_combinable_fields(
            IntegerField, Combinable.ADD, FloatField, FloatField
        )
    """
    _connector_combinators[connector].append((lhs, rhs, result))


for d in _connector_combinations:
    for connector, field_types in d.items():
        for lhs, rhs, result in field_types:
            register_combinable_fields(lhs, connector, rhs, result)


@functools.lru_cache(maxsize=128)
def _resolve_combined_type(connector, lhs_type, rhs_type):
    combinators = _connector_combinators.get(connector, ())
    for combinator_lhs_type, combinator_rhs_type, combined_type in combinators:
        if issubclass(lhs_type, combinator_lhs_type) and issubclass(
            rhs_type, combinator_rhs_type
        ):
            return combined_type


class CombinedExpression(SQLiteNumericMixin, Expression):
    def __init__(self, lhs, connector, rhs, output_field=None):
        super().__init__(output_field=output_field)
        self.connector = connector
        self.lhs = lhs
        self.rhs = rhs

    def __repr__(self):
        return "<{}: {}>".format(self.__class__.__name__, self)

    def __str__(self):
        return "{} {} {}".format(self.lhs, self.connector, self.rhs)

    def get_source_expressions(self):
        return [self.lhs, self.rhs]

    def set_source_expressions(self, exprs):
        self.lhs, self.rhs = exprs

    def _resolve_output_field(self):
        # We avoid using super() here for reasons given in
        # Expression._resolve_output_field()
        combined_type = _resolve_combined_type(
            self.connector,
            type(self.lhs._output_field_or_none),
            type(self.rhs._output_field_or_none),
        )
        if combined_type is None:
            raise FieldError(
                f"Cannot infer type of {self.connector!r} expression involving these "
                f"types: {self.lhs.output_field.__class__.__name__}, "
                f"{self.rhs.output_field.__class__.__name__}. You must set "
                f"output_field."
            )
        return combined_type()

    def as_sql(self, compiler, connection):
        expressions = []
        expression_params = []
        sql, params = compiler.compile(self.lhs)
        expressions.append(sql)
        expression_params.extend(params)
        sql, params = compiler.compile(self.rhs)
        expressions.append(sql)
        expression_params.extend(params)
        # order of precedence
        expression_wrapper = "(%s)"
        sql = connection.ops.combine_expression(self.connector, expressions)
        return expression_wrapper % sql, expression_params

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        lhs = self.lhs.resolve_expression(
            query, allow_joins, reuse, summarize, for_save
        )
        rhs = self.rhs.resolve_expression(
            query, allow_joins, reuse, summarize, for_save
        )
        if not isinstance(self, (DurationExpression, TemporalSubtraction)):
            try:
                lhs_type = lhs.output_field.get_internal_type()
            except (AttributeError, FieldError):
                lhs_type = None
            try:
                rhs_type = rhs.output_field.get_internal_type()
            except (AttributeError, FieldError):
                rhs_type = None
            if "DurationField" in {lhs_type, rhs_type} and lhs_type != rhs_type:
                return DurationExpression(
                    self.lhs, self.connector, self.rhs
                ).resolve_expression(
                    query,
                    allow_joins,
                    reuse,
                    summarize,
                    for_save,
                )
            datetime_fields = {"DateField", "DateTimeField", "TimeField"}
            if (
                self.connector == self.SUB
                and lhs_type in datetime_fields
                and lhs_type == rhs_type
            ):
                return TemporalSubtraction(self.lhs, self.rhs).resolve_expression(
                    query,
                    allow_joins,
                    reuse,
                    summarize,
                    for_save,
                )
        c = self.copy()
        c.is_summary = summarize
        c.lhs = lhs
        c.rhs = rhs
        return c


class DurationExpression(CombinedExpression):
    def compile(self, side, compiler, connection):
        try:
            output = side.output_field
        except FieldError:
            pass
        else:
            if output.get_internal_type() == "DurationField":
                sql, params = compiler.compile(side)
                return connection.ops.format_for_duration_arithmetic(sql), params
        return compiler.compile(side)

    def as_sql(self, compiler, connection):
        if connection.features.has_native_duration_field:
            return super().as_sql(compiler, connection)
        connection.ops.check_expression_support(self)
        expressions = []
        expression_params = []
        sql, params = self.compile(self.lhs, compiler, connection)
        expressions.append(sql)
        expression_params.extend(params)
        sql, params = self.compile(self.rhs, compiler, connection)
        expressions.append(sql)
        expression_params.extend(params)
        # order of precedence
        expression_wrapper = "(%s)"
        sql = connection.ops.combine_duration_expression(self.connector, expressions)
        return expression_wrapper % sql, expression_params

    def as_sqlite(self, compiler, connection, **extra_context):
        sql, params = self.as_sql(compiler, connection, **extra_context)
        if self.connector in {Combinable.MUL, Combinable.DIV}:
            try:
                lhs_type = self.lhs.output_field.get_internal_type()
                rhs_type = self.rhs.output_field.get_internal_type()
            except (AttributeError, FieldError):
                pass
            else:
                allowed_fields = {
                    "DecimalField",
                    "DurationField",
                    "FloatField",
                    "IntegerField",
                }
                if lhs_type not in allowed_fields or rhs_type not in allowed_fields:
                    raise DatabaseError(
                        f"Invalid arguments for operator {self.connector}."
                    )
        return sql, params


class TemporalSubtraction(CombinedExpression):
    output_field = fields.DurationField()

    def __init__(self, lhs, rhs):
        super().__init__(lhs, self.SUB, rhs)

    def as_sql(self, compiler, connection):
        connection.ops.check_expression_support(self)
        lhs = compiler.compile(self.lhs)
        rhs = compiler.compile(self.rhs)
        return connection.ops.subtract_temporals(
            self.lhs.output_field.get_internal_type(), lhs, rhs
        )


@deconstructible(path="django.db.models.F")
class F(Combinable):
    """An object capable of resolving references to existing query objects."""

    def __init__(self, name):
        """
        Arguments:
         * name: the name of the field this expression references
        """
        self.name = name

    def __repr__(self):
        return "{}({})".format(self.__class__.__name__, self.name)

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        return query.resolve_ref(self.name, allow_joins, reuse, summarize)

    def replace_expressions(self, replacements):
        return replacements.get(self, self)

    def asc(self, **kwargs):
        return OrderBy(self, **kwargs)

    def desc(self, **kwargs):
        return OrderBy(self, descending=True, **kwargs)

    def __eq__(self, other):
        return self.__class__ == other.__class__ and self.name == other.name

    def __hash__(self):
        return hash(self.name)

    def copy(self):
        return copy.copy(self)


class ResolvedOuterRef(F):
    """
    An object that contains a reference to an outer query.

    In this case, the reference to the outer query has been resolved because
    the inner query has been used as a subquery.
    """

    contains_aggregate = False
    contains_over_clause = False

    def as_sql(self, *args, **kwargs):
        raise ValueError(
            "This queryset contains a reference to an outer query and may "
            "only be used in a subquery."
        )

    def resolve_expression(self, *args, **kwargs):
        col = super().resolve_expression(*args, **kwargs)
        if col.contains_over_clause:
            raise NotSupportedError(
                f"Referencing outer query window expression is not supported: "
                f"{self.name}."
            )
        # FIXME: Rename possibly_multivalued to multivalued and fix detection
        # for non-multivalued JOINs (e.g. foreign key fields). This should take
        # into account only many-to-many and one-to-many relationships.
        col.possibly_multivalued = LOOKUP_SEP in self.name
        return col

    def relabeled_clone(self, relabels):
        return self

    def get_group_by_cols(self):
        return []


class OuterRef(F):
    contains_aggregate = False
    contains_over_clause = False

    def resolve_expression(self, *args, **kwargs):
        if isinstance(self.name, self.__class__):
            return self.name
        return ResolvedOuterRef(self.name)

    def relabeled_clone(self, relabels):
        return self


@deconstructible(path="django.db.models.Func")
class Func(SQLiteNumericMixin, Expression):
    """An SQL function call."""

    function = None
    template = "%(function)s(%(expressions)s)"
    arg_joiner = ", "
    arity = None  # The number of arguments the function accepts.

    def __init__(self, *expressions, output_field=None, **extra):
        if self.arity is not None and len(expressions) != self.arity:
            raise TypeError(
                "'%s' takes exactly %s %s (%s given)"
                % (
                    self.__class__.__name__,
                    self.arity,
                    "argument" if self.arity == 1 else "arguments",
                    len(expressions),
                )
            )
        super().__init__(output_field=output_field)
        self.source_expressions = self._parse_expressions(*expressions)
        self.extra = extra

    def __repr__(self):
        args = self.arg_joiner.join(str(arg) for arg in self.source_expressions)
        extra = {**self.extra, **self._get_repr_options()}
        if extra:
            extra = ", ".join(
                str(key) + "=" + str(val) for key, val in sorted(extra.items())
            )
            return "{}({}, {})".format(self.__class__.__name__, args, extra)
        return "{}({})".format(self.__class__.__name__, args)

    def _get_repr_options(self):
        """Return a dict of extra __init__() options to include in the repr."""
        return {}

    def get_source_expressions(self):
        return self.source_expressions

    def set_source_expressions(self, exprs):
        self.source_expressions = exprs

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        c = self.copy()
        c.is_summary = summarize
        for pos, arg in enumerate(c.source_expressions):
            c.source_expressions[pos] = arg.resolve_expression(
                query, allow_joins, reuse, summarize, for_save
            )
        return c

    def as_sql(
        self,
        compiler,
        connection,
        function=None,
        template=None,
        arg_joiner=None,
        **extra_context,
    ):
        connection.ops.check_expression_support(self)
        sql_parts = []
        params = []
        for arg in self.source_expressions:
            try:
                arg_sql, arg_params = compiler.compile(arg)
            except EmptyResultSet:
                empty_result_set_value = getattr(
                    arg, "empty_result_set_value", NotImplemented
                )
                if empty_result_set_value is NotImplemented:
                    raise
                arg_sql, arg_params = compiler.compile(Value(empty_result_set_value))
            except FullResultSet:
                arg_sql, arg_params = compiler.compile(Value(True))
            sql_parts.append(arg_sql)
            params.extend(arg_params)
        data = {**self.extra, **extra_context}
        # Use the first supplied value in this order: the parameter to this
        # method, a value supplied in __init__()'s **extra (the value in
        # `data`), or the value defined on the class.
        if function is not None:
            data["function"] = function
        else:
            data.setdefault("function", self.function)
        template = template or data.get("template", self.template)
        arg_joiner = arg_joiner or data.get("arg_joiner", self.arg_joiner)
        data["expressions"] = data["field"] = arg_joiner.join(sql_parts)
        return template % data, params

    def copy(self):
        copy = super().copy()
        copy.source_expressions = self.source_expressions[:]
        copy.extra = self.extra.copy()
        return copy


@deconstructible(path="django.db.models.Value")
class Value(SQLiteNumericMixin, Expression):
    """Represent a wrapped value as a node within an expression."""

    # Provide a default value for `for_save` in order to allow unresolved
    # instances to be compiled until a decision is taken in #25425.
    for_save = False

    def __init__(self, value, output_field=None):
        """
        Arguments:
         * value: the value this expression represents. The value will be
           added into the sql parameter list and properly quoted.

         * output_field: an instance of the model field type that this
           expression will return, such as IntegerField() or CharField().
        """
        super().__init__(output_field=output_field)
        self.value = value

    def __repr__(self):
        return f"{self.__class__.__name__}({self.value!r})"

    def as_sql(self, compiler, connection):
        connection.ops.check_expression_support(self)
        val = self.value
        output_field = self._output_field_or_none
        if output_field is not None:
            if self.for_save:
                val = output_field.get_db_prep_save(val, connection=connection)
            else:
                val = output_field.get_db_prep_value(val, connection=connection)
            if hasattr(output_field, "get_placeholder"):
                return output_field.get_placeholder(val, compiler, connection), [val]
        if val is None:
            # cx_Oracle does not always convert None to the appropriate
            # NULL type (like in case expressions using numbers), so we
            # use a literal SQL NULL
            return "NULL", []
        return "%s", [val]

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        c = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
        c.for_save = for_save
        return c

    def get_group_by_cols(self):
        return []

    def _resolve_output_field(self):
        if isinstance(self.value, str):
            return fields.CharField()
        if isinstance(self.value, bool):
            return fields.BooleanField()
        if isinstance(self.value, int):
            return fields.IntegerField()
        if isinstance(self.value, float):
            return fields.FloatField()
        if isinstance(self.value, datetime.datetime):
            return fields.DateTimeField()
        if isinstance(self.value, datetime.date):
            return fields.DateField()
        if isinstance(self.value, datetime.time):
            return fields.TimeField()
        if isinstance(self.value, datetime.timedelta):
            return fields.DurationField()
        if isinstance(self.value, Decimal):
            return fields.DecimalField()
        if isinstance(self.value, bytes):
            return fields.BinaryField()
        if isinstance(self.value, UUID):
            return fields.UUIDField()

    @property
    def empty_result_set_value(self):
        return self.value


class RawSQL(Expression):
    def __init__(self, sql, params, output_field=None):
        if output_field is None:
            output_field = fields.Field()
        self.sql, self.params = sql, params
        super().__init__(output_field=output_field)

    def __repr__(self):
        return "{}({}, {})".format(self.__class__.__name__, self.sql, self.params)

    def as_sql(self, compiler, connection):
        return "(%s)" % self.sql, self.params

    def get_group_by_cols(self):
        return [self]

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        # Resolve parents fields used in raw SQL.
        if query.model:
            for parent in query.model._meta.get_parent_list():
                for parent_field in parent._meta.local_fields:
                    _, column_name = parent_field.get_attname_column()
                    if column_name.lower() in self.sql.lower():
                        query.resolve_ref(
                            parent_field.name, allow_joins, reuse, summarize
                        )
                        break
        return super().resolve_expression(
            query, allow_joins, reuse, summarize, for_save
        )


class Star(Expression):
    def __repr__(self):
        return "'*'"

    def as_sql(self, compiler, connection):
        return "*", []


class Col(Expression):
    contains_column_references = True
    possibly_multivalued = False

    def __init__(self, alias, target, output_field=None):
        if output_field is None:
            output_field = target
        super().__init__(output_field=output_field)
        self.alias, self.target = alias, target

    def __repr__(self):
        alias, target = self.alias, self.target
        identifiers = (alias, str(target)) if alias else (str(target),)
        return "{}({})".format(self.__class__.__name__, ", ".join(identifiers))

    def as_sql(self, compiler, connection):
        alias, column = self.alias, self.target.column
        identifiers = (alias, column) if alias else (column,)
        sql = ".".join(map(compiler.quote_name_unless_alias, identifiers))
        return sql, []

    def relabeled_clone(self, relabels):
        if self.alias is None:
            return self
        return self.__class__(
            relabels.get(self.alias, self.alias), self.target, self.output_field
        )

    def get_group_by_cols(self):
        return [self]

    def get_db_converters(self, connection):
        if self.target == self.output_field:
            return self.output_field.get_db_converters(connection)
        return self.output_field.get_db_converters(
            connection
        ) + self.target.get_db_converters(connection)


class Ref(Expression):
    """
    Reference to column alias of the query. For example, Ref('sum_cost') in
    qs.annotate(sum_cost=Sum('cost')) query.
    """

    def __init__(self, refs, source):
        super().__init__()
        self.refs, self.source = refs, source

    def __repr__(self):
        return "{}({}, {})".format(self.__class__.__name__, self.refs, self.source)

    def get_source_expressions(self):
        return [self.source]

    def set_source_expressions(self, exprs):
        (self.source,) = exprs

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        # The sub-expression `source` has already been resolved, as this is
        # just a reference to the name of `source`.
        return self

    def get_refs(self):
        return {self.refs}

    def relabeled_clone(self, relabels):
        clone = self.copy()
        clone.source = self.source.relabeled_clone(relabels)
        return clone

    def as_sql(self, compiler, connection):
        return connection.ops.quote_name(self.refs), []

    def get_group_by_cols(self):
        return [self]


class ExpressionList(Func):
    """
    An expression containing multiple expressions. Can be used to provide a
    list of expressions as an argument to another expression, like a partition
    clause.
    """

    template = "%(expressions)s"

    def __init__(self, *expressions, **extra):
        if not expressions:
            raise ValueError(
                "%s requires at least one expression." % self.__class__.__name__
            )
        super().__init__(*expressions, **extra)

    def __str__(self):
        return self.arg_joiner.join(str(arg) for arg in self.source_expressions)

    def as_sqlite(self, compiler, connection, **extra_context):
        # Casting to numeric is unnecessary.
        return self.as_sql(compiler, connection, **extra_context)


class OrderByList(Func):
    template = "ORDER BY %(expressions)s"

    def __init__(self, *expressions, **extra):
        expressions = (
            (
                OrderBy(F(expr[1:]), descending=True)
                if isinstance(expr, str) and expr[0] == "-"
                else expr
            )
            for expr in expressions
        )
        super().__init__(*expressions, **extra)

    def as_sql(self, *args, **kwargs):
        if not self.source_expressions:
            return "", ()
        return super().as_sql(*args, **kwargs)

    def get_group_by_cols(self):
        group_by_cols = []
        for order_by in self.get_source_expressions():
            group_by_cols.extend(order_by.get_group_by_cols())
        return group_by_cols


@deconstructible(path="django.db.models.ExpressionWrapper")
class ExpressionWrapper(SQLiteNumericMixin, Expression):
    """
    An expression that can wrap another expression so that it can provide
    extra context to the inner expression, such as the output_field.
    """

    def __init__(self, expression, output_field):
        super().__init__(output_field=output_field)
        self.expression = expression

    def set_source_expressions(self, exprs):
        self.expression = exprs[0]

    def get_source_expressions(self):
        return [self.expression]

    def get_group_by_cols(self):
        if isinstance(self.expression, Expression):
            expression = self.expression.copy()
            expression.output_field = self.output_field
            return expression.get_group_by_cols()
        # For non-expressions e.g. an SQL WHERE clause, the entire
        # `expression` must be included in the GROUP BY clause.
        return super().get_group_by_cols()

    def as_sql(self, compiler, connection):
        return compiler.compile(self.expression)

    def __repr__(self):
        return "{}({})".format(self.__class__.__name__, self.expression)


class NegatedExpression(ExpressionWrapper):
    """The logical negation of a conditional expression."""

    def __init__(self, expression):
        super().__init__(expression, output_field=fields.BooleanField())

    def __invert__(self):
        return self.expression.copy()

    def as_sql(self, compiler, connection):
        try:
            sql, params = super().as_sql(compiler, connection)
        except EmptyResultSet:
            features = compiler.connection.features
            if not features.supports_boolean_expr_in_select_clause:
                return "1=1", ()
            return compiler.compile(Value(True))
        ops = compiler.connection.ops
        # Some database backends (e.g. Oracle) don't allow EXISTS() and filters
        # to be compared to another expression unless they're wrapped in a CASE
        # WHEN.
        if not ops.conditional_expression_supported_in_where_clause(self.expression):
            return f"CASE WHEN {sql} = 0 THEN 1 ELSE 0 END", params
        return f"NOT {sql}", params

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        resolved = super().resolve_expression(
            query, allow_joins, reuse, summarize, for_save
        )
        if not getattr(resolved.expression, "conditional", False):
            raise TypeError("Cannot negate non-conditional expressions.")
        return resolved

    def select_format(self, compiler, sql, params):
        # Wrap boolean expressions with a CASE WHEN expression if a database
        # backend (e.g. Oracle) doesn't support boolean expression in SELECT or
        # GROUP BY list.
        expression_supported_in_where_clause = (
            compiler.connection.ops.conditional_expression_supported_in_where_clause
        )
        if (
            not compiler.connection.features.supports_boolean_expr_in_select_clause
            # Avoid double wrapping.
            and expression_supported_in_where_clause(self.expression)
        ):
            sql = "CASE WHEN {} THEN 1 ELSE 0 END".format(sql)
        return sql, params


@deconstructible(path="django.db.models.When")
class When(Expression):
    template = "WHEN %(condition)s THEN %(result)s"
    # This isn't a complete conditional expression, must be used in Case().
    conditional = False

    def __init__(self, condition=None, then=None, **lookups):
        if lookups:
            if condition is None:
                condition, lookups = Q(**lookups), None
            elif getattr(condition, "conditional", False):
                condition, lookups = Q(condition, **lookups), None
        if condition is None or not getattr(condition, "conditional", False) or lookups:
            raise TypeError(
                "When() supports a Q object, a boolean expression, or lookups "
                "as a condition."
            )
        if isinstance(condition, Q) and not condition:
            raise ValueError("An empty Q() can't be used as a When() condition.")
        super().__init__(output_field=None)
        self.condition = condition
        self.result = self._parse_expressions(then)[0]

    def __str__(self):
        return "WHEN %r THEN %r" % (self.condition, self.result)

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self)

    def get_source_expressions(self):
        return [self.condition, self.result]

    def set_source_expressions(self, exprs):
        self.condition, self.result = exprs

    def get_source_fields(self):
        # We're only interested in the fields of the result expressions.
        return [self.result._output_field_or_none]

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        c = self.copy()
        c.is_summary = summarize
        if hasattr(c.condition, "resolve_expression"):
            c.condition = c.condition.resolve_expression(
                query, allow_joins, reuse, summarize, False
            )
        c.result = c.result.resolve_expression(
            query, allow_joins, reuse, summarize, for_save
        )
        return c

    def as_sql(self, compiler, connection, template=None, **extra_context):
        connection.ops.check_expression_support(self)
        template_params = extra_context
        sql_params = []
        condition_sql, condition_params = compiler.compile(self.condition)
        template_params["condition"] = condition_sql
        result_sql, result_params = compiler.compile(self.result)
        template_params["result"] = result_sql
        template = template or self.template
        return template % template_params, (
            *sql_params,
            *condition_params,
            *result_params,
        )

    def get_group_by_cols(self):
        # This is not a complete expression and cannot be used in GROUP BY.
        cols = []
        for source in self.get_source_expressions():
            cols.extend(source.get_group_by_cols())
        return cols


@deconstructible(path="django.db.models.Case")
class Case(SQLiteNumericMixin, Expression):
    """
    An SQL searched CASE expression:

        CASE
            WHEN n > 0
                THEN 'positive'
            WHEN n < 0
                THEN 'negative'
            ELSE 'zero'
        END
    """

    template = "CASE %(cases)s ELSE %(default)s END"
    case_joiner = " "

    def __init__(self, *cases, default=None, output_field=None, **extra):
        if not all(isinstance(case, When) for case in cases):
            raise TypeError("Positional arguments must all be When objects.")
        super().__init__(output_field)
        self.cases = list(cases)
        self.default = self._parse_expressions(default)[0]
        self.extra = extra

    def __str__(self):
        return "CASE %s, ELSE %r" % (
            ", ".join(str(c) for c in self.cases),
            self.default,
        )

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self)

    def get_source_expressions(self):
        return self.cases + [self.default]

    def set_source_expressions(self, exprs):
        *self.cases, self.default = exprs

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        c = self.copy()
        c.is_summary = summarize
        for pos, case in enumerate(c.cases):
            c.cases[pos] = case.resolve_expression(
                query, allow_joins, reuse, summarize, for_save
            )
        c.default = c.default.resolve_expression(
            query, allow_joins, reuse, summarize, for_save
        )
        return c

    def copy(self):
        c = super().copy()
        c.cases = c.cases[:]
        return c

    def as_sql(
        self, compiler, connection, template=None, case_joiner=None, **extra_context
    ):
        connection.ops.check_expression_support(self)
        if not self.cases:
            return compiler.compile(self.default)
        template_params = {**self.extra, **extra_context}
        case_parts = []
        sql_params = []
        default_sql, default_params = compiler.compile(self.default)
        for case in self.cases:
            try:
                case_sql, case_params = compiler.compile(case)
            except EmptyResultSet:
                continue
            except FullResultSet:
                default_sql, default_params = compiler.compile(case.result)
                break
            case_parts.append(case_sql)
            sql_params.extend(case_params)
        if not case_parts:
            return default_sql, default_params
        case_joiner = case_joiner or self.case_joiner
        template_params["cases"] = case_joiner.join(case_parts)
        template_params["default"] = default_sql
        sql_params.extend(default_params)
        template = template or template_params.get("template", self.template)
        sql = template % template_params
        if self._output_field_or_none is not None:
            sql = connection.ops.unification_cast_sql(self.output_field) % sql
        return sql, sql_params

    def get_group_by_cols(self):
        if not self.cases:
            return self.default.get_group_by_cols()
        return super().get_group_by_cols()


class Subquery(BaseExpression, Combinable):
    """
    An explicit subquery. It may contain OuterRef() references to the outer
    query which will be resolved when it is applied to that query.
    """

    template = "(%(subquery)s)"
    contains_aggregate = False
    empty_result_set_value = None
    subquery = True

    def __init__(self, queryset, output_field=None, **extra):
        # Allow the usage of both QuerySet and sql.Query objects.
        self.query = getattr(queryset, "query", queryset).clone()
        self.query.subquery = True
        self.extra = extra
        super().__init__(output_field)

    def get_source_expressions(self):
        return [self.query]

    def set_source_expressions(self, exprs):
        self.query = exprs[0]

    def _resolve_output_field(self):
        return self.query.output_field

    def copy(self):
        clone = super().copy()
        clone.query = clone.query.clone()
        return clone

    @property
    def external_aliases(self):
        return self.query.external_aliases

    def get_external_cols(self):
        return self.query.get_external_cols()

    def as_sql(self, compiler, connection, template=None, **extra_context):
        connection.ops.check_expression_support(self)
        template_params = {**self.extra, **extra_context}
        subquery_sql, sql_params = self.query.as_sql(compiler, connection)
        template_params["subquery"] = subquery_sql[1:-1]

        template = template or template_params.get("template", self.template)
        sql = template % template_params
        return sql, sql_params

    def get_group_by_cols(self):
        return self.query.get_group_by_cols(wrapper=self)


class Exists(Subquery):
    template = "EXISTS(%(subquery)s)"
    output_field = fields.BooleanField()
    empty_result_set_value = False

    def __init__(self, queryset, **kwargs):
        super().__init__(queryset, **kwargs)
        self.query = self.query.exists()

    def select_format(self, compiler, sql, params):
        # Wrap EXISTS() with a CASE WHEN expression if a database backend
        # (e.g. Oracle) doesn't support boolean expression in SELECT or GROUP
        # BY list.
        if not compiler.connection.features.supports_boolean_expr_in_select_clause:
            sql = "CASE WHEN {} THEN 1 ELSE 0 END".format(sql)
        return sql, params


@deconstructible(path="django.db.models.OrderBy")
class OrderBy(Expression):
    template = "%(expression)s %(ordering)s"
    conditional = False

    def __init__(self, expression, descending=False, nulls_first=None, nulls_last=None):
        if nulls_first and nulls_last:
            raise ValueError("nulls_first and nulls_last are mutually exclusive")
        if nulls_first is False or nulls_last is False:
            # When the deprecation ends, replace with:
            # raise ValueError(
            #     "nulls_first and nulls_last values must be True or None."
            # )
            warnings.warn(
                "Passing nulls_first=False or nulls_last=False is deprecated, use None "
                "instead.",
                RemovedInDjango50Warning,
                stacklevel=2,
            )
        self.nulls_first = nulls_first
        self.nulls_last = nulls_last
        self.descending = descending
        if not hasattr(expression, "resolve_expression"):
            raise ValueError("expression must be an expression type")
        self.expression = expression

    def __repr__(self):
        return "{}({}, descending={})".format(
            self.__class__.__name__, self.expression, self.descending
        )

    def set_source_expressions(self, exprs):
        self.expression = exprs[0]

    def get_source_expressions(self):
        return [self.expression]

    def as_sql(self, compiler, connection, template=None, **extra_context):
        template = template or self.template
        if connection.features.supports_order_by_nulls_modifier:
            if self.nulls_last:
                template = "%s NULLS LAST" % template
            elif self.nulls_first:
                template = "%s NULLS FIRST" % template
        else:
            if self.nulls_last and not (
                self.descending and connection.features.order_by_nulls_first
            ):
                template = "%%(expression)s IS NULL, %s" % template
            elif self.nulls_first and not (
                not self.descending and connection.features.order_by_nulls_first
            ):
                template = "%%(expression)s IS NOT NULL, %s" % template
        connection.ops.check_expression_support(self)
        expression_sql, params = compiler.compile(self.expression)
        placeholders = {
            "expression": expression_sql,
            "ordering": "DESC" if self.descending else "ASC",
            **extra_context,
        }
        params *= template.count("%(expression)s")
        return (template % placeholders).rstrip(), params

    def as_oracle(self, compiler, connection):
        # Oracle doesn't allow ORDER BY EXISTS() or filters unless it's wrapped
        # in a CASE WHEN.
        if connection.ops.conditional_expression_supported_in_where_clause(
            self.expression
        ):
            copy = self.copy()
            copy.expression = Case(
                When(self.expression, then=True),
                default=False,
            )
            return copy.as_sql(compiler, connection)
        return self.as_sql(compiler, connection)

    def get_group_by_cols(self):
        cols = []
        for source in self.get_source_expressions():
            cols.extend(source.get_group_by_cols())
        return cols

    def reverse_ordering(self):
        self.descending = not self.descending
        if self.nulls_first:
            self.nulls_last = True
            self.nulls_first = None
        elif self.nulls_last:
            self.nulls_first = True
            self.nulls_last = None
        return self

    def asc(self):
        self.descending = False

    def desc(self):
        self.descending = True


class Window(SQLiteNumericMixin, Expression):
    template = "%(expression)s OVER (%(window)s)"
    # Although the main expression may either be an aggregate or an
    # expression with an aggregate function, the GROUP BY that will
    # be introduced in the query as a result is not desired.
    contains_aggregate = False
    contains_over_clause = True

    def __init__(
        self,
        expression,
        partition_by=None,
        order_by=None,
        frame=None,
        output_field=None,
    ):
        self.partition_by = partition_by
        self.order_by = order_by
        self.frame = frame

        if not getattr(expression, "window_compatible", False):
            raise ValueError(
                "Expression '%s' isn't compatible with OVER clauses."
                % expression.__class__.__name__
            )

        if self.partition_by is not None:
            if not isinstance(self.partition_by, (tuple, list)):
                self.partition_by = (self.partition_by,)
            self.partition_by = ExpressionList(*self.partition_by)

        if self.order_by is not None:
            if isinstance(self.order_by, (list, tuple)):
                self.order_by = OrderByList(*self.order_by)
            elif isinstance(self.order_by, (BaseExpression, str)):
                self.order_by = OrderByList(self.order_by)
            else:
                raise ValueError(
                    "Window.order_by must be either a string reference to a "
                    "field, an expression, or a list or tuple of them."
                )
        super().__init__(output_field=output_field)
        self.source_expression = self._parse_expressions(expression)[0]

    def _resolve_output_field(self):
        return self.source_expression.output_field

    def get_source_expressions(self):
        return [self.source_expression, self.partition_by, self.order_by, self.frame]

    def set_source_expressions(self, exprs):
        self.source_expression, self.partition_by, self.order_by, self.frame = exprs

    def as_sql(self, compiler, connection, template=None):
        connection.ops.check_expression_support(self)
        if not connection.features.supports_over_clause:
            raise NotSupportedError("This backend does not support window expressions.")
        expr_sql, params = compiler.compile(self.source_expression)
        window_sql, window_params = [], ()

        if self.partition_by is not None:
            sql_expr, sql_params = self.partition_by.as_sql(
                compiler=compiler,
                connection=connection,
                template="PARTITION BY %(expressions)s",
            )
            window_sql.append(sql_expr)
            window_params += tuple(sql_params)

        if self.order_by is not None:
            order_sql, order_params = compiler.compile(self.order_by)
            window_sql.append(order_sql)
            window_params += tuple(order_params)

        if self.frame:
            frame_sql, frame_params = compiler.compile(self.frame)
            window_sql.append(frame_sql)
            window_params += tuple(frame_params)

        template = template or self.template

        return (
            template % {"expression": expr_sql, "window": " ".join(window_sql).strip()},
            (*params, *window_params),
        )

    def as_sqlite(self, compiler, connection):
        if isinstance(self.output_field, fields.DecimalField):
            # Casting to numeric must be outside of the window expression.
            copy = self.copy()
            source_expressions = copy.get_source_expressions()
            source_expressions[0].output_field = fields.FloatField()
            copy.set_source_expressions(source_expressions)
            return super(Window, copy).as_sqlite(compiler, connection)
        return self.as_sql(compiler, connection)

    def __str__(self):
        return "{} OVER ({}{}{})".format(
            str(self.source_expression),
            "PARTITION BY " + str(self.partition_by) if self.partition_by else "",
            str(self.order_by or ""),
            str(self.frame or ""),
        )

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self)

    def get_group_by_cols(self):
        group_by_cols = []
        if self.partition_by:
            group_by_cols.extend(self.partition_by.get_group_by_cols())
        if self.order_by is not None:
            group_by_cols.extend(self.order_by.get_group_by_cols())
        return group_by_cols


class WindowFrame(Expression):
    """
    Model the frame clause in window expressions. There are two types of frame
    clauses which are subclasses, however, all processing and validation (by no
    means intended to be complete) is done here. Thus, providing an end for a
    frame is optional (the default is UNBOUNDED FOLLOWING, which is the last
    row in the frame).
    """

    template = "%(frame_type)s BETWEEN %(start)s AND %(end)s"

    def __init__(self, start=None, end=None):
        self.start = Value(start)
        self.end = Value(end)

    def set_source_expressions(self, exprs):
        self.start, self.end = exprs

    def get_source_expressions(self):
        return [self.start, self.end]

    def as_sql(self, compiler, connection):
        connection.ops.check_expression_support(self)
        start, end = self.window_frame_start_end(
            connection, self.start.value, self.end.value
        )
        return (
            self.template
            % {
                "frame_type": self.frame_type,
                "start": start,
                "end": end,
            },
            [],
        )

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self)

    def get_group_by_cols(self):
        return []

    def __str__(self):
        if self.start.value is not None and self.start.value < 0:
            start = "%d %s" % (abs(self.start.value), connection.ops.PRECEDING)
        elif self.start.value is not None and self.start.value == 0:
            start = connection.ops.CURRENT_ROW
        else:
            start = connection.ops.UNBOUNDED_PRECEDING

        if self.end.value is not None and self.end.value > 0:
            end = "%d %s" % (self.end.value, connection.ops.FOLLOWING)
        elif self.end.value is not None and self.end.value == 0:
            end = connection.ops.CURRENT_ROW
        else:
            end = connection.ops.UNBOUNDED_FOLLOWING
        return self.template % {
            "frame_type": self.frame_type,
            "start": start,
            "end": end,
        }

    def window_frame_start_end(self, connection, start, end):
        raise NotImplementedError("Subclasses must implement window_frame_start_end().")


class RowRange(WindowFrame):
    frame_type = "ROWS"

    def window_frame_start_end(self, connection, start, end):
        return connection.ops.window_frame_rows_start_end(start, end)


class ValueRange(WindowFrame):
    frame_type = "RANGE"

    def window_frame_start_end(self, connection, start, end):
        return connection.ops.window_frame_range_start_end(start, end)

back/venvmsg/Lib/site-packages/django/db/models/expressions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""
"Rel objects" for related fields.

"Rel objects" (for lack of a better name) carry information about the relation
modeled by a related field and provide some utility functions. They're stored
in the ``remote_field`` attribute of the field.

They also act as reverse fields for the purposes of the Meta API because
they're the closest concept currently available.
"""

from django.core import exceptions
from django.utils.functional import cached_property
from django.utils.hashable import make_hashable

from . import BLANK_CHOICE_DASH
from .mixins import FieldCacheMixin


class ForeignObjectRel(FieldCacheMixin):
    """
    Used by ForeignObject to store information about the relation.

    ``_meta.get_fields()`` returns this class to provide access to the field
    flags for the reverse relation.
    """

    # Field flags
    auto_created = True
    concrete = False
    editable = False
    is_relation = True

    # Reverse relations are always nullable (Django can't enforce that a
    # foreign key on the related model points to this model).
    null = True
    empty_strings_allowed = False

    def __init__(
        self,
        field,
        to,
        related_name=None,
        related_query_name=None,
        limit_choices_to=None,
        parent_link=False,
        on_delete=None,
    ):
        self.field = field
        self.model = to
        self.related_name = related_name
        self.related_query_name = related_query_name
        self.limit_choices_to = {} if limit_choices_to is None else limit_choices_to
        self.parent_link = parent_link
        self.on_delete = on_delete

        self.symmetrical = False
        self.multiple = True

    # Some of the following cached_properties can't be initialized in
    # __init__ as the field doesn't have its model yet. Calling these methods
    # before field.contribute_to_class() has been called will result in
    # AttributeError
    @cached_property
    def hidden(self):
        return self.is_hidden()

    @cached_property
    def name(self):
        return self.field.related_query_name()

    @property
    def remote_field(self):
        return self.field

    @property
    def target_field(self):
        """
        When filtering against this relation, return the field on the remote
        model against which the filtering should happen.
        """
        target_fields = self.path_infos[-1].target_fields
        if len(target_fields) > 1:
            raise exceptions.FieldError(
                "Can't use target_field for multicolumn relations."
            )
        return target_fields[0]

    @cached_property
    def related_model(self):
        if not self.field.model:
            raise AttributeError(
                "This property can't be accessed before self.field.contribute_to_class "
                "has been called."
            )
        return self.field.model

    @cached_property
    def many_to_many(self):
        return self.field.many_to_many

    @cached_property
    def many_to_one(self):
        return self.field.one_to_many

    @cached_property
    def one_to_many(self):
        return self.field.many_to_one

    @cached_property
    def one_to_one(self):
        return self.field.one_to_one

    def get_lookup(self, lookup_name):
        return self.field.get_lookup(lookup_name)

    def get_lookups(self):
        return self.field.get_lookups()

    def get_transform(self, name):
        return self.field.get_transform(name)

    def get_internal_type(self):
        return self.field.get_internal_type()

    @property
    def db_type(self):
        return self.field.db_type

    def __repr__(self):
        return "<%s: %s.%s>" % (
            type(self).__name__,
            self.related_model._meta.app_label,
            self.related_model._meta.model_name,
        )

    @property
    def identity(self):
        return (
            self.field,
            self.model,
            self.related_name,
            self.related_query_name,
            make_hashable(self.limit_choices_to),
            self.parent_link,
            self.on_delete,
            self.symmetrical,
            self.multiple,
        )

    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented
        return self.identity == other.identity

    def __hash__(self):
        return hash(self.identity)

    def __getstate__(self):
        state = self.__dict__.copy()
        # Delete the path_infos cached property because it can be recalculated
        # at first invocation after deserialization. The attribute must be
        # removed because subclasses like ManyToOneRel may have a PathInfo
        # which contains an intermediate M2M table that's been dynamically
        # created and doesn't exist in the .models module.
        # This is a reverse relation, so there is no reverse_path_infos to
        # delete.
        state.pop("path_infos", None)
        return state

    def get_choices(
        self,
        include_blank=True,
        blank_choice=BLANK_CHOICE_DASH,
        limit_choices_to=None,
        ordering=(),
    ):
        """
        Return choices with a default blank choices included, for use
        as <select> choices for this field.

        Analog of django.db.models.fields.Field.get_choices(), provided
        initially for utilization by RelatedFieldListFilter.
        """
        limit_choices_to = limit_choices_to or self.limit_choices_to
        qs = self.related_model._default_manager.complex_filter(limit_choices_to)
        if ordering:
            qs = qs.order_by(*ordering)
        return (blank_choice if include_blank else []) + [(x.pk, str(x)) for x in qs]

    def is_hidden(self):
        """Should the related object be hidden?"""
        return bool(self.related_name) and self.related_name[-1] == "+"

    def get_joining_columns(self):
        return self.field.get_reverse_joining_columns()

    def get_extra_restriction(self, alias, related_alias):
        return self.field.get_extra_restriction(related_alias, alias)

    def set_field_name(self):
        """
        Set the related field's name, this is not available until later stages
        of app loading, so set_field_name is called from
        set_attributes_from_rel()
        """
        # By default foreign object doesn't relate to any remote field (for
        # example custom multicolumn joins currently have no remote field).
        self.field_name = None

    def get_accessor_name(self, model=None):
        # This method encapsulates the logic that decides what name to give an
        # accessor descriptor that retrieves related many-to-one or
        # many-to-many objects. It uses the lowercased object_name + "_set",
        # but this can be overridden with the "related_name" option. Due to
        # backwards compatibility ModelForms need to be able to provide an
        # alternate model. See BaseInlineFormSet.get_default_prefix().
        opts = model._meta if model else self.related_model._meta
        model = model or self.related_model
        if self.multiple:
            # If this is a symmetrical m2m relation on self, there is no
            # reverse accessor.
            if self.symmetrical and model == self.model:
                return None
        if self.related_name:
            return self.related_name
        return opts.model_name + ("_set" if self.multiple else "")

    def get_path_info(self, filtered_relation=None):
        if filtered_relation:
            return self.field.get_reverse_path_info(filtered_relation)
        else:
            return self.field.reverse_path_infos

    @cached_property
    def path_infos(self):
        return self.get_path_info()

    def get_cache_name(self):
        """
        Return the name of the cache key to use for storing an instance of the
        forward model on the reverse model.
        """
        return self.get_accessor_name()


class ManyToOneRel(ForeignObjectRel):
    """
    Used by the ForeignKey field to store information about the relation.

    ``_meta.get_fields()`` returns this class to provide access to the field
    flags for the reverse relation.

    Note: Because we somewhat abuse the Rel objects by using them as reverse
    fields we get the funny situation where
    ``ManyToOneRel.many_to_one == False`` and
    ``ManyToOneRel.one_to_many == True``. This is unfortunate but the actual
    ManyToOneRel class is a private API and there is work underway to turn
    reverse relations into actual fields.
    """

    def __init__(
        self,
        field,
        to,
        field_name,
        related_name=None,
        related_query_name=None,
        limit_choices_to=None,
        parent_link=False,
        on_delete=None,
    ):
        super().__init__(
            field,
            to,
            related_name=related_name,
            related_query_name=related_query_name,
            limit_choices_to=limit_choices_to,
            parent_link=parent_link,
            on_delete=on_delete,
        )

        self.field_name = field_name

    def __getstate__(self):
        state = super().__getstate__()
        state.pop("related_model", None)
        return state

    @property
    def identity(self):
        return super().identity + (self.field_name,)

    def get_related_field(self):
        """
        Return the Field in the 'to' object to which this relationship is tied.
        """
        field = self.model._meta.get_field(self.field_name)
        if not field.concrete:
            raise exceptions.FieldDoesNotExist(
                "No related field named '%s'" % self.field_name
            )
        return field

    def set_field_name(self):
        self.field_name = self.field_name or self.model._meta.pk.name


class OneToOneRel(ManyToOneRel):
    """
    Used by OneToOneField to store information about the relation.

    ``_meta.get_fields()`` returns this class to provide access to the field
    flags for the reverse relation.
    """

    def __init__(
        self,
        field,
        to,
        field_name,
        related_name=None,
        related_query_name=None,
        limit_choices_to=None,
        parent_link=False,
        on_delete=None,
    ):
        super().__init__(
            field,
            to,
            field_name,
            related_name=related_name,
            related_query_name=related_query_name,
            limit_choices_to=limit_choices_to,
            parent_link=parent_link,
            on_delete=on_delete,
        )

        self.multiple = False


class ManyToManyRel(ForeignObjectRel):
    """
    Used by ManyToManyField to store information about the relation.

    ``_meta.get_fields()`` returns this class to provide access to the field
    flags for the reverse relation.
    """

    def __init__(
        self,
        field,
        to,
        related_name=None,
        related_query_name=None,
        limit_choices_to=None,
        symmetrical=True,
        through=None,
        through_fields=None,
        db_constraint=True,
    ):
        super().__init__(
            field,
            to,
            related_name=related_name,
            related_query_name=related_query_name,
            limit_choices_to=limit_choices_to,
        )

        if through and not db_constraint:
            raise ValueError("Can't supply a through model and db_constraint=False")
        self.through = through

        if through_fields and not through:
            raise ValueError("Cannot specify through_fields without a through model")
        self.through_fields = through_fields

        self.symmetrical = symmetrical
        self.db_constraint = db_constraint

    @property
    def identity(self):
        return super().identity + (
            self.through,
            make_hashable(self.through_fields),
            self.db_constraint,
        )

    def get_related_field(self):
        """
        Return the field in the 'to' object to which this relationship is tied.
        Provided for symmetry with ManyToOneRel.
        """
        opts = self.through._meta
        if self.through_fields:
            field = opts.get_field(self.through_fields[0])
        else:
            for field in opts.fields:
                rel = getattr(field, "remote_field", None)
                if rel and rel.model == self.model:
                    break
        return field.foreign_related_fields[0]

back/venvmsg/Lib/site-packages/django/db/models/fields/reverse_related.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import itertools
import math

from django.core.exceptions import EmptyResultSet, FullResultSet
from django.db.models.expressions import Case, Expression, Func, Value, When
from django.db.models.fields import (
    BooleanField,
    CharField,
    DateTimeField,
    Field,
    IntegerField,
    UUIDField,
)
from django.db.models.query_utils import RegisterLookupMixin
from django.utils.datastructures import OrderedSet
from django.utils.functional import cached_property
from django.utils.hashable import make_hashable


class Lookup(Expression):
    lookup_name = None
    prepare_rhs = True
    can_use_none_as_rhs = False

    def __init__(self, lhs, rhs):
        self.lhs, self.rhs = lhs, rhs
        self.rhs = self.get_prep_lookup()
        self.lhs = self.get_prep_lhs()
        if hasattr(self.lhs, "get_bilateral_transforms"):
            bilateral_transforms = self.lhs.get_bilateral_transforms()
        else:
            bilateral_transforms = []
        if bilateral_transforms:
            # Warn the user as soon as possible if they are trying to apply
            # a bilateral transformation on a nested QuerySet: that won't work.
            from django.db.models.sql.query import Query  # avoid circular import

            if isinstance(rhs, Query):
                raise NotImplementedError(
                    "Bilateral transformations on nested querysets are not implemented."
                )
        self.bilateral_transforms = bilateral_transforms

    def apply_bilateral_transforms(self, value):
        for transform in self.bilateral_transforms:
            value = transform(value)
        return value

    def __repr__(self):
        return f"{self.__class__.__name__}({self.lhs!r}, {self.rhs!r})"

    def batch_process_rhs(self, compiler, connection, rhs=None):
        if rhs is None:
            rhs = self.rhs
        if self.bilateral_transforms:
            sqls, sqls_params = [], []
            for p in rhs:
                value = Value(p, output_field=self.lhs.output_field)
                value = self.apply_bilateral_transforms(value)
                value = value.resolve_expression(compiler.query)
                sql, sql_params = compiler.compile(value)
                sqls.append(sql)
                sqls_params.extend(sql_params)
        else:
            _, params = self.get_db_prep_lookup(rhs, connection)
            sqls, sqls_params = ["%s"] * len(params), params
        return sqls, sqls_params

    def get_source_expressions(self):
        if self.rhs_is_direct_value():
            return [self.lhs]
        return [self.lhs, self.rhs]

    def set_source_expressions(self, new_exprs):
        if len(new_exprs) == 1:
            self.lhs = new_exprs[0]
        else:
            self.lhs, self.rhs = new_exprs

    def get_prep_lookup(self):
        if not self.prepare_rhs or hasattr(self.rhs, "resolve_expression"):
            return self.rhs
        if hasattr(self.lhs, "output_field"):
            if hasattr(self.lhs.output_field, "get_prep_value"):
                return self.lhs.output_field.get_prep_value(self.rhs)
        elif self.rhs_is_direct_value():
            return Value(self.rhs)
        return self.rhs

    def get_prep_lhs(self):
        if hasattr(self.lhs, "resolve_expression"):
            return self.lhs
        return Value(self.lhs)

    def get_db_prep_lookup(self, value, connection):
        return ("%s", [value])

    def process_lhs(self, compiler, connection, lhs=None):
        lhs = lhs or self.lhs
        if hasattr(lhs, "resolve_expression"):
            lhs = lhs.resolve_expression(compiler.query)
        sql, params = compiler.compile(lhs)
        if isinstance(lhs, Lookup):
            # Wrapped in parentheses to respect operator precedence.
            sql = f"({sql})"
        return sql, params

    def process_rhs(self, compiler, connection):
        value = self.rhs
        if self.bilateral_transforms:
            if self.rhs_is_direct_value():
                # Do not call get_db_prep_lookup here as the value will be
                # transformed before being used for lookup
                value = Value(value, output_field=self.lhs.output_field)
            value = self.apply_bilateral_transforms(value)
            value = value.resolve_expression(compiler.query)
        if hasattr(value, "as_sql"):
            sql, params = compiler.compile(value)
            # Ensure expression is wrapped in parentheses to respect operator
            # precedence but avoid double wrapping as it can be misinterpreted
            # on some backends (e.g. subqueries on SQLite).
            if sql and sql[0] != "(":
                sql = "(%s)" % sql
            return sql, params
        else:
            return self.get_db_prep_lookup(value, connection)

    def rhs_is_direct_value(self):
        return not hasattr(self.rhs, "as_sql")

    def get_group_by_cols(self):
        cols = []
        for source in self.get_source_expressions():
            cols.extend(source.get_group_by_cols())
        return cols

    def as_oracle(self, compiler, connection):
        # Oracle doesn't allow EXISTS() and filters to be compared to another
        # expression unless they're wrapped in a CASE WHEN.
        wrapped = False
        exprs = []
        for expr in (self.lhs, self.rhs):
            if connection.ops.conditional_expression_supported_in_where_clause(expr):
                expr = Case(When(expr, then=True), default=False)
                wrapped = True
            exprs.append(expr)
        lookup = type(self)(*exprs) if wrapped else self
        return lookup.as_sql(compiler, connection)

    @cached_property
    def output_field(self):
        return BooleanField()

    @property
    def identity(self):
        return self.__class__, self.lhs, self.rhs

    def __eq__(self, other):
        if not isinstance(other, Lookup):
            return NotImplemented
        return self.identity == other.identity

    def __hash__(self):
        return hash(make_hashable(self.identity))

    def resolve_expression(
        self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False
    ):
        c = self.copy()
        c.is_summary = summarize
        c.lhs = self.lhs.resolve_expression(
            query, allow_joins, reuse, summarize, for_save
        )
        if hasattr(self.rhs, "resolve_expression"):
            c.rhs = self.rhs.resolve_expression(
                query, allow_joins, reuse, summarize, for_save
            )
        return c

    def select_format(self, compiler, sql, params):
        # Wrap filters with a CASE WHEN expression if a database backend
        # (e.g. Oracle) doesn't support boolean expression in SELECT or GROUP
        # BY list.
        if not compiler.connection.features.supports_boolean_expr_in_select_clause:
            sql = f"CASE WHEN {sql} THEN 1 ELSE 0 END"
        return sql, params


class Transform(RegisterLookupMixin, Func):
    """
    RegisterLookupMixin() is first so that get_lookup() and get_transform()
    first examine self and then check output_field.
    """

    bilateral = False
    arity = 1

    @property
    def lhs(self):
        return self.get_source_expressions()[0]

    def get_bilateral_transforms(self):
        if hasattr(self.lhs, "get_bilateral_transforms"):
            bilateral_transforms = self.lhs.get_bilateral_transforms()
        else:
            bilateral_transforms = []
        if self.bilateral:
            bilateral_transforms.append(self.__class__)
        return bilateral_transforms


class BuiltinLookup(Lookup):
    def process_lhs(self, compiler, connection, lhs=None):
        lhs_sql, params = super().process_lhs(compiler, connection, lhs)
        field_internal_type = self.lhs.output_field.get_internal_type()
        db_type = self.lhs.output_field.db_type(connection=connection)
        lhs_sql = connection.ops.field_cast_sql(db_type, field_internal_type) % lhs_sql
        lhs_sql = (
            connection.ops.lookup_cast(self.lookup_name, field_internal_type) % lhs_sql
        )
        return lhs_sql, list(params)

    def as_sql(self, compiler, connection):
        lhs_sql, params = self.process_lhs(compiler, connection)
        rhs_sql, rhs_params = self.process_rhs(compiler, connection)
        params.extend(rhs_params)
        rhs_sql = self.get_rhs_op(connection, rhs_sql)
        return "%s %s" % (lhs_sql, rhs_sql), params

    def get_rhs_op(self, connection, rhs):
        return connection.operators[self.lookup_name] % rhs


class FieldGetDbPrepValueMixin:
    """
    Some lookups require Field.get_db_prep_value() to be called on their
    inputs.
    """

    get_db_prep_lookup_value_is_iterable = False

    def get_db_prep_lookup(self, value, connection):
        # For relational fields, use the 'target_field' attribute of the
        # output_field.
        field = getattr(self.lhs.output_field, "target_field", None)
        get_db_prep_value = (
            getattr(field, "get_db_prep_value", None)
            or self.lhs.output_field.get_db_prep_value
        )
        return (
            "%s",
            [get_db_prep_value(v, connection, prepared=True) for v in value]
            if self.get_db_prep_lookup_value_is_iterable
            else [get_db_prep_value(value, connection, prepared=True)],
        )


class FieldGetDbPrepValueIterableMixin(FieldGetDbPrepValueMixin):
    """
    Some lookups require Field.get_db_prep_value() to be called on each value
    in an iterable.
    """

    get_db_prep_lookup_value_is_iterable = True

    def get_prep_lookup(self):
        if hasattr(self.rhs, "resolve_expression"):
            return self.rhs
        prepared_values = []
        for rhs_value in self.rhs:
            if hasattr(rhs_value, "resolve_expression"):
                # An expression will be handled by the database but can coexist
                # alongside real values.
                pass
            elif self.prepare_rhs and hasattr(self.lhs.output_field, "get_prep_value"):
                rhs_value = self.lhs.output_field.get_prep_value(rhs_value)
            prepared_values.append(rhs_value)
        return prepared_values

    def process_rhs(self, compiler, connection):
        if self.rhs_is_direct_value():
            # rhs should be an iterable of values. Use batch_process_rhs()
            # to prepare/transform those values.
            return self.batch_process_rhs(compiler, connection)
        else:
            return super().process_rhs(compiler, connection)

    def resolve_expression_parameter(self, compiler, connection, sql, param):
        params = [param]
        if hasattr(param, "resolve_expression"):
            param = param.resolve_expression(compiler.query)
        if hasattr(param, "as_sql"):
            sql, params = compiler.compile(param)
        return sql, params

    def batch_process_rhs(self, compiler, connection, rhs=None):
        pre_processed = super().batch_process_rhs(compiler, connection, rhs)
        # The params list may contain expressions which compile to a
        # sql/param pair. Zip them to get sql and param pairs that refer to the
        # same argument and attempt to replace them with the result of
        # compiling the param step.
        sql, params = zip(
            *(
                self.resolve_expression_parameter(compiler, connection, sql, param)
                for sql, param in zip(*pre_processed)
            )
        )
        params = itertools.chain.from_iterable(params)
        return sql, tuple(params)


class PostgresOperatorLookup(Lookup):
    """Lookup defined by operators on PostgreSQL."""

    postgres_operator = None

    def as_postgresql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        params = tuple(lhs_params) + tuple(rhs_params)
        return "%s %s %s" % (lhs, self.postgres_operator, rhs), params


@Field.register_lookup
class Exact(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "exact"

    def get_prep_lookup(self):
        from django.db.models.sql.query import Query  # avoid circular import

        if isinstance(self.rhs, Query):
            if self.rhs.has_limit_one():
                if not self.rhs.has_select_fields:
                    self.rhs.clear_select_clause()
                    self.rhs.add_fields(["pk"])
            else:
                raise ValueError(
                    "The QuerySet value for an exact lookup must be limited to "
                    "one result using slicing."
                )
        return super().get_prep_lookup()

    def as_sql(self, compiler, connection):
        # Avoid comparison against direct rhs if lhs is a boolean value. That
        # turns "boolfield__exact=True" into "WHERE boolean_field" instead of
        # "WHERE boolean_field = True" when allowed.
        if (
            isinstance(self.rhs, bool)
            and getattr(self.lhs, "conditional", False)
            and connection.ops.conditional_expression_supported_in_where_clause(
                self.lhs
            )
        ):
            lhs_sql, params = self.process_lhs(compiler, connection)
            template = "%s" if self.rhs else "NOT %s"
            return template % lhs_sql, params
        return super().as_sql(compiler, connection)


@Field.register_lookup
class IExact(BuiltinLookup):
    lookup_name = "iexact"
    prepare_rhs = False

    def process_rhs(self, qn, connection):
        rhs, params = super().process_rhs(qn, connection)
        if params:
            params[0] = connection.ops.prep_for_iexact_query(params[0])
        return rhs, params


@Field.register_lookup
class GreaterThan(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "gt"


@Field.register_lookup
class GreaterThanOrEqual(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "gte"


@Field.register_lookup
class LessThan(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "lt"


@Field.register_lookup
class LessThanOrEqual(FieldGetDbPrepValueMixin, BuiltinLookup):
    lookup_name = "lte"


class IntegerFieldFloatRounding:
    """
    Allow floats to work as query values for IntegerField. Without this, the
    decimal portion of the float would always be discarded.
    """

    def get_prep_lookup(self):
        if isinstance(self.rhs, float):
            self.rhs = math.ceil(self.rhs)
        return super().get_prep_lookup()


@IntegerField.register_lookup
class IntegerGreaterThanOrEqual(IntegerFieldFloatRounding, GreaterThanOrEqual):
    pass


@IntegerField.register_lookup
class IntegerLessThan(IntegerFieldFloatRounding, LessThan):
    pass


@Field.register_lookup
class In(FieldGetDbPrepValueIterableMixin, BuiltinLookup):
    lookup_name = "in"

    def get_prep_lookup(self):
        from django.db.models.sql.query import Query  # avoid circular import

        if isinstance(self.rhs, Query):
            self.rhs.clear_ordering(clear_default=True)
            if not self.rhs.has_select_fields:
                self.rhs.clear_select_clause()
                self.rhs.add_fields(["pk"])
        return super().get_prep_lookup()

    def process_rhs(self, compiler, connection):
        db_rhs = getattr(self.rhs, "_db", None)
        if db_rhs is not None and db_rhs != connection.alias:
            raise ValueError(
                "Subqueries aren't allowed across different databases. Force "
                "the inner query to be evaluated using `list(inner_query)`."
            )

        if self.rhs_is_direct_value():
            # Remove None from the list as NULL is never equal to anything.
            try:
                rhs = OrderedSet(self.rhs)
                rhs.discard(None)
            except TypeError:  # Unhashable items in self.rhs
                rhs = [r for r in self.rhs if r is not None]

            if not rhs:
                raise EmptyResultSet

            # rhs should be an iterable; use batch_process_rhs() to
            # prepare/transform those values.
            sqls, sqls_params = self.batch_process_rhs(compiler, connection, rhs)
            placeholder = "(" + ", ".join(sqls) + ")"
            return (placeholder, sqls_params)
        return super().process_rhs(compiler, connection)

    def get_rhs_op(self, connection, rhs):
        return "IN %s" % rhs

    def as_sql(self, compiler, connection):
        max_in_list_size = connection.ops.max_in_list_size()
        if (
            self.rhs_is_direct_value()
            and max_in_list_size
            and len(self.rhs) > max_in_list_size
        ):
            return self.split_parameter_list_as_sql(compiler, connection)
        return super().as_sql(compiler, connection)

    def split_parameter_list_as_sql(self, compiler, connection):
        # This is a special case for databases which limit the number of
        # elements which can appear in an 'IN' clause.
        max_in_list_size = connection.ops.max_in_list_size()
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.batch_process_rhs(compiler, connection)
        in_clause_elements = ["("]
        params = []
        for offset in range(0, len(rhs_params), max_in_list_size):
            if offset > 0:
                in_clause_elements.append(" OR ")
            in_clause_elements.append("%s IN (" % lhs)
            params.extend(lhs_params)
            sqls = rhs[offset : offset + max_in_list_size]
            sqls_params = rhs_params[offset : offset + max_in_list_size]
            param_group = ", ".join(sqls)
            in_clause_elements.append(param_group)
            in_clause_elements.append(")")
            params.extend(sqls_params)
        in_clause_elements.append(")")
        return "".join(in_clause_elements), params


class PatternLookup(BuiltinLookup):
    param_pattern = "%%%s%%"
    prepare_rhs = False

    def get_rhs_op(self, connection, rhs):
        # Assume we are in startswith. We need to produce SQL like:
        #     col LIKE %s, ['thevalue%']
        # For python values we can (and should) do that directly in Python,
        # but if the value is for example reference to other column, then
        # we need to add the % pattern match to the lookup by something like
        #     col LIKE othercol || '%%'
        # So, for Python values we don't need any special pattern, but for
        # SQL reference values or SQL transformations we need the correct
        # pattern added.
        if hasattr(self.rhs, "as_sql") or self.bilateral_transforms:
            pattern = connection.pattern_ops[self.lookup_name].format(
                connection.pattern_esc
            )
            return pattern.format(rhs)
        else:
            return super().get_rhs_op(connection, rhs)

    def process_rhs(self, qn, connection):
        rhs, params = super().process_rhs(qn, connection)
        if self.rhs_is_direct_value() and params and not self.bilateral_transforms:
            params[0] = self.param_pattern % connection.ops.prep_for_like_query(
                params[0]
            )
        return rhs, params


@Field.register_lookup
class Contains(PatternLookup):
    lookup_name = "contains"


@Field.register_lookup
class IContains(Contains):
    lookup_name = "icontains"


@Field.register_lookup
class StartsWith(PatternLookup):
    lookup_name = "startswith"
    param_pattern = "%s%%"


@Field.register_lookup
class IStartsWith(StartsWith):
    lookup_name = "istartswith"


@Field.register_lookup
class EndsWith(PatternLookup):
    lookup_name = "endswith"
    param_pattern = "%%%s"


@Field.register_lookup
class IEndsWith(EndsWith):
    lookup_name = "iendswith"


@Field.register_lookup
class Range(FieldGetDbPrepValueIterableMixin, BuiltinLookup):
    lookup_name = "range"

    def get_rhs_op(self, connection, rhs):
        return "BETWEEN %s AND %s" % (rhs[0], rhs[1])


@Field.register_lookup
class IsNull(BuiltinLookup):
    lookup_name = "isnull"
    prepare_rhs = False

    def as_sql(self, compiler, connection):
        if not isinstance(self.rhs, bool):
            raise ValueError(
                "The QuerySet value for an isnull lookup must be True or False."
            )
        if isinstance(self.lhs, Value) and self.lhs.value is None:
            if self.rhs:
                raise FullResultSet
            else:
                raise EmptyResultSet
        sql, params = self.process_lhs(compiler, connection)
        if self.rhs:
            return "%s IS NULL" % sql, params
        else:
            return "%s IS NOT NULL" % sql, params


@Field.register_lookup
class Regex(BuiltinLookup):
    lookup_name = "regex"
    prepare_rhs = False

    def as_sql(self, compiler, connection):
        if self.lookup_name in connection.operators:
            return super().as_sql(compiler, connection)
        else:
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            sql_template = connection.ops.regex_lookup(self.lookup_name)
            return sql_template % (lhs, rhs), lhs_params + rhs_params


@Field.register_lookup
class IRegex(Regex):
    lookup_name = "iregex"


class YearLookup(Lookup):
    def year_lookup_bounds(self, connection, year):
        from django.db.models.functions import ExtractIsoYear

        iso_year = isinstance(self.lhs, ExtractIsoYear)
        output_field = self.lhs.lhs.output_field
        if isinstance(output_field, DateTimeField):
            bounds = connection.ops.year_lookup_bounds_for_datetime_field(
                year,
                iso_year=iso_year,
            )
        else:
            bounds = connection.ops.year_lookup_bounds_for_date_field(
                year,
                iso_year=iso_year,
            )
        return bounds

    def as_sql(self, compiler, connection):
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            rhs_sql, _ = self.process_rhs(compiler, connection)
            rhs_sql = self.get_direct_rhs_sql(connection, rhs_sql)
            start, finish = self.year_lookup_bounds(connection, self.rhs)
            params.extend(self.get_bound_params(start, finish))
            return "%s %s" % (lhs_sql, rhs_sql), params
        return super().as_sql(compiler, connection)

    def get_direct_rhs_sql(self, connection, rhs):
        return connection.operators[self.lookup_name] % rhs

    def get_bound_params(self, start, finish):
        raise NotImplementedError(
            "subclasses of YearLookup must provide a get_bound_params() method"
        )


class YearExact(YearLookup, Exact):
    def get_direct_rhs_sql(self, connection, rhs):
        return "BETWEEN %s AND %s"

    def get_bound_params(self, start, finish):
        return (start, finish)


class YearGt(YearLookup, GreaterThan):
    def get_bound_params(self, start, finish):
        return (finish,)


class YearGte(YearLookup, GreaterThanOrEqual):
    def get_bound_params(self, start, finish):
        return (start,)


class YearLt(YearLookup, LessThan):
    def get_bound_params(self, start, finish):
        return (start,)


class YearLte(YearLookup, LessThanOrEqual):
    def get_bound_params(self, start, finish):
        return (finish,)


class UUIDTextMixin:
    """
    Strip hyphens from a value when filtering a UUIDField on backends without
    a native datatype for UUID.
    """

    def process_rhs(self, qn, connection):
        if not connection.features.has_native_uuid_field:
            from django.db.models.functions import Replace

            if self.rhs_is_direct_value():
                self.rhs = Value(self.rhs)
            self.rhs = Replace(
                self.rhs, Value("-"), Value(""), output_field=CharField()
            )
        rhs, params = super().process_rhs(qn, connection)
        return rhs, params


@UUIDField.register_lookup
class UUIDIExact(UUIDTextMixin, IExact):
    pass


@UUIDField.register_lookup
class UUIDContains(UUIDTextMixin, Contains):
    pass


@UUIDField.register_lookup
class UUIDIContains(UUIDTextMixin, IContains):
    pass


@UUIDField.register_lookup
class UUIDStartsWith(UUIDTextMixin, StartsWith):
    pass


@UUIDField.register_lookup
class UUIDIStartsWith(UUIDTextMixin, IStartsWith):
    pass


@UUIDField.register_lookup
class UUIDEndsWith(UUIDTextMixin, EndsWith):
    pass


@UUIDField.register_lookup
class UUIDIEndsWith(UUIDTextMixin, IEndsWith):
    pass

back/venvmsg/Lib/site-packages/django/db/models/lookups.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""
PHP date() style date formatting
See https://www.php.net/date for format strings

Usage:
>>> from datetime import datetime
>>> d = datetime.now()
>>> df = DateFormat(d)
>>> print(df.format('jS F Y H:i'))
7th October 2003 11:39
>>>
"""
import calendar
from datetime import date, datetime, time
from email.utils import format_datetime as format_datetime_rfc5322

from django.utils.dates import (
    MONTHS,
    MONTHS_3,
    MONTHS_ALT,
    MONTHS_AP,
    WEEKDAYS,
    WEEKDAYS_ABBR,
)
from django.utils.regex_helper import _lazy_re_compile
from django.utils.timezone import (
    _datetime_ambiguous_or_imaginary,
    get_default_timezone,
    is_naive,
    make_aware,
)
from django.utils.translation import gettext as _

re_formatchars = _lazy_re_compile(r"(?<!\\)([aAbcdDeEfFgGhHiIjlLmMnNoOPrsStTUuwWyYzZ])")
re_escaped = _lazy_re_compile(r"\\(.)")


class Formatter:
    def format(self, formatstr):
        pieces = []
        for i, piece in enumerate(re_formatchars.split(str(formatstr))):
            if i % 2:
                if type(self.data) is date and hasattr(TimeFormat, piece):
                    raise TypeError(
                        "The format for date objects may not contain "
                        "time-related format specifiers (found '%s')." % piece
                    )
                pieces.append(str(getattr(self, piece)()))
            elif piece:
                pieces.append(re_escaped.sub(r"\1", piece))
        return "".join(pieces)


class TimeFormat(Formatter):
    def __init__(self, obj):
        self.data = obj
        self.timezone = None

        if isinstance(obj, datetime):
            # Timezone is only supported when formatting datetime objects, not
            # date objects (timezone information not appropriate), or time
            # objects (against established django policy).
            if is_naive(obj):
                timezone = get_default_timezone()
            else:
                timezone = obj.tzinfo
            if not _datetime_ambiguous_or_imaginary(obj, timezone):
                self.timezone = timezone

    def a(self):
        "'a.m.' or 'p.m.'"
        if self.data.hour > 11:
            return _("p.m.")
        return _("a.m.")

    def A(self):
        "'AM' or 'PM'"
        if self.data.hour > 11:
            return _("PM")
        return _("AM")

    def e(self):
        """
        Timezone name.

        If timezone information is not available, return an empty string.
        """
        if not self.timezone:
            return ""

        try:
            if getattr(self.data, "tzinfo", None):
                return self.data.tzname() or ""
        except NotImplementedError:
            pass
        return ""

    def f(self):
        """
        Time, in 12-hour hours and minutes, with minutes left off if they're
        zero.
        Examples: '1', '1:30', '2:05', '2'
        Proprietary extension.
        """
        hour = self.data.hour % 12 or 12
        minute = self.data.minute
        return "%d:%02d" % (hour, minute) if minute else hour

    def g(self):
        "Hour, 12-hour format without leading zeros; i.e. '1' to '12'"
        return self.data.hour % 12 or 12

    def G(self):
        "Hour, 24-hour format without leading zeros; i.e. '0' to '23'"
        return self.data.hour

    def h(self):
        "Hour, 12-hour format; i.e. '01' to '12'"
        return "%02d" % (self.data.hour % 12 or 12)

    def H(self):
        "Hour, 24-hour format; i.e. '00' to '23'"
        return "%02d" % self.data.hour

    def i(self):
        "Minutes; i.e. '00' to '59'"
        return "%02d" % self.data.minute

    def O(self):  # NOQA: E743, E741
        """
        Difference to Greenwich time in hours; e.g. '+0200', '-0430'.

        If timezone information is not available, return an empty string.
        """
        if self.timezone is None:
            return ""

        offset = self.timezone.utcoffset(self.data)
        seconds = offset.days * 86400 + offset.seconds
        sign = "-" if seconds < 0 else "+"
        seconds = abs(seconds)
        return "%s%02d%02d" % (sign, seconds // 3600, (seconds // 60) % 60)

    def P(self):
        """
        Time, in 12-hour hours, minutes and 'a.m.'/'p.m.', with minutes left off
        if they're zero and the strings 'midnight' and 'noon' if appropriate.
        Examples: '1 a.m.', '1:30 p.m.', 'midnight', 'noon', '12:30 p.m.'
        Proprietary extension.
        """
        if self.data.minute == 0 and self.data.hour == 0:
            return _("midnight")
        if self.data.minute == 0 and self.data.hour == 12:
            return _("noon")
        return "%s %s" % (self.f(), self.a())

    def s(self):
        "Seconds; i.e. '00' to '59'"
        return "%02d" % self.data.second

    def T(self):
        """
        Time zone of this machine; e.g. 'EST' or 'MDT'.

        If timezone information is not available, return an empty string.
        """
        if self.timezone is None:
            return ""

        return str(self.timezone.tzname(self.data))

    def u(self):
        "Microseconds; i.e. '000000' to '999999'"
        return "%06d" % self.data.microsecond

    def Z(self):
        """
        Time zone offset in seconds (i.e. '-43200' to '43200'). The offset for
        timezones west of UTC is always negative, and for those east of UTC is
        always positive.

        If timezone information is not available, return an empty string.
        """
        if self.timezone is None:
            return ""

        offset = self.timezone.utcoffset(self.data)

        # `offset` is a datetime.timedelta. For negative values (to the west of
        # UTC) only days can be negative (days=-1) and seconds are always
        # positive. e.g. UTC-1 -> timedelta(days=-1, seconds=82800, microseconds=0)
        # Positive offsets have days=0
        return offset.days * 86400 + offset.seconds


class DateFormat(TimeFormat):
    def b(self):
        "Month, textual, 3 letters, lowercase; e.g. 'jan'"
        return MONTHS_3[self.data.month]

    def c(self):
        """
        ISO 8601 Format
        Example : '2008-01-02T10:30:00.000123'
        """
        return self.data.isoformat()

    def d(self):
        "Day of the month, 2 digits with leading zeros; i.e. '01' to '31'"
        return "%02d" % self.data.day

    def D(self):
        "Day of the week, textual, 3 letters; e.g. 'Fri'"
        return WEEKDAYS_ABBR[self.data.weekday()]

    def E(self):
        "Alternative month names as required by some locales. Proprietary extension."
        return MONTHS_ALT[self.data.month]

    def F(self):
        "Month, textual, long; e.g. 'January'"
        return MONTHS[self.data.month]

    def I(self):  # NOQA: E743, E741
        "'1' if daylight saving time, '0' otherwise."
        if self.timezone is None:
            return ""
        return "1" if self.timezone.dst(self.data) else "0"

    def j(self):
        "Day of the month without leading zeros; i.e. '1' to '31'"
        return self.data.day

    def l(self):  # NOQA: E743, E741
        "Day of the week, textual, long; e.g. 'Friday'"
        return WEEKDAYS[self.data.weekday()]

    def L(self):
        "Boolean for whether it is a leap year; i.e. True or False"
        return calendar.isleap(self.data.year)

    def m(self):
        "Month; i.e. '01' to '12'"
        return "%02d" % self.data.month

    def M(self):
        "Month, textual, 3 letters; e.g. 'Jan'"
        return MONTHS_3[self.data.month].title()

    def n(self):
        "Month without leading zeros; i.e. '1' to '12'"
        return self.data.month

    def N(self):
        "Month abbreviation in Associated Press style. Proprietary extension."
        return MONTHS_AP[self.data.month]

    def o(self):
        "ISO 8601 year number matching the ISO week number (W)"
        return self.data.isocalendar()[0]

    def r(self):
        "RFC 5322 formatted date; e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'"
        value = self.data
        if not isinstance(value, datetime):
            # Assume midnight in default timezone if datetime.date provided.
            default_timezone = get_default_timezone()
            value = datetime.combine(value, time.min).replace(tzinfo=default_timezone)
        elif is_naive(value):
            value = make_aware(value, timezone=self.timezone)
        return format_datetime_rfc5322(value)

    def S(self):
        """
        English ordinal suffix for the day of the month, 2 characters; i.e.
        'st', 'nd', 'rd' or 'th'.
        """
        if self.data.day in (11, 12, 13):  # Special case
            return "th"
        last = self.data.day % 10
        if last == 1:
            return "st"
        if last == 2:
            return "nd"
        if last == 3:
            return "rd"
        return "th"

    def t(self):
        "Number of days in the given month; i.e. '28' to '31'"
        return calendar.monthrange(self.data.year, self.data.month)[1]

    def U(self):
        "Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)"
        value = self.data
        if not isinstance(value, datetime):
            value = datetime.combine(value, time.min)
        return int(value.timestamp())

    def w(self):
        "Day of the week, numeric, i.e. '0' (Sunday) to '6' (Saturday)"
        return (self.data.weekday() + 1) % 7

    def W(self):
        "ISO-8601 week number of year, weeks starting on Monday"
        return self.data.isocalendar()[1]

    def y(self):
        """Year, 2 digits with leading zeros; e.g. '99'."""
        return "%02d" % (self.data.year % 100)

    def Y(self):
        """Year, 4 digits with leading zeros; e.g. '1999'."""
        return "%04d" % self.data.year

    def z(self):
        """Day of the year, i.e. 1 to 366."""
        return self.data.timetuple().tm_yday


def format(value, format_string):
    "Convenience function"
    df = DateFormat(value)
    return df.format(format_string)


def time_format(value, format_string):
    "Convenience function"
    tf = TimeFormat(value)
    return tf.format(format_string)

back/venvmsg/Lib/site-packages/django/utils/dateformat.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import codecs
import datetime
import locale
from decimal import Decimal
from urllib.parse import quote

from django.utils.functional import Promise


class DjangoUnicodeDecodeError(UnicodeDecodeError):
    def __init__(self, obj, *args):
        self.obj = obj
        super().__init__(*args)

    def __str__(self):
        return "%s. You passed in %r (%s)" % (
            super().__str__(),
            self.obj,
            type(self.obj),
        )


def smart_str(s, encoding="utf-8", strings_only=False, errors="strict"):
    """
    Return a string representing 's'. Treat bytestrings using the 'encoding'
    codec.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if isinstance(s, Promise):
        # The input is the result of a gettext_lazy() call.
        return s
    return force_str(s, encoding, strings_only, errors)


_PROTECTED_TYPES = (
    type(None),
    int,
    float,
    Decimal,
    datetime.datetime,
    datetime.date,
    datetime.time,
)


def is_protected_type(obj):
    """Determine if the object instance is of a protected type.

    Objects of protected types are preserved as-is when passed to
    force_str(strings_only=True).
    """
    return isinstance(obj, _PROTECTED_TYPES)


def force_str(s, encoding="utf-8", strings_only=False, errors="strict"):
    """
    Similar to smart_str(), except that lazy instances are resolved to
    strings, rather than kept as lazy objects.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    # Handle the common case first for performance reasons.
    if issubclass(type(s), str):
        return s
    if strings_only and is_protected_type(s):
        return s
    try:
        if isinstance(s, bytes):
            s = str(s, encoding, errors)
        else:
            s = str(s)
    except UnicodeDecodeError as e:
        raise DjangoUnicodeDecodeError(s, *e.args)
    return s


def smart_bytes(s, encoding="utf-8", strings_only=False, errors="strict"):
    """
    Return a bytestring version of 's', encoded as specified in 'encoding'.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if isinstance(s, Promise):
        # The input is the result of a gettext_lazy() call.
        return s
    return force_bytes(s, encoding, strings_only, errors)


def force_bytes(s, encoding="utf-8", strings_only=False, errors="strict"):
    """
    Similar to smart_bytes, except that lazy instances are resolved to
    strings, rather than kept as lazy objects.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    # Handle the common case first for performance reasons.
    if isinstance(s, bytes):
        if encoding == "utf-8":
            return s
        else:
            return s.decode("utf-8", errors).encode(encoding, errors)
    if strings_only and is_protected_type(s):
        return s
    if isinstance(s, memoryview):
        return bytes(s)
    return str(s).encode(encoding, errors)


def iri_to_uri(iri):
    """
    Convert an Internationalized Resource Identifier (IRI) portion to a URI
    portion that is suitable for inclusion in a URL.

    This is the algorithm from RFC 3987 Section 3.1, slightly simplified since
    the input is assumed to be a string rather than an arbitrary byte stream.

    Take an IRI (string or UTF-8 bytes, e.g. '/I ♥ Django/' or
    b'/I \xe2\x99\xa5 Django/') and return a string containing the encoded
    result with ASCII chars only (e.g. '/I%20%E2%99%A5%20Django/').
    """
    # The list of safe characters here is constructed from the "reserved" and
    # "unreserved" characters specified in RFC 3986 Sections 2.2 and 2.3:
    #     reserved    = gen-delims / sub-delims
    #     gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"
    #     sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
    #                   / "*" / "+" / "," / ";" / "="
    #     unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
    # Of the unreserved characters, urllib.parse.quote() already considers all
    # but the ~ safe.
    # The % character is also added to the list of safe characters here, as the
    # end of RFC 3987 Section 3.1 specifically mentions that % must not be
    # converted.
    if iri is None:
        return iri
    elif isinstance(iri, Promise):
        iri = str(iri)
    return quote(iri, safe="/#%[]=:;$&()+,!?*@'~")


# List of byte values that uri_to_iri() decodes from percent encoding.
# First, the unreserved characters from RFC 3986:
_ascii_ranges = [[45, 46, 95, 126], range(65, 91), range(97, 123)]
_hextobyte = {
    (fmt % char).encode(): bytes((char,))
    for ascii_range in _ascii_ranges
    for char in ascii_range
    for fmt in ["%02x", "%02X"]
}
# And then everything above 128, because bytes ≥ 128 are part of multibyte
# Unicode characters.
_hexdig = "0123456789ABCDEFabcdef"
_hextobyte.update(
    {(a + b).encode(): bytes.fromhex(a + b) for a in _hexdig[8:] for b in _hexdig}
)


def uri_to_iri(uri):
    """
    Convert a Uniform Resource Identifier(URI) into an Internationalized
    Resource Identifier(IRI).

    This is the algorithm from RFC 3987 Section 3.2, excluding step 4.

    Take an URI in ASCII bytes (e.g. '/I%20%E2%99%A5%20Django/') and return
    a string containing the encoded result (e.g. '/I%20♥%20Django/').
    """
    if uri is None:
        return uri
    uri = force_bytes(uri)
    # Fast selective unquote: First, split on '%' and then starting with the
    # second block, decode the first 2 bytes if they represent a hex code to
    # decode. The rest of the block is the part after '%AB', not containing
    # any '%'. Add that to the output without further processing.
    bits = uri.split(b"%")
    if len(bits) == 1:
        iri = uri
    else:
        parts = [bits[0]]
        append = parts.append
        hextobyte = _hextobyte
        for item in bits[1:]:
            hex = item[:2]
            if hex in hextobyte:
                append(hextobyte[item[:2]])
                append(item[2:])
            else:
                append(b"%")
                append(item)
        iri = b"".join(parts)
    return repercent_broken_unicode(iri).decode()


def escape_uri_path(path):
    """
    Escape the unsafe characters from the path portion of a Uniform Resource
    Identifier (URI).
    """
    # These are the "reserved" and "unreserved" characters specified in RFC
    # 3986 Sections 2.2 and 2.3:
    #   reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
    #   unreserved  = alphanum | mark
    #   mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
    # The list of safe characters here is constructed subtracting ";", "=",
    # and "?" according to RFC 3986 Section 3.3.
    # The reason for not subtracting and escaping "/" is that we are escaping
    # the entire path, not a path segment.
    return quote(path, safe="/:@&+$,-_.!~*'()")


def punycode(domain):
    """Return the Punycode of the given domain if it's non-ASCII."""
    return domain.encode("idna").decode("ascii")


def repercent_broken_unicode(path):
    """
    As per RFC 3987 Section 3.2, step three of converting a URI into an IRI,
    repercent-encode any octet produced that is not part of a strictly legal
    UTF-8 octet sequence.
    """
    changed_parts = []
    while True:
        try:
            path.decode()
        except UnicodeDecodeError as e:
            # CVE-2019-14235: A recursion shouldn't be used since the exception
            # handling uses massive amounts of memory
            repercent = quote(path[e.start : e.end], safe=b"/#%[]=:;$&()+,!?*@'~")
            changed_parts.append(path[: e.start] + repercent.encode())
            path = path[e.end :]
        else:
            return b"".join(changed_parts) + path


def filepath_to_uri(path):
    """Convert a file system path to a URI portion that is suitable for
    inclusion in a URL.

    Encode certain chars that would normally be recognized as special chars
    for URIs. Do not encode the ' character, as it is a valid character
    within URIs. See the encodeURIComponent() JavaScript function for details.
    """
    if path is None:
        return path
    # I know about `os.sep` and `os.altsep` but I want to leave
    # some flexibility for hardcoding separators.
    return quote(str(path).replace("\\", "/"), safe="/~!*()'")


def get_system_encoding():
    """
    The encoding for the character type functions. Fallback to 'ascii' if the
    #encoding is unsupported by Python or could not be determined. See tickets
    #10335 and #5846.
    """
    try:
        encoding = locale.getlocale()[1] or "ascii"
        codecs.lookup(encoding)
    except Exception:
        encoding = "ascii"
    return encoding


DEFAULT_LOCALE_ENCODING = get_system_encoding()

back/venvmsg/Lib/site-packages/django/utils/encoding.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import datetime
import decimal
import functools
import re
import unicodedata
from importlib import import_module

from django.conf import settings
from django.utils import dateformat, numberformat
from django.utils.functional import lazy
from django.utils.translation import check_for_language, get_language, to_locale

# format_cache is a mapping from (format_type, lang) to the format string.
# By using the cache, it is possible to avoid running get_format_modules
# repeatedly.
_format_cache = {}
_format_modules_cache = {}

ISO_INPUT_FORMATS = {
    "DATE_INPUT_FORMATS": ["%Y-%m-%d"],
    "TIME_INPUT_FORMATS": ["%H:%M:%S", "%H:%M:%S.%f", "%H:%M"],
    "DATETIME_INPUT_FORMATS": [
        "%Y-%m-%d %H:%M:%S",
        "%Y-%m-%d %H:%M:%S.%f",
        "%Y-%m-%d %H:%M",
        "%Y-%m-%d",
    ],
}


FORMAT_SETTINGS = frozenset(
    [
        "DECIMAL_SEPARATOR",
        "THOUSAND_SEPARATOR",
        "NUMBER_GROUPING",
        "FIRST_DAY_OF_WEEK",
        "MONTH_DAY_FORMAT",
        "TIME_FORMAT",
        "DATE_FORMAT",
        "DATETIME_FORMAT",
        "SHORT_DATE_FORMAT",
        "SHORT_DATETIME_FORMAT",
        "YEAR_MONTH_FORMAT",
        "DATE_INPUT_FORMATS",
        "TIME_INPUT_FORMATS",
        "DATETIME_INPUT_FORMATS",
    ]
)


def reset_format_cache():
    """Clear any cached formats.

    This method is provided primarily for testing purposes,
    so that the effects of cached formats can be removed.
    """
    global _format_cache, _format_modules_cache
    _format_cache = {}
    _format_modules_cache = {}


def iter_format_modules(lang, format_module_path=None):
    """Find format modules."""
    if not check_for_language(lang):
        return

    if format_module_path is None:
        format_module_path = settings.FORMAT_MODULE_PATH

    format_locations = []
    if format_module_path:
        if isinstance(format_module_path, str):
            format_module_path = [format_module_path]
        for path in format_module_path:
            format_locations.append(path + ".%s")
    format_locations.append("django.conf.locale.%s")
    locale = to_locale(lang)
    locales = [locale]
    if "_" in locale:
        locales.append(locale.split("_")[0])
    for location in format_locations:
        for loc in locales:
            try:
                yield import_module("%s.formats" % (location % loc))
            except ImportError:
                pass


def get_format_modules(lang=None):
    """Return a list of the format modules found."""
    if lang is None:
        lang = get_language()
    if lang not in _format_modules_cache:
        _format_modules_cache[lang] = list(
            iter_format_modules(lang, settings.FORMAT_MODULE_PATH)
        )
    return _format_modules_cache[lang]


def get_format(format_type, lang=None, use_l10n=None):
    """
    For a specific format type, return the format for the current
    language (locale). Default to the format in the settings.
    format_type is the name of the format, e.g. 'DATE_FORMAT'.

    If use_l10n is provided and is not None, it forces the value to
    be localized (or not), overriding the value of settings.USE_L10N.
    """
    if use_l10n is None:
        try:
            use_l10n = settings._USE_L10N_INTERNAL
        except AttributeError:
            use_l10n = settings.USE_L10N
    if use_l10n and lang is None:
        lang = get_language()
    format_type = str(format_type)  # format_type may be lazy.
    cache_key = (format_type, lang)
    try:
        return _format_cache[cache_key]
    except KeyError:
        pass

    # The requested format_type has not been cached yet. Try to find it in any
    # of the format_modules for the given lang if l10n is enabled. If it's not
    # there or if l10n is disabled, fall back to the project settings.
    val = None
    if use_l10n:
        for module in get_format_modules(lang):
            val = getattr(module, format_type, None)
            if val is not None:
                break
    if val is None:
        if format_type not in FORMAT_SETTINGS:
            return format_type
        val = getattr(settings, format_type)
    elif format_type in ISO_INPUT_FORMATS:
        # If a list of input formats from one of the format_modules was
        # retrieved, make sure the ISO_INPUT_FORMATS are in this list.
        val = list(val)
        for iso_input in ISO_INPUT_FORMATS.get(format_type, ()):
            if iso_input not in val:
                val.append(iso_input)
    _format_cache[cache_key] = val
    return val


get_format_lazy = lazy(get_format, str, list, tuple)


def date_format(value, format=None, use_l10n=None):
    """
    Format a datetime.date or datetime.datetime object using a
    localizable format.

    If use_l10n is provided and is not None, that will force the value to
    be localized (or not), overriding the value of settings.USE_L10N.
    """
    return dateformat.format(
        value, get_format(format or "DATE_FORMAT", use_l10n=use_l10n)
    )


def time_format(value, format=None, use_l10n=None):
    """
    Format a datetime.time object using a localizable format.

    If use_l10n is provided and is not None, it forces the value to
    be localized (or not), overriding the value of settings.USE_L10N.
    """
    return dateformat.time_format(
        value, get_format(format or "TIME_FORMAT", use_l10n=use_l10n)
    )


def number_format(value, decimal_pos=None, use_l10n=None, force_grouping=False):
    """
    Format a numeric value using localization settings.

    If use_l10n is provided and is not None, it forces the value to
    be localized (or not), overriding the value of settings.USE_L10N.
    """
    if use_l10n is None:
        try:
            use_l10n = settings._USE_L10N_INTERNAL
        except AttributeError:
            use_l10n = settings.USE_L10N
    lang = get_language() if use_l10n else None
    return numberformat.format(
        value,
        get_format("DECIMAL_SEPARATOR", lang, use_l10n=use_l10n),
        decimal_pos,
        get_format("NUMBER_GROUPING", lang, use_l10n=use_l10n),
        get_format("THOUSAND_SEPARATOR", lang, use_l10n=use_l10n),
        force_grouping=force_grouping,
        use_l10n=use_l10n,
    )


def localize(value, use_l10n=None):
    """
    Check if value is a localizable type (date, number...) and return it
    formatted as a string using current locale format.

    If use_l10n is provided and is not None, it forces the value to
    be localized (or not), overriding the value of settings.USE_L10N.
    """
    if isinstance(value, str):  # Handle strings first for performance reasons.
        return value
    elif isinstance(value, bool):  # Make sure booleans don't get treated as numbers
        return str(value)
    elif isinstance(value, (decimal.Decimal, float, int)):
        if use_l10n is False:
            return str(value)
        return number_format(value, use_l10n=use_l10n)
    elif isinstance(value, datetime.datetime):
        return date_format(value, "DATETIME_FORMAT", use_l10n=use_l10n)
    elif isinstance(value, datetime.date):
        return date_format(value, use_l10n=use_l10n)
    elif isinstance(value, datetime.time):
        return time_format(value, use_l10n=use_l10n)
    return value


def localize_input(value, default=None):
    """
    Check if an input value is a localizable type and return it
    formatted with the appropriate formatting string of the current locale.
    """
    if isinstance(value, str):  # Handle strings first for performance reasons.
        return value
    elif isinstance(value, bool):  # Don't treat booleans as numbers.
        return str(value)
    elif isinstance(value, (decimal.Decimal, float, int)):
        return number_format(value)
    elif isinstance(value, datetime.datetime):
        format = default or get_format("DATETIME_INPUT_FORMATS")[0]
        format = sanitize_strftime_format(format)
        return value.strftime(format)
    elif isinstance(value, datetime.date):
        format = default or get_format("DATE_INPUT_FORMATS")[0]
        format = sanitize_strftime_format(format)
        return value.strftime(format)
    elif isinstance(value, datetime.time):
        format = default or get_format("TIME_INPUT_FORMATS")[0]
        return value.strftime(format)
    return value


@functools.lru_cache
def sanitize_strftime_format(fmt):
    """
    Ensure that certain specifiers are correctly padded with leading zeros.

    For years < 1000 specifiers %C, %F, %G, and %Y don't work as expected for
    strftime provided by glibc on Linux as they don't pad the year or century
    with leading zeros. Support for specifying the padding explicitly is
    available, however, which can be used to fix this issue.

    FreeBSD, macOS, and Windows do not support explicitly specifying the
    padding, but return four digit years (with leading zeros) as expected.

    This function checks whether the %Y produces a correctly padded string and,
    if not, makes the following substitutions:

    - %C → %02C
    - %F → %010F
    - %G → %04G
    - %Y → %04Y

    See https://bugs.python.org/issue13305 for more details.
    """
    if datetime.date(1, 1, 1).strftime("%Y") == "0001":
        return fmt
    mapping = {"C": 2, "F": 10, "G": 4, "Y": 4}
    return re.sub(
        r"((?:^|[^%])(?:%%)*)%([CFGY])",
        lambda m: r"%s%%0%s%s" % (m[1], mapping[m[2]], m[2]),
        fmt,
    )


def sanitize_separators(value):
    """
    Sanitize a value according to the current decimal and
    thousand separator setting. Used with form field input.
    """
    if isinstance(value, str):
        parts = []
        decimal_separator = get_format("DECIMAL_SEPARATOR")
        if decimal_separator in value:
            value, decimals = value.split(decimal_separator, 1)
            parts.append(decimals)
        if settings.USE_THOUSAND_SEPARATOR:
            thousand_sep = get_format("THOUSAND_SEPARATOR")
            if (
                thousand_sep == "."
                and value.count(".") == 1
                and len(value.split(".")[-1]) != 3
            ):
                # Special case where we suspect a dot meant decimal separator
                # (see #22171).
                pass
            else:
                for replacement in {
                    thousand_sep,
                    unicodedata.normalize("NFKD", thousand_sep),
                }:
                    value = value.replace(replacement, "")
        parts.append(value)
        value = ".".join(reversed(parts))
    return value

back/venvmsg/Lib/site-packages/django/utils/formats.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
from collections import namedtuple
from datetime import datetime, time

from django import forms
from django.utils.dateparse import parse_datetime
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _

from .conf import settings
from .constants import EMPTY_VALUES
from .utils import handle_timezone
from .widgets import (
    BaseCSVWidget,
    CSVWidget,
    DateRangeWidget,
    LookupChoiceWidget,
    RangeWidget,
)

try:
    from django.utils.choices import BaseChoiceIterator, normalize_choices
except ImportError:
    DJANGO_50 = False
else:
    DJANGO_50 = True


class RangeField(forms.MultiValueField):
    widget = RangeWidget

    def __init__(self, fields=None, *args, **kwargs):
        if fields is None:
            fields = (forms.DecimalField(), forms.DecimalField())
        super().__init__(fields, *args, **kwargs)

    def compress(self, data_list):
        if data_list:
            return slice(*data_list)
        return None


class DateRangeField(RangeField):
    widget = DateRangeWidget

    def __init__(self, *args, **kwargs):
        fields = (forms.DateField(), forms.DateField())
        super().__init__(fields, *args, **kwargs)

    def compress(self, data_list):
        if data_list:
            start_date, stop_date = data_list
            if start_date:
                start_date = handle_timezone(
                    datetime.combine(start_date, time.min), False
                )
            if stop_date:
                stop_date = handle_timezone(
                    datetime.combine(stop_date, time.max), False
                )
            return slice(start_date, stop_date)
        return None


class DateTimeRangeField(RangeField):
    widget = DateRangeWidget

    def __init__(self, *args, **kwargs):
        fields = (forms.DateTimeField(), forms.DateTimeField())
        super().__init__(fields, *args, **kwargs)


class IsoDateTimeRangeField(RangeField):
    widget = DateRangeWidget

    def __init__(self, *args, **kwargs):
        fields = (IsoDateTimeField(), IsoDateTimeField())
        super().__init__(fields, *args, **kwargs)


class TimeRangeField(RangeField):
    widget = DateRangeWidget

    def __init__(self, *args, **kwargs):
        fields = (forms.TimeField(), forms.TimeField())
        super().__init__(fields, *args, **kwargs)


class Lookup(namedtuple("Lookup", ("value", "lookup_expr"))):
    def __new__(cls, value, lookup_expr):
        if value in EMPTY_VALUES or lookup_expr in EMPTY_VALUES:
            raise ValueError(
                "Empty values ([], (), {}, '', None) are not "
                "valid Lookup arguments. Return None instead."
            )

        return super().__new__(cls, value, lookup_expr)


class LookupChoiceField(forms.MultiValueField):
    default_error_messages = {
        "lookup_required": _("Select a lookup."),
    }

    def __init__(self, field, lookup_choices, *args, **kwargs):
        empty_label = kwargs.pop("empty_label", settings.EMPTY_CHOICE_LABEL)
        fields = (field, ChoiceField(choices=lookup_choices, empty_label=empty_label))
        widget = LookupChoiceWidget(widgets=[f.widget for f in fields])
        kwargs["widget"] = widget
        kwargs["help_text"] = field.help_text
        super().__init__(fields, *args, **kwargs)

    def compress(self, data_list):
        if len(data_list) == 2:
            value, lookup_expr = data_list
            if value not in EMPTY_VALUES:
                if lookup_expr not in EMPTY_VALUES:
                    return Lookup(value=value, lookup_expr=lookup_expr)
                else:
                    raise forms.ValidationError(
                        self.error_messages["lookup_required"], code="lookup_required"
                    )
        return None


class IsoDateTimeField(forms.DateTimeField):
    """
    Supports 'iso-8601' date format too which is out the scope of
    the ``datetime.strptime`` standard library

    # ISO 8601: ``http://www.w3.org/TR/NOTE-datetime``

    Based on Gist example by David Medina https://gist.github.com/copitux/5773821
    """

    ISO_8601 = "iso-8601"
    input_formats = [ISO_8601]

    def strptime(self, value, format):
        value = force_str(value)

        if format == self.ISO_8601:
            parsed = parse_datetime(value)
            if parsed is None:  # Continue with other formats if doesn't match
                raise ValueError
            return handle_timezone(parsed)
        return super().strptime(value, format)


class BaseCSVField(forms.Field):
    """
    Base field for validating CSV types. Value validation is performed by
    secondary base classes.

    ex::
        class IntegerCSVField(BaseCSVField, filters.IntegerField):
            pass

    """

    base_widget_class = BaseCSVWidget

    def __init__(self, *args, **kwargs):
        widget = kwargs.get("widget") or self.widget
        kwargs["widget"] = self._get_widget_class(widget)

        super().__init__(*args, **kwargs)

    def _get_widget_class(self, widget):
        # passthrough, allows for override
        if isinstance(widget, BaseCSVWidget) or (
            isinstance(widget, type) and issubclass(widget, BaseCSVWidget)
        ):
            return widget

        # complain since we are unable to reconstruct widget instances
        assert isinstance(
            widget, type
        ), "'%s.widget' must be a widget class, not %s." % (
            self.__class__.__name__,
            repr(widget),
        )

        bases = (
            self.base_widget_class,
            widget,
        )
        return type(str("CSV%s" % widget.__name__), bases, {})

    def clean(self, value):
        if value in self.empty_values and self.required:
            raise forms.ValidationError(
                self.error_messages["required"], code="required"
            )

        if value is None:
            return None
        return [super(BaseCSVField, self).clean(v) for v in value]


class BaseRangeField(BaseCSVField):
    # Force use of text input, as range must always have two inputs. A date
    # input would only allow a user to input one value and would always fail.
    widget = CSVWidget

    default_error_messages = {"invalid_values": _("Range query expects two values.")}

    def clean(self, value):
        value = super().clean(value)

        assert value is None or isinstance(value, list)

        if value and len(value) != 2:
            raise forms.ValidationError(
                self.error_messages["invalid_values"], code="invalid_values"
            )

        return value


class ChoiceIterator(BaseChoiceIterator if DJANGO_50 else object):
    # Emulates the behavior of ModelChoiceIterator, but instead wraps
    # the field's _choices iterable.

    def __init__(self, field, choices):
        self.field = field
        self.choices = choices

    def __iter__(self):
        if self.field.empty_label is not None:
            yield ("", self.field.empty_label)
        if self.field.null_label is not None:
            yield (self.field.null_value, self.field.null_label)
        if DJANGO_50:
            yield from normalize_choices(self.choices)
        else:
            yield from self.choices

    def __len__(self):
        add = 1 if self.field.empty_label is not None else 0
        add += 1 if self.field.null_label is not None else 0
        return len(self.choices) + add


class ModelChoiceIterator(forms.models.ModelChoiceIterator):
    # Extends the base ModelChoiceIterator to add in 'null' choice handling.
    # This is a bit verbose since we have to insert the null choice after the
    # empty choice, but before the remainder of the choices.

    def __iter__(self):
        iterable = super().__iter__()

        if self.field.empty_label is not None:
            yield next(iterable)
        if self.field.null_label is not None:
            yield (self.field.null_value, self.field.null_label)
        yield from iterable

    def __len__(self):
        add = 1 if self.field.null_label is not None else 0
        return super().__len__() + add


class ChoiceIteratorMixin:
    def __init__(self, *args, **kwargs):
        self.null_label = kwargs.pop("null_label", settings.NULL_CHOICE_LABEL)
        self.null_value = kwargs.pop("null_value", settings.NULL_CHOICE_VALUE)

        super().__init__(*args, **kwargs)

    @property
    def choices(self):
        return super().choices

    @choices.setter
    def choices(self, value):
        if DJANGO_50:
            value = self.iterator(self, value)
        else:
            super()._set_choices(value)
            value = self.iterator(self, self._choices)

        # Simple `super()` syntax for calling a parent property setter is
        # unsupported. See https://github.com/python/cpython/issues/59170
        super(ChoiceIteratorMixin, self.__class__).choices.__set__(self, value)


# Unlike their Model* counterparts, forms.ChoiceField and forms.MultipleChoiceField do not set empty_label
class ChoiceField(ChoiceIteratorMixin, forms.ChoiceField):
    iterator = ChoiceIterator

    def __init__(self, *args, **kwargs):
        self.empty_label = kwargs.pop("empty_label", settings.EMPTY_CHOICE_LABEL)
        super().__init__(*args, **kwargs)


class MultipleChoiceField(ChoiceIteratorMixin, forms.MultipleChoiceField):
    iterator = ChoiceIterator

    def __init__(self, *args, **kwargs):
        self.empty_label = None
        super().__init__(*args, **kwargs)


class ModelChoiceField(ChoiceIteratorMixin, forms.ModelChoiceField):
    iterator = ModelChoiceIterator

    def to_python(self, value):
        # bypass the queryset value check
        if self.null_label is not None and value == self.null_value:
            return value
        return super().to_python(value)


class ModelMultipleChoiceField(ChoiceIteratorMixin, forms.ModelMultipleChoiceField):
    iterator = ModelChoiceIterator

    def _check_values(self, value):
        null = self.null_label is not None and value and self.null_value in value
        if null:  # remove the null value and any potential duplicates
            value = [v for v in value if v != self.null_value]

        result = list(super()._check_values(value))
        result += [self.null_value] if null else []
        return result

back/venvmsg/Lib/site-packages/django_filters/fields.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""
File contains the standard library of Python 3.7.

DO NOT EDIT. If the standard library changes, a new list should be created
using the mkstdlibs.py script.
"""

stdlib = {
    "_ast",
    "_dummy_thread",
    "_thread",
    "abc",
    "aifc",
    "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",
    "getopt",
    "getpass",
    "gettext",
    "glob",
    "grp",
    "gzip",
    "hashlib",
    "heapq",
    "hmac",
    "html",
    "http",
    "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",
    "ntpath",
    "numbers",
    "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",
    "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",
    "sre_compile",
    "sre_constants",
    "sre_parse",
    "ssl",
    "stat",
    "statistics",
    "string",
    "stringprep",
    "struct",
    "subprocess",
    "sunau",
    "symbol",
    "symtable",
    "sys",
    "sysconfig",
    "syslog",
    "tabnanny",
    "tarfile",
    "telnetlib",
    "tempfile",
    "termios",
    "test",
    "textwrap",
    "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",
}

back/venvmsg/Lib/site-packages/isort/stdlibs/py37.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
from typing import Union

from .align import AlignMethod
from .cells import cell_len, set_cell_size
from .console import Console, ConsoleOptions, RenderResult
from .jupyter import JupyterMixin
from .measure import Measurement
from .style import Style
from .text import Text


class Rule(JupyterMixin):
    """A console renderable to draw a horizontal rule (line).

    Args:
        title (Union[str, Text], optional): Text to render in the rule. Defaults to "".
        characters (str, optional): Character(s) used to draw the line. Defaults to "─".
        style (StyleType, optional): Style of Rule. Defaults to "rule.line".
        end (str, optional): Character at end of Rule. defaults to "\\\\n"
        align (str, optional): How to align the title, one of "left", "center", or "right". Defaults to "center".
    """

    def __init__(
        self,
        title: Union[str, Text] = "",
        *,
        characters: str = "─",
        style: Union[str, Style] = "rule.line",
        end: str = "\n",
        align: AlignMethod = "center",
    ) -> None:
        if cell_len(characters) < 1:
            raise ValueError(
                "'characters' argument must have a cell width of at least 1"
            )
        if align not in ("left", "center", "right"):
            raise ValueError(
                f'invalid value for align, expected "left", "center", "right" (not {align!r})'
            )
        self.title = title
        self.characters = characters
        self.style = style
        self.end = end
        self.align = align

    def __repr__(self) -> str:
        return f"Rule({self.title!r}, {self.characters!r})"

    def __rich_console__(
        self, console: Console, options: ConsoleOptions
    ) -> RenderResult:
        width = options.max_width

        characters = (
            "-"
            if (options.ascii_only and not self.characters.isascii())
            else self.characters
        )

        chars_len = cell_len(characters)
        if not self.title:
            yield self._rule_line(chars_len, width)
            return

        if isinstance(self.title, Text):
            title_text = self.title
        else:
            title_text = console.render_str(self.title, style="rule.text")

        title_text.plain = title_text.plain.replace("\n", " ")
        title_text.expand_tabs()

        required_space = 4 if self.align == "center" else 2
        truncate_width = max(0, width - required_space)
        if not truncate_width:
            yield self._rule_line(chars_len, width)
            return

        rule_text = Text(end=self.end)
        if self.align == "center":
            title_text.truncate(truncate_width, overflow="ellipsis")
            side_width = (width - cell_len(title_text.plain)) // 2
            left = Text(characters * (side_width // chars_len + 1))
            left.truncate(side_width - 1)
            right_length = width - cell_len(left.plain) - cell_len(title_text.plain)
            right = Text(characters * (side_width // chars_len + 1))
            right.truncate(right_length)
            rule_text.append(left.plain + " ", self.style)
            rule_text.append(title_text)
            rule_text.append(" " + right.plain, self.style)
        elif self.align == "left":
            title_text.truncate(truncate_width, overflow="ellipsis")
            rule_text.append(title_text)
            rule_text.append(" ")
            rule_text.append(characters * (width - rule_text.cell_len), self.style)
        elif self.align == "right":
            title_text.truncate(truncate_width, overflow="ellipsis")
            rule_text.append(characters * (width - title_text.cell_len - 1), self.style)
            rule_text.append(" ")
            rule_text.append(title_text)

        rule_text.plain = set_cell_size(rule_text.plain, width)
        yield rule_text

    def _rule_line(self, chars_len: int, width: int) -> Text:
        rule_text = Text(self.characters * ((width // chars_len) + 1), self.style)
        rule_text.truncate(width)
        rule_text.plain = set_cell_size(rule_text.plain, width)
        return rule_text

    def __rich_measure__(
        self, console: Console, options: ConsoleOptions
    ) -> Measurement:
        return Measurement(1, 1)


if __name__ == "__main__":  # pragma: no cover
    import sys

    from pip._vendor.rich.console import Console

    try:
        text = sys.argv[1]
    except IndexError:
        text = "Hello, World"
    console = Console()
    console.print(Rule(title=text))

    console = Console()
    console.print(Rule("foo"), width=4)

back/venvmsg/Lib/site-packages/pip/_vendor/rich/rule.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import abc
from typing import BinaryIO, Iterable, Text

from ._compat import runtime_checkable, Protocol


class ResourceReader(metaclass=abc.ABCMeta):
    """Abstract base class for loaders to provide resource reading support."""

    @abc.abstractmethod
    def open_resource(self, resource: Text) -> BinaryIO:
        """Return an opened, file-like object for binary reading.

        The 'resource' argument is expected to represent only a file name.
        If the resource cannot be found, FileNotFoundError is raised.
        """
        # This deliberately raises FileNotFoundError instead of
        # NotImplementedError so that if this method is accidentally called,
        # it'll still do the right thing.
        raise FileNotFoundError

    @abc.abstractmethod
    def resource_path(self, resource: Text) -> Text:
        """Return the file system path to the specified resource.

        The 'resource' argument is expected to represent only a file name.
        If the resource does not exist on the file system, raise
        FileNotFoundError.
        """
        # This deliberately raises FileNotFoundError instead of
        # NotImplementedError so that if this method is accidentally called,
        # it'll still do the right thing.
        raise FileNotFoundError

    @abc.abstractmethod
    def is_resource(self, path: Text) -> bool:
        """Return True if the named 'path' is a resource.

        Files are resources, directories are not.
        """
        raise FileNotFoundError

    @abc.abstractmethod
    def contents(self) -> Iterable[str]:
        """Return an iterable of entries in `package`."""
        raise FileNotFoundError


@runtime_checkable
class Traversable(Protocol):
    """
    An object with a subset of pathlib.Path methods suitable for
    traversing directories and opening files.
    """

    @abc.abstractmethod
    def iterdir(self):
        """
        Yield Traversable objects in self
        """

    def read_bytes(self):
        """
        Read contents of self as bytes
        """
        with self.open('rb') as strm:
            return strm.read()

    def read_text(self, encoding=None):
        """
        Read contents of self as text
        """
        with self.open(encoding=encoding) as strm:
            return strm.read()

    @abc.abstractmethod
    def is_dir(self) -> bool:
        """
        Return True if self is a directory
        """

    @abc.abstractmethod
    def is_file(self) -> bool:
        """
        Return True if self is a file
        """

    @abc.abstractmethod
    def joinpath(self, child):
        """
        Return Traversable child in self
        """

    def __truediv__(self, child):
        """
        Return Traversable child in self
        """
        return self.joinpath(child)

    @abc.abstractmethod
    def open(self, mode='r', *args, **kwargs):
        """
        mode may be 'r' or 'rb' to open as text or binary. Return a handle
        suitable for reading (same as pathlib.Path.open).

        When opening as text, accepts encoding parameters such as those
        accepted by io.TextIOWrapper.
        """

    @abc.abstractproperty
    def name(self) -> str:
        """
        The base name of this object without any parent references.
        """


class TraversableResources(ResourceReader):
    """
    The required interface for providing traversable
    resources.
    """

    @abc.abstractmethod
    def files(self):
        """Return a Traversable object for the loaded package."""

    def open_resource(self, resource):
        return self.files().joinpath(resource).open('rb')

    def resource_path(self, resource):
        raise FileNotFoundError(resource)

    def is_resource(self, path):
        return self.files().joinpath(path).is_file()

    def contents(self):
        return (item.name for item in self.files().iterdir())

back/venvmsg/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""Base API."""
from __future__ import annotations

import os
from abc import ABC, abstractmethod
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import sys

    if sys.version_info >= (3, 8):  # pragma: no cover (py38+)
        from typing import Literal
    else:  # pragma: no cover (py38+)
        from typing_extensions import Literal


class PlatformDirsABC(ABC):
    """Abstract base class for platform directories."""

    def __init__(  # noqa: PLR0913
        self,
        appname: str | None = None,
        appauthor: str | None | Literal[False] = None,
        version: str | None = None,
        roaming: bool = False,  # noqa: FBT001, FBT002
        multipath: bool = False,  # noqa: FBT001, FBT002
        opinion: bool = True,  # noqa: FBT001, FBT002
        ensure_exists: bool = False,  # noqa: FBT001, FBT002
    ) -> None:
        """
        Create a new platform directory.

        :param appname: See `appname`.
        :param appauthor: See `appauthor`.
        :param version: See `version`.
        :param roaming: See `roaming`.
        :param multipath: See `multipath`.
        :param opinion: See `opinion`.
        :param ensure_exists: See `ensure_exists`.
        """
        self.appname = appname  #: The name of application.
        self.appauthor = appauthor
        """
        The name of the app author or distributing body for this application. Typically, it is the owning company name.
        Defaults to `appname`. You may pass ``False`` to disable it.
        """
        self.version = version
        """
        An optional version path element to append to the path. You might want to use this if you want multiple versions
        of your app to be able to run independently. If used, this would typically be ``<major>.<minor>``.
        """
        self.roaming = roaming
        """
        Whether to use the roaming appdata directory on Windows. That means that for users on a Windows network setup
        for roaming profiles, this user data will be synced on login (see
        `here <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>`_).
        """
        self.multipath = multipath
        """
        An optional parameter which indicates that the entire list of data dirs should be returned.
        By default, the first item would only be returned.
        """
        self.opinion = opinion  #: A flag to indicating to use opinionated values.
        self.ensure_exists = ensure_exists
        """
        Optionally create the directory (and any missing parents) upon access if it does not exist.
        By default, no directories are created.
        """

    def _append_app_name_and_version(self, *base: str) -> str:
        params = list(base[1:])
        if self.appname:
            params.append(self.appname)
            if self.version:
                params.append(self.version)
        path = os.path.join(base[0], *params)  # noqa: PTH118
        self._optionally_create_directory(path)
        return path

    def _optionally_create_directory(self, path: str) -> None:
        if self.ensure_exists:
            Path(path).mkdir(parents=True, exist_ok=True)

    @property
    @abstractmethod
    def user_data_dir(self) -> str:
        """:return: data directory tied to the user"""

    @property
    @abstractmethod
    def site_data_dir(self) -> str:
        """:return: data directory shared by users"""

    @property
    @abstractmethod
    def user_config_dir(self) -> str:
        """:return: config directory tied to the user"""

    @property
    @abstractmethod
    def site_config_dir(self) -> str:
        """:return: config directory shared by the users"""

    @property
    @abstractmethod
    def user_cache_dir(self) -> str:
        """:return: cache directory tied to the user"""

    @property
    @abstractmethod
    def site_cache_dir(self) -> str:
        """:return: cache directory shared by users"""

    @property
    @abstractmethod
    def user_state_dir(self) -> str:
        """:return: state directory tied to the user"""

    @property
    @abstractmethod
    def user_log_dir(self) -> str:
        """:return: log directory tied to the user"""

    @property
    @abstractmethod
    def user_documents_dir(self) -> str:
        """:return: documents directory tied to the user"""

    @property
    @abstractmethod
    def user_downloads_dir(self) -> str:
        """:return: downloads directory tied to the user"""

    @property
    @abstractmethod
    def user_pictures_dir(self) -> str:
        """:return: pictures directory tied to the user"""

    @property
    @abstractmethod
    def user_videos_dir(self) -> str:
        """:return: videos directory tied to the user"""

    @property
    @abstractmethod
    def user_music_dir(self) -> str:
        """:return: music directory tied to the user"""

    @property
    @abstractmethod
    def user_desktop_dir(self) -> str:
        """:return: desktop directory tied to the user"""

    @property
    @abstractmethod
    def user_runtime_dir(self) -> str:
        """:return: runtime directory tied to the user"""

    @property
    @abstractmethod
    def site_runtime_dir(self) -> str:
        """:return: runtime directory shared by users"""

    @property
    def user_data_path(self) -> Path:
        """:return: data path tied to the user"""
        return Path(self.user_data_dir)

    @property
    def site_data_path(self) -> Path:
        """:return: data path shared by users"""
        return Path(self.site_data_dir)

    @property
    def user_config_path(self) -> Path:
        """:return: config path tied to the user"""
        return Path(self.user_config_dir)

    @property
    def site_config_path(self) -> Path:
        """:return: config path shared by the users"""
        return Path(self.site_config_dir)

    @property
    def user_cache_path(self) -> Path:
        """:return: cache path tied to the user"""
        return Path(self.user_cache_dir)

    @property
    def site_cache_path(self) -> Path:
        """:return: cache path shared by users"""
        return Path(self.site_cache_dir)

    @property
    def user_state_path(self) -> Path:
        """:return: state path tied to the user"""
        return Path(self.user_state_dir)

    @property
    def user_log_path(self) -> Path:
        """:return: log path tied to the user"""
        return Path(self.user_log_dir)

    @property
    def user_documents_path(self) -> Path:
        """:return: documents path tied to the user"""
        return Path(self.user_documents_dir)

    @property
    def user_downloads_path(self) -> Path:
        """:return: downloads path tied to the user"""
        return Path(self.user_downloads_dir)

    @property
    def user_pictures_path(self) -> Path:
        """:return: pictures path tied to the user"""
        return Path(self.user_pictures_dir)

    @property
    def user_videos_path(self) -> Path:
        """:return: videos path tied to the user"""
        return Path(self.user_videos_dir)

    @property
    def user_music_path(self) -> Path:
        """:return: music path tied to the user"""
        return Path(self.user_music_dir)

    @property
    def user_desktop_path(self) -> Path:
        """:return: desktop path tied to the user"""
        return Path(self.user_desktop_dir)

    @property
    def user_runtime_path(self) -> Path:
        """:return: runtime path tied to the user"""
        return Path(self.user_runtime_dir)

    @property
    def site_runtime_path(self) -> Path:
        """:return: runtime path shared by users"""
        return Path(self.site_runtime_dir)

back/venvmsg/Lib/site-packages/platformdirs/api.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
"""macOS."""
from __future__ import annotations

import os.path
import sys

from .api import PlatformDirsABC


class MacOS(PlatformDirsABC):
    """
    Platform directories for the macOS operating system. Follows the guidance from `Apple documentation
    <https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html>`_.
    Makes use of the `appname <platformdirs.api.PlatformDirsABC.appname>`,
    `version <platformdirs.api.PlatformDirsABC.version>`,
    `ensure_exists <platformdirs.api.PlatformDirsABC.ensure_exists>`.
    """

    @property
    def user_data_dir(self) -> str:
        """:return: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
        return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support"))  # noqa: PTH111

    @property
    def site_data_dir(self) -> str:
        """
        :return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``.
          If we're using a Python binary managed by `Homebrew <https://brew.sh>`_, the directory
          will be under the Homebrew prefix, e.g. ``/opt/homebrew/share/$appname/$version``.
          If `multipath <platformdirs.api.PlatformDirsABC.multipath>` is enabled and we're in Homebrew,
          the response is a multi-path string separated by ":", e.g.
          ``/opt/homebrew/share/$appname/$version:/Library/Application Support/$appname/$version``
        """
        is_homebrew = sys.prefix.startswith("/opt/homebrew")
        path_list = [self._append_app_name_and_version("/opt/homebrew/share")] if is_homebrew else []
        path_list.append(self._append_app_name_and_version("/Library/Application Support"))
        if self.multipath:
            return os.pathsep.join(path_list)
        return path_list[0]

    @property
    def user_config_dir(self) -> str:
        """:return: config directory tied to the user, same as `user_data_dir`"""
        return self.user_data_dir

    @property
    def site_config_dir(self) -> str:
        """:return: config directory shared by the users, same as `site_data_dir`"""
        return self.site_data_dir

    @property
    def user_cache_dir(self) -> str:
        """:return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``"""
        return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches"))  # noqa: PTH111

    @property
    def site_cache_dir(self) -> str:
        """
        :return: cache directory shared by users, e.g. ``/Library/Caches/$appname/$version``.
          If we're using a Python binary managed by `Homebrew <https://brew.sh>`_, the directory
          will be under the Homebrew prefix, e.g. ``/opt/homebrew/var/cache/$appname/$version``.
          If `multipath <platformdirs.api.PlatformDirsABC.multipath>` is enabled and we're in Homebrew,
          the response is a multi-path string separated by ":", e.g.
          ``/opt/homebrew/var/cache/$appname/$version:/Library/Caches/$appname/$version``
        """
        is_homebrew = sys.prefix.startswith("/opt/homebrew")
        path_list = [self._append_app_name_and_version("/opt/homebrew/var/cache")] if is_homebrew else []
        path_list.append(self._append_app_name_and_version("/Library/Caches"))
        if self.multipath:
            return os.pathsep.join(path_list)
        return path_list[0]

    @property
    def user_state_dir(self) -> str:
        """:return: state directory tied to the user, same as `user_data_dir`"""
        return self.user_data_dir

    @property
    def user_log_dir(self) -> str:
        """:return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``"""
        return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs"))  # noqa: PTH111

    @property
    def user_documents_dir(self) -> str:
        """:return: documents directory tied to the user, e.g. ``~/Documents``"""
        return os.path.expanduser("~/Documents")  # noqa: PTH111

    @property
    def user_downloads_dir(self) -> str:
        """:return: downloads directory tied to the user, e.g. ``~/Downloads``"""
        return os.path.expanduser("~/Downloads")  # noqa: PTH111

    @property
    def user_pictures_dir(self) -> str:
        """:return: pictures directory tied to the user, e.g. ``~/Pictures``"""
        return os.path.expanduser("~/Pictures")  # noqa: PTH111

    @property
    def user_videos_dir(self) -> str:
        """:return: videos directory tied to the user, e.g. ``~/Movies``"""
        return os.path.expanduser("~/Movies")  # noqa: PTH111

    @property
    def user_music_dir(self) -> str:
        """:return: music directory tied to the user, e.g. ``~/Music``"""
        return os.path.expanduser("~/Music")  # noqa: PTH111

    @property
    def user_desktop_dir(self) -> str:
        """:return: desktop directory tied to the user, e.g. ``~/Desktop``"""
        return os.path.expanduser("~/Desktop")  # noqa: PTH111

    @property
    def user_runtime_dir(self) -> str:
        """:return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``"""
        return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems"))  # noqa: PTH111

    @property
    def site_runtime_dir(self) -> str:
        """:return: runtime directory shared by users, same as `user_runtime_dir`"""
        return self.user_runtime_dir


__all__ = [
    "MacOS",
]

back/venvmsg/Lib/site-packages/platformdirs/macos.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# file generated by setuptools_scm
# don't change, don't track in version control
TYPE_CHECKING = False
if TYPE_CHECKING:
    from typing import Tuple, Union
    VERSION_TUPLE = Tuple[Union[int, str], ...]
else:
    VERSION_TUPLE = object

version: str
__version__: str
__version_tuple__: VERSION_TUPLE
version_tuple: VERSION_TUPLE

__version__ = version = '3.11.0'
__version_tuple__ = version_tuple = (3, 11, 0)

back/venvmsg/Lib/site-packages/platformdirs/version.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

__all__ = [
    "__version__",
    "version",
    "modify_sys_path",
    "run_pylint",
    "run_symilar",
    "run_pyreverse",
]

import os
import sys
from collections.abc import Sequence
from typing import NoReturn

from pylint.__pkginfo__ import __version__

# pylint: disable=import-outside-toplevel


def run_pylint(argv: Sequence[str] | None = None) -> None:
    """Run pylint.

    argv can be a sequence of strings normally supplied as arguments on the command line
    """
    from pylint.lint import Run as PylintRun

    try:
        PylintRun(argv or sys.argv[1:])
    except KeyboardInterrupt:
        sys.exit(1)


def _run_pylint_config(argv: Sequence[str] | None = None) -> None:
    """Run pylint-config.

    argv can be a sequence of strings normally supplied as arguments on the command line
    """
    from pylint.lint.run import _PylintConfigRun

    _PylintConfigRun(argv or sys.argv[1:])


def run_pyreverse(argv: Sequence[str] | None = None) -> NoReturn:
    """Run pyreverse.

    argv can be a sequence of strings normally supplied as arguments on the command line
    """
    from pylint.pyreverse.main import Run as PyreverseRun

    PyreverseRun(argv or sys.argv[1:])


def run_symilar(argv: Sequence[str] | None = None) -> NoReturn:
    """Run symilar.

    argv can be a sequence of strings normally supplied as arguments on the command line
    """
    from pylint.checkers.similar import Run as SimilarRun

    SimilarRun(argv or sys.argv[1:])


def modify_sys_path() -> None:
    """Modify sys path for execution as Python module.

    Strip out the current working directory from sys.path.
    Having the working directory in `sys.path` means that `pylint` might
    inadvertently import user code from modules having the same name as
    stdlib or pylint's own modules.
    CPython issue: https://bugs.python.org/issue33053

    - Remove the first entry. This will always be either "" or the working directory
    - Remove the working directory from the second and third entries
      if PYTHONPATH includes a ":" at the beginning or the end.
      https://github.com/pylint-dev/pylint/issues/3636
      Don't remove it if PYTHONPATH contains the cwd or '.' as the entry will
      only be added once.
    - Don't remove the working directory from the rest. It will be included
      if pylint is installed in an editable configuration (as the last item).
      https://github.com/pylint-dev/pylint/issues/4161
    """
    cwd = os.getcwd()
    if sys.path[0] in ("", ".", cwd):
        sys.path.pop(0)
    env_pythonpath = os.environ.get("PYTHONPATH", "")
    if env_pythonpath.startswith(":") and env_pythonpath not in (f":{cwd}", ":."):
        sys.path.pop(0)
    elif env_pythonpath.endswith(":") and env_pythonpath not in (f"{cwd}:", ".:"):
        sys.path.pop(1)


version = __version__

back/venvmsg/Lib/site-packages/pylint/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
#!/usr/bin/env python

# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

import pylint

pylint.modify_sys_path()
pylint.run_pylint()

back/venvmsg/Lib/site-packages/pylint/__main__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""This module exists for compatibility reasons.

It's updated via tbump, do not modify.
"""

from __future__ import annotations

__version__ = "3.0.0"


def get_numversion_from_version(v: str) -> tuple[int, int, int]:
    """Kept for compatibility reason.

    See https://github.com/pylint-dev/pylint/issues/4399
    https://github.com/pylint-dev/pylint/issues/4420,
    """
    version = v.replace("pylint-", "")
    result_version = []
    for number in version.split(".")[0:3]:
        try:
            result_version.append(int(number))
        except ValueError:
            current_number = ""
            for char in number:
                if char.isdigit():
                    current_number += char
                else:
                    break
            try:
                result_version.append(int(current_number))
            except ValueError:
                result_version.append(0)
    while len(result_version) != 3:
        result_version.append(0)

    return tuple(result_version)  # type: ignore[return-value] # mypy can't infer the length


numversion = get_numversion_from_version(__version__)

back/venvmsg/Lib/site-packages/pylint/__pkginfo__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Utilities methods and classes for checkers.

Base id of standard checkers (used in msg and report ids):
01: base
02: classes
03: format
04: import
05: misc
06: variables
07: exceptions
08: similar
09: design_analysis
10: newstyle
11: typecheck
12: logging
13: string_format
14: string_constant
15: stdlib
16: python3 (This one was deleted but needs to be reserved for consistency with old messages)
17: refactoring
.
.
.
24: non-ascii-names
25: unicode
26: unsupported_version
27: private-import
28-50: not yet used: reserved for future internal checkers.
This file is not updated. Use
   script/get_unused_message_id_category.py
to get the next free checker id.

51-99: perhaps used: reserved for external checkers

The raw_metrics checker has no number associated since it doesn't emit any
messages nor reports. XXX not true, emit a 07 report !
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Literal

from pylint.checkers.base_checker import (
    BaseChecker,
    BaseRawFileChecker,
    BaseTokenChecker,
)
from pylint.checkers.deprecated import DeprecatedMixin
from pylint.utils import LinterStats, diff_string, register_plugins

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def table_lines_from_stats(
    stats: LinterStats,
    old_stats: LinterStats | None,
    stat_type: Literal["duplicated_lines", "message_types"],
) -> list[str]:
    """Get values listed in <columns> from <stats> and <old_stats>,
    and return a formatted list of values.

    The return value is designed to be given to a ureport.Table object
    """
    lines: list[str] = []
    if stat_type == "duplicated_lines":
        new: list[tuple[str, int | float]] = [
            ("nb_duplicated_lines", stats.duplicated_lines["nb_duplicated_lines"]),
            (
                "percent_duplicated_lines",
                stats.duplicated_lines["percent_duplicated_lines"],
            ),
        ]
        if old_stats:
            old: list[tuple[str, str | int | float]] = [
                (
                    "nb_duplicated_lines",
                    old_stats.duplicated_lines["nb_duplicated_lines"],
                ),
                (
                    "percent_duplicated_lines",
                    old_stats.duplicated_lines["percent_duplicated_lines"],
                ),
            ]
        else:
            old = [("nb_duplicated_lines", "NC"), ("percent_duplicated_lines", "NC")]
    elif stat_type == "message_types":
        new = [
            ("convention", stats.convention),
            ("refactor", stats.refactor),
            ("warning", stats.warning),
            ("error", stats.error),
        ]
        if old_stats:
            old = [
                ("convention", old_stats.convention),
                ("refactor", old_stats.refactor),
                ("warning", old_stats.warning),
                ("error", old_stats.error),
            ]
        else:
            old = [
                ("convention", "NC"),
                ("refactor", "NC"),
                ("warning", "NC"),
                ("error", "NC"),
            ]

    for index, value in enumerate(new):
        new_value = value[1]
        old_value = old[index][1]
        diff_str = (
            diff_string(old_value, new_value)
            if isinstance(old_value, float)
            else old_value
        )
        new_str = f"{new_value:.3f}" if isinstance(new_value, float) else str(new_value)
        old_str = f"{old_value:.3f}" if isinstance(old_value, float) else str(old_value)
        lines.extend((value[0].replace("_", " "), new_str, old_str, diff_str))  # type: ignore[arg-type]
    return lines


def initialize(linter: PyLinter) -> None:
    """Initialize linter with checkers in this package."""
    register_plugins(linter, __path__[0])


__all__ = [
    "BaseChecker",
    "BaseTokenChecker",
    "BaseRawFileChecker",
    "initialize",
    "DeprecatedMixin",
    "register_plugins",
]

back/venvmsg/Lib/site-packages/pylint/checkers/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checker for anything related to the async protocol (PEP 492)."""

from __future__ import annotations

import sys
from typing import TYPE_CHECKING

import astroid
from astroid import nodes, util

from pylint import checkers
from pylint.checkers import utils as checker_utils
from pylint.checkers.utils import decorated_with

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class AsyncChecker(checkers.BaseChecker):
    name = "async"
    msgs = {
        "E1700": (
            "Yield inside async function",
            "yield-inside-async-function",
            "Used when an `yield` or `yield from` statement is "
            "found inside an async function.",
            {"minversion": (3, 5)},
        ),
        "E1701": (
            "Async context manager '%s' doesn't implement __aenter__ and __aexit__.",
            "not-async-context-manager",
            "Used when an async context manager is used with an object "
            "that does not implement the async context management protocol.",
            {"minversion": (3, 5)},
        ),
    }

    def open(self) -> None:
        self._mixin_class_rgx = self.linter.config.mixin_class_rgx
        self._async_generators = ["contextlib.asynccontextmanager"]

    @checker_utils.only_required_for_messages("yield-inside-async-function")
    def visit_asyncfunctiondef(self, node: nodes.AsyncFunctionDef) -> None:
        for child in node.nodes_of_class(nodes.Yield):
            if child.scope() is node and (
                sys.version_info[:2] == (3, 5) or isinstance(child, nodes.YieldFrom)
            ):
                self.add_message("yield-inside-async-function", node=child)

    @checker_utils.only_required_for_messages("not-async-context-manager")
    def visit_asyncwith(self, node: nodes.AsyncWith) -> None:
        for ctx_mgr, _ in node.items:
            inferred = checker_utils.safe_infer(ctx_mgr)
            if inferred is None or isinstance(inferred, util.UninferableBase):
                continue

            if isinstance(inferred, nodes.AsyncFunctionDef):
                # Check if we are dealing with a function decorated
                # with contextlib.asynccontextmanager.
                if decorated_with(inferred, self._async_generators):
                    continue
            elif isinstance(inferred, astroid.bases.AsyncGenerator):
                # Check if we are dealing with a function decorated
                # with contextlib.asynccontextmanager.
                if decorated_with(inferred.parent, self._async_generators):
                    continue
            else:
                try:
                    inferred.getattr("__aenter__")
                    inferred.getattr("__aexit__")
                except astroid.exceptions.NotFoundError:
                    if isinstance(inferred, astroid.Instance):
                        # If we do not know the bases of this class,
                        # just skip it.
                        if not checker_utils.has_known_bases(inferred):
                            continue
                        # Ignore mixin classes if they match the rgx option.
                        if (
                            "not-async-context-manager"
                            in self.linter.config.ignored_checks_for_mixins
                            and self._mixin_class_rgx.match(inferred.name)
                        ):
                            continue
                else:
                    continue
            self.add_message(
                "not-async-context-manager", node=node, args=(inferred.name,)
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(AsyncChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/async.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter

COMPARISON_OP = frozenset(("<", "<=", ">", ">=", "!=", "=="))
IDENTITY_OP = frozenset(("is", "is not"))
MEMBERSHIP_OP = frozenset(("in", "not in"))


class BadChainedComparisonChecker(BaseChecker):
    """Checks for unintentional usage of chained comparison."""

    name = "bad-chained-comparison"
    msgs = {
        "W3601": (
            "Suspicious %s-part chained comparison using semantically incompatible operators (%s)",
            "bad-chained-comparison",
            "Used when there is a chained comparison where one expression is part "
            "of two comparisons that belong to different semantic groups "
            '("<" does not mean the same thing as "is", chaining them in '
            '"0 < x is None" is probably a mistake).',
        )
    }

    def _has_diff_semantic_groups(self, operators: list[str]) -> bool:
        # Check if comparison operators are in the same semantic group
        for semantic_group in (COMPARISON_OP, IDENTITY_OP, MEMBERSHIP_OP):
            if operators[0] in semantic_group:
                group = semantic_group
        return not all(o in group for o in operators)

    def visit_compare(self, node: nodes.Compare) -> None:
        operators = sorted({op[0] for op in node.ops})
        if self._has_diff_semantic_groups(operators):
            num_parts = f"{len(node.ops)}"
            incompatibles = (
                ", ".join(f"'{o}'" for o in operators[:-1]) + f" and '{operators[-1]}'"
            )
            self.add_message(
                "bad-chained-comparison",
                node=node,
                args=(num_parts, incompatibles),
                confidence=HIGH,
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(BadChainedComparisonChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/bad_chained_comparison.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

# pylint: disable=duplicate-code # This is similar to the __init__ of .name_checker

from __future__ import annotations

__all__ = [
    "NameChecker",
    "NamingStyle",
    "KNOWN_NAME_TYPES_WITH_STYLE",
    "SnakeCaseStyle",
    "CamelCaseStyle",
    "UpperCaseStyle",
    "PascalCaseStyle",
    "AnyStyle",
]

from typing import TYPE_CHECKING

from pylint.checkers.base.basic_checker import BasicChecker
from pylint.checkers.base.basic_error_checker import BasicErrorChecker
from pylint.checkers.base.comparison_checker import ComparisonChecker
from pylint.checkers.base.docstring_checker import DocStringChecker
from pylint.checkers.base.name_checker import (
    KNOWN_NAME_TYPES_WITH_STYLE,
    AnyStyle,
    CamelCaseStyle,
    NamingStyle,
    PascalCaseStyle,
    SnakeCaseStyle,
    UpperCaseStyle,
)
from pylint.checkers.base.name_checker.checker import NameChecker
from pylint.checkers.base.pass_checker import PassChecker

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def register(linter: PyLinter) -> None:
    linter.register_checker(BasicErrorChecker(linter))
    linter.register_checker(BasicChecker(linter))
    linter.register_checker(NameChecker(linter))
    linter.register_checker(DocStringChecker(linter))
    linter.register_checker(PassChecker(linter))
    linter.register_checker(ComparisonChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/base/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Basic checker for Python code."""

from __future__ import annotations

import collections
import itertools
from collections.abc import Iterator
from typing import TYPE_CHECKING, Literal, cast

import astroid
from astroid import nodes, objects, util

from pylint import utils as lint_utils
from pylint.checkers import BaseChecker, utils
from pylint.interfaces import HIGH, INFERENCE, Confidence
from pylint.reporters.ureports import nodes as reporter_nodes
from pylint.utils import LinterStats

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


class _BasicChecker(BaseChecker):
    """Permits separating multiple checks with the same checker name into
    classes/file.
    """

    name = "basic"


REVERSED_PROTOCOL_METHOD = "__reversed__"
SEQUENCE_PROTOCOL_METHODS = ("__getitem__", "__len__")
REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS, (REVERSED_PROTOCOL_METHOD,))
# A mapping from qname -> symbol, to be used when generating messages
# about dangerous default values as arguments
DEFAULT_ARGUMENT_SYMBOLS = dict(
    zip(
        [".".join(["builtins", x]) for x in ("set", "dict", "list")],
        ["set()", "{}", "[]"],
    ),
    **{
        x: f"{x}()"
        for x in (
            "collections.deque",
            "collections.ChainMap",
            "collections.Counter",
            "collections.OrderedDict",
            "collections.defaultdict",
            "collections.UserDict",
            "collections.UserList",
        )
    },
)


def report_by_type_stats(
    sect: reporter_nodes.Section,
    stats: LinterStats,
    old_stats: LinterStats | None,
) -> None:
    """Make a report of.

    * percentage of different types documented
    * percentage of different types with a bad name
    """
    # percentage of different types documented and/or with a bad name
    nice_stats: dict[str, dict[str, str]] = {}
    for node_type in ("module", "class", "method", "function"):
        node_type = cast(Literal["function", "class", "method", "module"], node_type)
        total = stats.get_node_count(node_type)
        nice_stats[node_type] = {}
        if total != 0:
            undocumented_node = stats.get_undocumented(node_type)
            documented = total - undocumented_node
            percent = (documented * 100.0) / total
            nice_stats[node_type]["percent_documented"] = f"{percent:.2f}"
            badname_node = stats.get_bad_names(node_type)
            percent = (badname_node * 100.0) / total
            nice_stats[node_type]["percent_badname"] = f"{percent:.2f}"
    lines = ["type", "number", "old number", "difference", "%documented", "%badname"]
    for node_type in ("module", "class", "method", "function"):
        node_type = cast(Literal["function", "class", "method", "module"], node_type)
        new = stats.get_node_count(node_type)
        old = old_stats.get_node_count(node_type) if old_stats else None
        diff_str = lint_utils.diff_string(old, new) if old else None
        lines += [
            node_type,
            str(new),
            str(old) if old else "NC",
            diff_str if diff_str else "NC",
            nice_stats[node_type].get("percent_documented", "0"),
            nice_stats[node_type].get("percent_badname", "0"),
        ]
    sect.append(reporter_nodes.Table(children=lines, cols=6, rheaders=1))


# pylint: disable-next = too-many-public-methods
class BasicChecker(_BasicChecker):
    """Basic checker.

    Checks for :
    * doc strings
    * number of arguments, local variables, branches, returns and statements in
    functions, methods
    * required module attributes
    * dangerous default values as arguments
    * redefinition of function / method / class
    * uses of the global statement
    """

    name = "basic"
    msgs = {
        "W0101": (
            "Unreachable code",
            "unreachable",
            'Used when there is some code behind a "return" or "raise" '
            "statement, which will never be accessed.",
        ),
        "W0102": (
            "Dangerous default value %s as argument",
            "dangerous-default-value",
            "Used when a mutable value as list or dictionary is detected in "
            "a default value for an argument.",
        ),
        "W0104": (
            "Statement seems to have no effect",
            "pointless-statement",
            "Used when a statement doesn't have (or at least seems to) any effect.",
        ),
        "W0105": (
            "String statement has no effect",
            "pointless-string-statement",
            "Used when a string is used as a statement (which of course "
            "has no effect). This is a particular case of W0104 with its "
            "own message so you can easily disable it if you're using "
            "those strings as documentation, instead of comments.",
        ),
        "W0106": (
            'Expression "%s" is assigned to nothing',
            "expression-not-assigned",
            "Used when an expression that is not a function call is assigned "
            "to nothing. Probably something else was intended.",
        ),
        "W0108": (
            "Lambda may not be necessary",
            "unnecessary-lambda",
            "Used when the body of a lambda expression is a function call "
            "on the same argument list as the lambda itself; such lambda "
            "expressions are in all but a few cases replaceable with the "
            "function being called in the body of the lambda.",
        ),
        "W0109": (
            "Duplicate key %r in dictionary",
            "duplicate-key",
            "Used when a dictionary expression binds the same key multiple times.",
        ),
        "W0122": (
            "Use of exec",
            "exec-used",
            "Raised when the 'exec' statement is used. It's dangerous to use this "
            "function for a user input, and it's also slower than actual code in "
            "general. This doesn't mean you should never use it, but you should "
            "consider alternatives first and restrict the functions available.",
        ),
        "W0123": (
            "Use of eval",
            "eval-used",
            'Used when you use the "eval" function, to discourage its '
            "usage. Consider using `ast.literal_eval` for safely evaluating "
            "strings containing Python expressions "
            "from untrusted sources.",
        ),
        "W0150": (
            "%s statement in finally block may swallow exception",
            "lost-exception",
            "Used when a break or a return statement is found inside the "
            "finally clause of a try...finally block: the exceptions raised "
            "in the try clause will be silently swallowed instead of being "
            "re-raised.",
        ),
        "W0199": (
            "Assert called on a populated tuple. Did you mean 'assert x,y'?",
            "assert-on-tuple",
            "A call of assert on a tuple will always evaluate to true if "
            "the tuple is not empty, and will always evaluate to false if "
            "it is.",
        ),
        "W0124": (
            'Following "as" with another context manager looks like a tuple.',
            "confusing-with-statement",
            "Emitted when a `with` statement component returns multiple values "
            "and uses name binding with `as` only for a part of those values, "
            "as in with ctx() as a, b. This can be misleading, since it's not "
            "clear if the context manager returns a tuple or if the node without "
            "a name binding is another context manager.",
        ),
        "W0125": (
            "Using a conditional statement with a constant value",
            "using-constant-test",
            "Emitted when a conditional statement (If or ternary if) "
            "uses a constant value for its test. This might not be what "
            "the user intended to do.",
        ),
        "W0126": (
            "Using a conditional statement with potentially wrong function or method call due to "
            "missing parentheses",
            "missing-parentheses-for-call-in-test",
            "Emitted when a conditional statement (If or ternary if) "
            "seems to wrongly call a function due to missing parentheses",
        ),
        "W0127": (
            "Assigning the same variable %r to itself",
            "self-assigning-variable",
            "Emitted when we detect that a variable is assigned to itself",
        ),
        "W0128": (
            "Redeclared variable %r in assignment",
            "redeclared-assigned-name",
            "Emitted when we detect that a variable was redeclared in the same assignment.",
        ),
        "E0111": (
            "The first reversed() argument is not a sequence",
            "bad-reversed-sequence",
            "Used when the first argument to reversed() builtin "
            "isn't a sequence (does not implement __reversed__, "
            "nor __getitem__ and __len__",
        ),
        "E0119": (
            "format function is not called on str",
            "misplaced-format-function",
            "Emitted when format function is not called on str object. "
            'e.g doing print("value: {}").format(123) instead of '
            'print("value: {}".format(123)). This might not be what the user '
            "intended to do.",
        ),
        "W0129": (
            "Assert statement has a string literal as its first argument. The assert will %s fail.",
            "assert-on-string-literal",
            "Used when an assert statement has a string literal as its first argument, which will "
            "cause the assert to always pass.",
        ),
        "W0130": (
            "Duplicate value %r in set",
            "duplicate-value",
            "This message is emitted when a set contains the same value two or more times.",
        ),
        "W0131": (
            "Named expression used without context",
            "named-expr-without-context",
            "Emitted if named expression is used to do a regular assignment "
            "outside a context like if, for, while, or a comprehension.",
        ),
        "W0133": (
            "Exception statement has no effect",
            "pointless-exception-statement",
            "Used when an exception is created without being assigned, raised or returned "
            "for subsequent use elsewhere.",
        ),
        "W0134": (
            "'return' shadowed by the 'finally' clause.",
            "return-in-finally",
            "Emitted when a 'return' statement is found in a 'finally' block. This will overwrite "
            "the return value of a function and should be avoided.",
        ),
    }

    reports = (("RP0101", "Statistics by type", report_by_type_stats),)

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._trys: list[nodes.Try]

    def open(self) -> None:
        """Initialize visit variables and statistics."""
        py_version = self.linter.config.py_version
        self._py38_plus = py_version >= (3, 8)
        self._trys = []
        self.linter.stats.reset_node_count()

    @utils.only_required_for_messages(
        "using-constant-test", "missing-parentheses-for-call-in-test"
    )
    def visit_if(self, node: nodes.If) -> None:
        self._check_using_constant_test(node, node.test)

    @utils.only_required_for_messages(
        "using-constant-test", "missing-parentheses-for-call-in-test"
    )
    def visit_ifexp(self, node: nodes.IfExp) -> None:
        self._check_using_constant_test(node, node.test)

    @utils.only_required_for_messages(
        "using-constant-test", "missing-parentheses-for-call-in-test"
    )
    def visit_comprehension(self, node: nodes.Comprehension) -> None:
        if node.ifs:
            for if_test in node.ifs:
                self._check_using_constant_test(node, if_test)

    def _check_using_constant_test(
        self,
        node: nodes.If | nodes.IfExp | nodes.Comprehension,
        test: nodes.NodeNG | None,
    ) -> None:
        const_nodes = (
            nodes.Module,
            nodes.GeneratorExp,
            nodes.Lambda,
            nodes.FunctionDef,
            nodes.ClassDef,
            astroid.bases.Generator,
            astroid.UnboundMethod,
            astroid.BoundMethod,
            nodes.Module,
        )
        structs = (nodes.Dict, nodes.Tuple, nodes.Set, nodes.List)

        # These nodes are excepted, since they are not constant
        # values, requiring a computation to happen.
        except_nodes = (
            nodes.Call,
            nodes.BinOp,
            nodes.BoolOp,
            nodes.UnaryOp,
            nodes.Subscript,
        )
        inferred = None
        emit = isinstance(test, (nodes.Const, *structs, *const_nodes))
        maybe_generator_call = None
        if not isinstance(test, except_nodes):
            inferred = utils.safe_infer(test)
            if isinstance(inferred, util.UninferableBase) and isinstance(
                test, nodes.Name
            ):
                emit, maybe_generator_call = BasicChecker._name_holds_generator(test)

        # Emit if calling a function that only returns GeneratorExp (always tests True)
        elif isinstance(test, nodes.Call):
            maybe_generator_call = test
        if maybe_generator_call:
            inferred_call = utils.safe_infer(maybe_generator_call.func)
            if isinstance(inferred_call, nodes.FunctionDef):
                # Can't use all(x) or not any(not x) for this condition, because it
                # will return True for empty generators, which is not what we want.
                all_returns_were_generator = None
                for return_node in inferred_call._get_return_nodes_skip_functions():
                    if not isinstance(return_node.value, nodes.GeneratorExp):
                        all_returns_were_generator = False
                        break
                    all_returns_were_generator = True
                if all_returns_were_generator:
                    self.add_message(
                        "using-constant-test", node=node, confidence=INFERENCE
                    )
                    return

        if emit:
            self.add_message("using-constant-test", node=test, confidence=INFERENCE)
        elif isinstance(inferred, const_nodes):
            # If the constant node is a FunctionDef or Lambda then
            # it may be an illicit function call due to missing parentheses
            call_inferred = None
            try:
                # Just forcing the generator to infer all elements.
                # astroid.exceptions.InferenceError are false positives
                # see https://github.com/pylint-dev/pylint/pull/8185
                if isinstance(inferred, nodes.FunctionDef):
                    call_inferred = list(inferred.infer_call_result(node))
                elif isinstance(inferred, nodes.Lambda):
                    call_inferred = list(inferred.infer_call_result(node))
            except astroid.InferenceError:
                call_inferred = None
            if call_inferred:
                self.add_message(
                    "missing-parentheses-for-call-in-test",
                    node=test,
                    confidence=INFERENCE,
                )
            self.add_message("using-constant-test", node=test, confidence=INFERENCE)

    @staticmethod
    def _name_holds_generator(test: nodes.Name) -> tuple[bool, nodes.Call | None]:
        """Return whether `test` tests a name certain to hold a generator, or optionally
        a call that should be then tested to see if *it* returns only generators.
        """
        assert isinstance(test, nodes.Name)
        emit = False
        maybe_generator_call = None
        lookup_result = test.frame().lookup(test.name)
        if not lookup_result:
            return emit, maybe_generator_call
        maybe_generator_assigned = (
            isinstance(assign_name.parent.value, nodes.GeneratorExp)
            for assign_name in lookup_result[1]
            if isinstance(assign_name.parent, nodes.Assign)
        )
        first_item = next(maybe_generator_assigned, None)
        if first_item is not None:
            # Emit if this variable is certain to hold a generator
            if all(itertools.chain((first_item,), maybe_generator_assigned)):
                emit = True
            # If this variable holds the result of a call, save it for next test
            elif (
                len(lookup_result[1]) == 1
                and isinstance(lookup_result[1][0].parent, nodes.Assign)
                and isinstance(lookup_result[1][0].parent.value, nodes.Call)
            ):
                maybe_generator_call = lookup_result[1][0].parent.value
        return emit, maybe_generator_call

    def visit_module(self, _: nodes.Module) -> None:
        """Check module name, docstring and required arguments."""
        self.linter.stats.node_count["module"] += 1

    def visit_classdef(self, _: nodes.ClassDef) -> None:
        """Check module name, docstring and redefinition
        increment branch counter.
        """
        self.linter.stats.node_count["klass"] += 1

    @utils.only_required_for_messages(
        "pointless-statement",
        "pointless-exception-statement",
        "pointless-string-statement",
        "expression-not-assigned",
        "named-expr-without-context",
    )
    def visit_expr(self, node: nodes.Expr) -> None:
        """Check for various kind of statements without effect."""
        expr = node.value
        if isinstance(expr, nodes.Const) and isinstance(expr.value, str):
            # treat string statement in a separated message
            # Handle PEP-257 attribute docstrings.
            # An attribute docstring is defined as being a string right after
            # an assignment at the module level, class level or __init__ level.
            scope = expr.scope()
            if isinstance(scope, (nodes.ClassDef, nodes.Module, nodes.FunctionDef)):
                if isinstance(scope, nodes.FunctionDef) and scope.name != "__init__":
                    pass
                else:
                    sibling = expr.previous_sibling()
                    if (
                        sibling is not None
                        and sibling.scope() is scope
                        and isinstance(sibling, (nodes.Assign, nodes.AnnAssign))
                    ):
                        return
            self.add_message("pointless-string-statement", node=node)
            return

        # Warn W0133 for exceptions that are used as statements
        if isinstance(expr, nodes.Call):
            name = ""
            if isinstance(expr.func, nodes.Name):
                name = expr.func.name
            elif isinstance(expr.func, nodes.Attribute):
                name = expr.func.attrname

            # Heuristic: only run inference for names that begin with an uppercase char
            # This reduces W0133's coverage, but retains acceptable runtime performance
            # For more details, see: https://github.com/pylint-dev/pylint/issues/8073
            inferred = utils.safe_infer(expr) if name[:1].isupper() else None
            if isinstance(inferred, objects.ExceptionInstance):
                self.add_message(
                    "pointless-exception-statement", node=node, confidence=INFERENCE
                )
            return

        # Ignore if this is :
        # * the unique child of a try/except body
        # * a yield statement
        # * an ellipsis (which can be used on Python 3 instead of pass)
        # warn W0106 if we have any underlying function call (we can't predict
        # side effects), else pointless-statement
        if (
            isinstance(expr, (nodes.Yield, nodes.Await))
            or (isinstance(node.parent, nodes.Try) and node.parent.body == [node])
            or (isinstance(expr, nodes.Const) and expr.value is Ellipsis)
        ):
            return
        if isinstance(expr, nodes.NamedExpr):
            self.add_message("named-expr-without-context", node=node, confidence=HIGH)
        elif any(expr.nodes_of_class(nodes.Call)):
            self.add_message(
                "expression-not-assigned", node=node, args=expr.as_string()
            )
        else:
            self.add_message("pointless-statement", node=node)

    @staticmethod
    def _filter_vararg(
        node: nodes.Lambda, call_args: list[nodes.NodeNG]
    ) -> Iterator[nodes.NodeNG]:
        # Return the arguments for the given call which are
        # not passed as vararg.
        for arg in call_args:
            if isinstance(arg, nodes.Starred):
                if (
                    isinstance(arg.value, nodes.Name)
                    and arg.value.name != node.args.vararg
                ):
                    yield arg
            else:
                yield arg

    @staticmethod
    def _has_variadic_argument(
        args: list[nodes.Starred | nodes.Keyword], variadic_name: str
    ) -> bool:
        return not args or any(
            isinstance(a.value, nodes.Name)
            and a.value.name != variadic_name
            or not isinstance(a.value, nodes.Name)
            for a in args
        )

    @utils.only_required_for_messages("unnecessary-lambda")
    # pylint: disable-next=too-many-return-statements
    def visit_lambda(self, node: nodes.Lambda) -> None:
        """Check whether the lambda is suspicious."""
        # if the body of the lambda is a call expression with the same
        # argument list as the lambda itself, then the lambda is
        # possibly unnecessary and at least suspicious.
        if node.args.defaults:
            # If the arguments of the lambda include defaults, then a
            # judgment cannot be made because there is no way to check
            # that the defaults defined by the lambda are the same as
            # the defaults defined by the function called in the body
            # of the lambda.
            return
        call = node.body
        if not isinstance(call, nodes.Call):
            # The body of the lambda must be a function call expression
            # for the lambda to be unnecessary.
            return
        if isinstance(node.body.func, nodes.Attribute) and isinstance(
            node.body.func.expr, nodes.Call
        ):
            # Chained call, the intermediate call might
            # return something else (but we don't check that, yet).
            return

        call_site = astroid.arguments.CallSite.from_call(call)
        ordinary_args = list(node.args.args)
        new_call_args = list(self._filter_vararg(node, call.args))
        if node.args.kwarg:
            if self._has_variadic_argument(call.kwargs, node.args.kwarg):
                return

        if node.args.vararg:
            if self._has_variadic_argument(call.starargs, node.args.vararg):
                return
        elif call.starargs:
            return

        if call.keywords:
            # Look for additional keyword arguments that are not part
            # of the lambda's signature
            lambda_kwargs = {keyword.name for keyword in node.args.defaults}
            if len(lambda_kwargs) != len(call_site.keyword_arguments):
                # Different lengths, so probably not identical
                return
            if set(call_site.keyword_arguments).difference(lambda_kwargs):
                return

        # The "ordinary" arguments must be in a correspondence such that:
        # ordinary_args[i].name == call.args[i].name.
        if len(ordinary_args) != len(new_call_args):
            return
        for arg, passed_arg in zip(ordinary_args, new_call_args):
            if not isinstance(passed_arg, nodes.Name):
                return
            if arg.name != passed_arg.name:
                return

        # The lambda is necessary if it uses its parameter in the function it is
        # calling in the lambda's body
        # e.g. lambda foo: (func1 if foo else func2)(foo)
        for name in call.func.nodes_of_class(nodes.Name):
            if name.lookup(name.name)[0] is node:
                return

        self.add_message("unnecessary-lambda", line=node.fromlineno, node=node)

    @utils.only_required_for_messages("dangerous-default-value")
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        """Check function name, docstring, arguments, redefinition,
        variable names, max locals.
        """
        if node.is_method():
            self.linter.stats.node_count["method"] += 1
        else:
            self.linter.stats.node_count["function"] += 1
        self._check_dangerous_default(node)

    visit_asyncfunctiondef = visit_functiondef

    def _check_dangerous_default(self, node: nodes.FunctionDef) -> None:
        """Check for dangerous default values as arguments."""

        def is_iterable(internal_node: nodes.NodeNG) -> bool:
            return isinstance(internal_node, (nodes.List, nodes.Set, nodes.Dict))

        defaults = (node.args.defaults or []) + (node.args.kw_defaults or [])
        for default in defaults:
            if not default:
                continue
            try:
                value = next(default.infer())
            except astroid.InferenceError:
                continue

            if (
                isinstance(value, astroid.Instance)
                and value.qname() in DEFAULT_ARGUMENT_SYMBOLS
            ):
                if value is default:
                    msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()]
                elif isinstance(value, astroid.Instance) or is_iterable(value):
                    # We are here in the following situation(s):
                    #   * a dict/set/list/tuple call which wasn't inferred
                    #     to a syntax node ({}, () etc.). This can happen
                    #     when the arguments are invalid or unknown to
                    #     the inference.
                    #   * a variable from somewhere else, which turns out to be a list
                    #     or a dict.
                    if is_iterable(default):
                        msg = value.pytype()
                    elif isinstance(default, nodes.Call):
                        msg = f"{value.name}() ({value.qname()})"
                    else:
                        msg = f"{default.as_string()} ({value.qname()})"
                else:
                    # this argument is a name
                    msg = f"{default.as_string()} ({DEFAULT_ARGUMENT_SYMBOLS[value.qname()]})"
                self.add_message("dangerous-default-value", node=node, args=(msg,))

    @utils.only_required_for_messages("unreachable", "lost-exception")
    def visit_return(self, node: nodes.Return) -> None:
        """Return node visitor.

        1 - check if the node has a right sibling (if so, that's some
        unreachable code)
        2 - check if the node is inside the 'finally' clause of a 'try...finally'
        block
        """
        self._check_unreachable(node)
        # Is it inside final body of a try...finally block ?
        self._check_not_in_finally(node, "return", (nodes.FunctionDef,))

    @utils.only_required_for_messages("unreachable")
    def visit_continue(self, node: nodes.Continue) -> None:
        """Check is the node has a right sibling (if so, that's some unreachable
        code).
        """
        self._check_unreachable(node)

    @utils.only_required_for_messages("unreachable", "lost-exception")
    def visit_break(self, node: nodes.Break) -> None:
        """Break node visitor.

        1 - check if the node has a right sibling (if so, that's some
        unreachable code)
        2 - check if the node is inside the 'finally' clause of a 'try...finally'
        block
        """
        # 1 - Is it right sibling ?
        self._check_unreachable(node)
        # 2 - Is it inside final body of a try...finally block ?
        self._check_not_in_finally(node, "break", (nodes.For, nodes.While))

    @utils.only_required_for_messages("unreachable")
    def visit_raise(self, node: nodes.Raise) -> None:
        """Check if the node has a right sibling (if so, that's some unreachable
        code).
        """
        self._check_unreachable(node)

    def _check_misplaced_format_function(self, call_node: nodes.Call) -> None:
        if not isinstance(call_node.func, nodes.Attribute):
            return
        if call_node.func.attrname != "format":
            return

        expr = utils.safe_infer(call_node.func.expr)
        if isinstance(expr, util.UninferableBase):
            return
        if not expr:
            # we are doubtful on inferred type of node, so here just check if format
            # was called on print()
            call_expr = call_node.func.expr
            if not isinstance(call_expr, nodes.Call):
                return
            if (
                isinstance(call_expr.func, nodes.Name)
                and call_expr.func.name == "print"
            ):
                self.add_message("misplaced-format-function", node=call_node)

    @utils.only_required_for_messages(
        "eval-used",
        "exec-used",
        "bad-reversed-sequence",
        "misplaced-format-function",
        "unreachable",
    )
    def visit_call(self, node: nodes.Call) -> None:
        """Visit a Call node."""
        if utils.is_terminating_func(node):
            self._check_unreachable(node, confidence=INFERENCE)
        self._check_misplaced_format_function(node)
        if isinstance(node.func, nodes.Name):
            name = node.func.name
            # ignore the name if it's not a builtin (i.e. not defined in the
            # locals nor globals scope)
            if not (name in node.frame() or name in node.root()):
                if name == "exec":
                    self.add_message("exec-used", node=node)
                elif name == "reversed":
                    self._check_reversed(node)
                elif name == "eval":
                    self.add_message("eval-used", node=node)

    @utils.only_required_for_messages("assert-on-tuple", "assert-on-string-literal")
    def visit_assert(self, node: nodes.Assert) -> None:
        """Check whether assert is used on a tuple or string literal."""
        if isinstance(node.test, nodes.Tuple) and len(node.test.elts) > 0:
            self.add_message("assert-on-tuple", node=node, confidence=HIGH)

        if isinstance(node.test, nodes.Const) and isinstance(node.test.value, str):
            if node.test.value:
                when = "never"
            else:
                when = "always"
            self.add_message("assert-on-string-literal", node=node, args=(when,))

    @utils.only_required_for_messages("duplicate-key")
    def visit_dict(self, node: nodes.Dict) -> None:
        """Check duplicate key in dictionary."""
        keys = set()
        for k, _ in node.items:
            if isinstance(k, nodes.Const):
                key = k.value
            elif isinstance(k, nodes.Attribute):
                key = k.as_string()
            else:
                continue
            if key in keys:
                self.add_message("duplicate-key", node=node, args=key)
            keys.add(key)

    @utils.only_required_for_messages("duplicate-value")
    def visit_set(self, node: nodes.Set) -> None:
        """Check duplicate value in set."""
        values = set()
        for v in node.elts:
            if isinstance(v, nodes.Const):
                value = v.value
            else:
                continue
            if value in values:
                self.add_message(
                    "duplicate-value", node=node, args=value, confidence=HIGH
                )
            values.add(value)

    def visit_try(self, node: nodes.Try) -> None:
        """Update try block flag."""
        self._trys.append(node)

        for final_node in node.finalbody:
            for return_node in final_node.nodes_of_class(nodes.Return):
                self.add_message("return-in-finally", node=return_node, confidence=HIGH)

    def leave_try(self, _: nodes.Try) -> None:
        """Update try block flag."""
        self._trys.pop()

    def _check_unreachable(
        self,
        node: nodes.Return | nodes.Continue | nodes.Break | nodes.Raise | nodes.Call,
        confidence: Confidence = HIGH,
    ) -> None:
        """Check unreachable code."""
        unreachable_statement = node.next_sibling()
        if unreachable_statement is not None:
            if (
                isinstance(node, nodes.Return)
                and isinstance(unreachable_statement, nodes.Expr)
                and isinstance(unreachable_statement.value, nodes.Yield)
            ):
                # Don't add 'unreachable' for empty generators.
                # Only add warning if 'yield' is followed by another node.
                unreachable_statement = unreachable_statement.next_sibling()
                if unreachable_statement is None:
                    return
            self.add_message(
                "unreachable", node=unreachable_statement, confidence=confidence
            )

    def _check_not_in_finally(
        self,
        node: nodes.Break | nodes.Return,
        node_name: str,
        breaker_classes: tuple[nodes.NodeNG, ...] = (),
    ) -> None:
        """Check that a node is not inside a 'finally' clause of a
        'try...finally' statement.

        If we find a parent which type is in breaker_classes before
        a 'try...finally' block we skip the whole check.
        """
        # if self._trys is empty, we're not an in try block
        if not self._trys:
            return
        # the node could be a grand-grand...-child of the 'try...finally'
        _parent = node.parent
        _node = node
        while _parent and not isinstance(_parent, breaker_classes):
            if hasattr(_parent, "finalbody") and _node in _parent.finalbody:
                self.add_message("lost-exception", node=node, args=node_name)
                return
            _node = _parent
            _parent = _node.parent

    def _check_reversed(self, node: nodes.Call) -> None:
        """Check that the argument to `reversed` is a sequence."""
        try:
            argument = utils.safe_infer(utils.get_argument_from_call(node, position=0))
        except utils.NoSuchArgumentError:
            pass
        else:
            if isinstance(argument, util.UninferableBase):
                return
            if argument is None:
                # Nothing was inferred.
                # Try to see if we have iter().
                if isinstance(node.args[0], nodes.Call):
                    try:
                        func = next(node.args[0].func.infer())
                    except astroid.InferenceError:
                        return
                    if getattr(
                        func, "name", None
                    ) == "iter" and utils.is_builtin_object(func):
                        self.add_message("bad-reversed-sequence", node=node)
                return

            if isinstance(argument, (nodes.List, nodes.Tuple)):
                return

            # dicts are reversible, but only from Python 3.8 onward. Prior to
            # that, any class based on dict must explicitly provide a
            # __reversed__ method
            if not self._py38_plus and isinstance(argument, astroid.Instance):
                if any(
                    ancestor.name == "dict" and utils.is_builtin_object(ancestor)
                    for ancestor in itertools.chain(
                        (argument._proxied,), argument._proxied.ancestors()
                    )
                ):
                    try:
                        argument.locals[REVERSED_PROTOCOL_METHOD]
                    except KeyError:
                        self.add_message("bad-reversed-sequence", node=node)
                    return

            if hasattr(argument, "getattr"):
                # everything else is not a proper sequence for reversed()
                for methods in REVERSED_METHODS:
                    for meth in methods:
                        try:
                            argument.getattr(meth)
                        except astroid.NotFoundError:
                            break
                    else:
                        break
                else:
                    self.add_message("bad-reversed-sequence", node=node)
            else:
                self.add_message("bad-reversed-sequence", node=node)

    @utils.only_required_for_messages("confusing-with-statement")
    def visit_with(self, node: nodes.With) -> None:
        # a "with" statement with multiple managers corresponds
        # to one AST "With" node with multiple items
        pairs = node.items
        if pairs:
            for prev_pair, pair in zip(pairs, pairs[1:]):
                if isinstance(prev_pair[1], nodes.AssignName) and (
                    pair[1] is None and not isinstance(pair[0], nodes.Call)
                ):
                    # Don't emit a message if the second is a function call
                    # there's no way that can be mistaken for a name assignment.
                    # If the line number doesn't match
                    # we assume it's a nested "with".
                    self.add_message("confusing-with-statement", node=node)

    def _check_self_assigning_variable(self, node: nodes.Assign) -> None:
        # Detect assigning to the same variable.

        scope = node.scope()
        scope_locals = scope.locals

        rhs_names = []
        targets = node.targets
        if isinstance(targets[0], nodes.Tuple):
            if len(targets) != 1:
                # A complex assignment, so bail out early.
                return
            targets = targets[0].elts
            if len(targets) == 1:
                # Unpacking a variable into the same name.
                return

        if isinstance(node.value, nodes.Name):
            if len(targets) != 1:
                return
            rhs_names = [node.value]
        elif isinstance(node.value, nodes.Tuple):
            rhs_count = len(node.value.elts)
            if len(targets) != rhs_count or rhs_count == 1:
                return
            rhs_names = node.value.elts

        for target, lhs_name in zip(targets, rhs_names):
            if not isinstance(lhs_name, nodes.Name):
                continue
            if not isinstance(target, nodes.AssignName):
                continue
            # Check that the scope is different from a class level, which is usually
            # a pattern to expose module level attributes as class level ones.
            if isinstance(scope, nodes.ClassDef) and target.name in scope_locals:
                continue
            if target.name == lhs_name.name:
                self.add_message(
                    "self-assigning-variable", args=(target.name,), node=target
                )

    def _check_redeclared_assign_name(self, targets: list[nodes.NodeNG | None]) -> None:
        dummy_variables_rgx = self.linter.config.dummy_variables_rgx

        for target in targets:
            if not isinstance(target, nodes.Tuple):
                continue

            found_names = []
            for element in target.elts:
                if isinstance(element, nodes.Tuple):
                    self._check_redeclared_assign_name([element])
                elif isinstance(element, nodes.AssignName) and element.name != "_":
                    if dummy_variables_rgx and dummy_variables_rgx.match(element.name):
                        return
                    found_names.append(element.name)

            names = collections.Counter(found_names)
            for name, count in names.most_common():
                if count > 1:
                    self.add_message(
                        "redeclared-assigned-name", args=(name,), node=target
                    )

    @utils.only_required_for_messages(
        "self-assigning-variable", "redeclared-assigned-name"
    )
    def visit_assign(self, node: nodes.Assign) -> None:
        self._check_self_assigning_variable(node)
        self._check_redeclared_assign_name(node.targets)

    @utils.only_required_for_messages("redeclared-assigned-name")
    def visit_for(self, node: nodes.For) -> None:
        self._check_redeclared_assign_name([node.target])

back/venvmsg/Lib/site-packages/pylint/checkers/base/basic_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Basic Error checker from the basic checker."""

from __future__ import annotations

import itertools
from collections.abc import Iterator
from typing import Any

import astroid
from astroid import nodes
from astroid.typing import InferenceResult

from pylint.checkers import utils
from pylint.checkers.base.basic_checker import _BasicChecker
from pylint.checkers.utils import infer_all
from pylint.interfaces import HIGH

ABC_METACLASSES = {"_py_abc.ABCMeta", "abc.ABCMeta"}  # Python 3.7+,
# List of methods which can be redefined
REDEFINABLE_METHODS = frozenset(("__module__",))
TYPING_FORWARD_REF_QNAME = "typing.ForwardRef"


def _get_break_loop_node(break_node: nodes.Break) -> nodes.For | nodes.While | None:
    """Returns the loop node that holds the break node in arguments.

    Args:
        break_node (astroid.Break): the break node of interest.

    Returns:
        astroid.For or astroid.While: the loop node holding the break node.
    """
    loop_nodes = (nodes.For, nodes.While)
    parent = break_node.parent
    while not isinstance(parent, loop_nodes) or break_node in getattr(
        parent, "orelse", []
    ):
        break_node = parent
        parent = parent.parent
        if parent is None:
            break
    return parent


def _loop_exits_early(loop: nodes.For | nodes.While) -> bool:
    """Returns true if a loop may end with a break statement.

    Args:
        loop (astroid.For, astroid.While): the loop node inspected.

    Returns:
        bool: True if the loop may end with a break statement, False otherwise.
    """
    loop_nodes = (nodes.For, nodes.While)
    definition_nodes = (nodes.FunctionDef, nodes.ClassDef)
    inner_loop_nodes: list[nodes.For | nodes.While] = [
        _node
        for _node in loop.nodes_of_class(loop_nodes, skip_klass=definition_nodes)
        if _node != loop
    ]
    return any(
        _node
        for _node in loop.nodes_of_class(nodes.Break, skip_klass=definition_nodes)
        if _get_break_loop_node(_node) not in inner_loop_nodes
    )


def _has_abstract_methods(node: nodes.ClassDef) -> bool:
    """Determine if the given `node` has abstract methods.

    The methods should be made abstract by decorating them
    with `abc` decorators.
    """
    return len(utils.unimplemented_abstract_methods(node)) > 0


def redefined_by_decorator(node: nodes.FunctionDef) -> bool:
    """Return True if the object is a method redefined via decorator.

    For example:
        @property
        def x(self): return self._x
        @x.setter
        def x(self, value): self._x = value
    """
    if node.decorators:
        for decorator in node.decorators.nodes:
            if (
                isinstance(decorator, nodes.Attribute)
                and getattr(decorator.expr, "name", None) == node.name
            ):
                return True
    return False


class BasicErrorChecker(_BasicChecker):
    msgs = {
        "E0100": (
            "__init__ method is a generator",
            "init-is-generator",
            "Used when the special class method __init__ is turned into a "
            "generator by a yield in its body.",
        ),
        "E0101": (
            "Explicit return in __init__",
            "return-in-init",
            "Used when the special class method __init__ has an explicit "
            "return value.",
        ),
        "E0102": (
            "%s already defined line %s",
            "function-redefined",
            "Used when a function / class / method is redefined.",
        ),
        "E0103": (
            "%r not properly in loop",
            "not-in-loop",
            "Used when break or continue keywords are used outside a loop.",
        ),
        "E0104": (
            "Return outside function",
            "return-outside-function",
            'Used when a "return" statement is found outside a function or method.',
        ),
        "E0105": (
            "Yield outside function",
            "yield-outside-function",
            'Used when a "yield" statement is found outside a function or method.',
        ),
        "E0106": (
            "Return with argument inside generator",
            "return-arg-in-generator",
            'Used when a "return" statement with an argument is found '
            'in a generator function or method (e.g. with some "yield" statements).',
            {"maxversion": (3, 3)},
        ),
        "E0107": (
            "Use of the non-existent %s operator",
            "nonexistent-operator",
            "Used when you attempt to use the C-style pre-increment or "
            "pre-decrement operator -- and ++, which doesn't exist in Python.",
        ),
        "E0108": (
            "Duplicate argument name %s in function definition",
            "duplicate-argument-name",
            "Duplicate argument names in function definitions are syntax errors.",
        ),
        "E0110": (
            "Abstract class %r with abstract methods instantiated",
            "abstract-class-instantiated",
            "Used when an abstract class with `abc.ABCMeta` as metaclass "
            "has abstract methods and is instantiated.",
        ),
        "W0120": (
            "Else clause on loop without a break statement, remove the else and"
            " de-indent all the code inside it",
            "useless-else-on-loop",
            "Loops should only have an else clause if they can exit early "
            "with a break statement, otherwise the statements under else "
            "should be on the same scope as the loop itself.",
        ),
        "E0112": (
            "More than one starred expression in assignment",
            "too-many-star-expressions",
            "Emitted when there are more than one starred "
            "expressions (`*x`) in an assignment. This is a SyntaxError.",
        ),
        "E0113": (
            "Starred assignment target must be in a list or tuple",
            "invalid-star-assignment-target",
            "Emitted when a star expression is used as a starred assignment target.",
        ),
        "E0114": (
            "Can use starred expression only in assignment target",
            "star-needs-assignment-target",
            "Emitted when a star expression is not used in an assignment target.",
        ),
        "E0115": (
            "Name %r is nonlocal and global",
            "nonlocal-and-global",
            "Emitted when a name is both nonlocal and global.",
        ),
        "E0116": (
            "'continue' not supported inside 'finally' clause",
            "continue-in-finally",
            "Emitted when the `continue` keyword is found "
            "inside a finally clause, which is a SyntaxError.",
        ),
        "E0117": (
            "nonlocal name %s found without binding",
            "nonlocal-without-binding",
            "Emitted when a nonlocal variable does not have an attached "
            "name somewhere in the parent scopes",
        ),
        "E0118": (
            "Name %r is used prior to global declaration",
            "used-prior-global-declaration",
            "Emitted when a name is used prior a global declaration, "
            "which results in an error since Python 3.6.",
            {"minversion": (3, 6)},
        ),
    }

    def open(self) -> None:
        py_version = self.linter.config.py_version
        self._py38_plus = py_version >= (3, 8)

    @utils.only_required_for_messages("function-redefined")
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        self._check_redefinition("class", node)

    def _too_many_starred_for_tuple(self, assign_tuple: nodes.Tuple) -> bool:
        starred_count = 0
        for elem in assign_tuple.itered():
            if isinstance(elem, nodes.Tuple):
                return self._too_many_starred_for_tuple(elem)
            if isinstance(elem, nodes.Starred):
                starred_count += 1
        return starred_count > 1

    @utils.only_required_for_messages(
        "too-many-star-expressions", "invalid-star-assignment-target"
    )
    def visit_assign(self, node: nodes.Assign) -> None:
        # Check *a, *b = ...
        assign_target = node.targets[0]
        # Check *a = b
        if isinstance(node.targets[0], nodes.Starred):
            self.add_message("invalid-star-assignment-target", node=node)

        if not isinstance(assign_target, nodes.Tuple):
            return
        if self._too_many_starred_for_tuple(assign_target):
            self.add_message("too-many-star-expressions", node=node)

    @utils.only_required_for_messages("star-needs-assignment-target")
    def visit_starred(self, node: nodes.Starred) -> None:
        """Check that a Starred expression is used in an assignment target."""
        if isinstance(node.parent, nodes.Call):
            # f(*args) is converted to Call(args=[Starred]), so ignore
            # them for this check.
            return
        if isinstance(node.parent, (nodes.List, nodes.Tuple, nodes.Set, nodes.Dict)):
            # PEP 448 unpacking.
            return

        stmt = node.statement()
        if not isinstance(stmt, nodes.Assign):
            return

        if stmt.value is node or stmt.value.parent_of(node):
            self.add_message("star-needs-assignment-target", node=node)

    @utils.only_required_for_messages(
        "init-is-generator",
        "return-in-init",
        "function-redefined",
        "return-arg-in-generator",
        "duplicate-argument-name",
        "nonlocal-and-global",
        "used-prior-global-declaration",
    )
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        self._check_nonlocal_and_global(node)
        self._check_name_used_prior_global(node)
        if not redefined_by_decorator(
            node
        ) and not utils.is_registered_in_singledispatch_function(node):
            self._check_redefinition(node.is_method() and "method" or "function", node)
        # checks for max returns, branch, return in __init__
        returns = node.nodes_of_class(
            nodes.Return, skip_klass=(nodes.FunctionDef, nodes.ClassDef)
        )
        if node.is_method() and node.name == "__init__":
            if node.is_generator():
                self.add_message("init-is-generator", node=node)
            else:
                values = [r.value for r in returns]
                # Are we returning anything but None from constructors
                if any(v for v in values if not utils.is_none(v)):
                    self.add_message("return-in-init", node=node)
        # Check for duplicate names by clustering args with same name for detailed report
        arg_clusters = {}
        arguments: Iterator[Any] = filter(None, [node.args.args, node.args.kwonlyargs])
        for arg in itertools.chain.from_iterable(arguments):
            if arg.name in arg_clusters:
                self.add_message(
                    "duplicate-argument-name",
                    node=arg,
                    args=(arg.name,),
                    confidence=HIGH,
                )
            else:
                arg_clusters[arg.name] = arg

    visit_asyncfunctiondef = visit_functiondef

    def _check_name_used_prior_global(self, node: nodes.FunctionDef) -> None:
        scope_globals = {
            name: child
            for child in node.nodes_of_class(nodes.Global)
            for name in child.names
            if child.scope() is node
        }

        if not scope_globals:
            return

        for node_name in node.nodes_of_class(nodes.Name):
            if node_name.scope() is not node:
                continue

            name = node_name.name
            corresponding_global = scope_globals.get(name)
            if not corresponding_global:
                continue

            global_lineno = corresponding_global.fromlineno
            if global_lineno and global_lineno > node_name.fromlineno:
                self.add_message(
                    "used-prior-global-declaration", node=node_name, args=(name,)
                )

    def _check_nonlocal_and_global(self, node: nodes.FunctionDef) -> None:
        """Check that a name is both nonlocal and global."""

        def same_scope(current: nodes.Global | nodes.Nonlocal) -> bool:
            return current.scope() is node

        from_iter = itertools.chain.from_iterable
        nonlocals = set(
            from_iter(
                child.names
                for child in node.nodes_of_class(nodes.Nonlocal)
                if same_scope(child)
            )
        )

        if not nonlocals:
            return

        global_vars = set(
            from_iter(
                child.names
                for child in node.nodes_of_class(nodes.Global)
                if same_scope(child)
            )
        )
        for name in nonlocals.intersection(global_vars):
            self.add_message("nonlocal-and-global", args=(name,), node=node)

    @utils.only_required_for_messages("return-outside-function")
    def visit_return(self, node: nodes.Return) -> None:
        if not isinstance(node.frame(), nodes.FunctionDef):
            self.add_message("return-outside-function", node=node)

    @utils.only_required_for_messages("yield-outside-function")
    def visit_yield(self, node: nodes.Yield) -> None:
        self._check_yield_outside_func(node)

    @utils.only_required_for_messages("yield-outside-function")
    def visit_yieldfrom(self, node: nodes.YieldFrom) -> None:
        self._check_yield_outside_func(node)

    @utils.only_required_for_messages("not-in-loop", "continue-in-finally")
    def visit_continue(self, node: nodes.Continue) -> None:
        self._check_in_loop(node, "continue")

    @utils.only_required_for_messages("not-in-loop")
    def visit_break(self, node: nodes.Break) -> None:
        self._check_in_loop(node, "break")

    @utils.only_required_for_messages("useless-else-on-loop")
    def visit_for(self, node: nodes.For) -> None:
        self._check_else_on_loop(node)

    @utils.only_required_for_messages("useless-else-on-loop")
    def visit_while(self, node: nodes.While) -> None:
        self._check_else_on_loop(node)

    @utils.only_required_for_messages("nonexistent-operator")
    def visit_unaryop(self, node: nodes.UnaryOp) -> None:
        """Check use of the non-existent ++ and -- operators."""
        if (
            (node.op in "+-")
            and isinstance(node.operand, nodes.UnaryOp)
            and (node.operand.op == node.op)
            and (node.col_offset + 1 == node.operand.col_offset)
        ):
            self.add_message("nonexistent-operator", node=node, args=node.op * 2)

    def _check_nonlocal_without_binding(self, node: nodes.Nonlocal, name: str) -> None:
        current_scope = node.scope()
        while current_scope.parent is not None:
            if not isinstance(current_scope, (nodes.ClassDef, nodes.FunctionDef)):
                self.add_message("nonlocal-without-binding", args=(name,), node=node)
                return

            # Search for `name` in the parent scope if:
            #  `current_scope` is the same scope in which the `nonlocal` name is declared
            #  or `name` is not in `current_scope.locals`.
            if current_scope is node.scope() or name not in current_scope.locals:
                current_scope = current_scope.parent.scope()
                continue

            # Okay, found it.
            return

        if not isinstance(current_scope, nodes.FunctionDef):
            self.add_message(
                "nonlocal-without-binding", args=(name,), node=node, confidence=HIGH
            )

    @utils.only_required_for_messages("nonlocal-without-binding")
    def visit_nonlocal(self, node: nodes.Nonlocal) -> None:
        for name in node.names:
            self._check_nonlocal_without_binding(node, name)

    @utils.only_required_for_messages("abstract-class-instantiated")
    def visit_call(self, node: nodes.Call) -> None:
        """Check instantiating abstract class with
        abc.ABCMeta as metaclass.
        """
        for inferred in infer_all(node.func):
            self._check_inferred_class_is_abstract(inferred, node)

    def _check_inferred_class_is_abstract(
        self, inferred: InferenceResult, node: nodes.Call
    ) -> None:
        if not isinstance(inferred, nodes.ClassDef):
            return

        klass = utils.node_frame_class(node)
        if klass is inferred:
            # Don't emit the warning if the class is instantiated
            # in its own body or if the call is not an instance
            # creation. If the class is instantiated into its own
            # body, we're expecting that it knows what it is doing.
            return

        # __init__ was called
        abstract_methods = _has_abstract_methods(inferred)

        if not abstract_methods:
            return

        metaclass = inferred.metaclass()

        if metaclass is None:
            # Python 3.4 has `abc.ABC`, which won't be detected
            # by ClassNode.metaclass()
            for ancestor in inferred.ancestors():
                if ancestor.qname() == "abc.ABC":
                    self.add_message(
                        "abstract-class-instantiated", args=(inferred.name,), node=node
                    )
                    break

            return

        if metaclass.qname() in ABC_METACLASSES:
            self.add_message(
                "abstract-class-instantiated", args=(inferred.name,), node=node
            )

    def _check_yield_outside_func(self, node: nodes.Yield) -> None:
        if not isinstance(node.frame(), (nodes.FunctionDef, nodes.Lambda)):
            self.add_message("yield-outside-function", node=node)

    def _check_else_on_loop(self, node: nodes.For | nodes.While) -> None:
        """Check that any loop with an else clause has a break statement."""
        if node.orelse and not _loop_exits_early(node):
            self.add_message(
                "useless-else-on-loop",
                node=node,
                # This is not optimal, but the line previous
                # to the first statement in the else clause
                # will usually be the one that contains the else:.
                line=node.orelse[0].lineno - 1,
            )

    def _check_in_loop(
        self, node: nodes.Continue | nodes.Break, node_name: str
    ) -> None:
        """Check that a node is inside a for or while loop."""
        for parent in node.node_ancestors():
            if isinstance(parent, (nodes.For, nodes.While)):
                if node not in parent.orelse:
                    return

            if isinstance(parent, (nodes.ClassDef, nodes.FunctionDef)):
                break
            if (
                isinstance(parent, nodes.Try)
                and node in parent.finalbody
                and isinstance(node, nodes.Continue)
                and not self._py38_plus
            ):
                self.add_message("continue-in-finally", node=node)

        self.add_message("not-in-loop", node=node, args=node_name)

    def _check_redefinition(
        self, redeftype: str, node: nodes.Call | nodes.FunctionDef
    ) -> None:
        """Check for redefinition of a function / method / class name."""
        parent_frame = node.parent.frame()

        # Ignore function stubs created for type information
        redefinitions = [
            i
            for i in parent_frame.locals[node.name]
            if not (isinstance(i.parent, nodes.AnnAssign) and i.parent.simple)
        ]
        defined_self = next(
            (local for local in redefinitions if not utils.is_overload_stub(local)),
            node,
        )
        if defined_self is not node and not astroid.are_exclusive(node, defined_self):
            # Additional checks for methods which are not considered
            # redefined, since they are already part of the base API.
            if (
                isinstance(parent_frame, nodes.ClassDef)
                and node.name in REDEFINABLE_METHODS
            ):
                return

            # Skip typing.overload() functions.
            if utils.is_overload_stub(node):
                return

            # Exempt functions redefined on a condition.
            if isinstance(node.parent, nodes.If):
                # Exempt "if not <func>" cases
                if (
                    isinstance(node.parent.test, nodes.UnaryOp)
                    and node.parent.test.op == "not"
                    and isinstance(node.parent.test.operand, nodes.Name)
                    and node.parent.test.operand.name == node.name
                ):
                    return

                # Exempt "if <func> is not None" cases
                # pylint: disable=too-many-boolean-expressions
                if (
                    isinstance(node.parent.test, nodes.Compare)
                    and isinstance(node.parent.test.left, nodes.Name)
                    and node.parent.test.left.name == node.name
                    and node.parent.test.ops[0][0] == "is"
                    and isinstance(node.parent.test.ops[0][1], nodes.Const)
                    and node.parent.test.ops[0][1].value is None
                ):
                    return

            # Check if we have forward references for this node.
            try:
                redefinition_index = redefinitions.index(node)
            except ValueError:
                pass
            else:
                for redefinition in redefinitions[:redefinition_index]:
                    inferred = utils.safe_infer(redefinition)
                    if (
                        inferred
                        and isinstance(inferred, astroid.Instance)
                        and inferred.qname() == TYPING_FORWARD_REF_QNAME
                    ):
                        return

            dummy_variables_rgx = self.linter.config.dummy_variables_rgx
            if dummy_variables_rgx and dummy_variables_rgx.match(node.name):
                return
            self.add_message(
                "function-redefined",
                node=node,
                args=(redeftype, defined_self.fromlineno),
            )

back/venvmsg/Lib/site-packages/pylint/checkers/base/basic_error_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Comparison checker from the basic checker."""

import astroid
from astroid import nodes

from pylint.checkers import utils
from pylint.checkers.base.basic_checker import _BasicChecker
from pylint.interfaces import HIGH

LITERAL_NODE_TYPES = (nodes.Const, nodes.Dict, nodes.List, nodes.Set)
COMPARISON_OPERATORS = frozenset(("==", "!=", "<", ">", "<=", ">="))
TYPECHECK_COMPARISON_OPERATORS = frozenset(("is", "is not", "==", "!="))
TYPE_QNAME = "builtins.type"


def _is_one_arg_pos_call(call: nodes.NodeNG) -> bool:
    """Is this a call with exactly 1 positional argument ?"""
    return isinstance(call, nodes.Call) and len(call.args) == 1 and not call.keywords


class ComparisonChecker(_BasicChecker):
    """Checks for comparisons.

    - singleton comparison: 'expr == True', 'expr == False' and 'expr == None'
    - yoda condition: 'const "comp" right' where comp can be '==', '!=', '<',
      '<=', '>' or '>=', and right can be a variable, an attribute, a method or
      a function
    """

    msgs = {
        "C0121": (
            "Comparison %s should be %s",
            "singleton-comparison",
            "Used when an expression is compared to singleton "
            "values like True, False or None.",
        ),
        "C0123": (
            "Use isinstance() rather than type() for a typecheck.",
            "unidiomatic-typecheck",
            "The idiomatic way to perform an explicit typecheck in "
            "Python is to use isinstance(x, Y) rather than "
            "type(x) == Y, type(x) is Y. Though there are unusual "
            "situations where these give different results.",
            {"old_names": [("W0154", "old-unidiomatic-typecheck")]},
        ),
        "R0123": (
            "In '%s', use '%s' when comparing constant literals not '%s' ('%s')",
            "literal-comparison",
            "Used when comparing an object to a literal, which is usually "
            "what you do not want to do, since you can compare to a different "
            "literal than what was expected altogether.",
        ),
        "R0124": (
            "Redundant comparison - %s",
            "comparison-with-itself",
            "Used when something is compared against itself.",
        ),
        "R0133": (
            "Comparison between constants: '%s %s %s' has a constant value",
            "comparison-of-constants",
            "When two literals are compared with each other the result is a constant. "
            "Using the constant directly is both easier to read and more performant. "
            "Initializing 'True' and 'False' this way is not required since Python 2.3.",
        ),
        "W0143": (
            "Comparing against a callable, did you omit the parenthesis?",
            "comparison-with-callable",
            "This message is emitted when pylint detects that a comparison with a "
            "callable was made, which might suggest that some parenthesis were omitted, "
            "resulting in potential unwanted behaviour.",
        ),
        "W0177": (
            "Comparison %s should be %s",
            "nan-comparison",
            "Used when an expression is compared to NaN "
            "values like numpy.NaN and float('nan').",
        ),
    }

    def _check_singleton_comparison(
        self,
        left_value: nodes.NodeNG,
        right_value: nodes.NodeNG,
        root_node: nodes.Compare,
        checking_for_absence: bool = False,
    ) -> None:
        """Check if == or != is being used to compare a singleton value."""

        if utils.is_singleton_const(left_value):
            singleton, other_value = left_value.value, right_value
        elif utils.is_singleton_const(right_value):
            singleton, other_value = right_value.value, left_value
        else:
            return

        singleton_comparison_example = {False: "'{} is {}'", True: "'{} is not {}'"}

        # True/False singletons have a special-cased message in case the user is
        # mistakenly using == or != to check for truthiness
        if singleton in {True, False}:
            suggestion_template = (
                "{} if checking for the singleton value {}, or {} if testing for {}"
            )
            truthiness_example = {False: "not {}", True: "{}"}
            truthiness_phrase = {True: "truthiness", False: "falsiness"}

            # Looks for comparisons like x == True or x != False
            checking_truthiness = singleton is not checking_for_absence

            suggestion = suggestion_template.format(
                singleton_comparison_example[checking_for_absence].format(
                    left_value.as_string(), right_value.as_string()
                ),
                singleton,
                (
                    "'bool({})'"
                    if not utils.is_test_condition(root_node) and checking_truthiness
                    else "'{}'"
                ).format(
                    truthiness_example[checking_truthiness].format(
                        other_value.as_string()
                    )
                ),
                truthiness_phrase[checking_truthiness],
            )
        else:
            suggestion = singleton_comparison_example[checking_for_absence].format(
                left_value.as_string(), right_value.as_string()
            )
        self.add_message(
            "singleton-comparison",
            node=root_node,
            args=(f"'{root_node.as_string()}'", suggestion),
        )

    def _check_nan_comparison(
        self,
        left_value: nodes.NodeNG,
        right_value: nodes.NodeNG,
        root_node: nodes.Compare,
        checking_for_absence: bool = False,
    ) -> None:
        def _is_float_nan(node: nodes.NodeNG) -> bool:
            try:
                if isinstance(node, nodes.Call) and len(node.args) == 1:
                    if (
                        node.args[0].value.lower() == "nan"
                        and node.inferred()[0].pytype() == "builtins.float"
                    ):
                        return True
                return False
            except AttributeError:
                return False

        def _is_numpy_nan(node: nodes.NodeNG) -> bool:
            if isinstance(node, nodes.Attribute) and node.attrname == "NaN":
                if isinstance(node.expr, nodes.Name):
                    return node.expr.name in {"numpy", "nmp", "np"}
            return False

        def _is_nan(node: nodes.NodeNG) -> bool:
            return _is_float_nan(node) or _is_numpy_nan(node)

        nan_left = _is_nan(left_value)
        if not nan_left and not _is_nan(right_value):
            return

        absence_text = ""
        if checking_for_absence:
            absence_text = "not "
        if nan_left:
            suggestion = f"'{absence_text}math.isnan({right_value.as_string()})'"
        else:
            suggestion = f"'{absence_text}math.isnan({left_value.as_string()})'"
        self.add_message(
            "nan-comparison",
            node=root_node,
            args=(f"'{root_node.as_string()}'", suggestion),
        )

    def _check_literal_comparison(
        self, literal: nodes.NodeNG, node: nodes.Compare
    ) -> None:
        """Check if we compare to a literal, which is usually what we do not want to do."""
        is_other_literal = isinstance(literal, (nodes.List, nodes.Dict, nodes.Set))
        is_const = False
        if isinstance(literal, nodes.Const):
            if isinstance(literal.value, bool) or literal.value is None:
                # Not interested in these values.
                return
            is_const = isinstance(literal.value, (bytes, str, int, float))

        if is_const or is_other_literal:
            incorrect_node_str = node.as_string()
            if "is not" in incorrect_node_str:
                equal_or_not_equal = "!="
                is_or_is_not = "is not"
            else:
                equal_or_not_equal = "=="
                is_or_is_not = "is"
            fixed_node_str = incorrect_node_str.replace(
                is_or_is_not, equal_or_not_equal
            )
            self.add_message(
                "literal-comparison",
                args=(
                    incorrect_node_str,
                    equal_or_not_equal,
                    is_or_is_not,
                    fixed_node_str,
                ),
                node=node,
                confidence=HIGH,
            )

    def _check_logical_tautology(self, node: nodes.Compare) -> None:
        """Check if identifier is compared against itself.

        :param node: Compare node
        :Example:
        val = 786
        if val == val:  # [comparison-with-itself]
            pass
        """
        left_operand = node.left
        right_operand = node.ops[0][1]
        operator = node.ops[0][0]
        if isinstance(left_operand, nodes.Const) and isinstance(
            right_operand, nodes.Const
        ):
            left_operand = left_operand.value
            right_operand = right_operand.value
        elif isinstance(left_operand, nodes.Name) and isinstance(
            right_operand, nodes.Name
        ):
            left_operand = left_operand.name
            right_operand = right_operand.name

        if left_operand == right_operand:
            suggestion = f"{left_operand} {operator} {right_operand}"
            self.add_message("comparison-with-itself", node=node, args=(suggestion,))

    def _check_constants_comparison(self, node: nodes.Compare) -> None:
        """When two constants are being compared it is always a logical tautology."""
        left_operand = node.left
        if not isinstance(left_operand, nodes.Const):
            return

        right_operand = node.ops[0][1]
        if not isinstance(right_operand, nodes.Const):
            return

        operator = node.ops[0][0]
        self.add_message(
            "comparison-of-constants",
            node=node,
            args=(left_operand.value, operator, right_operand.value),
            confidence=HIGH,
        )

    def _check_callable_comparison(self, node: nodes.Compare) -> None:
        operator = node.ops[0][0]
        if operator not in COMPARISON_OPERATORS:
            return

        bare_callables = (nodes.FunctionDef, astroid.BoundMethod)
        left_operand, right_operand = node.left, node.ops[0][1]
        # this message should be emitted only when there is comparison of bare callable
        # with non bare callable.
        number_of_bare_callables = 0
        for operand in left_operand, right_operand:
            inferred = utils.safe_infer(operand)
            # Ignore callables that raise, as well as typing constants
            # implemented as functions (that raise via their decorator)
            if (
                isinstance(inferred, bare_callables)
                and "typing._SpecialForm" not in inferred.decoratornames()
                and not any(isinstance(x, nodes.Raise) for x in inferred.body)
            ):
                number_of_bare_callables += 1
        if number_of_bare_callables == 1:
            self.add_message("comparison-with-callable", node=node)

    @utils.only_required_for_messages(
        "singleton-comparison",
        "unidiomatic-typecheck",
        "literal-comparison",
        "comparison-with-itself",
        "comparison-of-constants",
        "comparison-with-callable",
        "nan-comparison",
    )
    def visit_compare(self, node: nodes.Compare) -> None:
        self._check_callable_comparison(node)
        self._check_logical_tautology(node)
        self._check_unidiomatic_typecheck(node)
        self._check_constants_comparison(node)
        # NOTE: this checker only works with binary comparisons like 'x == 42'
        # but not 'x == y == 42'
        if len(node.ops) != 1:
            return

        left = node.left
        operator, right = node.ops[0]

        if operator in {"==", "!="}:
            self._check_singleton_comparison(
                left, right, node, checking_for_absence=operator == "!="
            )

        if operator in {"==", "!=", "is", "is not"}:
            self._check_nan_comparison(
                left, right, node, checking_for_absence=operator in {"!=", "is not"}
            )
        if operator in {"is", "is not"}:
            self._check_literal_comparison(right, node)

    def _check_unidiomatic_typecheck(self, node: nodes.Compare) -> None:
        operator, right = node.ops[0]
        if operator in TYPECHECK_COMPARISON_OPERATORS:
            left = node.left
            if _is_one_arg_pos_call(left):
                self._check_type_x_is_y(node, left, operator, right)

    def _check_type_x_is_y(
        self,
        node: nodes.Compare,
        left: nodes.NodeNG,
        operator: str,
        right: nodes.NodeNG,
    ) -> None:
        """Check for expressions like type(x) == Y."""
        left_func = utils.safe_infer(left.func)
        if not (
            isinstance(left_func, nodes.ClassDef) and left_func.qname() == TYPE_QNAME
        ):
            return

        if operator in {"is", "is not"} and _is_one_arg_pos_call(right):
            right_func = utils.safe_infer(right.func)
            if (
                isinstance(right_func, nodes.ClassDef)
                and right_func.qname() == TYPE_QNAME
            ):
                # type(x) == type(a)
                right_arg = utils.safe_infer(right.args[0])
                if not isinstance(right_arg, LITERAL_NODE_TYPES):
                    # not e.g. type(x) == type([])
                    return
        self.add_message("unidiomatic-typecheck", node=node)

back/venvmsg/Lib/site-packages/pylint/checkers/base/comparison_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Docstring checker from the basic checker."""

from __future__ import annotations

import re
from typing import Literal

import astroid
from astroid import nodes

from pylint import interfaces
from pylint.checkers import utils
from pylint.checkers.base.basic_checker import _BasicChecker
from pylint.checkers.utils import (
    is_overload_stub,
    is_property_deleter,
    is_property_setter,
)

# do not require a doc string on private/system methods
NO_REQUIRED_DOC_RGX = re.compile("^_")


def _infer_dunder_doc_attribute(
    node: nodes.Module | nodes.ClassDef | nodes.FunctionDef,
) -> str | None:
    # Try to see if we have a `__doc__` attribute.
    try:
        docstring = node["__doc__"]
    except KeyError:
        return None

    docstring = utils.safe_infer(docstring)
    if not docstring:
        return None
    if not isinstance(docstring, nodes.Const):
        return None
    return str(docstring.value)


class DocStringChecker(_BasicChecker):
    msgs = {
        "C0112": (
            "Empty %s docstring",
            "empty-docstring",
            "Used when a module, function, class or method has an empty "
            "docstring (it would be too easy ;).",
            {"old_names": [("W0132", "old-empty-docstring")]},
        ),
        "C0114": (
            "Missing module docstring",
            "missing-module-docstring",
            "Used when a module has no docstring. "
            "Empty modules do not require a docstring.",
            {"old_names": [("C0111", "missing-docstring")]},
        ),
        "C0115": (
            "Missing class docstring",
            "missing-class-docstring",
            "Used when a class has no docstring. "
            "Even an empty class must have a docstring.",
            {"old_names": [("C0111", "missing-docstring")]},
        ),
        "C0116": (
            "Missing function or method docstring",
            "missing-function-docstring",
            "Used when a function or method has no docstring. "
            "Some special methods like __init__ do not require a "
            "docstring.",
            {"old_names": [("C0111", "missing-docstring")]},
        ),
    }
    options = (
        (
            "no-docstring-rgx",
            {
                "default": NO_REQUIRED_DOC_RGX,
                "type": "regexp",
                "metavar": "<regexp>",
                "help": "Regular expression which should only match "
                "function or class names that do not require a "
                "docstring.",
            },
        ),
        (
            "docstring-min-length",
            {
                "default": -1,
                "type": "int",
                "metavar": "<int>",
                "help": (
                    "Minimum line length for functions/classes that"
                    " require docstrings, shorter ones are exempt."
                ),
            },
        ),
    )

    def open(self) -> None:
        self.linter.stats.reset_undocumented()

    @utils.only_required_for_messages("missing-module-docstring", "empty-docstring")
    def visit_module(self, node: nodes.Module) -> None:
        self._check_docstring("module", node)

    @utils.only_required_for_messages("missing-class-docstring", "empty-docstring")
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        if self.linter.config.no_docstring_rgx.match(node.name) is None:
            self._check_docstring("class", node)

    @utils.only_required_for_messages("missing-function-docstring", "empty-docstring")
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        if self.linter.config.no_docstring_rgx.match(node.name) is None:
            ftype = "method" if node.is_method() else "function"
            if (
                is_property_setter(node)
                or is_property_deleter(node)
                or is_overload_stub(node)
            ):
                return

            if isinstance(node.parent.frame(), nodes.ClassDef):
                overridden = False
                confidence = (
                    interfaces.INFERENCE
                    if utils.has_known_bases(node.parent.frame())
                    else interfaces.INFERENCE_FAILURE
                )
                # check if node is from a method overridden by its ancestor
                for ancestor in node.parent.frame().ancestors():
                    if ancestor.qname() == "builtins.object":
                        continue
                    if node.name in ancestor and isinstance(
                        ancestor[node.name], nodes.FunctionDef
                    ):
                        overridden = True
                        break
                self._check_docstring(
                    ftype, node, report_missing=not overridden, confidence=confidence  # type: ignore[arg-type]
                )
            elif isinstance(node.parent.frame(), nodes.Module):
                self._check_docstring(ftype, node)  # type: ignore[arg-type]
            else:
                return

    visit_asyncfunctiondef = visit_functiondef

    def _check_docstring(
        self,
        node_type: Literal["class", "function", "method", "module"],
        node: nodes.Module | nodes.ClassDef | nodes.FunctionDef,
        report_missing: bool = True,
        confidence: interfaces.Confidence = interfaces.HIGH,
    ) -> None:
        """Check if the node has a non-empty docstring."""
        docstring = node.doc_node.value if node.doc_node else None
        if docstring is None:
            docstring = _infer_dunder_doc_attribute(node)

        if docstring is None:
            if not report_missing:
                return
            lines = utils.get_node_last_lineno(node) - node.lineno

            if node_type == "module" and not lines:
                # If the module does not have a body, there's no reason
                # to require a docstring.
                return
            max_lines = self.linter.config.docstring_min_length

            if node_type != "module" and max_lines > -1 and lines < max_lines:
                return
            if node_type == "class":
                self.linter.stats.undocumented["klass"] += 1
            else:
                self.linter.stats.undocumented[node_type] += 1
            if (
                node.body
                and isinstance(node.body[0], nodes.Expr)
                and isinstance(node.body[0].value, nodes.Call)
            ):
                # Most likely a string with a format call. Let's see.
                func = utils.safe_infer(node.body[0].value.func)
                if isinstance(func, astroid.BoundMethod) and isinstance(
                    func.bound, astroid.Instance
                ):
                    # Strings.
                    if func.bound.name in {"str", "unicode", "bytes"}:
                        return
            if node_type == "module":
                message = "missing-module-docstring"
            elif node_type == "class":
                message = "missing-class-docstring"
            else:
                message = "missing-function-docstring"
            self.add_message(message, node=node, confidence=confidence)
        elif not docstring.strip():
            if node_type == "class":
                self.linter.stats.undocumented["klass"] += 1
            else:
                self.linter.stats.undocumented[node_type] += 1
            self.add_message(
                "empty-docstring", node=node, args=(node_type,), confidence=confidence
            )

back/venvmsg/Lib/site-packages/pylint/checkers/base/docstring_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

__all__ = [
    "NameChecker",
    "NamingStyle",
    "KNOWN_NAME_TYPES_WITH_STYLE",
    "SnakeCaseStyle",
    "CamelCaseStyle",
    "UpperCaseStyle",
    "PascalCaseStyle",
    "AnyStyle",
]

from pylint.checkers.base.name_checker.checker import NameChecker
from pylint.checkers.base.name_checker.naming_style import (
    KNOWN_NAME_TYPES_WITH_STYLE,
    AnyStyle,
    CamelCaseStyle,
    NamingStyle,
    PascalCaseStyle,
    SnakeCaseStyle,
    UpperCaseStyle,
)

back/venvmsg/Lib/site-packages/pylint/checkers/base/name_checker/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Basic checker for Python code."""

from __future__ import annotations

import argparse
import collections
import itertools
import re
import sys
from collections.abc import Iterable
from enum import Enum, auto
from re import Pattern
from typing import TYPE_CHECKING, Tuple

import astroid
from astroid import nodes

from pylint import constants, interfaces
from pylint.checkers import utils
from pylint.checkers.base.basic_checker import _BasicChecker
from pylint.checkers.base.name_checker.naming_style import (
    KNOWN_NAME_TYPES,
    KNOWN_NAME_TYPES_WITH_STYLE,
    NAMING_STYLES,
    _create_naming_options,
)
from pylint.checkers.utils import is_property_deleter, is_property_setter
from pylint.typing import Options

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter

_BadNamesTuple = Tuple[nodes.NodeNG, str, str, interfaces.Confidence]

# Default patterns for name types that do not have styles
DEFAULT_PATTERNS = {
    "typevar": re.compile(
        r"^_{0,2}(?!T[A-Z])(?:[A-Z]+|(?:[A-Z]+[a-z]+)+T?(?<!Type))(?:_co(?:ntra)?)?$"
    ),
    "typealias": re.compile(
        r"^_{0,2}(?!T[A-Z]|Type)[A-Z]+[a-z0-9]+(?:[A-Z][a-z0-9]+)*$"
    ),
}

BUILTIN_PROPERTY = "builtins.property"
TYPE_VAR_QNAME = frozenset(
    (
        "typing.TypeVar",
        "typing_extensions.TypeVar",
    )
)


class TypeVarVariance(Enum):
    invariant = auto()
    covariant = auto()
    contravariant = auto()
    double_variant = auto()


def _get_properties(config: argparse.Namespace) -> tuple[set[str], set[str]]:
    """Returns a tuple of property classes and names.

    Property classes are fully qualified, such as 'abc.abstractproperty' and
    property names are the actual names, such as 'abstract_property'.
    """
    property_classes = {BUILTIN_PROPERTY}
    property_names: set[str] = set()  # Not returning 'property', it has its own check.
    if config is not None:
        property_classes.update(config.property_classes)
        property_names.update(
            prop.rsplit(".", 1)[-1] for prop in config.property_classes
        )
    return property_classes, property_names


def _redefines_import(node: nodes.AssignName) -> bool:
    """Detect that the given node (AssignName) is inside an
    exception handler and redefines an import from the tryexcept body.

    Returns True if the node redefines an import, False otherwise.
    """
    current = node
    while current and not isinstance(current.parent, nodes.ExceptHandler):
        current = current.parent
    if not current or not utils.error_of_type(current.parent, ImportError):
        return False
    try_block = current.parent.parent
    for import_node in try_block.nodes_of_class((nodes.ImportFrom, nodes.Import)):
        for name, alias in import_node.names:
            if alias:
                if alias == node.name:
                    return True
            elif name == node.name:
                return True
    return False


def _determine_function_name_type(
    node: nodes.FunctionDef, config: argparse.Namespace
) -> str:
    """Determine the name type whose regex the function's name should match.

    :param node: A function node.
    :param config: Configuration from which to pull additional property classes.

    :returns: One of ('function', 'method', 'attr')
    """
    property_classes, property_names = _get_properties(config)
    if not node.is_method():
        return "function"

    if is_property_setter(node) or is_property_deleter(node):
        # If the function is decorated using the prop_method.{setter,getter}
        # form, treat it like an attribute as well.
        return "attr"

    decorators = node.decorators.nodes if node.decorators else []
    for decorator in decorators:
        # If the function is a property (decorated with @property
        # or @abc.abstractproperty), the name type is 'attr'.
        if isinstance(decorator, nodes.Name) or (
            isinstance(decorator, nodes.Attribute)
            and decorator.attrname in property_names
        ):
            inferred = utils.safe_infer(decorator)
            if (
                inferred
                and hasattr(inferred, "qname")
                and inferred.qname() in property_classes
            ):
                return "attr"
    return "method"


# Name categories that are always consistent with all naming conventions.
EXEMPT_NAME_CATEGORIES = {"exempt", "ignore"}


def _is_multi_naming_match(
    match: re.Match[str] | None, node_type: str, confidence: interfaces.Confidence
) -> bool:
    return (
        match is not None
        and match.lastgroup is not None
        and match.lastgroup not in EXEMPT_NAME_CATEGORIES
        and (node_type != "method" or confidence != interfaces.INFERENCE_FAILURE)
    )


class NameChecker(_BasicChecker):
    msgs = {
        "C0103": (
            '%s name "%s" doesn\'t conform to %s',
            "invalid-name",
            "Used when the name doesn't conform to naming rules "
            "associated to its type (constant, variable, class...).",
        ),
        "C0104": (
            'Disallowed name "%s"',
            "disallowed-name",
            "Used when the name matches bad-names or bad-names-rgxs- (unauthorized names).",
            {
                "old_names": [
                    ("C0102", "blacklisted-name"),
                ]
            },
        ),
        "C0105": (
            "Type variable name does not reflect variance%s",
            "typevar-name-incorrect-variance",
            "Emitted when a TypeVar name doesn't reflect its type variance. "
            "According to PEP8, it is recommended to add suffixes '_co' and "
            "'_contra' to the variables used to declare covariant or "
            "contravariant behaviour respectively. Invariant (default) variables "
            "do not require a suffix. The message is also emitted when invariant "
            "variables do have a suffix.",
        ),
        "C0131": (
            "TypeVar cannot be both covariant and contravariant",
            "typevar-double-variance",
            'Emitted when both the "covariant" and "contravariant" '
            'keyword arguments are set to "True" in a TypeVar.',
        ),
        "C0132": (
            'TypeVar name "%s" does not match assigned variable name "%s"',
            "typevar-name-mismatch",
            "Emitted when a TypeVar is assigned to a variable "
            "that does not match its name argument.",
        ),
    }

    _options: Options = (
        (
            "good-names",
            {
                "default": ("i", "j", "k", "ex", "Run", "_"),
                "type": "csv",
                "metavar": "<names>",
                "help": "Good variable names which should always be accepted,"
                " separated by a comma.",
            },
        ),
        (
            "good-names-rgxs",
            {
                "default": "",
                "type": "regexp_csv",
                "metavar": "<names>",
                "help": "Good variable names regexes, separated by a comma. If names match any regex,"
                " they will always be accepted",
            },
        ),
        (
            "bad-names",
            {
                "default": ("foo", "bar", "baz", "toto", "tutu", "tata"),
                "type": "csv",
                "metavar": "<names>",
                "help": "Bad variable names which should always be refused, "
                "separated by a comma.",
            },
        ),
        (
            "bad-names-rgxs",
            {
                "default": "",
                "type": "regexp_csv",
                "metavar": "<names>",
                "help": "Bad variable names regexes, separated by a comma. If names match any regex,"
                " they will always be refused",
            },
        ),
        (
            "name-group",
            {
                "default": (),
                "type": "csv",
                "metavar": "<name1:name2>",
                "help": (
                    "Colon-delimited sets of names that determine each"
                    " other's naming style when the name regexes"
                    " allow several styles."
                ),
            },
        ),
        (
            "include-naming-hint",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Include a hint for the correct naming format with invalid-name.",
            },
        ),
        (
            "property-classes",
            {
                "default": ("abc.abstractproperty",),
                "type": "csv",
                "metavar": "<decorator names>",
                "help": "List of decorators that produce properties, such as "
                "abc.abstractproperty. Add to this list to register "
                "other decorators that produce valid properties. "
                "These decorators are taken in consideration only for invalid-name.",
            },
        ),
    )
    options: Options = _options + _create_naming_options()

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._name_group: dict[str, str] = {}
        self._bad_names: dict[str, dict[str, list[_BadNamesTuple]]] = {}
        self._name_regexps: dict[str, re.Pattern[str]] = {}
        self._name_hints: dict[str, str] = {}
        self._good_names_rgxs_compiled: list[re.Pattern[str]] = []
        self._bad_names_rgxs_compiled: list[re.Pattern[str]] = []

    def open(self) -> None:
        self.linter.stats.reset_bad_names()
        for group in self.linter.config.name_group:
            for name_type in group.split(":"):
                self._name_group[name_type] = f"group_{group}"

        regexps, hints = self._create_naming_rules()
        self._name_regexps = regexps
        self._name_hints = hints
        self._good_names_rgxs_compiled = [
            re.compile(rgxp) for rgxp in self.linter.config.good_names_rgxs
        ]
        self._bad_names_rgxs_compiled = [
            re.compile(rgxp) for rgxp in self.linter.config.bad_names_rgxs
        ]

    def _create_naming_rules(self) -> tuple[dict[str, Pattern[str]], dict[str, str]]:
        regexps: dict[str, Pattern[str]] = {}
        hints: dict[str, str] = {}

        for name_type in KNOWN_NAME_TYPES:
            if name_type in KNOWN_NAME_TYPES_WITH_STYLE:
                naming_style_name = getattr(
                    self.linter.config, f"{name_type}_naming_style"
                )
                regexps[name_type] = NAMING_STYLES[naming_style_name].get_regex(
                    name_type
                )
            else:
                naming_style_name = "predefined"
                regexps[name_type] = DEFAULT_PATTERNS[name_type]

            custom_regex_setting_name = f"{name_type}_rgx"
            custom_regex = getattr(self.linter.config, custom_regex_setting_name, None)
            if custom_regex is not None:
                regexps[name_type] = custom_regex

            if custom_regex is not None:
                hints[name_type] = f"{custom_regex.pattern!r} pattern"
            else:
                hints[name_type] = f"{naming_style_name} naming style"

        return regexps, hints

    @utils.only_required_for_messages("disallowed-name", "invalid-name")
    def visit_module(self, node: nodes.Module) -> None:
        self._check_name("module", node.name.split(".")[-1], node)
        self._bad_names = {}

    def leave_module(self, _: nodes.Module) -> None:
        for all_groups in self._bad_names.values():
            if len(all_groups) < 2:
                continue
            groups: collections.defaultdict[
                int, list[list[_BadNamesTuple]]
            ] = collections.defaultdict(list)
            min_warnings = sys.maxsize
            prevalent_group, _ = max(all_groups.items(), key=lambda item: len(item[1]))
            for group in all_groups.values():
                groups[len(group)].append(group)
                min_warnings = min(len(group), min_warnings)
            if len(groups[min_warnings]) > 1:
                by_line = sorted(
                    groups[min_warnings],
                    key=lambda group: min(
                        warning[0].lineno
                        for warning in group
                        if warning[0].lineno is not None
                    ),
                )
                warnings: Iterable[_BadNamesTuple] = itertools.chain(*by_line[1:])
            else:
                warnings = groups[min_warnings][0]
            for args in warnings:
                self._raise_name_warning(prevalent_group, *args)

    @utils.only_required_for_messages("disallowed-name", "invalid-name")
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        self._check_name("class", node.name, node)
        for attr, anodes in node.instance_attrs.items():
            if not any(node.instance_attr_ancestors(attr)):
                self._check_name("attr", attr, anodes[0])

    @utils.only_required_for_messages("disallowed-name", "invalid-name")
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        # Do not emit any warnings if the method is just an implementation
        # of a base class method.
        confidence = interfaces.HIGH
        if node.is_method():
            if utils.overrides_a_method(node.parent.frame(), node.name):
                return
            confidence = (
                interfaces.INFERENCE
                if utils.has_known_bases(node.parent.frame())
                else interfaces.INFERENCE_FAILURE
            )

        self._check_name(
            _determine_function_name_type(node, config=self.linter.config),
            node.name,
            node,
            confidence,
        )
        # Check argument names
        args = node.args.args
        if args is not None:
            self._recursive_check_names(args)

    visit_asyncfunctiondef = visit_functiondef

    @utils.only_required_for_messages(
        "disallowed-name",
        "invalid-name",
        "typevar-name-incorrect-variance",
        "typevar-double-variance",
        "typevar-name-mismatch",
    )
    def visit_assignname(  # pylint: disable=too-many-branches
        self, node: nodes.AssignName
    ) -> None:
        """Check module level assigned names."""
        frame = node.frame()
        assign_type = node.assign_type()

        # Check names defined in comprehensions
        if isinstance(assign_type, nodes.Comprehension):
            self._check_name("inlinevar", node.name, node)

        # Check names defined in module scope
        elif isinstance(frame, nodes.Module):
            # Check names defined in Assign nodes
            if isinstance(assign_type, nodes.Assign):
                inferred_assign_type = utils.safe_infer(assign_type.value)

                # Check TypeVar's and TypeAliases assigned alone or in tuple assignment
                if isinstance(node.parent, nodes.Assign):
                    if self._assigns_typevar(assign_type.value):
                        self._check_name("typevar", assign_type.targets[0].name, node)
                        return
                    if self._assigns_typealias(assign_type.value):
                        self._check_name("typealias", assign_type.targets[0].name, node)
                        return

                if (
                    isinstance(node.parent, nodes.Tuple)
                    and isinstance(assign_type.value, nodes.Tuple)
                    # protect against unbalanced tuple unpacking
                    and node.parent.elts.index(node) < len(assign_type.value.elts)
                ):
                    assigner = assign_type.value.elts[node.parent.elts.index(node)]
                    if self._assigns_typevar(assigner):
                        self._check_name(
                            "typevar",
                            assign_type.targets[0]
                            .elts[node.parent.elts.index(node)]
                            .name,
                            node,
                        )
                        return
                    if self._assigns_typealias(assigner):
                        self._check_name(
                            "typealias",
                            assign_type.targets[0]
                            .elts[node.parent.elts.index(node)]
                            .name,
                            node,
                        )
                        return

                # Check classes (TypeVar's are classes so they need to be excluded first)
                elif isinstance(inferred_assign_type, nodes.ClassDef):
                    self._check_name("class", node.name, node)

                # Don't emit if the name redefines an import in an ImportError except handler.
                elif not _redefines_import(node) and isinstance(
                    inferred_assign_type, nodes.Const
                ):
                    self._check_name("const", node.name, node)
                else:
                    self._check_name(
                        "variable", node.name, node, disallowed_check_only=True
                    )

            # Check names defined in AnnAssign nodes
            elif isinstance(assign_type, nodes.AnnAssign):
                if utils.is_assign_name_annotated_with(node, "Final"):
                    self._check_name("const", node.name, node)
                elif self._assigns_typealias(assign_type.annotation):
                    self._check_name("typealias", node.name, node)

        # Check names defined in function scopes
        elif isinstance(frame, nodes.FunctionDef):
            # global introduced variable aren't in the function locals
            if node.name in frame and node.name not in frame.argnames():
                if not _redefines_import(node):
                    if isinstance(
                        assign_type, nodes.AnnAssign
                    ) and self._assigns_typealias(assign_type.annotation):
                        self._check_name("typealias", node.name, node)
                    else:
                        self._check_name("variable", node.name, node)

        # Check names defined in class scopes
        elif isinstance(frame, nodes.ClassDef):
            if utils.is_enum_member(node) or utils.is_assign_name_annotated_with(
                node, "Final"
            ):
                self._check_name("class_const", node.name, node)
            else:
                self._check_name("class_attribute", node.name, node)

    def _recursive_check_names(self, args: list[nodes.AssignName]) -> None:
        """Check names in a possibly recursive list <arg>."""
        for arg in args:
            self._check_name("argument", arg.name, arg)

    def _find_name_group(self, node_type: str) -> str:
        return self._name_group.get(node_type, node_type)

    def _raise_name_warning(
        self,
        prevalent_group: str | None,
        node: nodes.NodeNG,
        node_type: str,
        name: str,
        confidence: interfaces.Confidence,
        warning: str = "invalid-name",
    ) -> None:
        type_label = constants.HUMAN_READABLE_TYPES[node_type]
        hint = self._name_hints[node_type]
        if prevalent_group:
            # This happens in the multi naming match case. The expected
            # prevalent group needs to be spelled out to make the message
            # correct.
            hint = f"the `{prevalent_group}` group in the {hint}"
        if self.linter.config.include_naming_hint:
            hint += f" ({self._name_regexps[node_type].pattern!r} pattern)"
        args = (
            (type_label.capitalize(), name, hint)
            if warning == "invalid-name"
            else (type_label.capitalize(), name)
        )

        self.add_message(warning, node=node, args=args, confidence=confidence)
        self.linter.stats.increase_bad_name(node_type, 1)

    def _name_allowed_by_regex(self, name: str) -> bool:
        return name in self.linter.config.good_names or any(
            pattern.match(name) for pattern in self._good_names_rgxs_compiled
        )

    def _name_disallowed_by_regex(self, name: str) -> bool:
        return name in self.linter.config.bad_names or any(
            pattern.match(name) for pattern in self._bad_names_rgxs_compiled
        )

    def _check_name(
        self,
        node_type: str,
        name: str,
        node: nodes.NodeNG,
        confidence: interfaces.Confidence = interfaces.HIGH,
        disallowed_check_only: bool = False,
    ) -> None:
        """Check for a name using the type's regexp."""

        def _should_exempt_from_invalid_name(node: nodes.NodeNG) -> bool:
            if node_type == "variable":
                inferred = utils.safe_infer(node)
                if isinstance(inferred, nodes.ClassDef):
                    return True
            return False

        if self._name_allowed_by_regex(name=name):
            return
        if self._name_disallowed_by_regex(name=name):
            self.linter.stats.increase_bad_name(node_type, 1)
            self.add_message(
                "disallowed-name", node=node, args=name, confidence=interfaces.HIGH
            )
            return
        regexp = self._name_regexps[node_type]
        match = regexp.match(name)

        if _is_multi_naming_match(match, node_type, confidence):
            name_group = self._find_name_group(node_type)
            bad_name_group = self._bad_names.setdefault(name_group, {})
            # Ignored because this is checked by the if statement
            warnings = bad_name_group.setdefault(match.lastgroup, [])  # type: ignore[union-attr, arg-type]
            warnings.append((node, node_type, name, confidence))

        if (
            match is None
            and not disallowed_check_only
            and not _should_exempt_from_invalid_name(node)
        ):
            self._raise_name_warning(None, node, node_type, name, confidence)

        # Check TypeVar names for variance suffixes
        if node_type == "typevar":
            self._check_typevar(name, node)

    @staticmethod
    def _assigns_typevar(node: nodes.NodeNG | None) -> bool:
        """Check if a node is assigning a TypeVar."""
        if isinstance(node, astroid.Call):
            inferred = utils.safe_infer(node.func)
            if (
                isinstance(inferred, astroid.ClassDef)
                and inferred.qname() in TYPE_VAR_QNAME
            ):
                return True
        return False

    @staticmethod
    def _assigns_typealias(node: nodes.NodeNG | None) -> bool:
        """Check if a node is assigning a TypeAlias."""
        inferred = utils.safe_infer(node)
        if isinstance(inferred, nodes.ClassDef):
            qname = inferred.qname()
            if qname == "typing.TypeAlias":
                return True
            if qname == ".Union":
                # Union is a special case because it can be used as a type alias
                # or as a type annotation. We only want to check the former.
                assert node is not None
                return not isinstance(node.parent, nodes.AnnAssign)
        elif isinstance(inferred, nodes.FunctionDef):
            # TODO: when py3.12 is minimum, remove this condition
            # TypeAlias became a class in python 3.12
            if inferred.qname() == "typing.TypeAlias":
                return True
        return False

    def _check_typevar(self, name: str, node: nodes.AssignName) -> None:
        """Check for TypeVar lint violations."""
        if isinstance(node.parent, nodes.Assign):
            keywords = node.assign_type().value.keywords
            args = node.assign_type().value.args
        elif isinstance(node.parent, nodes.Tuple):
            keywords = (
                node.assign_type().value.elts[node.parent.elts.index(node)].keywords
            )
            args = node.assign_type().value.elts[node.parent.elts.index(node)].args

        variance = TypeVarVariance.invariant
        name_arg = None
        for kw in keywords:
            if variance == TypeVarVariance.double_variant:
                pass
            elif kw.arg == "covariant" and kw.value.value:
                variance = (
                    TypeVarVariance.covariant
                    if variance != TypeVarVariance.contravariant
                    else TypeVarVariance.double_variant
                )
            elif kw.arg == "contravariant" and kw.value.value:
                variance = (
                    TypeVarVariance.contravariant
                    if variance != TypeVarVariance.covariant
                    else TypeVarVariance.double_variant
                )

            if kw.arg == "name" and isinstance(kw.value, nodes.Const):
                name_arg = kw.value.value

        if name_arg is None and args and isinstance(args[0], nodes.Const):
            name_arg = args[0].value

        if variance == TypeVarVariance.double_variant:
            self.add_message(
                "typevar-double-variance",
                node=node,
                confidence=interfaces.INFERENCE,
            )
            self.add_message(
                "typevar-name-incorrect-variance",
                node=node,
                args=("",),
                confidence=interfaces.INFERENCE,
            )
        elif variance == TypeVarVariance.covariant and not name.endswith("_co"):
            suggest_name = f"{re.sub('_contra$', '', name)}_co"
            self.add_message(
                "typevar-name-incorrect-variance",
                node=node,
                args=(f'. "{name}" is covariant, use "{suggest_name}" instead'),
                confidence=interfaces.INFERENCE,
            )
        elif variance == TypeVarVariance.contravariant and not name.endswith("_contra"):
            suggest_name = f"{re.sub('_co$', '', name)}_contra"
            self.add_message(
                "typevar-name-incorrect-variance",
                node=node,
                args=(f'. "{name}" is contravariant, use "{suggest_name}" instead'),
                confidence=interfaces.INFERENCE,
            )
        elif variance == TypeVarVariance.invariant and (
            name.endswith("_co") or name.endswith("_contra")
        ):
            suggest_name = re.sub("_contra$|_co$", "", name)
            self.add_message(
                "typevar-name-incorrect-variance",
                node=node,
                args=(f'. "{name}" is invariant, use "{suggest_name}" instead'),
                confidence=interfaces.INFERENCE,
            )

        if name_arg is not None and name_arg != name:
            self.add_message(
                "typevar-name-mismatch",
                node=node,
                args=(name_arg, name),
                confidence=interfaces.INFERENCE,
            )

back/venvmsg/Lib/site-packages/pylint/checkers/base/name_checker/checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import re
from re import Pattern

from pylint import constants
from pylint.typing import OptionDict, Options


class NamingStyle:
    """Class to register all accepted forms of a single naming style.

    It may seem counter-intuitive that single naming style has multiple "accepted"
    forms of regular expressions, but we need to special-case stuff like dunder
    names in method names.
    """

    ANY: Pattern[str] = re.compile(".*")
    CLASS_NAME_RGX: Pattern[str] = ANY
    MOD_NAME_RGX: Pattern[str] = ANY
    CONST_NAME_RGX: Pattern[str] = ANY
    COMP_VAR_RGX: Pattern[str] = ANY
    DEFAULT_NAME_RGX: Pattern[str] = ANY
    CLASS_ATTRIBUTE_RGX: Pattern[str] = ANY

    @classmethod
    def get_regex(cls, name_type: str) -> Pattern[str]:
        return {
            "module": cls.MOD_NAME_RGX,
            "const": cls.CONST_NAME_RGX,
            "class": cls.CLASS_NAME_RGX,
            "function": cls.DEFAULT_NAME_RGX,
            "method": cls.DEFAULT_NAME_RGX,
            "attr": cls.DEFAULT_NAME_RGX,
            "argument": cls.DEFAULT_NAME_RGX,
            "variable": cls.DEFAULT_NAME_RGX,
            "class_attribute": cls.CLASS_ATTRIBUTE_RGX,
            "class_const": cls.CONST_NAME_RGX,
            "inlinevar": cls.COMP_VAR_RGX,
        }[name_type]


class SnakeCaseStyle(NamingStyle):
    """Regex rules for snake_case naming style."""

    CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]*$")
    MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]*$")
    CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]*|__.*__)$")
    COMP_VAR_RGX = CLASS_NAME_RGX
    DEFAULT_NAME_RGX = re.compile(
        r"([^\W\dA-Z][^\WA-Z]*|_[^\WA-Z]*|__[^\WA-Z\d_][^\WA-Z]+__)$"
    )
    CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]*|__.*__)$")


class CamelCaseStyle(NamingStyle):
    """Regex rules for camelCase naming style."""

    CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]*$")
    MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]*$")
    CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]*|__.*__)$")
    COMP_VAR_RGX = MOD_NAME_RGX
    DEFAULT_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]*|__[^\W\dA-Z_]\w+__)$")
    CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\W_]*|__.*__)$")


class PascalCaseStyle(NamingStyle):
    """Regex rules for PascalCase naming style."""

    CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\W_]*$")
    MOD_NAME_RGX = CLASS_NAME_RGX
    CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]*|__.*__)$")
    COMP_VAR_RGX = CLASS_NAME_RGX
    DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]*|__[^\W\dA-Z_]\w+__)$")
    CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\W_]*$")


class UpperCaseStyle(NamingStyle):
    """Regex rules for UPPER_CASE naming style."""

    CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\Wa-z]*$")
    MOD_NAME_RGX = CLASS_NAME_RGX
    CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]*|__.*__)$")
    COMP_VAR_RGX = CLASS_NAME_RGX
    DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]*|__[^\W\dA-Z_]\w+__)$")
    CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\Wa-z]*$")


class AnyStyle(NamingStyle):
    pass


NAMING_STYLES = {
    "snake_case": SnakeCaseStyle,
    "camelCase": CamelCaseStyle,
    "PascalCase": PascalCaseStyle,
    "UPPER_CASE": UpperCaseStyle,
    "any": AnyStyle,
}

# Name types that have a style option
KNOWN_NAME_TYPES_WITH_STYLE = {
    "module",
    "const",
    "class",
    "function",
    "method",
    "attr",
    "argument",
    "variable",
    "class_attribute",
    "class_const",
    "inlinevar",
}


DEFAULT_NAMING_STYLES = {
    "module": "snake_case",
    "const": "UPPER_CASE",
    "class": "PascalCase",
    "function": "snake_case",
    "method": "snake_case",
    "attr": "snake_case",
    "argument": "snake_case",
    "variable": "snake_case",
    "class_attribute": "any",
    "class_const": "UPPER_CASE",
    "inlinevar": "any",
}


# Name types that have a 'rgx' option
KNOWN_NAME_TYPES = {
    *KNOWN_NAME_TYPES_WITH_STYLE,
    "typevar",
    "typealias",
}


def _create_naming_options() -> Options:
    name_options: list[tuple[str, OptionDict]] = []
    for name_type in sorted(KNOWN_NAME_TYPES):
        human_readable_name = constants.HUMAN_READABLE_TYPES[name_type]
        name_type_hyphened = name_type.replace("_", "-")

        help_msg = f"Regular expression matching correct {human_readable_name} names. "
        if name_type in KNOWN_NAME_TYPES_WITH_STYLE:
            help_msg += f"Overrides {name_type_hyphened}-naming-style. "
        help_msg += (
            f"If left empty, {human_readable_name} names will be checked "
            "with the set naming style."
        )

        # Add style option for names that support it
        if name_type in KNOWN_NAME_TYPES_WITH_STYLE:
            default_style = DEFAULT_NAMING_STYLES[name_type]
            name_options.append(
                (
                    f"{name_type_hyphened}-naming-style",
                    {
                        "default": default_style,
                        "type": "choice",
                        "choices": list(NAMING_STYLES.keys()),
                        "metavar": "<style>",
                        "help": f"Naming style matching correct {human_readable_name} names.",
                    },
                )
            )

        name_options.append(
            (
                f"{name_type_hyphened}-rgx",
                {
                    "default": None,
                    "type": "regexp",
                    "metavar": "<regexp>",
                    "help": help_msg,
                },
            )
        )
    return tuple(name_options)

back/venvmsg/Lib/site-packages/pylint/checkers/base/name_checker/naming_style.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from astroid import nodes

from pylint.checkers import utils
from pylint.checkers.base.basic_checker import _BasicChecker


class PassChecker(_BasicChecker):
    """Check if the pass statement is really necessary."""

    msgs = {
        "W0107": (
            "Unnecessary pass statement",
            "unnecessary-pass",
            'Used when a "pass" statement can be removed without affecting '
            "the behaviour of the code.",
        )
    }

    @utils.only_required_for_messages("unnecessary-pass")
    def visit_pass(self, node: nodes.Pass) -> None:
        if len(node.parent.child_sequence(node)) > 1 or (
            isinstance(node.parent, (nodes.ClassDef, nodes.FunctionDef))
            and node.parent.doc_node
        ):
            self.add_message("unnecessary-pass", node=node)

back/venvmsg/Lib/site-packages/pylint/checkers/base/pass_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import abc
import functools
from collections.abc import Iterable, Sequence
from inspect import cleandoc
from tokenize import TokenInfo
from typing import TYPE_CHECKING, Any

from astroid import nodes

from pylint.config.arguments_provider import _ArgumentsProvider
from pylint.constants import _MSG_ORDER, MAIN_CHECKER_NAME, WarningScope
from pylint.exceptions import InvalidMessageError
from pylint.interfaces import Confidence
from pylint.message.message_definition import MessageDefinition
from pylint.typing import (
    ExtraMessageOptions,
    MessageDefinitionTuple,
    OptionDict,
    Options,
    ReportsCallable,
)
from pylint.utils import get_rst_section, get_rst_title

if TYPE_CHECKING:
    from pylint.lint import PyLinter


@functools.total_ordering
class BaseChecker(_ArgumentsProvider):
    # checker name (you may reuse an existing one)
    name: str = ""
    # ordered list of options to control the checker behaviour
    options: Options = ()
    # messages issued by this checker
    msgs: dict[str, MessageDefinitionTuple] = {}
    # reports issued by this checker
    reports: tuple[tuple[str, str, ReportsCallable], ...] = ()
    # mark this checker as enabled or not.
    enabled: bool = True

    def __init__(self, linter: PyLinter) -> None:
        """Checker instances should have the linter as argument."""
        if self.name is not None:
            self.name = self.name.lower()
        self.linter = linter
        _ArgumentsProvider.__init__(self, linter)

    def __gt__(self, other: Any) -> bool:
        """Permits sorting checkers for stable doc and tests.

        The main checker is always the first one, then builtin checkers in alphabetical
        order, then extension checkers in alphabetical order.
        """
        if not isinstance(other, BaseChecker):
            return False
        if self.name == MAIN_CHECKER_NAME:
            return False
        if other.name == MAIN_CHECKER_NAME:
            return True
        self_is_builtin = type(self).__module__.startswith("pylint.checkers")
        if self_is_builtin ^ type(other).__module__.startswith("pylint.checkers"):
            return not self_is_builtin
        return self.name > other.name

    def __eq__(self, other: Any) -> bool:
        """Permit to assert Checkers are equal."""
        if not isinstance(other, BaseChecker):
            return False
        return f"{self.name}{self.msgs}" == f"{other.name}{other.msgs}"

    def __hash__(self) -> int:
        """Make Checker hashable."""
        return hash(f"{self.name}{self.msgs}")

    def __repr__(self) -> str:
        status = "Checker" if self.enabled else "Disabled checker"
        msgs = "', '".join(self.msgs.keys())
        return f"{status} '{self.name}' (responsible for '{msgs}')"

    def __str__(self) -> str:
        """This might be incomplete because multiple classes inheriting BaseChecker
        can have the same name.

        See: MessageHandlerMixIn.get_full_documentation()
        """
        return self.get_full_documentation(
            msgs=self.msgs, options=self._options_and_values(), reports=self.reports
        )

    def get_full_documentation(
        self,
        msgs: dict[str, MessageDefinitionTuple],
        options: Iterable[tuple[str, OptionDict, Any]],
        reports: Sequence[tuple[str, str, ReportsCallable]],
        doc: str | None = None,
        module: str | None = None,
        show_options: bool = True,
    ) -> str:
        result = ""
        checker_title = f"{self.name.replace('_', ' ').title()} checker"
        if module:
            # Provide anchor to link against
            result += f".. _{module}:\n\n"
        result += f"{get_rst_title(checker_title, '~')}\n"
        if module:
            result += f"This checker is provided by ``{module}``.\n"
        result += f"Verbatim name of the checker is ``{self.name}``.\n\n"
        if doc:
            # Provide anchor to link against
            result += get_rst_title(f"{checker_title} Documentation", "^")
            result += f"{cleandoc(doc)}\n\n"
        # options might be an empty generator and not be False when cast to boolean
        options_list = list(options)
        if options_list:
            if show_options:
                result += get_rst_title(f"{checker_title} Options", "^")
                result += f"{get_rst_section(None, options_list)}\n"
            else:
                result += f"See also :ref:`{self.name} checker's options' documentation <{self.name}-options>`\n\n"
        if msgs:
            result += get_rst_title(f"{checker_title} Messages", "^")
            for msgid, msg in sorted(
                msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1])
            ):
                msg_def = self.create_message_definition_from_tuple(msgid, msg)
                result += f"{msg_def.format_help(checkerref=False)}\n"
            result += "\n"
        if reports:
            result += get_rst_title(f"{checker_title} Reports", "^")
            for report in reports:
                result += f":{report[0]}: {report[1]}\n"
            result += "\n"
        result += "\n"
        return result

    def add_message(
        self,
        msgid: str,
        line: int | None = None,
        node: nodes.NodeNG | None = None,
        args: Any = None,
        confidence: Confidence | None = None,
        col_offset: int | None = None,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        self.linter.add_message(
            msgid, line, node, args, confidence, col_offset, end_lineno, end_col_offset
        )

    def check_consistency(self) -> None:
        """Check the consistency of msgid.

        msg ids for a checker should be a string of len 4, where the two first
        characters are the checker id and the two last the msg id in this
        checker.

        :raises InvalidMessageError: If the checker id in the messages are not
        always the same.
        """
        checker_id = None
        existing_ids = []
        for message in self.messages:
            # Id's for shared messages such as the 'deprecated-*' messages
            # can be inconsistent with their checker id.
            if message.shared:
                continue
            if checker_id is not None and checker_id != message.msgid[1:3]:
                error_msg = "Inconsistent checker part in message id "
                error_msg += f"'{message.msgid}' (expected 'x{checker_id}xx' "
                error_msg += f"because we already had {existing_ids})."
                raise InvalidMessageError(error_msg)
            checker_id = message.msgid[1:3]
            existing_ids.append(message.msgid)

    def create_message_definition_from_tuple(
        self, msgid: str, msg_tuple: MessageDefinitionTuple
    ) -> MessageDefinition:
        if isinstance(self, (BaseTokenChecker, BaseRawFileChecker)):
            default_scope = WarningScope.LINE
        else:
            default_scope = WarningScope.NODE
        options: ExtraMessageOptions = {}
        if len(msg_tuple) == 4:
            (msg, symbol, descr, msg_options) = msg_tuple  # type: ignore[misc]
            options = ExtraMessageOptions(**msg_options)
        elif len(msg_tuple) == 3:
            (msg, symbol, descr) = msg_tuple  # type: ignore[misc]
        else:
            error_msg = """Messages should have a msgid, a symbol and a description. Something like this :

"W1234": (
    "message",
    "message-symbol",
    "Message description with detail.",
    ...
),
"""
            raise InvalidMessageError(error_msg)
        options.setdefault("scope", default_scope)
        return MessageDefinition(self, msgid, msg, descr, symbol, **options)

    @property
    def messages(self) -> list[MessageDefinition]:
        return [
            self.create_message_definition_from_tuple(msgid, msg_tuple)
            for msgid, msg_tuple in sorted(self.msgs.items())
        ]

    def open(self) -> None:
        """Called before visiting project (i.e. set of modules)."""

    def close(self) -> None:
        """Called after visiting project (i.e set of modules)."""

    def get_map_data(self) -> Any:
        return None

    # pylint: disable-next=unused-argument
    def reduce_map_data(self, linter: PyLinter, data: list[Any]) -> None:
        return None


class BaseTokenChecker(BaseChecker):
    """Base class for checkers that want to have access to the token stream."""

    @abc.abstractmethod
    def process_tokens(self, tokens: list[TokenInfo]) -> None:
        """Should be overridden by subclasses."""
        raise NotImplementedError()


class BaseRawFileChecker(BaseChecker):
    """Base class for checkers which need to parse the raw file."""

    @abc.abstractmethod
    def process_module(self, node: nodes.Module) -> None:
        """Process a module.

        The module's content is accessible via ``astroid.stream``
        """
        raise NotImplementedError()

back/venvmsg/Lib/site-packages/pylint/checkers/base_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from pylint.checkers.classes.class_checker import ClassChecker
from pylint.checkers.classes.special_methods_checker import SpecialMethodsChecker

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def register(linter: PyLinter) -> None:
    linter.register_checker(ClassChecker(linter))
    linter.register_checker(SpecialMethodsChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/classes/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Classes checker for Python code."""

from __future__ import annotations

from collections import defaultdict
from collections.abc import Callable, Sequence
from functools import cached_property
from itertools import chain, zip_longest
from re import Pattern
from typing import TYPE_CHECKING, Any, NamedTuple, Union

import astroid
from astroid import bases, nodes, util
from astroid.nodes import LocalsDictNodeNG
from astroid.typing import SuccessfulInferenceResult

from pylint.checkers import BaseChecker, utils
from pylint.checkers.utils import (
    PYMETHODS,
    class_is_abstract,
    decorated_with,
    decorated_with_property,
    get_outer_class,
    has_known_bases,
    is_attr_private,
    is_attr_protected,
    is_builtin_object,
    is_comprehension,
    is_iterable,
    is_property_setter,
    is_property_setter_or_deleter,
    node_frame_class,
    only_required_for_messages,
    safe_infer,
    unimplemented_abstract_methods,
    uninferable_final_decorators,
)
from pylint.interfaces import HIGH, INFERENCE
from pylint.typing import MessageDefinitionTuple

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


_AccessNodes = Union[nodes.Attribute, nodes.AssignAttr]

INVALID_BASE_CLASSES = {"bool", "range", "slice", "memoryview"}
ALLOWED_PROPERTIES = {"bultins.property", "functools.cached_property"}
BUILTIN_DECORATORS = {"builtins.property", "builtins.classmethod"}
ASTROID_TYPE_COMPARATORS = {
    nodes.Const: lambda a, b: a.value == b.value,
    nodes.ClassDef: lambda a, b: a.qname == b.qname,
    nodes.Tuple: lambda a, b: a.elts == b.elts,
    nodes.List: lambda a, b: a.elts == b.elts,
    nodes.Dict: lambda a, b: a.items == b.items,
    nodes.Name: lambda a, b: set(a.infer()) == set(b.infer()),
}

# Dealing with useless override detection, with regard
# to parameters vs arguments


class _CallSignature(NamedTuple):
    args: list[str | None]
    kws: dict[str | None, str | None]
    starred_args: list[str]
    starred_kws: list[str]


class _ParameterSignature(NamedTuple):
    args: list[str]
    kwonlyargs: list[str]
    varargs: str
    kwargs: str


def _signature_from_call(call: nodes.Call) -> _CallSignature:
    kws = {}
    args = []
    starred_kws = []
    starred_args = []
    for keyword in call.keywords or []:
        arg, value = keyword.arg, keyword.value
        if arg is None and isinstance(value, nodes.Name):
            # Starred node, and we are interested only in names,
            # otherwise some transformation might occur for the parameter.
            starred_kws.append(value.name)
        elif isinstance(value, nodes.Name):
            kws[arg] = value.name
        else:
            kws[arg] = None

    for arg in call.args:
        if isinstance(arg, nodes.Starred) and isinstance(arg.value, nodes.Name):
            # Positional variadic and a name, otherwise some transformation
            # might have occurred.
            starred_args.append(arg.value.name)
        elif isinstance(arg, nodes.Name):
            args.append(arg.name)
        else:
            args.append(None)

    return _CallSignature(args, kws, starred_args, starred_kws)


def _signature_from_arguments(arguments: nodes.Arguments) -> _ParameterSignature:
    kwarg = arguments.kwarg
    vararg = arguments.vararg
    args = [
        arg.name
        for arg in chain(arguments.posonlyargs, arguments.args)
        if arg.name != "self"
    ]
    kwonlyargs = [arg.name for arg in arguments.kwonlyargs]
    return _ParameterSignature(args, kwonlyargs, vararg, kwarg)


def _definition_equivalent_to_call(
    definition: _ParameterSignature, call: _CallSignature
) -> bool:
    """Check if a definition signature is equivalent to a call."""
    if definition.kwargs:
        if definition.kwargs not in call.starred_kws:
            return False
    elif call.starred_kws:
        return False
    if definition.varargs:
        if definition.varargs not in call.starred_args:
            return False
    elif call.starred_args:
        return False
    if any(kw not in call.kws for kw in definition.kwonlyargs):
        return False
    if definition.args != call.args:
        return False

    # No extra kwargs in call.
    return all(kw in call.args or kw in definition.kwonlyargs for kw in call.kws)


def _is_trivial_super_delegation(function: nodes.FunctionDef) -> bool:
    """Check whether a function definition is a method consisting only of a
    call to the same function on the superclass.
    """
    if (
        not function.is_method()
        # Adding decorators to a function changes behavior and
        # constitutes a non-trivial change.
        or function.decorators
    ):
        return False

    body = function.body
    if len(body) != 1:
        # Multiple statements, which means this overridden method
        # could do multiple things we are not aware of.
        return False

    statement = body[0]
    if not isinstance(statement, (nodes.Expr, nodes.Return)):
        # Doing something else than what we are interested in.
        return False

    call = statement.value
    if (
        not isinstance(call, nodes.Call)
        # Not a super() attribute access.
        or not isinstance(call.func, nodes.Attribute)
    ):
        return False

    # Anything other than a super call is non-trivial.
    super_call = safe_infer(call.func.expr)
    if not isinstance(super_call, astroid.objects.Super):
        return False

    # The name should be the same.
    if call.func.attrname != function.name:
        return False

    # Should be a super call with the MRO pointer being the
    # current class and the type being the current instance.
    current_scope = function.parent.scope()
    if (
        super_call.mro_pointer != current_scope
        or not isinstance(super_call.type, astroid.Instance)
        or super_call.type.name != current_scope.name
    ):
        return False

    return True


# Deal with parameters overriding in two methods.


def _positional_parameters(method: nodes.FunctionDef) -> list[nodes.AssignName]:
    positional = method.args.args
    if method.is_bound() and method.type in {"classmethod", "method"}:
        positional = positional[1:]
    return positional  # type: ignore[no-any-return]


class _DefaultMissing:
    """Sentinel value for missing arg default, use _DEFAULT_MISSING."""


_DEFAULT_MISSING = _DefaultMissing()


def _has_different_parameters_default_value(
    original: nodes.Arguments, overridden: nodes.Arguments
) -> bool:
    """Check if original and overridden methods arguments have different default values.

    Return True if one of the overridden arguments has a default
    value different from the default value of the original argument
    If one of the method doesn't have argument (.args is None)
    return False
    """
    if original.args is None or overridden.args is None:
        return False

    for param in chain(original.args, original.kwonlyargs):
        try:
            original_default = original.default_value(param.name)
        except astroid.exceptions.NoDefault:
            original_default = _DEFAULT_MISSING
        try:
            overridden_default = overridden.default_value(param.name)
            if original_default is _DEFAULT_MISSING:
                # Only the original has a default.
                return True
        except astroid.exceptions.NoDefault:
            if original_default is _DEFAULT_MISSING:
                # Both have a default, no difference
                continue
            # Only the override has a default.
            return True

        original_type = type(original_default)
        if not isinstance(overridden_default, original_type):
            # Two args with same name but different types
            return True
        is_same_fn: Callable[[Any, Any], bool] | None = ASTROID_TYPE_COMPARATORS.get(
            original_type
        )
        if is_same_fn is None:
            # If the default value comparison is unhandled, assume the value is different
            return True
        if not is_same_fn(original_default, overridden_default):
            # Two args with same type but different values
            return True
    return False


def _has_different_parameters(
    original: list[nodes.AssignName],
    overridden: list[nodes.AssignName],
    dummy_parameter_regex: Pattern[str],
) -> list[str]:
    result: list[str] = []
    zipped = zip_longest(original, overridden)
    for original_param, overridden_param in zipped:
        if not overridden_param:
            return ["Number of parameters "]

        if not original_param:
            try:
                overridden_param.parent.default_value(overridden_param.name)
                continue
            except astroid.NoDefault:
                return ["Number of parameters "]

        # check for the arguments' name
        names = [param.name for param in (original_param, overridden_param)]
        if any(dummy_parameter_regex.match(name) for name in names):
            continue
        if original_param.name != overridden_param.name:
            result.append(
                f"Parameter '{original_param.name}' has been renamed "
                f"to '{overridden_param.name}' in"
            )

    return result


def _has_different_keyword_only_parameters(
    original: list[nodes.AssignName],
    overridden: list[nodes.AssignName],
) -> list[str]:
    """Determine if the two methods have different keyword only parameters."""
    original_names = [i.name for i in original]
    overridden_names = [i.name for i in overridden]

    if any(name not in overridden_names for name in original_names):
        return ["Number of parameters "]

    for name in overridden_names:
        if name in original_names:
            continue

        try:
            overridden[0].parent.default_value(name)
        except astroid.NoDefault:
            return ["Number of parameters "]

    return []


def _different_parameters(
    original: nodes.FunctionDef,
    overridden: nodes.FunctionDef,
    dummy_parameter_regex: Pattern[str],
) -> list[str]:
    """Determine if the two methods have different parameters.

    They are considered to have different parameters if:

       * they have different positional parameters, including different names

       * one of the methods is having variadics, while the other is not

       * they have different keyword only parameters.
    """
    output_messages = []
    original_parameters = _positional_parameters(original)
    overridden_parameters = _positional_parameters(overridden)

    # Copy kwonlyargs list so that we don't affect later function linting
    original_kwonlyargs = original.args.kwonlyargs

    # Allow positional/keyword variadic in overridden to match against any
    # positional/keyword argument in original.
    # Keep any arguments that are found separately in overridden to satisfy
    # later tests
    if overridden.args.vararg:
        overridden_names = [v.name for v in overridden_parameters]
        original_parameters = [
            v for v in original_parameters if v.name in overridden_names
        ]

    if overridden.args.kwarg:
        overridden_names = [v.name for v in overridden.args.kwonlyargs]
        original_kwonlyargs = [
            v for v in original.args.kwonlyargs if v.name in overridden_names
        ]

    different_positional = _has_different_parameters(
        original_parameters, overridden_parameters, dummy_parameter_regex
    )
    different_kwonly = _has_different_keyword_only_parameters(
        original_kwonlyargs, overridden.args.kwonlyargs
    )
    if different_kwonly and different_positional:
        if "Number " in different_positional[0] and "Number " in different_kwonly[0]:
            output_messages.append("Number of parameters ")
            output_messages += different_positional[1:]
            output_messages += different_kwonly[1:]
        else:
            output_messages += different_positional
            output_messages += different_kwonly
    else:
        if different_positional:
            output_messages += different_positional
        if different_kwonly:
            output_messages += different_kwonly

    # Arguments will only violate LSP if there are variadics in the original
    # that are then removed from the overridden
    kwarg_lost = original.args.kwarg and not overridden.args.kwarg
    vararg_lost = original.args.vararg and not overridden.args.vararg

    if kwarg_lost or vararg_lost:
        output_messages += ["Variadics removed in"]

    if original.name in PYMETHODS:
        # Ignore the difference for special methods. If the parameter
        # numbers are different, then that is going to be caught by
        # unexpected-special-method-signature.
        # If the names are different, it doesn't matter, since they can't
        # be used as keyword arguments anyway.
        output_messages.clear()

    return output_messages


def _is_invalid_base_class(cls: nodes.ClassDef) -> bool:
    return cls.name in INVALID_BASE_CLASSES and is_builtin_object(cls)


def _has_data_descriptor(cls: nodes.ClassDef, attr: str) -> bool:
    attributes = cls.getattr(attr)
    for attribute in attributes:
        try:
            for inferred in attribute.infer():
                if isinstance(inferred, astroid.Instance):
                    try:
                        inferred.getattr("__get__")
                        inferred.getattr("__set__")
                    except astroid.NotFoundError:
                        continue
                    else:
                        return True
        except astroid.InferenceError:
            # Can't infer, avoid emitting a false positive in this case.
            return True
    return False


def _called_in_methods(
    func: LocalsDictNodeNG,
    klass: nodes.ClassDef,
    methods: Sequence[str],
) -> bool:
    """Check if the func was called in any of the given methods,
    belonging to the *klass*.

    Returns True if so, False otherwise.
    """
    if not isinstance(func, nodes.FunctionDef):
        return False
    for method in methods:
        try:
            inferred = klass.getattr(method)
        except astroid.NotFoundError:
            continue
        for infer_method in inferred:
            for call in infer_method.nodes_of_class(nodes.Call):
                try:
                    bound = next(call.func.infer())
                except (astroid.InferenceError, StopIteration):
                    continue
                if not isinstance(bound, astroid.BoundMethod):
                    continue
                func_obj = bound._proxied
                if isinstance(func_obj, astroid.UnboundMethod):
                    func_obj = func_obj._proxied
                if func_obj.name == func.name:
                    return True
    return False


def _is_attribute_property(name: str, klass: nodes.ClassDef) -> bool:
    """Check if the given attribute *name* is a property in the given *klass*.

    It will look for `property` calls or for functions
    with the given name, decorated by `property` or `property`
    subclasses.
    Returns ``True`` if the name is a property in the given klass,
    ``False`` otherwise.
    """

    try:
        attributes = klass.getattr(name)
    except astroid.NotFoundError:
        return False
    property_name = "builtins.property"
    for attr in attributes:
        if isinstance(attr, util.UninferableBase):
            continue
        try:
            inferred = next(attr.infer())
        except astroid.InferenceError:
            continue
        if isinstance(inferred, nodes.FunctionDef) and decorated_with_property(
            inferred
        ):
            return True
        if inferred.pytype() != property_name:
            continue

        cls = node_frame_class(inferred)
        if cls == klass.declared_metaclass():
            continue
        return True
    return False


def _has_same_layout_slots(
    slots: list[nodes.Const | None], assigned_value: nodes.Name
) -> bool:
    inferred = next(assigned_value.infer())
    if isinstance(inferred, nodes.ClassDef):
        other_slots = inferred.slots()
        if all(
            first_slot and second_slot and first_slot.value == second_slot.value
            for (first_slot, second_slot) in zip_longest(slots, other_slots)
        ):
            return True
    return False


MSGS: dict[str, MessageDefinitionTuple] = {
    "F0202": (
        "Unable to check methods signature (%s / %s)",
        "method-check-failed",
        "Used when Pylint has been unable to check methods signature "
        "compatibility for an unexpected reason. Please report this kind "
        "if you don't make sense of it.",
    ),
    "E0202": (
        "An attribute defined in %s line %s hides this method",
        "method-hidden",
        "Used when a class defines a method which is hidden by an "
        "instance attribute from an ancestor class or set by some "
        "client code.",
    ),
    "E0203": (
        "Access to member %r before its definition line %s",
        "access-member-before-definition",
        "Used when an instance member is accessed before it's actually assigned.",
    ),
    "W0201": (
        "Attribute %r defined outside __init__",
        "attribute-defined-outside-init",
        "Used when an instance attribute is defined outside the __init__ method.",
    ),
    "W0212": (
        "Access to a protected member %s of a client class",  # E0214
        "protected-access",
        "Used when a protected member (i.e. class member with a name "
        "beginning with an underscore) is access outside the class or a "
        "descendant of the class where it's defined.",
    ),
    "W0213": (
        "Flag member %(overlap)s shares bit positions with %(sources)s",
        "implicit-flag-alias",
        "Used when multiple integer values declared within an enum.IntFlag "
        "class share a common bit position.",
    ),
    "E0211": (
        "Method %r has no argument",
        "no-method-argument",
        "Used when a method which should have the bound instance as "
        "first argument has no argument defined.",
    ),
    "E0213": (
        'Method %r should have "self" as first argument',
        "no-self-argument",
        'Used when a method has an attribute different the "self" as '
        "first argument. This is considered as an error since this is "
        "a so common convention that you shouldn't break it!",
    ),
    "C0202": (
        "Class method %s should have %s as first argument",
        "bad-classmethod-argument",
        "Used when a class method has a first argument named differently "
        "than the value specified in valid-classmethod-first-arg option "
        '(default to "cls"), recommended to easily differentiate them '
        "from regular instance methods.",
    ),
    "C0203": (
        "Metaclass method %s should have %s as first argument",
        "bad-mcs-method-argument",
        "Used when a metaclass method has a first argument named "
        "differently than the value specified in valid-classmethod-first"
        '-arg option (default to "cls"), recommended to easily '
        "differentiate them from regular instance methods.",
    ),
    "C0204": (
        "Metaclass class method %s should have %s as first argument",
        "bad-mcs-classmethod-argument",
        "Used when a metaclass class method has a first argument named "
        "differently than the value specified in valid-metaclass-"
        'classmethod-first-arg option (default to "mcs"), recommended to '
        "easily differentiate them from regular instance methods.",
    ),
    "W0211": (
        "Static method with %r as first argument",
        "bad-staticmethod-argument",
        'Used when a static method has "self" or a value specified in '
        "valid-classmethod-first-arg option or "
        "valid-metaclass-classmethod-first-arg option as first argument.",
    ),
    "W0221": (
        "%s %s %r method",
        "arguments-differ",
        "Used when a method has a different number of arguments than in "
        "the implemented interface or in an overridden method. Extra arguments "
        "with default values are ignored.",
    ),
    "W0222": (
        "Signature differs from %s %r method",
        "signature-differs",
        "Used when a method signature is different than in the "
        "implemented interface or in an overridden method.",
    ),
    "W0223": (
        "Method %r is abstract in class %r but is not overridden in child class %r",
        "abstract-method",
        "Used when an abstract method (i.e. raise NotImplementedError) is "
        "not overridden in concrete class.",
    ),
    "W0231": (
        "__init__ method from base class %r is not called",
        "super-init-not-called",
        "Used when an ancestor class method has an __init__ method "
        "which is not called by a derived class.",
    ),
    "W0233": (
        "__init__ method from a non direct base class %r is called",
        "non-parent-init-called",
        "Used when an __init__ method is called on a class which is not "
        "in the direct ancestors for the analysed class.",
    ),
    "W0246": (
        "Useless parent or super() delegation in method %r",
        "useless-parent-delegation",
        "Used whenever we can detect that an overridden method is useless, "
        "relying on parent or super() delegation to do the same thing as another method "
        "from the MRO.",
        {"old_names": [("W0235", "useless-super-delegation")]},
    ),
    "W0236": (
        "Method %r was expected to be %r, found it instead as %r",
        "invalid-overridden-method",
        "Used when we detect that a method was overridden in a way "
        "that does not match its base class "
        "which could result in potential bugs at runtime.",
    ),
    "W0237": (
        "%s %s %r method",
        "arguments-renamed",
        "Used when a method parameter has a different name than in "
        "the implemented interface or in an overridden method.",
    ),
    "W0238": (
        "Unused private member `%s.%s`",
        "unused-private-member",
        "Emitted when a private member of a class is defined but not used.",
    ),
    "W0239": (
        "Method %r overrides a method decorated with typing.final which is defined in class %r",
        "overridden-final-method",
        "Used when a method decorated with typing.final has been overridden.",
    ),
    "W0240": (
        "Class %r is a subclass of a class decorated with typing.final: %r",
        "subclassed-final-class",
        "Used when a class decorated with typing.final has been subclassed.",
    ),
    "W0244": (
        "Redefined slots %r in subclass",
        "redefined-slots-in-subclass",
        "Used when a slot is re-defined in a subclass.",
    ),
    "W0245": (
        "Super call without brackets",
        "super-without-brackets",
        "Used when a call to super does not have brackets and thus is not an actual "
        "call and does not work as expected.",
    ),
    "E0236": (
        "Invalid object %r in __slots__, must contain only non empty strings",
        "invalid-slots-object",
        "Used when an invalid (non-string) object occurs in __slots__.",
    ),
    "E0237": (
        "Assigning to attribute %r not defined in class slots",
        "assigning-non-slot",
        "Used when assigning to an attribute not defined in the class slots.",
    ),
    "E0238": (
        "Invalid __slots__ object",
        "invalid-slots",
        "Used when an invalid __slots__ is found in class. "
        "Only a string, an iterable or a sequence is permitted.",
    ),
    "E0239": (
        "Inheriting %r, which is not a class.",
        "inherit-non-class",
        "Used when a class inherits from something which is not a class.",
    ),
    "E0240": (
        "Inconsistent method resolution order for class %r",
        "inconsistent-mro",
        "Used when a class has an inconsistent method resolution order.",
    ),
    "E0241": (
        "Duplicate bases for class %r",
        "duplicate-bases",
        "Duplicate use of base classes in derived classes raise TypeErrors.",
    ),
    "E0242": (
        "Value %r in slots conflicts with class variable",
        "class-variable-slots-conflict",
        "Used when a value in __slots__ conflicts with a class variable, property or method.",
    ),
    "E0243": (
        "Invalid assignment to '__class__'. Should be a class definition but got a '%s'",
        "invalid-class-object",
        "Used when an invalid object is assigned to a __class__ property. "
        "Only a class is permitted.",
    ),
    "E0244": (
        'Extending inherited Enum class "%s"',
        "invalid-enum-extension",
        "Used when a class tries to extend an inherited Enum class. "
        "Doing so will raise a TypeError at runtime.",
    ),
    "R0202": (
        "Consider using a decorator instead of calling classmethod",
        "no-classmethod-decorator",
        "Used when a class method is defined without using the decorator syntax.",
    ),
    "R0203": (
        "Consider using a decorator instead of calling staticmethod",
        "no-staticmethod-decorator",
        "Used when a static method is defined without using the decorator syntax.",
    ),
    "C0205": (
        "Class __slots__ should be a non-string iterable",
        "single-string-used-for-slots",
        "Used when a class __slots__ is a simple string, rather than an iterable.",
    ),
    "R0205": (
        "Class %r inherits from object, can be safely removed from bases in python3",
        "useless-object-inheritance",
        "Used when a class inherit from object, which under python3 is implicit, "
        "hence can be safely removed from bases.",
    ),
    "R0206": (
        "Cannot have defined parameters for properties",
        "property-with-parameters",
        "Used when we detect that a property also has parameters, which are useless, "
        "given that properties cannot be called with additional arguments.",
    ),
}


def _scope_default() -> defaultdict[str, list[_AccessNodes]]:
    # It's impossible to nest defaultdicts so we must use a function
    return defaultdict(list)


class ScopeAccessMap:
    """Store the accessed variables per scope."""

    def __init__(self) -> None:
        self._scopes: defaultdict[
            nodes.ClassDef, defaultdict[str, list[_AccessNodes]]
        ] = defaultdict(_scope_default)

    def set_accessed(self, node: _AccessNodes) -> None:
        """Set the given node as accessed."""

        frame = node_frame_class(node)
        if frame is None:
            # The node does not live in a class.
            return
        self._scopes[frame][node.attrname].append(node)

    def accessed(self, scope: nodes.ClassDef) -> dict[str, list[_AccessNodes]]:
        """Get the accessed variables for the given scope."""
        return self._scopes.get(scope, {})


class ClassChecker(BaseChecker):
    """Checker for class nodes.

    Checks for :
    * methods without self as first argument
    * overridden methods signature
    * access only to existent members via self
    * attributes not defined in the __init__ method
    * unreachable code
    """

    # configuration section name
    name = "classes"
    # messages
    msgs = MSGS
    # configuration options
    options = (
        (
            "defining-attr-methods",
            {
                "default": (
                    "__init__",
                    "__new__",
                    "setUp",
                    "asyncSetUp",
                    "__post_init__",
                ),
                "type": "csv",
                "metavar": "<method names>",
                "help": "List of method names used to declare (i.e. assign) \
instance attributes.",
            },
        ),
        (
            "valid-classmethod-first-arg",
            {
                "default": ("cls",),
                "type": "csv",
                "metavar": "<argument names>",
                "help": "List of valid names for the first argument in \
a class method.",
            },
        ),
        (
            "valid-metaclass-classmethod-first-arg",
            {
                "default": ("mcs",),
                "type": "csv",
                "metavar": "<argument names>",
                "help": "List of valid names for the first argument in \
a metaclass class method.",
            },
        ),
        (
            "exclude-protected",
            {
                "default": (
                    # namedtuple public API.
                    "_asdict",
                    "_fields",
                    "_replace",
                    "_source",
                    "_make",
                    "os._exit",
                ),
                "type": "csv",
                "metavar": "<protected access exclusions>",
                "help": (
                    "List of member names, which should be excluded "
                    "from the protected access warning."
                ),
            },
        ),
        (
            "check-protected-access-in-special-methods",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Warn about protected attribute access inside special methods",
            },
        ),
    )

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._accessed = ScopeAccessMap()
        self._first_attrs: list[str | None] = []

    def open(self) -> None:
        self._mixin_class_rgx = self.linter.config.mixin_class_rgx
        py_version = self.linter.config.py_version
        self._py38_plus = py_version >= (3, 8)

    @cached_property
    def _dummy_rgx(self) -> Pattern[str]:
        return self.linter.config.dummy_variables_rgx  # type: ignore[no-any-return]

    @only_required_for_messages(
        "abstract-method",
        "invalid-slots",
        "single-string-used-for-slots",
        "invalid-slots-object",
        "class-variable-slots-conflict",
        "inherit-non-class",
        "useless-object-inheritance",
        "inconsistent-mro",
        "duplicate-bases",
        "redefined-slots-in-subclass",
        "invalid-enum-extension",
        "subclassed-final-class",
        "implicit-flag-alias",
    )
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        """Init visit variable _accessed."""
        self._check_bases_classes(node)
        self._check_slots(node)
        self._check_proper_bases(node)
        self._check_typing_final(node)
        self._check_consistent_mro(node)

    def _check_consistent_mro(self, node: nodes.ClassDef) -> None:
        """Detect that a class has a consistent mro or duplicate bases."""
        try:
            node.mro()
        except astroid.InconsistentMroError:
            self.add_message("inconsistent-mro", args=node.name, node=node)
        except astroid.DuplicateBasesError:
            self.add_message("duplicate-bases", args=node.name, node=node)

    def _check_enum_base(self, node: nodes.ClassDef, ancestor: nodes.ClassDef) -> None:
        members = ancestor.getattr("__members__")
        if members and isinstance(members[0], nodes.Dict) and members[0].items:
            for _, name_node in members[0].items:
                # Exempt type annotations without value assignments
                if all(
                    isinstance(item.parent, nodes.AnnAssign)
                    and item.parent.value is None
                    for item in ancestor.getattr(name_node.name)
                ):
                    continue
                self.add_message(
                    "invalid-enum-extension",
                    args=ancestor.name,
                    node=node,
                    confidence=INFERENCE,
                )
                break

        if ancestor.is_subtype_of("enum.IntFlag"):
            # Collect integer flag assignments present on the class
            assignments = defaultdict(list)
            for assign_name in node.nodes_of_class(nodes.AssignName):
                if isinstance(assign_name.parent, nodes.Assign):
                    value = getattr(assign_name.parent.value, "value", None)
                    if isinstance(value, int):
                        assignments[value].append(assign_name)

            # For each bit position, collect all the flags that set the bit
            bit_flags = defaultdict(set)
            for flag in assignments:
                flag_bits = (i for i, c in enumerate(reversed(bin(flag))) if c == "1")
                for bit in flag_bits:
                    bit_flags[bit].add(flag)

            # Collect the minimum, unique values that each flag overlaps with
            overlaps = defaultdict(list)
            for flags in bit_flags.values():
                source, *conflicts = sorted(flags)
                for conflict in conflicts:
                    overlaps[conflict].append(source)

            # Report the overlapping values
            for overlap in overlaps:
                for assignment_node in assignments[overlap]:
                    self.add_message(
                        "implicit-flag-alias",
                        node=assignment_node,
                        args={
                            "overlap": f"<{node.name}.{assignment_node.name}: {overlap}>",
                            "sources": ", ".join(
                                f"<{node.name}.{assignments[source][0].name}: {source}> "
                                f"({overlap} & {source} = {overlap & source})"
                                for source in overlaps[overlap]
                            ),
                        },
                        confidence=INFERENCE,
                    )

    def _check_proper_bases(self, node: nodes.ClassDef) -> None:
        """Detect that a class inherits something which is not
        a class or a type.
        """
        for base in node.bases:
            ancestor = safe_infer(base)
            if not ancestor:
                continue
            if isinstance(ancestor, astroid.Instance) and ancestor.is_subtype_of(
                "builtins.type"
            ):
                continue

            if not isinstance(ancestor, nodes.ClassDef) or _is_invalid_base_class(
                ancestor
            ):
                self.add_message("inherit-non-class", args=base.as_string(), node=node)

            if isinstance(ancestor, nodes.ClassDef) and ancestor.is_subtype_of(
                "enum.Enum"
            ):
                self._check_enum_base(node, ancestor)

            if ancestor.name == object.__name__:
                self.add_message(
                    "useless-object-inheritance", args=node.name, node=node
                )

    def _check_typing_final(self, node: nodes.ClassDef) -> None:
        """Detect that a class does not subclass a class decorated with
        `typing.final`.
        """
        if not self._py38_plus:
            return
        for base in node.bases:
            ancestor = safe_infer(base)
            if not ancestor:
                continue

            if isinstance(ancestor, nodes.ClassDef) and (
                decorated_with(ancestor, ["typing.final"])
                or uninferable_final_decorators(ancestor.decorators)
            ):
                self.add_message(
                    "subclassed-final-class",
                    args=(node.name, ancestor.name),
                    node=node,
                )

    @only_required_for_messages(
        "unused-private-member",
        "attribute-defined-outside-init",
        "access-member-before-definition",
    )
    def leave_classdef(self, node: nodes.ClassDef) -> None:
        """Checker for Class nodes.

        check that instance attributes are defined in __init__ and check
        access to existent members
        """
        self._check_unused_private_functions(node)
        self._check_unused_private_variables(node)
        self._check_unused_private_attributes(node)
        self._check_attribute_defined_outside_init(node)

    def _check_unused_private_functions(self, node: nodes.ClassDef) -> None:
        for function_def in node.nodes_of_class(nodes.FunctionDef):
            if not is_attr_private(function_def.name):
                continue
            parent_scope = function_def.parent.scope()
            if isinstance(parent_scope, nodes.FunctionDef):
                # Handle nested functions
                if function_def.name in (
                    n.name for n in parent_scope.nodes_of_class(nodes.Name)
                ):
                    continue
            for child in node.nodes_of_class((nodes.Name, nodes.Attribute)):
                # Check for cases where the functions are used as a variable instead of as a
                # method call
                if isinstance(child, nodes.Name) and child.name == function_def.name:
                    break
                if isinstance(child, nodes.Attribute):
                    # Ignore recursive calls
                    if (
                        child.attrname != function_def.name
                        or child.scope() == function_def
                    ):
                        continue

                    # Check self.__attrname, cls.__attrname, node_name.__attrname
                    if isinstance(child.expr, nodes.Name) and child.expr.name in {
                        "self",
                        "cls",
                        node.name,
                    }:
                        break

                    # Check type(self).__attrname
                    if isinstance(child.expr, nodes.Call):
                        inferred = safe_infer(child.expr)
                        if (
                            isinstance(inferred, nodes.ClassDef)
                            and inferred.name == node.name
                        ):
                            break
            else:
                name_stack = []
                curr = parent_scope
                # Generate proper names for nested functions
                while curr != node:
                    name_stack.append(curr.name)
                    curr = curr.parent.scope()

                outer_level_names = f"{'.'.join(reversed(name_stack))}"
                function_repr = f"{outer_level_names}.{function_def.name}({function_def.args.as_string()})"
                self.add_message(
                    "unused-private-member",
                    node=function_def,
                    args=(node.name, function_repr.lstrip(".")),
                )

    def _check_unused_private_variables(self, node: nodes.ClassDef) -> None:
        """Check if private variables are never used within a class."""
        for assign_name in node.nodes_of_class(nodes.AssignName):
            if isinstance(assign_name.parent, nodes.Arguments):
                continue  # Ignore function arguments
            if not is_attr_private(assign_name.name):
                continue
            for child in node.nodes_of_class((nodes.Name, nodes.Attribute)):
                if isinstance(child, nodes.Name) and child.name == assign_name.name:
                    break
                if isinstance(child, nodes.Attribute):
                    if not isinstance(child.expr, nodes.Name):
                        break
                    if child.attrname == assign_name.name and child.expr.name in (
                        "self",
                        "cls",
                        node.name,
                    ):
                        break
            else:
                args = (node.name, assign_name.name)
                self.add_message("unused-private-member", node=assign_name, args=args)

    def _check_unused_private_attributes(self, node: nodes.ClassDef) -> None:
        for assign_attr in node.nodes_of_class(nodes.AssignAttr):
            if not is_attr_private(assign_attr.attrname) or not isinstance(
                assign_attr.expr, nodes.Name
            ):
                continue

            # Logic for checking false positive when using __new__,
            # Get the returned object names of the __new__ magic function
            # Then check if the attribute was consumed in other instance methods
            acceptable_obj_names: list[str] = ["self"]
            scope = assign_attr.scope()
            if isinstance(scope, nodes.FunctionDef) and scope.name == "__new__":
                acceptable_obj_names.extend(
                    [
                        return_node.value.name
                        for return_node in scope.nodes_of_class(nodes.Return)
                        if isinstance(return_node.value, nodes.Name)
                    ]
                )

            for attribute in node.nodes_of_class(nodes.Attribute):
                if attribute.attrname != assign_attr.attrname:
                    continue

                if not isinstance(attribute.expr, nodes.Name):
                    continue

                if assign_attr.expr.name in {
                    "cls",
                    node.name,
                } and attribute.expr.name in {"cls", "self", node.name}:
                    # If assigned to cls or class name, can be accessed by cls/self/class name
                    break

                if (
                    assign_attr.expr.name in acceptable_obj_names
                    and attribute.expr.name == "self"
                ):
                    # If assigned to self.attrib, can only be accessed by self
                    # Or if __new__ was used, the returned object names are acceptable
                    break

                if assign_attr.expr.name == attribute.expr.name == node.name:
                    # Recognise attributes which are accessed via the class name
                    break

            else:
                args = (node.name, assign_attr.attrname)
                self.add_message("unused-private-member", node=assign_attr, args=args)

    def _check_attribute_defined_outside_init(self, cnode: nodes.ClassDef) -> None:
        # check access to existent members on non metaclass classes
        if (
            "attribute-defined-outside-init"
            in self.linter.config.ignored_checks_for_mixins
            and self._mixin_class_rgx.match(cnode.name)
        ):
            # We are in a mixin class. No need to try to figure out if
            # something is missing, since it is most likely that it will
            # miss.
            return

        accessed = self._accessed.accessed(cnode)
        if cnode.type != "metaclass":
            self._check_accessed_members(cnode, accessed)
        # checks attributes are defined in an allowed method such as __init__
        if not self.linter.is_message_enabled("attribute-defined-outside-init"):
            return
        defining_methods = self.linter.config.defining_attr_methods
        current_module = cnode.root()
        for attr, nodes_lst in cnode.instance_attrs.items():
            # Exclude `__dict__` as it is already defined.
            if attr == "__dict__":
                continue

            # Skip nodes which are not in the current module and it may screw up
            # the output, while it's not worth it
            nodes_lst = [
                n
                for n in nodes_lst
                if not isinstance(n.statement(), (nodes.Delete, nodes.AugAssign))
                and n.root() is current_module
            ]
            if not nodes_lst:
                continue  # error detected by typechecking

            # Check if any method attr is defined in is a defining method
            # or if we have the attribute defined in a setter.
            frames = (node.frame() for node in nodes_lst)
            if any(
                frame.name in defining_methods or is_property_setter(frame)
                for frame in frames
            ):
                continue

            # check attribute is defined in a parent's __init__
            for parent in cnode.instance_attr_ancestors(attr):
                attr_defined = False
                # check if any parent method attr is defined in is a defining method
                for node in parent.instance_attrs[attr]:
                    if node.frame().name in defining_methods:
                        attr_defined = True
                if attr_defined:
                    # we're done :)
                    break
            else:
                # check attribute is defined as a class attribute
                try:
                    cnode.local_attr(attr)
                except astroid.NotFoundError:
                    for node in nodes_lst:
                        if node.frame().name not in defining_methods:
                            # If the attribute was set by a call in any
                            # of the defining methods, then don't emit
                            # the warning.
                            if _called_in_methods(
                                node.frame(), cnode, defining_methods
                            ):
                                continue
                            self.add_message(
                                "attribute-defined-outside-init", args=attr, node=node
                            )

    # pylint: disable = too-many-branches
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        """Check method arguments, overriding."""
        # ignore actual functions
        if not node.is_method():
            return

        self._check_useless_super_delegation(node)
        self._check_property_with_parameters(node)

        # 'is_method()' is called and makes sure that this is a 'nodes.ClassDef'
        klass: nodes.ClassDef = node.parent.frame()
        # check first argument is self if this is actually a method
        self._check_first_arg_for_type(node, klass.type == "metaclass")
        if node.name == "__init__":
            self._check_init(node, klass)
            return
        # check signature if the method overloads inherited method
        for overridden in klass.local_attr_ancestors(node.name):
            # get astroid for the searched method
            try:
                parent_function = overridden[node.name]
            except KeyError:
                # we have found the method but it's not in the local
                # dictionary.
                # This may happen with astroid build from living objects
                continue
            if not isinstance(parent_function, nodes.FunctionDef):
                continue
            self._check_signature(node, parent_function, klass)
            self._check_invalid_overridden_method(node, parent_function)
            break

        if node.decorators:
            for decorator in node.decorators.nodes:
                if isinstance(decorator, nodes.Attribute) and decorator.attrname in {
                    "getter",
                    "setter",
                    "deleter",
                }:
                    # attribute affectation will call this method, not hiding it
                    return
                if isinstance(decorator, nodes.Name):
                    if decorator.name in ALLOWED_PROPERTIES:
                        # attribute affectation will either call a setter or raise
                        # an attribute error, anyway not hiding the function
                        return

                if isinstance(decorator, nodes.Attribute):
                    if self._check_functools_or_not(decorator):
                        return

                # Infer the decorator and see if it returns something useful
                inferred = safe_infer(decorator)
                if not inferred:
                    return
                if isinstance(inferred, nodes.FunctionDef):
                    # Okay, it's a decorator, let's see what it can infer.
                    try:
                        inferred = next(inferred.infer_call_result(inferred))
                    except astroid.InferenceError:
                        return
                try:
                    if (
                        isinstance(inferred, (astroid.Instance, nodes.ClassDef))
                        and inferred.getattr("__get__")
                        and inferred.getattr("__set__")
                    ):
                        return
                except astroid.AttributeInferenceError:
                    pass

        # check if the method is hidden by an attribute
        # pylint: disable = too-many-try-statements
        try:
            overridden = klass.instance_attr(node.name)[0]
            overridden_frame = overridden.frame()
            if (
                isinstance(overridden_frame, nodes.FunctionDef)
                and overridden_frame.type == "method"
            ):
                overridden_frame = overridden_frame.parent.frame()
            if not (
                isinstance(overridden_frame, nodes.ClassDef)
                and klass.is_subtype_of(overridden_frame.qname())
            ):
                return

            # If a subclass defined the method then it's not our fault.
            for ancestor in klass.ancestors():
                if node.name in ancestor.instance_attrs and is_attr_private(node.name):
                    return
                for obj in ancestor.lookup(node.name)[1]:
                    if isinstance(obj, nodes.FunctionDef):
                        return
            args = (overridden.root().name, overridden.fromlineno)
            self.add_message("method-hidden", args=args, node=node)
        except astroid.NotFoundError:
            pass

    visit_asyncfunctiondef = visit_functiondef

    def _check_useless_super_delegation(self, function: nodes.FunctionDef) -> None:
        """Check if the given function node is an useless method override.

        We consider it *useless* if it uses the super() builtin, but having
        nothing additional whatsoever than not implementing the method at all.
        If the method uses super() to delegate an operation to the rest of the MRO,
        and if the method called is the same as the current one, the arguments
        passed to super() are the same as the parameters that were passed to
        this method, then the method could be removed altogether, by letting
        other implementation to take precedence.
        """
        if not _is_trivial_super_delegation(function):
            return

        call: nodes.Call = function.body[0].value

        # Classes that override __eq__ should also override
        # __hash__, even a trivial override is meaningful
        if function.name == "__hash__":
            for other_method in function.parent.mymethods():
                if other_method.name == "__eq__":
                    return

        # Check values of default args
        klass = function.parent.frame()
        meth_node = None
        for overridden in klass.local_attr_ancestors(function.name):
            # get astroid for the searched method
            try:
                meth_node = overridden[function.name]
            except KeyError:
                # we have found the method but it's not in the local
                # dictionary.
                # This may happen with astroid build from living objects
                continue
            if (
                not isinstance(meth_node, nodes.FunctionDef)
                # If the method have an ancestor which is not a
                # function then it is legitimate to redefine it
                or _has_different_parameters_default_value(
                    meth_node.args, function.args
                )
                # arguments to builtins such as Exception.__init__() cannot be inspected
                or (meth_node.args.args is None and function.argnames() != ["self"])
            ):
                return
            break

        # Detect if the parameters are the same as the call's arguments.
        params = _signature_from_arguments(function.args)
        args = _signature_from_call(call)

        if meth_node is not None:
            # Detect if the super method uses varargs and the function doesn't or makes some of those explicit
            if meth_node.args.vararg and (
                not function.args.vararg
                or len(function.args.args) > len(meth_node.args.args)
            ):
                return

            def form_annotations(arguments: nodes.Arguments) -> list[str]:
                annotations = chain(
                    (arguments.posonlyargs_annotations or []), arguments.annotations
                )
                return [ann.as_string() for ann in annotations if ann is not None]

            called_annotations = form_annotations(function.args)
            overridden_annotations = form_annotations(meth_node.args)
            if called_annotations and overridden_annotations:
                if called_annotations != overridden_annotations:
                    return

            if (
                function.returns is not None
                and meth_node.returns is not None
                and meth_node.returns.as_string() != function.returns.as_string()
            ):
                # Override adds typing information to the return type
                return

        if _definition_equivalent_to_call(params, args):
            self.add_message(
                "useless-parent-delegation",
                node=function,
                args=(function.name,),
                confidence=INFERENCE,
            )

    def _check_property_with_parameters(self, node: nodes.FunctionDef) -> None:
        if (
            node.args.args
            and len(node.args.args) > 1
            and decorated_with_property(node)
            and not is_property_setter(node)
        ):
            self.add_message("property-with-parameters", node=node)

    def _check_invalid_overridden_method(
        self,
        function_node: nodes.FunctionDef,
        parent_function_node: nodes.FunctionDef,
    ) -> None:
        parent_is_property = decorated_with_property(
            parent_function_node
        ) or is_property_setter_or_deleter(parent_function_node)
        current_is_property = decorated_with_property(
            function_node
        ) or is_property_setter_or_deleter(function_node)
        if parent_is_property and not current_is_property:
            self.add_message(
                "invalid-overridden-method",
                args=(function_node.name, "property", function_node.type),
                node=function_node,
            )
        elif not parent_is_property and current_is_property:
            self.add_message(
                "invalid-overridden-method",
                args=(function_node.name, "method", "property"),
                node=function_node,
            )

        parent_is_async = isinstance(parent_function_node, nodes.AsyncFunctionDef)
        current_is_async = isinstance(function_node, nodes.AsyncFunctionDef)

        if parent_is_async and not current_is_async:
            self.add_message(
                "invalid-overridden-method",
                args=(function_node.name, "async", "non-async"),
                node=function_node,
            )

        elif not parent_is_async and current_is_async:
            self.add_message(
                "invalid-overridden-method",
                args=(function_node.name, "non-async", "async"),
                node=function_node,
            )
        if (
            decorated_with(parent_function_node, ["typing.final"])
            or uninferable_final_decorators(parent_function_node.decorators)
        ) and self._py38_plus:
            self.add_message(
                "overridden-final-method",
                args=(function_node.name, parent_function_node.parent.frame().name),
                node=function_node,
            )

    def _check_functools_or_not(self, decorator: nodes.Attribute) -> bool:
        if decorator.attrname != "cached_property":
            return False

        if not isinstance(decorator.expr, nodes.Name):
            return False

        _, import_nodes = decorator.expr.lookup(decorator.expr.name)

        if not import_nodes:
            return False
        import_node = import_nodes[0]

        if not isinstance(import_node, (astroid.Import, astroid.ImportFrom)):
            return False

        return "functools" in dict(import_node.names)

    def _check_slots(self, node: nodes.ClassDef) -> None:
        if "__slots__" not in node.locals:
            return

        for slots in node.ilookup("__slots__"):
            # check if __slots__ is a valid type
            if isinstance(slots, util.UninferableBase):
                continue
            if not is_iterable(slots) and not is_comprehension(slots):
                self.add_message("invalid-slots", node=node)
                continue

            if isinstance(slots, nodes.Const):
                # a string, ignore the following checks
                self.add_message("single-string-used-for-slots", node=node)
                continue
            if not hasattr(slots, "itered"):
                # we can't obtain the values, maybe a .deque?
                continue

            if isinstance(slots, nodes.Dict):
                values = [item[0] for item in slots.items]
            else:
                values = slots.itered()
            if isinstance(values, util.UninferableBase):
                continue
            for elt in values:
                try:
                    self._check_slots_elt(elt, node)
                except astroid.InferenceError:
                    continue
            self._check_redefined_slots(node, slots, values)

    def _check_redefined_slots(
        self,
        node: nodes.ClassDef,
        slots_node: nodes.NodeNG,
        slots_list: list[nodes.NodeNG],
    ) -> None:
        """Check if `node` redefines a slot which is defined in an ancestor class."""
        slots_names: list[str] = []
        for slot in slots_list:
            if isinstance(slot, nodes.Const):
                slots_names.append(slot.value)
            else:
                inferred_slot = safe_infer(slot)
                inferred_slot_value = getattr(inferred_slot, "value", None)
                if isinstance(inferred_slot_value, str):
                    slots_names.append(inferred_slot_value)

        # Slots of all parent classes
        ancestors_slots_names = {
            slot.value
            for ancestor in node.local_attr_ancestors("__slots__")
            for slot in ancestor.slots() or []
        }

        # Slots which are common to `node` and its parent classes
        redefined_slots = ancestors_slots_names.intersection(slots_names)

        if redefined_slots:
            self.add_message(
                "redefined-slots-in-subclass",
                args=([name for name in slots_names if name in redefined_slots],),
                node=slots_node,
            )

    def _check_slots_elt(
        self, elt: SuccessfulInferenceResult, node: nodes.ClassDef
    ) -> None:
        for inferred in elt.infer():
            if isinstance(inferred, util.UninferableBase):
                continue
            if not isinstance(inferred, nodes.Const) or not isinstance(
                inferred.value, str
            ):
                self.add_message(
                    "invalid-slots-object",
                    args=elt.as_string(),
                    node=elt,
                    confidence=INFERENCE,
                )
                continue
            if not inferred.value:
                self.add_message(
                    "invalid-slots-object",
                    args=elt.as_string(),
                    node=elt,
                    confidence=INFERENCE,
                )

            # Check if we have a conflict with a class variable.
            class_variable = node.locals.get(inferred.value)
            if class_variable:
                # Skip annotated assignments which don't conflict at all with slots.
                if len(class_variable) == 1:
                    parent = class_variable[0].parent
                    if isinstance(parent, nodes.AnnAssign) and parent.value is None:
                        return
                self.add_message(
                    "class-variable-slots-conflict", args=(inferred.value,), node=elt
                )

    def leave_functiondef(self, node: nodes.FunctionDef) -> None:
        """On method node, check if this method couldn't be a function.

        ignore class, static and abstract methods, initializer,
        methods overridden from a parent class.
        """
        if node.is_method():
            if node.args.args is not None:
                self._first_attrs.pop()

    leave_asyncfunctiondef = leave_functiondef

    def visit_attribute(self, node: nodes.Attribute) -> None:
        """Check if the getattr is an access to a class member
        if so, register it.

        Also check for access to protected
        class member from outside its class (but ignore __special__
        methods)
        """
        self._check_super_without_brackets(node)

        # Check self
        if self._uses_mandatory_method_param(node):
            self._accessed.set_accessed(node)
            return
        if not self.linter.is_message_enabled("protected-access"):
            return

        self._check_protected_attribute_access(node)

    def _check_super_without_brackets(self, node: nodes.Attribute) -> None:
        """Check if there is a function call on a super call without brackets."""
        # Check if attribute call is in frame definition in class definition
        frame = node.frame()
        if not isinstance(frame, nodes.FunctionDef):
            return
        if not isinstance(frame.parent.frame(), nodes.ClassDef):
            return
        if not isinstance(node.parent, nodes.Call):
            return
        if not isinstance(node.expr, nodes.Name):
            return
        if node.expr.name == "super":
            self.add_message("super-without-brackets", node=node.expr, confidence=HIGH)

    @only_required_for_messages(
        "assigning-non-slot", "invalid-class-object", "access-member-before-definition"
    )
    def visit_assignattr(self, node: nodes.AssignAttr) -> None:
        if isinstance(
            node.assign_type(), nodes.AugAssign
        ) and self._uses_mandatory_method_param(node):
            self._accessed.set_accessed(node)
        self._check_in_slots(node)
        self._check_invalid_class_object(node)

    def _check_invalid_class_object(self, node: nodes.AssignAttr) -> None:
        if not node.attrname == "__class__":
            return
        if isinstance(node.parent, nodes.Tuple):
            class_index = -1
            for i, elt in enumerate(node.parent.elts):
                if hasattr(elt, "attrname") and elt.attrname == "__class__":
                    class_index = i
            if class_index == -1:
                # This should not happen because we checked that the node name
                # is '__class__' earlier, but let's not be too confident here
                return  # pragma: no cover
            inferred = safe_infer(node.parent.parent.value.elts[class_index])
        else:
            inferred = safe_infer(node.parent.value)
        if (
            isinstance(inferred, (nodes.ClassDef, util.UninferableBase))
            or inferred is None
        ):
            # If is uninferable, we allow it to prevent false positives
            return
        self.add_message(
            "invalid-class-object",
            node=node,
            args=inferred.__class__.__name__,
            confidence=INFERENCE,
        )

    def _check_in_slots(self, node: nodes.AssignAttr) -> None:
        """Check that the given AssignAttr node
        is defined in the class slots.
        """
        inferred = safe_infer(node.expr)
        if not isinstance(inferred, astroid.Instance):
            return

        klass = inferred._proxied
        if not has_known_bases(klass):
            return
        if "__slots__" not in klass.locals or not klass.newstyle:
            return
        # If `__setattr__` is defined on the class, then we can't reason about
        # what will happen when assigning to an attribute.
        if any(
            base.locals.get("__setattr__")
            for base in klass.mro()
            if base.qname() != "builtins.object"
        ):
            return

        # If 'typing.Generic' is a base of bases of klass, the cached version
        # of 'slots()' might have been evaluated incorrectly, thus deleted cache entry.
        if any(base.qname() == "typing.Generic" for base in klass.mro()):
            cache = getattr(klass, "__cache", None)
            if cache and cache.get(klass.slots) is not None:
                del cache[klass.slots]

        slots = klass.slots()
        if slots is None:
            return
        # If any ancestor doesn't use slots, the slots
        # defined for this class are superfluous.
        if any(
            "__slots__" not in ancestor.locals
            and ancestor.name not in ("Generic", "object")
            for ancestor in klass.ancestors()
        ):
            return

        if not any(slot.value == node.attrname for slot in slots):
            # If we have a '__dict__' in slots, then
            # assigning any name is valid.
            if not any(slot.value == "__dict__" for slot in slots):
                if _is_attribute_property(node.attrname, klass):
                    # Properties circumvent the slots mechanism,
                    # so we should not emit a warning for them.
                    return
                if node.attrname != "__class__" and utils.is_class_attr(
                    node.attrname, klass
                ):
                    return
                if node.attrname in klass.locals:
                    for local_name in klass.locals.get(node.attrname):
                        statement = local_name.statement()
                        if (
                            isinstance(statement, nodes.AnnAssign)
                            and not statement.value
                        ):
                            return
                    if _has_data_descriptor(klass, node.attrname):
                        # Descriptors circumvent the slots mechanism as well.
                        return
                if node.attrname == "__class__" and _has_same_layout_slots(
                    slots, node.parent.value
                ):
                    return
                self.add_message(
                    "assigning-non-slot",
                    args=(node.attrname,),
                    node=node,
                    confidence=INFERENCE,
                )

    @only_required_for_messages(
        "protected-access", "no-classmethod-decorator", "no-staticmethod-decorator"
    )
    def visit_assign(self, assign_node: nodes.Assign) -> None:
        self._check_classmethod_declaration(assign_node)
        node = assign_node.targets[0]
        if not isinstance(node, nodes.AssignAttr):
            return

        if self._uses_mandatory_method_param(node):
            return
        self._check_protected_attribute_access(node)

    def _check_classmethod_declaration(self, node: nodes.Assign) -> None:
        """Checks for uses of classmethod() or staticmethod().

        When a @classmethod or @staticmethod decorator should be used instead.
        A message will be emitted only if the assignment is at a class scope
        and only if the classmethod's argument belongs to the class where it
        is defined.
        `node` is an assign node.
        """
        if not isinstance(node.value, nodes.Call):
            return

        # check the function called is "classmethod" or "staticmethod"
        func = node.value.func
        if not isinstance(func, nodes.Name) or func.name not in (
            "classmethod",
            "staticmethod",
        ):
            return

        msg = (
            "no-classmethod-decorator"
            if func.name == "classmethod"
            else "no-staticmethod-decorator"
        )
        # assignment must be at a class scope
        parent_class = node.scope()
        if not isinstance(parent_class, nodes.ClassDef):
            return

        # Check if the arg passed to classmethod is a class member
        classmeth_arg = node.value.args[0]
        if not isinstance(classmeth_arg, nodes.Name):
            return

        method_name = classmeth_arg.name
        if any(method_name == member.name for member in parent_class.mymethods()):
            self.add_message(msg, node=node.targets[0])

    def _check_protected_attribute_access(
        self, node: nodes.Attribute | nodes.AssignAttr
    ) -> None:
        """Given an attribute access node (set or get), check if attribute
        access is legitimate.

        Call _check_first_attr with node before calling
        this method. Valid cases are:
        * self._attr in a method or cls._attr in a classmethod. Checked by
        _check_first_attr.
        * Klass._attr inside "Klass" class.
        * Klass2._attr inside "Klass" class when Klass2 is a base class of
            Klass.
        """
        attrname = node.attrname

        if (
            not is_attr_protected(attrname)
            or attrname in self.linter.config.exclude_protected
        ):
            return

        # Typing annotations in function definitions can include protected members
        if utils.is_node_in_type_annotation_context(node):
            return

        # Return if `attrname` is defined at the module-level or as a class attribute
        # and is listed in `exclude-protected`.
        inferred = safe_infer(node.expr)
        if (
            inferred
            and isinstance(inferred, (nodes.ClassDef, nodes.Module))
            and f"{inferred.name}.{attrname}" in self.linter.config.exclude_protected
        ):
            return

        klass = node_frame_class(node)
        if klass is None:
            # We are not in a class, no remaining valid case
            self.add_message("protected-access", node=node, args=attrname)
            return

        # In classes, check we are not getting a parent method
        # through the class object or through super

        # If the expression begins with a call to super, that's ok.
        if (
            isinstance(node.expr, nodes.Call)
            and isinstance(node.expr.func, nodes.Name)
            and node.expr.func.name == "super"
        ):
            return

        # If the expression begins with a call to type(self), that's ok.
        if self._is_type_self_call(node.expr):
            return

        # Check if we are inside the scope of a class or nested inner class
        inside_klass = True
        outer_klass = klass
        callee = node.expr.as_string()
        parents_callee = callee.split(".")
        parents_callee.reverse()
        for callee in parents_callee:
            if not outer_klass or callee != outer_klass.name:
                inside_klass = False
                break

            # Move up one level within the nested classes
            outer_klass = get_outer_class(outer_klass)

        # We are in a class, one remaining valid cases, Klass._attr inside
        # Klass
        if not (inside_klass or callee in klass.basenames):
            # Detect property assignments in the body of the class.
            # This is acceptable:
            #
            # class A:
            #     b = property(lambda: self._b)

            stmt = node.parent.statement()
            if (
                isinstance(stmt, nodes.Assign)
                and len(stmt.targets) == 1
                and isinstance(stmt.targets[0], nodes.AssignName)
            ):
                name = stmt.targets[0].name
                if _is_attribute_property(name, klass):
                    return

            if (
                self._is_classmethod(node.frame())
                and self._is_inferred_instance(node.expr, klass)
                and self._is_class_or_instance_attribute(attrname, klass)
            ):
                return

            licit_protected_member = not attrname.startswith("__")
            if (
                not self.linter.config.check_protected_access_in_special_methods
                and licit_protected_member
                and self._is_called_inside_special_method(node)
            ):
                return

            self.add_message("protected-access", node=node, args=attrname)

    @staticmethod
    def _is_called_inside_special_method(node: nodes.NodeNG) -> bool:
        """Returns true if the node is located inside a special (aka dunder) method."""
        frame_name = node.frame().name
        return frame_name and frame_name in PYMETHODS

    def _is_type_self_call(self, expr: nodes.NodeNG) -> bool:
        return (
            isinstance(expr, nodes.Call)
            and isinstance(expr.func, nodes.Name)
            and expr.func.name == "type"
            and len(expr.args) == 1
            and self._is_mandatory_method_param(expr.args[0])
        )

    @staticmethod
    def _is_classmethod(func: LocalsDictNodeNG) -> bool:
        """Check if the given *func* node is a class method."""
        return isinstance(func, nodes.FunctionDef) and (
            func.type == "classmethod" or func.name == "__class_getitem__"
        )

    @staticmethod
    def _is_inferred_instance(expr: nodes.NodeNG, klass: nodes.ClassDef) -> bool:
        """Check if the inferred value of the given *expr* is an instance of
        *klass*.
        """
        inferred = safe_infer(expr)
        if not isinstance(inferred, astroid.Instance):
            return False
        return inferred._proxied is klass

    @staticmethod
    def _is_class_or_instance_attribute(name: str, klass: nodes.ClassDef) -> bool:
        """Check if the given attribute *name* is a class or instance member of the
        given *klass*.

        Returns ``True`` if the name is a property in the given klass,
        ``False`` otherwise.
        """

        if utils.is_class_attr(name, klass):
            return True

        try:
            klass.instance_attr(name)
            return True
        except astroid.NotFoundError:
            return False

    def _check_accessed_members(
        self, node: nodes.ClassDef, accessed: dict[str, list[_AccessNodes]]
    ) -> None:
        """Check that accessed members are defined."""
        excs = ("AttributeError", "Exception", "BaseException")
        for attr, nodes_lst in accessed.items():
            try:
                # is it a class attribute ?
                node.local_attr(attr)
                # yes, stop here
                continue
            except astroid.NotFoundError:
                pass
            # is it an instance attribute of a parent class ?
            try:
                next(node.instance_attr_ancestors(attr))
                # yes, stop here
                continue
            except StopIteration:
                pass
            # is it an instance attribute ?
            try:
                defstmts = node.instance_attr(attr)
            except astroid.NotFoundError:
                pass
            else:
                # filter out augment assignment nodes
                defstmts = [stmt for stmt in defstmts if stmt not in nodes_lst]
                if not defstmts:
                    # only augment assignment for this node, no-member should be
                    # triggered by the typecheck checker
                    continue
                # filter defstmts to only pick the first one when there are
                # several assignments in the same scope
                scope = defstmts[0].scope()
                defstmts = [
                    stmt
                    for i, stmt in enumerate(defstmts)
                    if i == 0 or stmt.scope() is not scope
                ]
                # if there are still more than one, don't attempt to be smarter
                # than we can be
                if len(defstmts) == 1:
                    defstmt = defstmts[0]
                    # check that if the node is accessed in the same method as
                    # it's defined, it's accessed after the initial assignment
                    frame = defstmt.frame()
                    lno = defstmt.fromlineno
                    for _node in nodes_lst:
                        if (
                            _node.frame() is frame
                            and _node.fromlineno < lno
                            and not astroid.are_exclusive(
                                _node.statement(), defstmt, excs
                            )
                        ):
                            self.add_message(
                                "access-member-before-definition",
                                node=_node,
                                args=(attr, lno),
                            )

    def _check_first_arg_for_type(
        self, node: nodes.FunctionDef, metaclass: bool
    ) -> None:
        """Check the name of first argument, expect:.

        * 'self' for a regular method
        * 'cls' for a class method or a metaclass regular method (actually
          valid-classmethod-first-arg value)
        * 'mcs' for a metaclass class method (actually
          valid-metaclass-classmethod-first-arg)
        * not one of the above for a static method
        """
        # don't care about functions with unknown argument (builtins)
        if node.args.args is None:
            return
        if node.args.posonlyargs:
            first_arg = node.args.posonlyargs[0].name
        elif node.args.args:
            first_arg = node.argnames()[0]
        else:
            first_arg = None
        self._first_attrs.append(first_arg)
        first = self._first_attrs[-1]
        # static method
        if node.type == "staticmethod":
            if (
                first_arg == "self"
                or first_arg in self.linter.config.valid_classmethod_first_arg
                or first_arg in self.linter.config.valid_metaclass_classmethod_first_arg
            ):
                self.add_message("bad-staticmethod-argument", args=first, node=node)
                return
            self._first_attrs[-1] = None
        elif "builtins.staticmethod" in node.decoratornames():
            # Check if there is a decorator which is not named `staticmethod`
            # but is assigned to one.
            return
        # class / regular method with no args
        elif not (
            node.args.args
            or node.args.posonlyargs
            or node.args.vararg
            or node.args.kwarg
        ):
            self.add_message("no-method-argument", node=node, args=node.name)
        # metaclass
        elif metaclass:
            # metaclass __new__ or classmethod
            if node.type == "classmethod":
                self._check_first_arg_config(
                    first,
                    self.linter.config.valid_metaclass_classmethod_first_arg,
                    node,
                    "bad-mcs-classmethod-argument",
                    node.name,
                )
            # metaclass regular method
            else:
                self._check_first_arg_config(
                    first,
                    self.linter.config.valid_classmethod_first_arg,
                    node,
                    "bad-mcs-method-argument",
                    node.name,
                )
        # regular class with class method
        elif node.type == "classmethod" or node.name == "__class_getitem__":
            self._check_first_arg_config(
                first,
                self.linter.config.valid_classmethod_first_arg,
                node,
                "bad-classmethod-argument",
                node.name,
            )
        # regular class with regular method without self as argument
        elif first != "self":
            self.add_message("no-self-argument", node=node, args=node.name)

    def _check_first_arg_config(
        self,
        first: str | None,
        config: Sequence[str],
        node: nodes.FunctionDef,
        message: str,
        method_name: str,
    ) -> None:
        if first not in config:
            if len(config) == 1:
                valid = repr(config[0])
            else:
                valid = ", ".join(repr(v) for v in config[:-1])
                valid = f"{valid} or {config[-1]!r}"
            self.add_message(message, args=(method_name, valid), node=node)

    def _check_bases_classes(self, node: nodes.ClassDef) -> None:
        """Check that the given class node implements abstract methods from
        base classes.
        """

        def is_abstract(method: nodes.FunctionDef) -> bool:
            return method.is_abstract(pass_is_abstract=False)  # type: ignore[no-any-return]

        # check if this class abstract
        if class_is_abstract(node):
            return

        methods = sorted(
            unimplemented_abstract_methods(node, is_abstract).items(),
            key=lambda item: item[0],
        )
        for name, method in methods:
            owner = method.parent.frame()
            if owner is node:
                continue
            # owner is not this class, it must be a parent class
            # check that the ancestor's method is not abstract
            if name in node.locals:
                # it is redefined as an attribute or with a descriptor
                continue

            self.add_message(
                "abstract-method",
                node=node,
                args=(name, owner.name, node.name),
                confidence=INFERENCE,
            )

    def _check_init(self, node: nodes.FunctionDef, klass_node: nodes.ClassDef) -> None:
        """Check that the __init__ method call super or ancestors'__init__
        method (unless it is used for type hinting with `typing.overload`).
        """
        if not self.linter.is_message_enabled(
            "super-init-not-called"
        ) and not self.linter.is_message_enabled("non-parent-init-called"):
            return
        to_call = _ancestors_to_call(klass_node)
        not_called_yet = dict(to_call)
        parents_with_called_inits: set[bases.UnboundMethod] = set()
        for stmt in node.nodes_of_class(nodes.Call):
            expr = stmt.func
            if not isinstance(expr, nodes.Attribute) or expr.attrname != "__init__":
                continue
            # skip the test if using super
            if (
                isinstance(expr.expr, nodes.Call)
                and isinstance(expr.expr.func, nodes.Name)
                and expr.expr.func.name == "super"
            ):
                return
            # pylint: disable = too-many-try-statements
            try:
                for klass in expr.expr.infer():
                    if isinstance(klass, util.UninferableBase):
                        continue
                    # The inferred klass can be super(), which was
                    # assigned to a variable and the `__init__`
                    # was called later.
                    #
                    # base = super()
                    # base.__init__(...)

                    if (
                        isinstance(klass, astroid.Instance)
                        and isinstance(klass._proxied, nodes.ClassDef)
                        and is_builtin_object(klass._proxied)
                        and klass._proxied.name == "super"
                    ):
                        return
                    if isinstance(klass, astroid.objects.Super):
                        return
                    try:
                        method = not_called_yet.pop(klass)
                        # Record that the class' init has been called
                        parents_with_called_inits.add(node_frame_class(method))
                    except KeyError:
                        if klass not in klass_node.ancestors(recurs=False):
                            self.add_message(
                                "non-parent-init-called", node=expr, args=klass.name
                            )
            except astroid.InferenceError:
                continue
        for klass, method in not_called_yet.items():
            # Check if the init of the class that defines this init has already
            # been called.
            if node_frame_class(method) in parents_with_called_inits:
                return

            if utils.is_protocol_class(klass):
                return

            if decorated_with(node, ["typing.overload"]):
                continue
            self.add_message(
                "super-init-not-called",
                args=klass.name,
                node=node,
                confidence=INFERENCE,
            )

    def _check_signature(
        self,
        method1: nodes.FunctionDef,
        refmethod: nodes.FunctionDef,
        cls: nodes.ClassDef,
    ) -> None:
        """Check that the signature of the two given methods match."""
        if not (
            isinstance(method1, nodes.FunctionDef)
            and isinstance(refmethod, nodes.FunctionDef)
        ):
            self.add_message(
                "method-check-failed", args=(method1, refmethod), node=method1
            )
            return

        instance = cls.instantiate_class()
        method1 = astroid.scoped_nodes.function_to_method(method1, instance)
        refmethod = astroid.scoped_nodes.function_to_method(refmethod, instance)

        # Don't care about functions with unknown argument (builtins).
        if method1.args.args is None or refmethod.args.args is None:
            return

        # Ignore private to class methods.
        if is_attr_private(method1.name):
            return
        # Ignore setters, they have an implicit extra argument,
        # which shouldn't be taken in consideration.
        if is_property_setter(method1):
            return

        arg_differ_output = _different_parameters(
            refmethod, method1, dummy_parameter_regex=self._dummy_rgx
        )

        class_type = "overriding"

        if len(arg_differ_output) > 0:
            for msg in arg_differ_output:
                if "Number" in msg:
                    total_args_method1 = len(method1.args.args)
                    if method1.args.vararg:
                        total_args_method1 += 1
                    if method1.args.kwarg:
                        total_args_method1 += 1
                    if method1.args.kwonlyargs:
                        total_args_method1 += len(method1.args.kwonlyargs)
                    total_args_refmethod = len(refmethod.args.args)
                    if refmethod.args.vararg:
                        total_args_refmethod += 1
                    if refmethod.args.kwarg:
                        total_args_refmethod += 1
                    if refmethod.args.kwonlyargs:
                        total_args_refmethod += len(refmethod.args.kwonlyargs)
                    error_type = "arguments-differ"
                    msg_args = (
                        msg
                        + f"was {total_args_refmethod} in '{refmethod.parent.frame().name}.{refmethod.name}' and "
                        f"is now {total_args_method1} in",
                        class_type,
                        f"{method1.parent.frame().name}.{method1.name}",
                    )
                elif "renamed" in msg:
                    error_type = "arguments-renamed"
                    msg_args = (
                        msg,
                        class_type,
                        f"{method1.parent.frame().name}.{method1.name}",
                    )
                else:
                    error_type = "arguments-differ"
                    msg_args = (
                        msg,
                        class_type,
                        f"{method1.parent.frame().name}.{method1.name}",
                    )
                self.add_message(error_type, args=msg_args, node=method1)
        elif (
            len(method1.args.defaults) < len(refmethod.args.defaults)
            and not method1.args.vararg
        ):
            class_type = "overridden"
            self.add_message(
                "signature-differs", args=(class_type, method1.name), node=method1
            )

    def _uses_mandatory_method_param(
        self, node: nodes.Attribute | nodes.Assign | nodes.AssignAttr
    ) -> bool:
        """Check that attribute lookup name use first attribute variable name.

        Name is `self` for method, `cls` for classmethod and `mcs` for metaclass.
        """
        return self._is_mandatory_method_param(node.expr)

    def _is_mandatory_method_param(self, node: nodes.NodeNG) -> bool:
        """Check if nodes.Name corresponds to first attribute variable name.

        Name is `self` for method, `cls` for classmethod and `mcs` for metaclass.
        Static methods return False.
        """
        if self._first_attrs:
            first_attr = self._first_attrs[-1]
        else:
            # It's possible the function was already unregistered.
            closest_func = utils.get_node_first_ancestor_of_type(
                node, nodes.FunctionDef
            )
            if closest_func is None:
                return False
            if not closest_func.is_bound():
                return False
            if not closest_func.args.args:
                return False
            first_attr = closest_func.args.args[0].name
        return isinstance(node, nodes.Name) and node.name == first_attr


def _ancestors_to_call(
    klass_node: nodes.ClassDef, method_name: str = "__init__"
) -> dict[nodes.ClassDef, bases.UnboundMethod]:
    """Return a dictionary where keys are the list of base classes providing
    the queried method, and so that should/may be called from the method node.
    """
    to_call: dict[nodes.ClassDef, bases.UnboundMethod] = {}
    for base_node in klass_node.ancestors(recurs=False):
        try:
            init_node = next(base_node.igetattr(method_name))
            if not isinstance(init_node, astroid.UnboundMethod):
                continue
            if init_node.is_abstract():
                continue
            to_call[base_node] = init_node
        except astroid.InferenceError:
            continue
    return to_call

back/venvmsg/Lib/site-packages/pylint/checkers/classes/class_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Special methods checker and helper function's module."""

from __future__ import annotations

from collections.abc import Callable

import astroid
from astroid import bases, nodes, util
from astroid.context import InferenceContext
from astroid.typing import InferenceResult

from pylint.checkers import BaseChecker
from pylint.checkers.utils import (
    PYMETHODS,
    SPECIAL_METHODS_PARAMS,
    decorated_with,
    is_function_body_ellipsis,
    only_required_for_messages,
    safe_infer,
)
from pylint.lint.pylinter import PyLinter

NEXT_METHOD = "__next__"


def _safe_infer_call_result(
    node: nodes.FunctionDef,
    caller: nodes.FunctionDef,
    context: InferenceContext | None = None,
) -> InferenceResult | None:
    """Safely infer the return value of a function.

    Returns None if inference failed or if there is some ambiguity (more than
    one node has been inferred). Otherwise, returns inferred value.
    """
    try:
        inferit = node.infer_call_result(caller, context=context)
        value = next(inferit)
    except astroid.InferenceError:
        return None  # inference failed
    except StopIteration:
        return None  # no values inferred
    try:
        next(inferit)
        return None  # there is ambiguity on the inferred node
    except astroid.InferenceError:
        return None  # there is some kind of ambiguity
    except StopIteration:
        return value


class SpecialMethodsChecker(BaseChecker):
    """Checker which verifies that special methods
    are implemented correctly.
    """

    name = "classes"
    msgs = {
        "E0301": (
            "__iter__ returns non-iterator",
            "non-iterator-returned",
            "Used when an __iter__ method returns something which is not an "
            f"iterable (i.e. has no `{NEXT_METHOD}` method)",
            {
                "old_names": [
                    ("W0234", "old-non-iterator-returned-1"),
                    ("E0234", "old-non-iterator-returned-2"),
                ]
            },
        ),
        "E0302": (
            "The special method %r expects %s param(s), %d %s given",
            "unexpected-special-method-signature",
            "Emitted when a special method was defined with an "
            "invalid number of parameters. If it has too few or "
            "too many, it might not work at all.",
            {"old_names": [("E0235", "bad-context-manager")]},
        ),
        "E0303": (
            "__len__ does not return non-negative integer",
            "invalid-length-returned",
            "Used when a __len__ method returns something which is not a "
            "non-negative integer",
        ),
        "E0304": (
            "__bool__ does not return bool",
            "invalid-bool-returned",
            "Used when a __bool__ method returns something which is not a bool",
        ),
        "E0305": (
            "__index__ does not return int",
            "invalid-index-returned",
            "Used when an __index__ method returns something which is not "
            "an integer",
        ),
        "E0306": (
            "__repr__ does not return str",
            "invalid-repr-returned",
            "Used when a __repr__ method returns something which is not a string",
        ),
        "E0307": (
            "__str__ does not return str",
            "invalid-str-returned",
            "Used when a __str__ method returns something which is not a string",
        ),
        "E0308": (
            "__bytes__ does not return bytes",
            "invalid-bytes-returned",
            "Used when a __bytes__ method returns something which is not bytes",
        ),
        "E0309": (
            "__hash__ does not return int",
            "invalid-hash-returned",
            "Used when a __hash__ method returns something which is not an integer",
        ),
        "E0310": (
            "__length_hint__ does not return non-negative integer",
            "invalid-length-hint-returned",
            "Used when a __length_hint__ method returns something which is not a "
            "non-negative integer",
        ),
        "E0311": (
            "__format__ does not return str",
            "invalid-format-returned",
            "Used when a __format__ method returns something which is not a string",
        ),
        "E0312": (
            "__getnewargs__ does not return a tuple",
            "invalid-getnewargs-returned",
            "Used when a __getnewargs__ method returns something which is not "
            "a tuple",
        ),
        "E0313": (
            "__getnewargs_ex__ does not return a tuple containing (tuple, dict)",
            "invalid-getnewargs-ex-returned",
            "Used when a __getnewargs_ex__ method returns something which is not "
            "of the form tuple(tuple, dict)",
        ),
    }

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._protocol_map: dict[
            str, Callable[[nodes.FunctionDef, InferenceResult], None]
        ] = {
            "__iter__": self._check_iter,
            "__len__": self._check_len,
            "__bool__": self._check_bool,
            "__index__": self._check_index,
            "__repr__": self._check_repr,
            "__str__": self._check_str,
            "__bytes__": self._check_bytes,
            "__hash__": self._check_hash,
            "__length_hint__": self._check_length_hint,
            "__format__": self._check_format,
            "__getnewargs__": self._check_getnewargs,
            "__getnewargs_ex__": self._check_getnewargs_ex,
        }

    @only_required_for_messages(
        "unexpected-special-method-signature",
        "non-iterator-returned",
        "invalid-length-returned",
        "invalid-bool-returned",
        "invalid-index-returned",
        "invalid-repr-returned",
        "invalid-str-returned",
        "invalid-bytes-returned",
        "invalid-hash-returned",
        "invalid-length-hint-returned",
        "invalid-format-returned",
        "invalid-getnewargs-returned",
        "invalid-getnewargs-ex-returned",
    )
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        if not node.is_method():
            return

        inferred = _safe_infer_call_result(node, node)
        # Only want to check types that we are able to infer
        if (
            inferred
            and node.name in self._protocol_map
            and not is_function_body_ellipsis(node)
        ):
            self._protocol_map[node.name](node, inferred)

        if node.name in PYMETHODS:
            self._check_unexpected_method_signature(node)

    visit_asyncfunctiondef = visit_functiondef

    def _check_unexpected_method_signature(self, node: nodes.FunctionDef) -> None:
        expected_params = SPECIAL_METHODS_PARAMS[node.name]

        if expected_params is None:
            # This can support a variable number of parameters.
            return
        if not node.args.args and not node.args.vararg:
            # Method has no parameter, will be caught
            # by no-method-argument.
            return

        if decorated_with(node, ["builtins.staticmethod"]):
            # We expect to not take in consideration self.
            all_args = node.args.args
        else:
            all_args = node.args.args[1:]
        mandatory = len(all_args) - len(node.args.defaults)
        optional = len(node.args.defaults)
        current_params = mandatory + optional

        emit = False  # If we don't know we choose a false negative
        if isinstance(expected_params, tuple):
            # The expected number of parameters can be any value from this
            # tuple, although the user should implement the method
            # to take all of them in consideration.
            emit = mandatory not in expected_params
            # mypy thinks that expected_params has type tuple[int, int] | int | None
            # But at this point it must be 'tuple[int, int]' because of the type check
            expected_params = f"between {expected_params[0]} or {expected_params[1]}"  # type: ignore[assignment]
        else:
            # If the number of mandatory parameters doesn't
            # suffice, the expected parameters for this
            # function will be deduced from the optional
            # parameters.
            rest = expected_params - mandatory
            if rest == 0:
                emit = False
            elif rest < 0:
                emit = True
            elif rest > 0:
                emit = not ((optional - rest) >= 0 or node.args.vararg)

        if emit:
            verb = "was" if current_params <= 1 else "were"
            self.add_message(
                "unexpected-special-method-signature",
                args=(node.name, expected_params, current_params, verb),
                node=node,
            )

    @staticmethod
    def _is_wrapped_type(node: InferenceResult, type_: str) -> bool:
        return (
            isinstance(node, bases.Instance)
            and node.name == type_
            and not isinstance(node, nodes.Const)
        )

    @staticmethod
    def _is_int(node: InferenceResult) -> bool:
        if SpecialMethodsChecker._is_wrapped_type(node, "int"):
            return True

        return isinstance(node, nodes.Const) and isinstance(node.value, int)

    @staticmethod
    def _is_str(node: InferenceResult) -> bool:
        if SpecialMethodsChecker._is_wrapped_type(node, "str"):
            return True

        return isinstance(node, nodes.Const) and isinstance(node.value, str)

    @staticmethod
    def _is_bool(node: InferenceResult) -> bool:
        if SpecialMethodsChecker._is_wrapped_type(node, "bool"):
            return True

        return isinstance(node, nodes.Const) and isinstance(node.value, bool)

    @staticmethod
    def _is_bytes(node: InferenceResult) -> bool:
        if SpecialMethodsChecker._is_wrapped_type(node, "bytes"):
            return True

        return isinstance(node, nodes.Const) and isinstance(node.value, bytes)

    @staticmethod
    def _is_tuple(node: InferenceResult) -> bool:
        if SpecialMethodsChecker._is_wrapped_type(node, "tuple"):
            return True

        return isinstance(node, nodes.Const) and isinstance(node.value, tuple)

    @staticmethod
    def _is_dict(node: InferenceResult) -> bool:
        if SpecialMethodsChecker._is_wrapped_type(node, "dict"):
            return True

        return isinstance(node, nodes.Const) and isinstance(node.value, dict)

    @staticmethod
    def _is_iterator(node: InferenceResult) -> bool:
        if isinstance(node, bases.Generator):
            # Generators can be iterated.
            return True
        if isinstance(node, nodes.ComprehensionScope):
            # Comprehensions can be iterated.
            return True

        if isinstance(node, bases.Instance):
            try:
                node.local_attr(NEXT_METHOD)
                return True
            except astroid.NotFoundError:
                pass
        elif isinstance(node, nodes.ClassDef):
            metaclass = node.metaclass()
            if metaclass and isinstance(metaclass, nodes.ClassDef):
                try:
                    metaclass.local_attr(NEXT_METHOD)
                    return True
                except astroid.NotFoundError:
                    pass
        return False

    def _check_iter(self, node: nodes.FunctionDef, inferred: InferenceResult) -> None:
        if not self._is_iterator(inferred):
            self.add_message("non-iterator-returned", node=node)

    def _check_len(self, node: nodes.FunctionDef, inferred: InferenceResult) -> None:
        if not self._is_int(inferred):
            self.add_message("invalid-length-returned", node=node)
        elif isinstance(inferred, nodes.Const) and inferred.value < 0:
            self.add_message("invalid-length-returned", node=node)

    def _check_bool(self, node: nodes.FunctionDef, inferred: InferenceResult) -> None:
        if not self._is_bool(inferred):
            self.add_message("invalid-bool-returned", node=node)

    def _check_index(self, node: nodes.FunctionDef, inferred: InferenceResult) -> None:
        if not self._is_int(inferred):
            self.add_message("invalid-index-returned", node=node)

    def _check_repr(self, node: nodes.FunctionDef, inferred: InferenceResult) -> None:
        if not self._is_str(inferred):
            self.add_message("invalid-repr-returned", node=node)

    def _check_str(self, node: nodes.FunctionDef, inferred: InferenceResult) -> None:
        if not self._is_str(inferred):
            self.add_message("invalid-str-returned", node=node)

    def _check_bytes(self, node: nodes.FunctionDef, inferred: InferenceResult) -> None:
        if not self._is_bytes(inferred):
            self.add_message("invalid-bytes-returned", node=node)

    def _check_hash(self, node: nodes.FunctionDef, inferred: InferenceResult) -> None:
        if not self._is_int(inferred):
            self.add_message("invalid-hash-returned", node=node)

    def _check_length_hint(
        self, node: nodes.FunctionDef, inferred: InferenceResult
    ) -> None:
        if not self._is_int(inferred):
            self.add_message("invalid-length-hint-returned", node=node)
        elif isinstance(inferred, nodes.Const) and inferred.value < 0:
            self.add_message("invalid-length-hint-returned", node=node)

    def _check_format(self, node: nodes.FunctionDef, inferred: InferenceResult) -> None:
        if not self._is_str(inferred):
            self.add_message("invalid-format-returned", node=node)

    def _check_getnewargs(
        self, node: nodes.FunctionDef, inferred: InferenceResult
    ) -> None:
        if not self._is_tuple(inferred):
            self.add_message("invalid-getnewargs-returned", node=node)

    def _check_getnewargs_ex(
        self, node: nodes.FunctionDef, inferred: InferenceResult
    ) -> None:
        if not self._is_tuple(inferred):
            self.add_message("invalid-getnewargs-ex-returned", node=node)
            return

        if not isinstance(inferred, nodes.Tuple):
            # If it's not an astroid.Tuple we can't analyze it further
            return

        found_error = False

        if len(inferred.elts) != 2:
            found_error = True
        else:
            for arg, check in (
                (inferred.elts[0], self._is_tuple),
                (inferred.elts[1], self._is_dict),
            ):
                if isinstance(arg, nodes.Call):
                    arg = safe_infer(arg)

                if arg and not isinstance(arg, util.UninferableBase):
                    if not check(arg):
                        found_error = True
                        break

        if found_error:
            self.add_message("invalid-getnewargs-ex-returned", node=node)

back/venvmsg/Lib/site-packages/pylint/checkers/classes/special_methods_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Dataclass checkers for Python code."""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes
from astroid.brain.brain_dataclasses import DATACLASS_MODULES

from pylint.checkers import BaseChecker, utils
from pylint.interfaces import INFERENCE

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def _is_dataclasses_module(node: nodes.Module) -> bool:
    """Utility function to check if node is from dataclasses_module."""
    return node.name in DATACLASS_MODULES


def _check_name_or_attrname_eq_to(
    node: nodes.Name | nodes.Attribute, check_with: str
) -> bool:
    """Utility function to check either a Name/Attribute node's name/attrname with a
    given string.
    """
    if isinstance(node, nodes.Name):
        return str(node.name) == check_with
    return str(node.attrname) == check_with


class DataclassChecker(BaseChecker):
    """Checker that detects invalid or problematic usage in dataclasses.

    Checks for
    * invalid-field-call
    """

    name = "dataclass"
    msgs = {
        "E3701": (
            "Invalid usage of field(), %s",
            "invalid-field-call",
            "The dataclasses.field() specifier should only be used as the value of "
            "an assignment within a dataclass, or within the make_dataclass() function.",
        ),
    }

    @utils.only_required_for_messages("invalid-field-call")
    def visit_call(self, node: nodes.Call) -> None:
        self._check_invalid_field_call(node)

    def _check_invalid_field_call(self, node: nodes.Call) -> None:
        """Checks for correct usage of the dataclasses.field() specifier in
        dataclasses or within the make_dataclass() function.

        Emits message
        when field() is detected to be used outside a class decorated with
        @dataclass decorator and outside make_dataclass() function, or when it
        is used improperly within a dataclass.
        """
        if not isinstance(node.func, (nodes.Name, nodes.Attribute)):
            return
        if not _check_name_or_attrname_eq_to(node.func, "field"):
            return
        inferred_func = utils.safe_infer(node.func)
        if not (
            isinstance(inferred_func, nodes.FunctionDef)
            and _is_dataclasses_module(inferred_func.root())
        ):
            return
        scope_node = node.parent
        while scope_node and not isinstance(scope_node, (nodes.ClassDef, nodes.Call)):
            scope_node = scope_node.parent

        if isinstance(scope_node, nodes.Call):
            self._check_invalid_field_call_within_call(node, scope_node)
            return

        if not scope_node or not scope_node.is_dataclass:
            self.add_message(
                "invalid-field-call",
                node=node,
                args=(
                    "it should be used within a dataclass or the make_dataclass() function.",
                ),
                confidence=INFERENCE,
            )
            return

        if not (isinstance(node.parent, nodes.AnnAssign) and node == node.parent.value):
            self.add_message(
                "invalid-field-call",
                node=node,
                args=("it should be the value of an assignment within a dataclass.",),
                confidence=INFERENCE,
            )

    def _check_invalid_field_call_within_call(
        self, node: nodes.Call, scope_node: nodes.Call
    ) -> None:
        """Checks for special case where calling field is valid as an argument of the
        make_dataclass() function.
        """
        inferred_func = utils.safe_infer(scope_node.func)
        if (
            isinstance(scope_node.func, (nodes.Name, nodes.AssignName))
            and scope_node.func.name == "make_dataclass"
            and isinstance(inferred_func, nodes.FunctionDef)
            and _is_dataclasses_module(inferred_func.root())
        ):
            return
        self.add_message(
            "invalid-field-call",
            node=node,
            args=(
                "it should be used within a dataclass or the make_dataclass() function.",
            ),
            confidence=INFERENCE,
        )


def register(linter: PyLinter) -> None:
    linter.register_checker(DataclassChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/dataclass_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checker mixin for deprecated functionality."""

from __future__ import annotations

from collections.abc import Container, Iterable
from itertools import chain

import astroid
from astroid import nodes

from pylint.checkers import utils
from pylint.checkers.base_checker import BaseChecker
from pylint.checkers.utils import get_import_name, infer_all, safe_infer
from pylint.typing import MessageDefinitionTuple

ACCEPTABLE_NODES = (
    astroid.BoundMethod,
    astroid.UnboundMethod,
    nodes.FunctionDef,
    nodes.ClassDef,
)


class DeprecatedMixin(BaseChecker):
    """A mixin implementing logic for checking deprecated symbols.

    A class implementing mixin must define "deprecated-method" Message.
    """

    DEPRECATED_MODULE_MESSAGE: dict[str, MessageDefinitionTuple] = {
        "W4901": (
            "Deprecated module %r",
            "deprecated-module",
            "A module marked as deprecated is imported.",
            {"old_names": [("W0402", "old-deprecated-module")], "shared": True},
        ),
    }

    DEPRECATED_METHOD_MESSAGE: dict[str, MessageDefinitionTuple] = {
        "W4902": (
            "Using deprecated method %s()",
            "deprecated-method",
            "The method is marked as deprecated and will be removed in the future.",
            {"old_names": [("W1505", "old-deprecated-method")], "shared": True},
        ),
    }

    DEPRECATED_ARGUMENT_MESSAGE: dict[str, MessageDefinitionTuple] = {
        "W4903": (
            "Using deprecated argument %s of method %s()",
            "deprecated-argument",
            "The argument is marked as deprecated and will be removed in the future.",
            {"old_names": [("W1511", "old-deprecated-argument")], "shared": True},
        ),
    }

    DEPRECATED_CLASS_MESSAGE: dict[str, MessageDefinitionTuple] = {
        "W4904": (
            "Using deprecated class %s of module %s",
            "deprecated-class",
            "The class is marked as deprecated and will be removed in the future.",
            {"old_names": [("W1512", "old-deprecated-class")], "shared": True},
        ),
    }

    DEPRECATED_DECORATOR_MESSAGE: dict[str, MessageDefinitionTuple] = {
        "W4905": (
            "Using deprecated decorator %s()",
            "deprecated-decorator",
            "The decorator is marked as deprecated and will be removed in the future.",
            {"old_names": [("W1513", "old-deprecated-decorator")], "shared": True},
        ),
    }

    @utils.only_required_for_messages(
        "deprecated-method",
        "deprecated-argument",
        "deprecated-class",
    )
    def visit_call(self, node: nodes.Call) -> None:
        """Called when a :class:`nodes.Call` node is visited."""
        self.check_deprecated_class_in_call(node)
        for inferred in infer_all(node.func):
            # Calling entry point for deprecation check logic.
            self.check_deprecated_method(node, inferred)

    @utils.only_required_for_messages(
        "deprecated-module",
        "deprecated-class",
    )
    def visit_import(self, node: nodes.Import) -> None:
        """Triggered when an import statement is seen."""
        for name in (name for name, _ in node.names):
            self.check_deprecated_module(node, name)
            if "." in name:
                # Checking deprecation for import module with class
                mod_name, class_name = name.split(".", 1)
                self.check_deprecated_class(node, mod_name, (class_name,))

    def deprecated_decorators(self) -> Iterable[str]:
        """Callback returning the deprecated decorators.

        Returns:
            collections.abc.Container of deprecated decorator names.
        """
        return ()

    @utils.only_required_for_messages("deprecated-decorator")
    def visit_decorators(self, node: nodes.Decorators) -> None:
        """Triggered when a decorator statement is seen."""
        children = list(node.get_children())
        if not children:
            return
        if isinstance(children[0], nodes.Call):
            inf = safe_infer(children[0].func)
        else:
            inf = safe_infer(children[0])
        qname = inf.qname() if inf else None
        if qname in self.deprecated_decorators():
            self.add_message("deprecated-decorator", node=node, args=qname)

    @utils.only_required_for_messages(
        "deprecated-module",
        "deprecated-class",
    )
    def visit_importfrom(self, node: nodes.ImportFrom) -> None:
        """Triggered when a from statement is seen."""
        basename = node.modname
        basename = get_import_name(node, basename)
        self.check_deprecated_module(node, basename)
        class_names = (name for name, _ in node.names)
        self.check_deprecated_class(node, basename, class_names)

    def deprecated_methods(self) -> Container[str]:
        """Callback returning the deprecated methods/functions.

        Returns:
            collections.abc.Container of deprecated function/method names.
        """
        return ()

    def deprecated_arguments(self, method: str) -> Iterable[tuple[int | None, str]]:
        """Callback returning the deprecated arguments of method/function.

        Args:
            method (str): name of function/method checked for deprecated arguments

        Returns:
            collections.abc.Iterable in form:
                ((POSITION1, PARAM1), (POSITION2: PARAM2) ...)
            where
                * POSITIONX - position of deprecated argument PARAMX in function definition.
                  If argument is keyword-only, POSITIONX should be None.
                * PARAMX - name of the deprecated argument.
            E.g. suppose function:

            .. code-block:: python
                def bar(arg1, arg2, arg3, arg4, arg5='spam')

            with deprecated arguments `arg2` and `arg4`. `deprecated_arguments` should return:

            .. code-block:: python
                ((1, 'arg2'), (3, 'arg4'))
        """
        # pylint: disable=unused-argument
        return ()

    def deprecated_modules(self) -> Iterable[str]:
        """Callback returning the deprecated modules.

        Returns:
            collections.abc.Container of deprecated module names.
        """
        return ()

    def deprecated_classes(self, module: str) -> Iterable[str]:
        """Callback returning the deprecated classes of module.

        Args:
            module (str): name of module checked for deprecated classes

        Returns:
            collections.abc.Container of deprecated class names.
        """
        # pylint: disable=unused-argument
        return ()

    def check_deprecated_module(self, node: nodes.Import, mod_path: str | None) -> None:
        """Checks if the module is deprecated."""
        for mod_name in self.deprecated_modules():
            if mod_path == mod_name or mod_path and mod_path.startswith(mod_name + "."):
                self.add_message("deprecated-module", node=node, args=mod_path)

    def check_deprecated_method(self, node: nodes.Call, inferred: nodes.NodeNG) -> None:
        """Executes the checker for the given node.

        This method should be called from the checker implementing this mixin.
        """

        # Reject nodes which aren't of interest to us.
        if not isinstance(inferred, ACCEPTABLE_NODES):
            return

        if isinstance(node.func, nodes.Attribute):
            func_name = node.func.attrname
        elif isinstance(node.func, nodes.Name):
            func_name = node.func.name
        else:
            # Not interested in other nodes.
            return

        qnames = {inferred.qname(), func_name}
        if any(name in self.deprecated_methods() for name in qnames):
            self.add_message("deprecated-method", node=node, args=(func_name,))
            return
        num_of_args = len(node.args)
        kwargs = {kw.arg for kw in node.keywords} if node.keywords else {}
        deprecated_arguments = (self.deprecated_arguments(qn) for qn in qnames)
        for position, arg_name in chain(*deprecated_arguments):
            if arg_name in kwargs:
                # function was called with deprecated argument as keyword argument
                self.add_message(
                    "deprecated-argument", node=node, args=(arg_name, func_name)
                )
            elif position is not None and position < num_of_args:
                # function was called with deprecated argument as positional argument
                self.add_message(
                    "deprecated-argument", node=node, args=(arg_name, func_name)
                )

    def check_deprecated_class(
        self, node: nodes.NodeNG, mod_name: str, class_names: Iterable[str]
    ) -> None:
        """Checks if the class is deprecated."""

        for class_name in class_names:
            if class_name in self.deprecated_classes(mod_name):
                self.add_message(
                    "deprecated-class", node=node, args=(class_name, mod_name)
                )

    def check_deprecated_class_in_call(self, node: nodes.Call) -> None:
        """Checks if call the deprecated class."""

        if isinstance(node.func, nodes.Attribute) and isinstance(
            node.func.expr, nodes.Name
        ):
            mod_name = node.func.expr.name
            class_name = node.func.attrname
            self.check_deprecated_class(node, mod_name, (class_name,))

back/venvmsg/Lib/site-packages/pylint/checkers/deprecated.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Check for signs of poor design."""

from __future__ import annotations

import re
from collections import defaultdict
from collections.abc import Iterator
from typing import TYPE_CHECKING

import astroid
from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import is_enum, only_required_for_messages
from pylint.typing import MessageDefinitionTuple

if TYPE_CHECKING:
    from pylint.lint import PyLinter

MSGS: dict[
    str, MessageDefinitionTuple
] = {  # pylint: disable=consider-using-namedtuple-or-dataclass
    "R0901": (
        "Too many ancestors (%s/%s)",
        "too-many-ancestors",
        "Used when class has too many parent classes, try to reduce "
        "this to get a simpler (and so easier to use) class.",
    ),
    "R0902": (
        "Too many instance attributes (%s/%s)",
        "too-many-instance-attributes",
        "Used when class has too many instance attributes, try to reduce "
        "this to get a simpler (and so easier to use) class.",
    ),
    "R0903": (
        "Too few public methods (%s/%s)",
        "too-few-public-methods",
        "Used when class has too few public methods, so be sure it's "
        "really worth it.",
    ),
    "R0904": (
        "Too many public methods (%s/%s)",
        "too-many-public-methods",
        "Used when class has too many public methods, try to reduce "
        "this to get a simpler (and so easier to use) class.",
    ),
    "R0911": (
        "Too many return statements (%s/%s)",
        "too-many-return-statements",
        "Used when a function or method has too many return statement, "
        "making it hard to follow.",
    ),
    "R0912": (
        "Too many branches (%s/%s)",
        "too-many-branches",
        "Used when a function or method has too many branches, "
        "making it hard to follow.",
    ),
    "R0913": (
        "Too many arguments (%s/%s)",
        "too-many-arguments",
        "Used when a function or method takes too many arguments.",
    ),
    "R0914": (
        "Too many local variables (%s/%s)",
        "too-many-locals",
        "Used when a function or method has too many local variables.",
    ),
    "R0915": (
        "Too many statements (%s/%s)",
        "too-many-statements",
        "Used when a function or method has too many statements. You "
        "should then split it in smaller functions / methods.",
    ),
    "R0916": (
        "Too many boolean expressions in if statement (%s/%s)",
        "too-many-boolean-expressions",
        "Used when an if statement contains too many boolean expressions.",
    ),
}
SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
DATACLASSES_DECORATORS = frozenset({"dataclass", "attrs"})
DATACLASS_IMPORT = "dataclasses"
TYPING_NAMEDTUPLE = "typing.NamedTuple"
TYPING_TYPEDDICT = "typing.TypedDict"

# Set of stdlib classes to ignore when calculating number of ancestors
STDLIB_CLASSES_IGNORE_ANCESTOR = frozenset(
    (
        "builtins.object",
        "builtins.tuple",
        "builtins.dict",
        "builtins.list",
        "builtins.set",
        "bulitins.frozenset",
        "collections.ChainMap",
        "collections.Counter",
        "collections.OrderedDict",
        "collections.UserDict",
        "collections.UserList",
        "collections.UserString",
        "collections.defaultdict",
        "collections.deque",
        "collections.namedtuple",
        "_collections_abc.Awaitable",
        "_collections_abc.Coroutine",
        "_collections_abc.AsyncIterable",
        "_collections_abc.AsyncIterator",
        "_collections_abc.AsyncGenerator",
        "_collections_abc.Hashable",
        "_collections_abc.Iterable",
        "_collections_abc.Iterator",
        "_collections_abc.Generator",
        "_collections_abc.Reversible",
        "_collections_abc.Sized",
        "_collections_abc.Container",
        "_collections_abc.Collection",
        "_collections_abc.Set",
        "_collections_abc.MutableSet",
        "_collections_abc.Mapping",
        "_collections_abc.MutableMapping",
        "_collections_abc.MappingView",
        "_collections_abc.KeysView",
        "_collections_abc.ItemsView",
        "_collections_abc.ValuesView",
        "_collections_abc.Sequence",
        "_collections_abc.MutableSequence",
        "_collections_abc.ByteString",
        "typing.Tuple",
        "typing.List",
        "typing.Dict",
        "typing.Set",
        "typing.FrozenSet",
        "typing.Deque",
        "typing.DefaultDict",
        "typing.OrderedDict",
        "typing.Counter",
        "typing.ChainMap",
        "typing.Awaitable",
        "typing.Coroutine",
        "typing.AsyncIterable",
        "typing.AsyncIterator",
        "typing.AsyncGenerator",
        "typing.Iterable",
        "typing.Iterator",
        "typing.Generator",
        "typing.Reversible",
        "typing.Container",
        "typing.Collection",
        "typing.AbstractSet",
        "typing.MutableSet",
        "typing.Mapping",
        "typing.MutableMapping",
        "typing.Sequence",
        "typing.MutableSequence",
        "typing.ByteString",
        "typing.MappingView",
        "typing.KeysView",
        "typing.ItemsView",
        "typing.ValuesView",
        "typing.ContextManager",
        "typing.AsyncContextManager",
        "typing.Hashable",
        "typing.Sized",
        TYPING_NAMEDTUPLE,
        TYPING_TYPEDDICT,
    )
)


def _is_exempt_from_public_methods(node: astroid.ClassDef) -> bool:
    """Check if a class is exempt from too-few-public-methods."""

    # If it's a typing.Namedtuple, typing.TypedDict or an Enum
    for ancestor in node.ancestors():
        if is_enum(ancestor):
            return True
        if ancestor.qname() in (TYPING_NAMEDTUPLE, TYPING_TYPEDDICT):
            return True

    # Or if it's a dataclass
    if not node.decorators:
        return False

    root_locals = set(node.root().locals)
    for decorator in node.decorators.nodes:
        if isinstance(decorator, astroid.Call):
            decorator = decorator.func
        if not isinstance(decorator, (astroid.Name, astroid.Attribute)):
            continue
        if isinstance(decorator, astroid.Name):
            name = decorator.name
        else:
            name = decorator.attrname
        if name in DATACLASSES_DECORATORS and (
            root_locals.intersection(DATACLASSES_DECORATORS)
            or DATACLASS_IMPORT in root_locals
        ):
            return True
    return False


def _count_boolean_expressions(bool_op: nodes.BoolOp) -> int:
    """Counts the number of boolean expressions in BoolOp `bool_op` (recursive).

    example: a and (b or c or (d and e)) ==> 5 boolean expressions
    """
    nb_bool_expr = 0
    for bool_expr in bool_op.get_children():
        if isinstance(bool_expr, astroid.BoolOp):
            nb_bool_expr += _count_boolean_expressions(bool_expr)
        else:
            nb_bool_expr += 1
    return nb_bool_expr


def _count_methods_in_class(node: nodes.ClassDef) -> int:
    all_methods = sum(1 for method in node.methods() if not method.name.startswith("_"))
    # Special methods count towards the number of public methods,
    # but don't count towards there being too many methods.
    for method in node.mymethods():
        if SPECIAL_OBJ.search(method.name) and method.name != "__init__":
            all_methods += 1
    return all_methods


def _get_parents_iter(
    node: nodes.ClassDef, ignored_parents: frozenset[str]
) -> Iterator[nodes.ClassDef]:
    r"""Get parents of ``node``, excluding ancestors of ``ignored_parents``.

    If we have the following inheritance diagram:

             F
            /
        D  E
         \/
          B  C
           \/
            A      # class A(B, C): ...

    And ``ignored_parents`` is ``{"E"}``, then this function will return
    ``{A, B, C, D}`` -- both ``E`` and its ancestors are excluded.
    """
    parents: set[nodes.ClassDef] = set()
    to_explore = list(node.ancestors(recurs=False))
    while to_explore:
        parent = to_explore.pop()
        if parent.qname() in ignored_parents:
            continue
        if parent not in parents:
            # This guard might appear to be performing the same function as
            # adding the resolved parents to a set to eliminate duplicates
            # (legitimate due to diamond inheritance patterns), but its
            # additional purpose is to prevent cycles (not normally possible,
            # but potential due to inference) and thus guarantee termination
            # of the while-loop
            yield parent
            parents.add(parent)
            to_explore.extend(parent.ancestors(recurs=False))


def _get_parents(
    node: nodes.ClassDef, ignored_parents: frozenset[str]
) -> set[nodes.ClassDef]:
    return set(_get_parents_iter(node, ignored_parents))


class MisdesignChecker(BaseChecker):
    """Checker of potential misdesigns.

    Checks for sign of poor/misdesign:
    * number of methods, attributes, local variables...
    * size, complexity of functions, methods
    """

    # configuration section name
    name = "design"
    # messages
    msgs = MSGS
    # configuration options
    options = (
        (
            "max-args",
            {
                "default": 5,
                "type": "int",
                "metavar": "<int>",
                "help": "Maximum number of arguments for function / method.",
            },
        ),
        (
            "max-locals",
            {
                "default": 15,
                "type": "int",
                "metavar": "<int>",
                "help": "Maximum number of locals for function / method body.",
            },
        ),
        (
            "max-returns",
            {
                "default": 6,
                "type": "int",
                "metavar": "<int>",
                "help": "Maximum number of return / yield for function / "
                "method body.",
            },
        ),
        (
            "max-branches",
            {
                "default": 12,
                "type": "int",
                "metavar": "<int>",
                "help": "Maximum number of branch for function / method body.",
            },
        ),
        (
            "max-statements",
            {
                "default": 50,
                "type": "int",
                "metavar": "<int>",
                "help": "Maximum number of statements in function / method body.",
            },
        ),
        (
            "max-parents",
            {
                "default": 7,
                "type": "int",
                "metavar": "<num>",
                "help": "Maximum number of parents for a class (see R0901).",
            },
        ),
        (
            "ignored-parents",
            {
                "default": (),
                "type": "csv",
                "metavar": "<comma separated list of class names>",
                "help": "List of qualified class names to ignore when counting class parents (see R0901)",
            },
        ),
        (
            "max-attributes",
            {
                "default": 7,
                "type": "int",
                "metavar": "<num>",
                "help": "Maximum number of attributes for a class \
(see R0902).",
            },
        ),
        (
            "min-public-methods",
            {
                "default": 2,
                "type": "int",
                "metavar": "<num>",
                "help": "Minimum number of public methods for a class \
(see R0903).",
            },
        ),
        (
            "max-public-methods",
            {
                "default": 20,
                "type": "int",
                "metavar": "<num>",
                "help": "Maximum number of public methods for a class \
(see R0904).",
            },
        ),
        (
            "max-bool-expr",
            {
                "default": 5,
                "type": "int",
                "metavar": "<num>",
                "help": "Maximum number of boolean expressions in an if "
                "statement (see R0916).",
            },
        ),
        (
            "exclude-too-few-public-methods",
            {
                "default": [],
                "type": "regexp_csv",
                "metavar": "<pattern>[,<pattern>...]",
                "help": "List of regular expressions of class ancestor names "
                "to ignore when counting public methods (see R0903)",
            },
        ),
    )

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._returns: list[int]
        self._branches: defaultdict[nodes.LocalsDictNodeNG, int]
        self._stmts: list[int]

    def open(self) -> None:
        """Initialize visit variables."""
        self.linter.stats.reset_node_count()
        self._returns = []
        self._branches = defaultdict(int)
        self._stmts = []
        self._exclude_too_few_public_methods = (
            self.linter.config.exclude_too_few_public_methods
        )

    def _inc_all_stmts(self, amount: int) -> None:
        for i, _ in enumerate(self._stmts):
            self._stmts[i] += amount

    @only_required_for_messages(
        "too-many-ancestors",
        "too-many-instance-attributes",
        "too-few-public-methods",
        "too-many-public-methods",
    )
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        """Check size of inheritance hierarchy and number of instance attributes."""
        parents = _get_parents(
            node,
            STDLIB_CLASSES_IGNORE_ANCESTOR.union(self.linter.config.ignored_parents),
        )
        nb_parents = len(parents)
        if nb_parents > self.linter.config.max_parents:
            self.add_message(
                "too-many-ancestors",
                node=node,
                args=(nb_parents, self.linter.config.max_parents),
            )

        # Something at inference time is modifying instance_attrs to add
        # properties from parent classes. Given how much we cache inference
        # results, mutating instance_attrs can become a real mess. Filter
        # them out here until the root cause is solved.
        # https://github.com/pylint-dev/astroid/issues/2273
        root = node.root()
        filtered_attrs = [
            k for (k, v) in node.instance_attrs.items() if v[0].root() is root
        ]
        if len(filtered_attrs) > self.linter.config.max_attributes:
            self.add_message(
                "too-many-instance-attributes",
                node=node,
                args=(len(filtered_attrs), self.linter.config.max_attributes),
            )

    @only_required_for_messages("too-few-public-methods", "too-many-public-methods")
    def leave_classdef(self, node: nodes.ClassDef) -> None:
        """Check number of public methods."""
        my_methods = sum(
            1 for method in node.mymethods() if not method.name.startswith("_")
        )

        # Does the class contain less than n public methods ?
        # This checks only the methods defined in the current class,
        # since the user might not have control over the classes
        # from the ancestors. It avoids some false positives
        # for classes such as unittest.TestCase, which provides
        # a lot of assert methods. It doesn't make sense to warn
        # when the user subclasses TestCase to add his own tests.
        if my_methods > self.linter.config.max_public_methods:
            self.add_message(
                "too-many-public-methods",
                node=node,
                args=(my_methods, self.linter.config.max_public_methods),
            )

        # Stop here if the class is excluded via configuration.
        if node.type == "class" and self._exclude_too_few_public_methods:
            for ancestor in node.ancestors():
                if any(
                    pattern.match(ancestor.qname())
                    for pattern in self._exclude_too_few_public_methods
                ):
                    return

        # Stop here for exception, metaclass, interface classes and other
        # classes for which we don't need to count the methods.
        if node.type != "class" or _is_exempt_from_public_methods(node):
            return

        # Does the class contain more than n public methods ?
        # This checks all the methods defined by ancestors and
        # by the current class.
        all_methods = _count_methods_in_class(node)
        if all_methods < self.linter.config.min_public_methods:
            self.add_message(
                "too-few-public-methods",
                node=node,
                args=(all_methods, self.linter.config.min_public_methods),
            )

    @only_required_for_messages(
        "too-many-return-statements",
        "too-many-branches",
        "too-many-arguments",
        "too-many-locals",
        "too-many-statements",
        "keyword-arg-before-vararg",
    )
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        """Check function name, docstring, arguments, redefinition,
        variable names, max locals.
        """
        # init branch and returns counters
        self._returns.append(0)
        # check number of arguments
        args = node.args.args + node.args.posonlyargs + node.args.kwonlyargs
        ignored_argument_names = self.linter.config.ignored_argument_names
        if args is not None:
            ignored_args_num = 0
            if ignored_argument_names:
                ignored_args_num = sum(
                    1 for arg in args if ignored_argument_names.match(arg.name)
                )

            argnum = len(args) - ignored_args_num
            if argnum > self.linter.config.max_args:
                self.add_message(
                    "too-many-arguments",
                    node=node,
                    args=(len(args), self.linter.config.max_args),
                )
        else:
            ignored_args_num = 0
        # check number of local variables
        locnum = len(node.locals) - ignored_args_num

        # decrement number of local variables if '_' is one of them
        if "_" in node.locals:
            locnum -= 1

        if locnum > self.linter.config.max_locals:
            self.add_message(
                "too-many-locals",
                node=node,
                args=(locnum, self.linter.config.max_locals),
            )
        # init new statements counter
        self._stmts.append(1)

    visit_asyncfunctiondef = visit_functiondef

    @only_required_for_messages(
        "too-many-return-statements",
        "too-many-branches",
        "too-many-arguments",
        "too-many-locals",
        "too-many-statements",
    )
    def leave_functiondef(self, node: nodes.FunctionDef) -> None:
        """Most of the work is done here on close:
        checks for max returns, branch, return in __init__.
        """
        returns = self._returns.pop()
        if returns > self.linter.config.max_returns:
            self.add_message(
                "too-many-return-statements",
                node=node,
                args=(returns, self.linter.config.max_returns),
            )
        branches = self._branches[node]
        if branches > self.linter.config.max_branches:
            self.add_message(
                "too-many-branches",
                node=node,
                args=(branches, self.linter.config.max_branches),
            )
        # check number of statements
        stmts = self._stmts.pop()
        if stmts > self.linter.config.max_statements:
            self.add_message(
                "too-many-statements",
                node=node,
                args=(stmts, self.linter.config.max_statements),
            )

    leave_asyncfunctiondef = leave_functiondef

    def visit_return(self, _: nodes.Return) -> None:
        """Count number of returns."""
        if not self._returns:
            return  # return outside function, reported by the base checker
        self._returns[-1] += 1

    def visit_default(self, node: nodes.NodeNG) -> None:
        """Default visit method -> increments the statements counter if
        necessary.
        """
        if node.is_statement:
            self._inc_all_stmts(1)

    def visit_try(self, node: nodes.Try) -> None:
        """Increments the branches counter."""
        branches = len(node.handlers)
        if node.orelse:
            branches += 1
        if node.finalbody:
            branches += 1
        self._inc_branch(node, branches)
        self._inc_all_stmts(branches)

    @only_required_for_messages("too-many-boolean-expressions", "too-many-branches")
    def visit_if(self, node: nodes.If) -> None:
        """Increments the branches counter and checks boolean expressions."""
        self._check_boolean_expressions(node)
        branches = 1
        # don't double count If nodes coming from some 'elif'
        if node.orelse and (
            len(node.orelse) > 1 or not isinstance(node.orelse[0], astroid.If)
        ):
            branches += 1
        self._inc_branch(node, branches)
        self._inc_all_stmts(branches)

    def _check_boolean_expressions(self, node: nodes.If) -> None:
        """Go through "if" node `node` and count its boolean expressions
        if the 'if' node test is a BoolOp node.
        """
        condition = node.test
        if not isinstance(condition, astroid.BoolOp):
            return
        nb_bool_expr = _count_boolean_expressions(condition)
        if nb_bool_expr > self.linter.config.max_bool_expr:
            self.add_message(
                "too-many-boolean-expressions",
                node=condition,
                args=(nb_bool_expr, self.linter.config.max_bool_expr),
            )

    def visit_while(self, node: nodes.While) -> None:
        """Increments the branches counter."""
        branches = 1
        if node.orelse:
            branches += 1
        self._inc_branch(node, branches)

    visit_for = visit_while

    def _inc_branch(self, node: nodes.NodeNG, branchesnum: int = 1) -> None:
        """Increments the branches counter."""
        self._branches[node.scope()] += branchesnum


def register(linter: PyLinter) -> None:
    linter.register_checker(MisdesignChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/design_analysis.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import Instance, nodes
from astroid.util import UninferableBase

from pylint.checkers import BaseChecker
from pylint.checkers.utils import safe_infer
from pylint.constants import DUNDER_METHODS, UNNECESSARY_DUNDER_CALL_LAMBDA_EXCEPTIONS
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class DunderCallChecker(BaseChecker):
    """Check for unnecessary dunder method calls.

    Docs: https://docs.python.org/3/reference/datamodel.html#basic-customization
    We exclude names in list pylint.constants.EXTRA_DUNDER_METHODS such as
    __index__ (see https://github.com/pylint-dev/pylint/issues/6795)
    since these either have no alternative method of being called or
    have a genuine use case for being called manually.

    Additionally, we exclude classes that are not instantiated since these
    might be used to access the dunder methods of a base class of an instance.
    We also exclude dunder method calls on super() since
    these can't be written in an alternative manner.
    """

    name = "unnecessary-dunder-call"
    priority = -1
    msgs = {
        "C2801": (
            "Unnecessarily calls dunder method %s. %s.",
            "unnecessary-dunder-call",
            "Used when a dunder method is manually called instead "
            "of using the corresponding function/method/operator.",
        ),
    }
    options = ()

    def open(self) -> None:
        self._dunder_methods: dict[str, str] = {}
        for since_vers, dunder_methods in DUNDER_METHODS.items():
            if since_vers <= self.linter.config.py_version:
                self._dunder_methods.update(dunder_methods)

    @staticmethod
    def within_dunder_or_lambda_def(node: nodes.NodeNG) -> bool:
        """Check if dunder method call is within a dunder method definition."""
        parent = node.parent
        while parent is not None:
            if (
                isinstance(parent, nodes.FunctionDef)
                and parent.name.startswith("__")
                and parent.name.endswith("__")
                or DunderCallChecker.is_lambda_rule_exception(parent, node)
            ):
                return True
            parent = parent.parent
        return False

    @staticmethod
    def is_lambda_rule_exception(ancestor: nodes.NodeNG, node: nodes.NodeNG) -> bool:
        return (
            isinstance(ancestor, nodes.Lambda)
            and node.func.attrname in UNNECESSARY_DUNDER_CALL_LAMBDA_EXCEPTIONS
        )

    def visit_call(self, node: nodes.Call) -> None:
        """Check if method being called is an unnecessary dunder method."""
        if (
            isinstance(node.func, nodes.Attribute)
            and node.func.attrname in self._dunder_methods
            and not self.within_dunder_or_lambda_def(node)
            and not (
                isinstance(node.func.expr, nodes.Call)
                and isinstance(node.func.expr.func, nodes.Name)
                and node.func.expr.func.name == "super"
            )
        ):
            inf_expr = safe_infer(node.func.expr)
            if not (
                inf_expr is None or isinstance(inf_expr, (Instance, UninferableBase))
            ):
                # Skip dunder calls to non instantiated classes.
                return

            self.add_message(
                "unnecessary-dunder-call",
                node=node,
                args=(node.func.attrname, self._dunder_methods[node.func.attrname]),
                confidence=HIGH,
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(DunderCallChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/dunder_methods.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Ellipsis checker for Python code."""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import only_required_for_messages

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class EllipsisChecker(BaseChecker):
    name = "unnecessary_ellipsis"
    msgs = {
        "W2301": (
            "Unnecessary ellipsis constant",
            "unnecessary-ellipsis",
            "Used when the ellipsis constant is encountered and can be avoided. "
            "A line of code consisting of an ellipsis is unnecessary if "
            "there is a docstring on the preceding line or if there is a "
            "statement in the same scope.",
        )
    }

    @only_required_for_messages("unnecessary-ellipsis")
    def visit_const(self, node: nodes.Const) -> None:
        """Check if the ellipsis constant is used unnecessarily.

        Emits a warning when:
         - A line consisting of an ellipsis is preceded by a docstring.
         - A statement exists in the same scope as the ellipsis.
           For example: A function consisting of an ellipsis followed by a
           return statement on the next line.
        """
        if (
            node.pytype() == "builtins.Ellipsis"
            and isinstance(node.parent, nodes.Expr)
            and (
                (
                    isinstance(node.parent.parent, (nodes.ClassDef, nodes.FunctionDef))
                    and node.parent.parent.doc_node
                )
                or len(node.parent.parent.body) > 1
            )
        ):
            self.add_message("unnecessary-ellipsis", node=node)


def register(linter: PyLinter) -> None:
    linter.register_checker(EllipsisChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/ellipsis_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checks for various exception related errors."""

from __future__ import annotations

import builtins
import inspect
from collections.abc import Generator
from typing import TYPE_CHECKING, Any

import astroid
from astroid import nodes, objects, util
from astroid.context import InferenceContext
from astroid.typing import InferenceResult, SuccessfulInferenceResult

from pylint import checkers
from pylint.checkers import utils
from pylint.interfaces import HIGH, INFERENCE
from pylint.typing import MessageDefinitionTuple

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def _builtin_exceptions() -> set[str]:
    def predicate(obj: Any) -> bool:
        return isinstance(obj, type) and issubclass(obj, BaseException)

    members = inspect.getmembers(builtins, predicate)
    return {exc.__name__ for (_, exc) in members}


def _annotated_unpack_infer(
    stmt: nodes.NodeNG, context: InferenceContext | None = None
) -> Generator[tuple[nodes.NodeNG, SuccessfulInferenceResult], None, None]:
    """Recursively generate nodes inferred by the given statement.

    If the inferred value is a list or a tuple, recurse on the elements.
    Returns an iterator which yields tuples in the format
    ('original node', 'inferred node').
    """
    if isinstance(stmt, (nodes.List, nodes.Tuple)):
        for elt in stmt.elts:
            inferred = utils.safe_infer(elt)
            if inferred and not isinstance(inferred, util.UninferableBase):
                yield elt, inferred
        return
    for inferred in stmt.infer(context):
        if isinstance(inferred, util.UninferableBase):
            continue
        yield stmt, inferred


def _is_raising(body: list[nodes.NodeNG]) -> bool:
    """Return whether the given statement node raises an exception."""
    return any(isinstance(node, nodes.Raise) for node in body)


MSGS: dict[str, MessageDefinitionTuple] = {
    "E0701": (
        "Bad except clauses order (%s)",
        "bad-except-order",
        "Used when except clauses are not in the correct order (from the "
        "more specific to the more generic). If you don't fix the order, "
        "some exceptions may not be caught by the most specific handler.",
    ),
    "E0702": (
        "Raising %s while only classes or instances are allowed",
        "raising-bad-type",
        "Used when something which is neither a class nor an instance "
        "is raised (i.e. a `TypeError` will be raised).",
    ),
    "E0704": (
        "The raise statement is not inside an except clause",
        "misplaced-bare-raise",
        "Used when a bare raise is not used inside an except clause. "
        "This generates an error, since there are no active exceptions "
        "to be reraised. An exception to this rule is represented by "
        "a bare raise inside a finally clause, which might work, as long "
        "as an exception is raised inside the try block, but it is "
        "nevertheless a code smell that must not be relied upon.",
    ),
    "E0705": (
        "Exception cause set to something which is not an exception, nor None",
        "bad-exception-cause",
        'Used when using the syntax "raise ... from ...", '
        "where the exception cause is not an exception, "
        "nor None.",
        {"old_names": [("E0703", "bad-exception-context")]},
    ),
    "E0710": (
        "Raising a new style class which doesn't inherit from BaseException",
        "raising-non-exception",
        "Used when a new style class which doesn't inherit from "
        "BaseException is raised.",
    ),
    "E0711": (
        "NotImplemented raised - should raise NotImplementedError",
        "notimplemented-raised",
        "Used when NotImplemented is raised instead of NotImplementedError",
    ),
    "E0712": (
        "Catching an exception which doesn't inherit from Exception: %s",
        "catching-non-exception",
        "Used when a class which doesn't inherit from "
        "Exception is used as an exception in an except clause.",
    ),
    "W0702": (
        "No exception type(s) specified",
        "bare-except",
        "A bare ``except:`` clause will catch ``SystemExit`` and "
        "``KeyboardInterrupt`` exceptions, making it harder to interrupt a program "
        "with ``Control-C``, and can disguise other problems. If you want to catch "
        "all exceptions that signal program errors, use ``except Exception:`` (bare "
        "except is equivalent to ``except BaseException:``).",
    ),
    "W0718": (
        "Catching too general exception %s",
        "broad-exception-caught",
        "If you use a naked ``except Exception:`` clause, you might end up catching "
        "exceptions other than the ones you expect to catch. This can hide bugs or "
        "make it harder to debug programs when unrelated errors are hidden.",
        {"old_names": [("W0703", "broad-except")]},
    ),
    "W0705": (
        "Catching previously caught exception type %s",
        "duplicate-except",
        "Used when an except catches a type that was already caught by "
        "a previous handler.",
    ),
    "W0706": (
        "The except handler raises immediately",
        "try-except-raise",
        "Used when an except handler uses raise as its first or only "
        "operator. This is useless because it raises back the exception "
        "immediately. Remove the raise operator or the entire "
        "try-except-raise block!",
    ),
    "W0707": (
        "Consider explicitly re-raising using %s'%s from %s'",
        "raise-missing-from",
        "Python's exception chaining shows the traceback of the current exception, "
        "but also of the original exception. When you raise a new exception after "
        "another exception was caught it's likely that the second exception is a "
        "friendly re-wrapping of the first exception. In such cases `raise from` "
        "provides a better link between the two tracebacks in the final error.",
    ),
    "W0711": (
        'Exception to catch is the result of a binary "%s" operation',
        "binary-op-exception",
        "Used when the exception to catch is of the form "
        '"except A or B:".  If intending to catch multiple, '
        'rewrite as "except (A, B):"',
    ),
    "W0715": (
        "Exception arguments suggest string formatting might be intended",
        "raising-format-tuple",
        "Used when passing multiple arguments to an exception "
        "constructor, the first of them a string literal containing what "
        "appears to be placeholders intended for formatting",
    ),
    "W0716": (
        "Invalid exception operation. %s",
        "wrong-exception-operation",
        "Used when an operation is done against an exception, but the operation "
        "is not valid for the exception in question. Usually emitted when having "
        "binary operations between exceptions in except handlers.",
    ),
    "W0719": (
        "Raising too general exception: %s",
        "broad-exception-raised",
        "Raising exceptions that are too generic force you to catch exceptions "
        "generically too. It will force you to use a naked ``except Exception:`` "
        "clause. You might then end up catching exceptions other than the ones "
        "you expect to catch. This can hide bugs or make it harder to debug programs "
        "when unrelated errors are hidden.",
    ),
}


class BaseVisitor:
    """Base class for visitors defined in this module."""

    def __init__(self, checker: ExceptionsChecker, node: nodes.Raise) -> None:
        self._checker = checker
        self._node = node

    def visit(self, node: SuccessfulInferenceResult) -> None:
        name = node.__class__.__name__.lower()
        dispatch_meth = getattr(self, "visit_" + name, None)
        if dispatch_meth:
            dispatch_meth(node)
        else:
            self.visit_default(node)

    def visit_default(self, _: nodes.NodeNG) -> None:
        """Default implementation for all the nodes."""


class ExceptionRaiseRefVisitor(BaseVisitor):
    """Visit references (anything that is not an AST leaf)."""

    def visit_name(self, node: nodes.Name) -> None:
        if node.name == "NotImplemented":
            self._checker.add_message(
                "notimplemented-raised", node=self._node, confidence=HIGH
            )
            return
        try:
            exceptions = [
                c
                for _, c in _annotated_unpack_infer(node)
                if isinstance(c, nodes.ClassDef)
            ]
        except astroid.InferenceError:
            return

        for exception in exceptions:
            if self._checker._is_overgeneral_exception(exception):
                self._checker.add_message(
                    "broad-exception-raised",
                    args=exception.name,
                    node=self._node,
                    confidence=INFERENCE,
                )

    def visit_call(self, node: nodes.Call) -> None:
        if isinstance(node.func, nodes.Name):
            self.visit_name(node.func)
        if (
            len(node.args) > 1
            and isinstance(node.args[0], nodes.Const)
            and isinstance(node.args[0].value, str)
        ):
            msg = node.args[0].value
            if "%" in msg or ("{" in msg and "}" in msg):
                self._checker.add_message(
                    "raising-format-tuple", node=self._node, confidence=HIGH
                )


class ExceptionRaiseLeafVisitor(BaseVisitor):
    """Visitor for handling leaf kinds of a raise value."""

    def visit_const(self, node: nodes.Const) -> None:
        self._checker.add_message(
            "raising-bad-type",
            node=self._node,
            args=node.value.__class__.__name__,
            confidence=INFERENCE,
        )

    def visit_instance(self, instance: objects.ExceptionInstance) -> None:
        cls = instance._proxied
        self.visit_classdef(cls)

    # Exception instances have a particular class type
    visit_exceptioninstance = visit_instance

    def visit_classdef(self, node: nodes.ClassDef) -> None:
        if not utils.inherit_from_std_ex(node) and utils.has_known_bases(node):
            if node.newstyle:
                self._checker.add_message(
                    "raising-non-exception",
                    node=self._node,
                    confidence=INFERENCE,
                )

    def visit_tuple(self, _: nodes.Tuple) -> None:
        self._checker.add_message(
            "raising-bad-type",
            node=self._node,
            args="tuple",
            confidence=INFERENCE,
        )

    def visit_default(self, node: nodes.NodeNG) -> None:
        name = getattr(node, "name", node.__class__.__name__)
        self._checker.add_message(
            "raising-bad-type",
            node=self._node,
            args=name,
            confidence=INFERENCE,
        )


class ExceptionsChecker(checkers.BaseChecker):
    """Exception related checks."""

    name = "exceptions"
    msgs = MSGS
    options = (
        (
            "overgeneral-exceptions",
            {
                "default": ("builtins.BaseException", "builtins.Exception"),
                "type": "csv",
                "metavar": "<comma-separated class names>",
                "help": "Exceptions that will emit a warning when caught.",
            },
        ),
    )

    def open(self) -> None:
        self._builtin_exceptions = _builtin_exceptions()
        super().open()

    @utils.only_required_for_messages(
        "misplaced-bare-raise",
        "raising-bad-type",
        "raising-non-exception",
        "notimplemented-raised",
        "bad-exception-cause",
        "raising-format-tuple",
        "raise-missing-from",
        "broad-exception-raised",
    )
    def visit_raise(self, node: nodes.Raise) -> None:
        if node.exc is None:
            self._check_misplaced_bare_raise(node)
            return

        if node.cause is None:
            self._check_raise_missing_from(node)
        else:
            self._check_bad_exception_cause(node)

        expr = node.exc
        ExceptionRaiseRefVisitor(self, node).visit(expr)

        inferred = utils.safe_infer(expr)
        if inferred is None or isinstance(inferred, util.UninferableBase):
            return
        ExceptionRaiseLeafVisitor(self, node).visit(inferred)

    def _check_misplaced_bare_raise(self, node: nodes.Raise) -> None:
        # Filter out if it's present in __exit__.
        scope = node.scope()
        if (
            isinstance(scope, nodes.FunctionDef)
            and scope.is_method()
            and scope.name == "__exit__"
        ):
            return

        current = node
        # Stop when a new scope is generated or when the raise
        # statement is found inside a Try.
        ignores = (nodes.ExceptHandler, nodes.FunctionDef)
        while current and not isinstance(current.parent, ignores):
            current = current.parent

        expected = (nodes.ExceptHandler,)
        if not current or not isinstance(current.parent, expected):
            self.add_message("misplaced-bare-raise", node=node, confidence=HIGH)

    def _check_bad_exception_cause(self, node: nodes.Raise) -> None:
        """Verify that the exception cause is properly set.

        An exception cause can be only `None` or an exception.
        """
        cause = utils.safe_infer(node.cause)
        if cause is None or isinstance(cause, util.UninferableBase):
            return

        if isinstance(cause, nodes.Const):
            if cause.value is not None:
                self.add_message("bad-exception-cause", node=node, confidence=INFERENCE)
        elif not isinstance(cause, nodes.ClassDef) and not utils.inherit_from_std_ex(
            cause
        ):
            self.add_message("bad-exception-cause", node=node, confidence=INFERENCE)

    def _check_raise_missing_from(self, node: nodes.Raise) -> None:
        if node.exc is None:
            # This is a plain `raise`, raising the previously-caught exception. No need for a
            # cause.
            return
        # We'd like to check whether we're inside an `except` clause:
        containing_except_node = utils.find_except_wrapper_node_in_scope(node)
        if not containing_except_node:
            return
        # We found a surrounding `except`! We're almost done proving there's a
        # `raise-missing-from` here. The only thing we need to protect against is that maybe
        # the `raise` is raising the exception that was caught, possibly with some shenanigans
        # like `exc.with_traceback(whatever)`. We won't analyze these, we'll just assume
        # there's a violation on two simple cases: `raise SomeException(whatever)` and `raise
        # SomeException`.
        if containing_except_node.name is None:
            # The `except` doesn't have an `as exception:` part, meaning there's no way that
            # the `raise` is raising the same exception.
            class_of_old_error = "Exception"
            if isinstance(containing_except_node.type, (nodes.Name, nodes.Tuple)):
                # 'except ZeroDivisionError' or 'except (ZeroDivisionError, ValueError)'
                class_of_old_error = containing_except_node.type.as_string()
            self.add_message(
                "raise-missing-from",
                node=node,
                args=(
                    f"'except {class_of_old_error} as exc' and ",
                    node.as_string(),
                    "exc",
                ),
                confidence=HIGH,
            )
        elif (
            isinstance(node.exc, nodes.Call)
            and isinstance(node.exc.func, nodes.Name)
            or isinstance(node.exc, nodes.Name)
            and node.exc.name != containing_except_node.name.name
        ):
            # We have a `raise SomeException(whatever)` or a `raise SomeException`
            self.add_message(
                "raise-missing-from",
                node=node,
                args=("", node.as_string(), containing_except_node.name.name),
                confidence=HIGH,
            )

    def _check_catching_non_exception(
        self,
        handler: nodes.ExceptHandler,
        exc: SuccessfulInferenceResult,
        part: nodes.NodeNG,
    ) -> None:
        if isinstance(exc, nodes.Tuple):
            # Check if it is a tuple of exceptions.
            inferred = [utils.safe_infer(elt) for elt in exc.elts]
            if any(isinstance(node, util.UninferableBase) for node in inferred):
                # Don't emit if we don't know every component.
                return
            if all(
                node
                and (utils.inherit_from_std_ex(node) or not utils.has_known_bases(node))
                for node in inferred
            ):
                return

        if not isinstance(exc, nodes.ClassDef):
            # Don't emit the warning if the inferred stmt
            # is None, but the exception handler is something else,
            # maybe it was redefined.
            if isinstance(exc, nodes.Const) and exc.value is None:
                if (
                    isinstance(handler.type, nodes.Const) and handler.type.value is None
                ) or handler.type.parent_of(exc):
                    # If the exception handler catches None or
                    # the exception component, which is None, is
                    # defined by the entire exception handler, then
                    # emit a warning.
                    self.add_message(
                        "catching-non-exception",
                        node=handler.type,
                        args=(part.as_string(),),
                    )
            else:
                self.add_message(
                    "catching-non-exception",
                    node=handler.type,
                    args=(part.as_string(),),
                )
            return

        if (
            not utils.inherit_from_std_ex(exc)
            and exc.name not in self._builtin_exceptions
        ):
            if utils.has_known_bases(exc):
                self.add_message(
                    "catching-non-exception", node=handler.type, args=(exc.name,)
                )

    def _check_try_except_raise(self, node: nodes.Try) -> None:
        def gather_exceptions_from_handler(
            handler: nodes.ExceptHandler,
        ) -> list[InferenceResult] | None:
            exceptions: list[InferenceResult] = []
            if handler.type:
                exceptions_in_handler = utils.safe_infer(handler.type)
                if isinstance(exceptions_in_handler, nodes.Tuple):
                    exceptions = list(
                        {
                            exception
                            for exception in exceptions_in_handler.elts
                            if isinstance(exception, (nodes.Name, nodes.Attribute))
                        }
                    )
                elif exceptions_in_handler:
                    exceptions = [exceptions_in_handler]
                else:
                    # Break when we cannot infer anything reliably.
                    return None
            return exceptions

        bare_raise = False
        handler_having_bare_raise = None
        exceptions_in_bare_handler: list[InferenceResult] | None = []
        for handler in node.handlers:
            if bare_raise:
                # check that subsequent handler is not parent of handler which had bare raise.
                # since utils.safe_infer can fail for bare except, check it before.
                # also break early if bare except is followed by bare except.

                excs_in_current_handler = gather_exceptions_from_handler(handler)
                if not excs_in_current_handler:
                    break
                if exceptions_in_bare_handler is None:
                    # It can be `None` when the inference failed
                    break
                for exc_in_current_handler in excs_in_current_handler:
                    inferred_current = utils.safe_infer(exc_in_current_handler)
                    if any(
                        utils.is_subclass_of(utils.safe_infer(e), inferred_current)
                        for e in exceptions_in_bare_handler
                    ):
                        bare_raise = False
                        break

            # `raise` as the first operator inside the except handler
            if _is_raising([handler.body[0]]):
                # flags when there is a bare raise
                if handler.body[0].exc is None:
                    bare_raise = True
                    handler_having_bare_raise = handler
                    exceptions_in_bare_handler = gather_exceptions_from_handler(handler)
        else:
            if bare_raise:
                self.add_message("try-except-raise", node=handler_having_bare_raise)

    @utils.only_required_for_messages("wrong-exception-operation")
    def visit_binop(self, node: nodes.BinOp) -> None:
        if isinstance(node.parent, nodes.ExceptHandler):
            # except (V | A)
            suggestion = f"Did you mean '({node.left.as_string()}, {node.right.as_string()})' instead?"
            self.add_message("wrong-exception-operation", node=node, args=(suggestion,))

    @utils.only_required_for_messages("wrong-exception-operation")
    def visit_compare(self, node: nodes.Compare) -> None:
        if isinstance(node.parent, nodes.ExceptHandler):
            # except (V < A)
            suggestion = (
                f"Did you mean '({node.left.as_string()}, "
                f"{', '.join(o.as_string() for _, o in node.ops)})' instead?"
            )
            self.add_message("wrong-exception-operation", node=node, args=(suggestion,))

    @utils.only_required_for_messages(
        "bare-except",
        "broad-exception-caught",
        "try-except-raise",
        "binary-op-exception",
        "bad-except-order",
        "catching-non-exception",
        "duplicate-except",
    )
    def visit_try(self, node: nodes.Try) -> None:
        """Check for empty except."""
        self._check_try_except_raise(node)
        exceptions_classes: list[Any] = []
        nb_handlers = len(node.handlers)
        for index, handler in enumerate(node.handlers):
            if handler.type is None:
                if not _is_raising(handler.body):
                    self.add_message("bare-except", node=handler, confidence=HIGH)

                # check if an "except:" is followed by some other
                # except
                if index < (nb_handlers - 1):
                    msg = "empty except clause should always appear last"
                    self.add_message(
                        "bad-except-order", node=node, args=msg, confidence=HIGH
                    )

            elif isinstance(handler.type, nodes.BoolOp):
                self.add_message(
                    "binary-op-exception",
                    node=handler,
                    args=handler.type.op,
                    confidence=HIGH,
                )
            else:
                try:
                    exceptions = list(_annotated_unpack_infer(handler.type))
                except astroid.InferenceError:
                    continue

                for part, exception in exceptions:
                    if isinstance(
                        exception, astroid.Instance
                    ) and utils.inherit_from_std_ex(exception):
                        exception = exception._proxied

                    self._check_catching_non_exception(handler, exception, part)

                    if not isinstance(exception, nodes.ClassDef):
                        continue

                    exc_ancestors = [
                        anc
                        for anc in exception.ancestors()
                        if isinstance(anc, nodes.ClassDef)
                    ]

                    for previous_exc in exceptions_classes:
                        if previous_exc in exc_ancestors:
                            msg = f"{previous_exc.name} is an ancestor class of {exception.name}"
                            self.add_message(
                                "bad-except-order",
                                node=handler.type,
                                args=msg,
                                confidence=INFERENCE,
                            )
                    if self._is_overgeneral_exception(exception) and not _is_raising(
                        handler.body
                    ):
                        self.add_message(
                            "broad-exception-caught",
                            args=exception.name,
                            node=handler.type,
                            confidence=INFERENCE,
                        )

                    if exception in exceptions_classes:
                        self.add_message(
                            "duplicate-except",
                            args=exception.name,
                            node=handler.type,
                            confidence=INFERENCE,
                        )

                exceptions_classes += [exc for _, exc in exceptions]

    def _is_overgeneral_exception(self, exception: nodes.ClassDef) -> bool:
        return exception.qname() in self.linter.config.overgeneral_exceptions


def register(linter: PyLinter) -> None:
    linter.register_checker(ExceptionsChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/exceptions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Python code format's checker.

By default, try to follow Guido's style guide :

https://www.python.org/doc/essays/styleguide/

Some parts of the process_token method is based from The Tab Nanny std module.
"""

from __future__ import annotations

import tokenize
from functools import reduce
from re import Match
from typing import TYPE_CHECKING, Literal

from astroid import nodes

from pylint.checkers import BaseRawFileChecker, BaseTokenChecker
from pylint.checkers.utils import only_required_for_messages
from pylint.constants import WarningScope
from pylint.interfaces import HIGH
from pylint.typing import MessageDefinitionTuple
from pylint.utils.pragma_parser import OPTION_PO, PragmaParserError, parse_pragma

if TYPE_CHECKING:
    from pylint.lint import PyLinter


_KEYWORD_TOKENS = {
    "assert",
    "del",
    "elif",
    "except",
    "for",
    "if",
    "in",
    "not",
    "raise",
    "return",
    "while",
    "yield",
    "with",
    "=",
    ":=",
}
_JUNK_TOKENS = {tokenize.COMMENT, tokenize.NL}


MSGS: dict[str, MessageDefinitionTuple] = {
    "C0301": (
        "Line too long (%s/%s)",
        "line-too-long",
        "Used when a line is longer than a given number of characters.",
    ),
    "C0302": (
        "Too many lines in module (%s/%s)",  # was W0302
        "too-many-lines",
        "Used when a module has too many lines, reducing its readability.",
    ),
    "C0303": (
        "Trailing whitespace",
        "trailing-whitespace",
        "Used when there is whitespace between the end of a line and the newline.",
    ),
    "C0304": (
        "Final newline missing",
        "missing-final-newline",
        "Used when the last line in a file is missing a newline.",
    ),
    "C0305": (
        "Trailing newlines",
        "trailing-newlines",
        "Used when there are trailing blank lines in a file.",
    ),
    "W0311": (
        "Bad indentation. Found %s %s, expected %s",
        "bad-indentation",
        "Used when an unexpected number of indentation's tabulations or "
        "spaces has been found.",
    ),
    "W0301": (
        "Unnecessary semicolon",  # was W0106
        "unnecessary-semicolon",
        'Used when a statement is ended by a semi-colon (";"), which '
        "isn't necessary (that's python, not C ;).",
    ),
    "C0321": (
        "More than one statement on a single line",
        "multiple-statements",
        "Used when more than on statement are found on the same line.",
        {"scope": WarningScope.NODE},
    ),
    "C0325": (
        "Unnecessary parens after %r keyword",
        "superfluous-parens",
        "Used when a single item in parentheses follows an if, for, or "
        "other keyword.",
    ),
    "C0327": (
        "Mixed line endings LF and CRLF",
        "mixed-line-endings",
        "Used when there are mixed (LF and CRLF) newline signs in a file.",
    ),
    "C0328": (
        "Unexpected line ending format. There is '%s' while it should be '%s'.",
        "unexpected-line-ending-format",
        "Used when there is different newline than expected.",
    ),
}


def _last_token_on_line_is(tokens: TokenWrapper, line_end: int, token: str) -> bool:
    return (
        line_end > 0
        and tokens.token(line_end - 1) == token
        or line_end > 1
        and tokens.token(line_end - 2) == token
        and tokens.type(line_end - 1) == tokenize.COMMENT
    )


class TokenWrapper:
    """A wrapper for readable access to token information."""

    def __init__(self, tokens: list[tokenize.TokenInfo]) -> None:
        self._tokens = tokens

    def token(self, idx: int) -> str:
        return self._tokens[idx][1]

    def type(self, idx: int) -> int:
        return self._tokens[idx][0]

    def start_line(self, idx: int) -> int:
        return self._tokens[idx][2][0]

    def start_col(self, idx: int) -> int:
        return self._tokens[idx][2][1]

    def line(self, idx: int) -> str:
        return self._tokens[idx][4]


class FormatChecker(BaseTokenChecker, BaseRawFileChecker):
    """Formatting checker.

    Checks for :
    * unauthorized constructions
    * strict indentation
    * line length
    """

    # configuration section name
    name = "format"
    # messages
    msgs = MSGS
    # configuration options
    # for available dict keys/values see the optik parser 'add_option' method
    options = (
        (
            "max-line-length",
            {
                "default": 100,
                "type": "int",
                "metavar": "<int>",
                "help": "Maximum number of characters on a single line.",
            },
        ),
        (
            "ignore-long-lines",
            {
                "type": "regexp",
                "metavar": "<regexp>",
                "default": r"^\s*(# )?<?https?://\S+>?$",
                "help": (
                    "Regexp for a line that is allowed to be longer than the limit."
                ),
            },
        ),
        (
            "single-line-if-stmt",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": (
                    "Allow the body of an if to be on the same "
                    "line as the test if there is no else."
                ),
            },
        ),
        (
            "single-line-class-stmt",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": (
                    "Allow the body of a class to be on the same "
                    "line as the declaration if body contains "
                    "single statement."
                ),
            },
        ),
        (
            "max-module-lines",
            {
                "default": 1000,
                "type": "int",
                "metavar": "<int>",
                "help": "Maximum number of lines in a module.",
            },
        ),
        (
            "indent-string",
            {
                "default": "    ",
                "type": "non_empty_string",
                "metavar": "<string>",
                "help": "String used as indentation unit. This is usually "
                '"    " (4 spaces) or "\\t" (1 tab).',
            },
        ),
        (
            "indent-after-paren",
            {
                "type": "int",
                "metavar": "<int>",
                "default": 4,
                "help": "Number of spaces of indent required inside a hanging "
                "or continued line.",
            },
        ),
        (
            "expected-line-ending-format",
            {
                "type": "choice",
                "metavar": "<empty or LF or CRLF>",
                "default": "",
                "choices": ["", "LF", "CRLF"],
                "help": (
                    "Expected format of line ending, "
                    "e.g. empty (any line ending), LF or CRLF."
                ),
            },
        ),
    )

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._lines: dict[int, str] = {}
        self._visited_lines: dict[int, Literal[1, 2]] = {}

    def new_line(self, tokens: TokenWrapper, line_end: int, line_start: int) -> None:
        """A new line has been encountered, process it if necessary."""
        if _last_token_on_line_is(tokens, line_end, ";"):
            self.add_message("unnecessary-semicolon", line=tokens.start_line(line_end))

        line_num = tokens.start_line(line_start)
        line = tokens.line(line_start)
        if tokens.type(line_start) not in _JUNK_TOKENS:
            self._lines[line_num] = line.split("\n")[0]
        self.check_lines(tokens, line_start, line, line_num)

    def process_module(self, node: nodes.Module) -> None:
        pass

    # pylint: disable-next = too-many-return-statements, too-many-branches
    def _check_keyword_parentheses(
        self, tokens: list[tokenize.TokenInfo], start: int
    ) -> None:
        """Check that there are not unnecessary parentheses after a keyword.

        Parens are unnecessary if there is exactly one balanced outer pair on a
        line and contains no commas (i.e. is not a tuple).

        Args:
        tokens: The entire list of Tokens.
        start: The position of the keyword in the token list.
        """
        # If the next token is not a paren, we're fine.
        if tokens[start + 1].string != "(":
            return
        if (
            tokens[start].string == "not"
            and start > 0
            and tokens[start - 1].string == "is"
        ):
            # If this is part of an `is not` expression, we have a binary operator
            # so the parentheses are not necessarily redundant.
            return
        found_and_or = False
        contains_walrus_operator = False
        walrus_operator_depth = 0
        contains_double_parens = 0
        depth = 0
        keyword_token = str(tokens[start].string)
        line_num = tokens[start].start[0]
        for i in range(start, len(tokens) - 1):
            token = tokens[i]

            # If we hit a newline, then assume any parens were for continuation.
            if token.type == tokenize.NL:
                return
            # Since the walrus operator doesn't exist below python3.8, the tokenizer
            # generates independent tokens
            if (
                token.string == ":="  # <-- python3.8+ path
                or token.string + tokens[i + 1].string == ":="
            ):
                contains_walrus_operator = True
                walrus_operator_depth = depth
            if token.string == "(":
                depth += 1
                if tokens[i + 1].string == "(":
                    contains_double_parens = 1
            elif token.string == ")":
                depth -= 1
                if depth:
                    if contains_double_parens and tokens[i + 1].string == ")":
                        # For walrus operators in `if (not)` conditions and comprehensions
                        if keyword_token in {"in", "if", "not"}:
                            continue
                        return
                    contains_double_parens -= 1
                    continue
                # ')' can't happen after if (foo), since it would be a syntax error.
                if tokens[i + 1].string in {":", ")", "]", "}", "in"} or tokens[
                    i + 1
                ].type in {tokenize.NEWLINE, tokenize.ENDMARKER, tokenize.COMMENT}:
                    if contains_walrus_operator and walrus_operator_depth - 1 == depth:
                        return
                    # The empty tuple () is always accepted.
                    if i == start + 2:
                        return
                    if found_and_or:
                        return
                    if keyword_token == "in":
                        # This special case was added in https://github.com/pylint-dev/pylint/pull/4948
                        # but it could be removed in the future. Avoid churn for now.
                        return
                    self.add_message(
                        "superfluous-parens", line=line_num, args=keyword_token
                    )
                return
            elif depth == 1:
                # This is a tuple, which is always acceptable.
                if token[1] == ",":
                    return
                # 'and' and 'or' are the only boolean operators with lower precedence
                # than 'not', so parens are only required when they are found.
                if token[1] in {"and", "or"}:
                    found_and_or = True
                # A yield inside an expression must always be in parentheses,
                # quit early without error.
                elif token[1] == "yield":
                    return
                # A generator expression always has a 'for' token in it, and
                # the 'for' token is only legal inside parens when it is in a
                # generator expression.  The parens are necessary here, so bail
                # without an error.
                elif token[1] == "for":
                    return
                # A generator expression can have an 'else' token in it.
                # We check the rest of the tokens to see if any problems occur after
                # the 'else'.
                elif token[1] == "else":
                    if "(" in (i.string for i in tokens[i:]):
                        self._check_keyword_parentheses(tokens[i:], 0)
                    return

    def process_tokens(self, tokens: list[tokenize.TokenInfo]) -> None:
        """Process tokens and search for:

        - too long lines (i.e. longer than <max_chars>)
        - optionally bad construct (if given, bad_construct must be a compiled
          regular expression).
        """
        indents = [0]
        check_equal = False
        line_num = 0
        self._lines = {}
        self._visited_lines = {}
        self._last_line_ending: str | None = None
        last_blank_line_num = 0
        for idx, (tok_type, string, start, _, line) in enumerate(tokens):
            if start[0] != line_num:
                line_num = start[0]
                # A tokenizer oddity: if an indented line contains a multi-line
                # docstring, the line member of the INDENT token does not contain
                # the full line; therefore we check the next token on the line.
                if tok_type == tokenize.INDENT:
                    self.new_line(TokenWrapper(tokens), idx - 1, idx + 1)
                else:
                    self.new_line(TokenWrapper(tokens), idx - 1, idx)

            if tok_type == tokenize.NEWLINE:
                # a program statement, or ENDMARKER, will eventually follow,
                # after some (possibly empty) run of tokens of the form
                #     (NL | COMMENT)* (INDENT | DEDENT+)?
                # If an INDENT appears, setting check_equal is wrong, and will
                # be undone when we see the INDENT.
                check_equal = True
                self._check_line_ending(string, line_num)
            elif tok_type == tokenize.INDENT:
                check_equal = False
                self.check_indent_level(string, indents[-1] + 1, line_num)
                indents.append(indents[-1] + 1)
            elif tok_type == tokenize.DEDENT:
                # there's nothing we need to check here!  what's important is
                # that when the run of DEDENTs ends, the indentation of the
                # program statement (or ENDMARKER) that triggered the run is
                # equal to what's left at the top of the indents stack
                check_equal = True
                if len(indents) > 1:
                    del indents[-1]
            elif tok_type == tokenize.NL:
                if not line.strip("\r\n"):
                    last_blank_line_num = line_num
            elif tok_type not in (tokenize.COMMENT, tokenize.ENCODING):
                # This is the first concrete token following a NEWLINE, so it
                # must be the first token of the next program statement, or an
                # ENDMARKER; the "line" argument exposes the leading white-space
                # for this statement; in the case of ENDMARKER, line is an empty
                # string, so will properly match the empty string with which the
                # "indents" stack was seeded
                if check_equal:
                    check_equal = False
                    self.check_indent_level(line, indents[-1], line_num)

            if tok_type == tokenize.NUMBER and string.endswith("l"):
                self.add_message("lowercase-l-suffix", line=line_num)

            if string in _KEYWORD_TOKENS:
                self._check_keyword_parentheses(tokens, idx)

        line_num -= 1  # to be ok with "wc -l"
        if line_num > self.linter.config.max_module_lines:
            # Get the line where the too-many-lines (or its message id)
            # was disabled or default to 1.
            message_definition = self.linter.msgs_store.get_message_definitions(
                "too-many-lines"
            )[0]
            names = (message_definition.msgid, "too-many-lines")
            lineno = next(
                filter(None, (self.linter._pragma_lineno.get(name) for name in names)),
                1,
            )
            self.add_message(
                "too-many-lines",
                args=(line_num, self.linter.config.max_module_lines),
                line=lineno,
            )

        # See if there are any trailing lines.  Do not complain about empty
        # files like __init__.py markers.
        if line_num == last_blank_line_num and line_num > 0:
            self.add_message("trailing-newlines", line=line_num)

    def _check_line_ending(self, line_ending: str, line_num: int) -> None:
        # check if line endings are mixed
        if self._last_line_ending is not None:
            # line_ending == "" indicates a synthetic newline added at
            # the end of a file that does not, in fact, end with a
            # newline.
            if line_ending and line_ending != self._last_line_ending:
                self.add_message("mixed-line-endings", line=line_num)

        self._last_line_ending = line_ending

        # check if line ending is as expected
        expected = self.linter.config.expected_line_ending_format
        if expected:
            # reduce multiple \n\n\n\n to one \n
            line_ending = reduce(lambda x, y: x + y if x != y else x, line_ending, "")
            line_ending = "LF" if line_ending == "\n" else "CRLF"
            if line_ending != expected:
                self.add_message(
                    "unexpected-line-ending-format",
                    args=(line_ending, expected),
                    line=line_num,
                )

    @only_required_for_messages("multiple-statements")
    def visit_default(self, node: nodes.NodeNG) -> None:
        """Check the node line number and check it if not yet done."""
        if not node.is_statement:
            return
        if not node.root().pure_python:
            return
        prev_sibl = node.previous_sibling()
        if prev_sibl is not None:
            prev_line = prev_sibl.fromlineno
        elif isinstance(node.parent, nodes.Module):
            prev_line = 0
        else:
            prev_line = node.parent.statement().fromlineno
        line = node.fromlineno
        assert line, node
        if prev_line == line and self._visited_lines.get(line) != 2:
            self._check_multi_statement_line(node, line)
            return
        if line in self._visited_lines:
            return
        try:
            tolineno = node.blockstart_tolineno
        except AttributeError:
            tolineno = node.tolineno
        assert tolineno, node
        lines: list[str] = []
        for line in range(line, tolineno + 1):  # noqa: B020
            self._visited_lines[line] = 1
            try:
                lines.append(self._lines[line].rstrip())
            except KeyError:
                lines.append("")

    def _check_multi_statement_line(self, node: nodes.NodeNG, line: int) -> None:
        """Check for lines containing multiple statements."""
        # Do not warn about multiple nested context managers
        # in with statements.
        if isinstance(node, nodes.With):
            return
        if (
            isinstance(node.parent, nodes.If)
            and not node.parent.orelse
            and self.linter.config.single_line_if_stmt
        ):
            return
        if (
            isinstance(node.parent, nodes.ClassDef)
            and len(node.parent.body) == 1
            and self.linter.config.single_line_class_stmt
        ):
            return

        # Functions stubs with ``Ellipsis`` as body are exempted.
        if (
            isinstance(node.parent, nodes.FunctionDef)
            and isinstance(node, nodes.Expr)
            and isinstance(node.value, nodes.Const)
            and node.value.value is Ellipsis
        ):
            return

        self.add_message("multiple-statements", node=node)
        self._visited_lines[line] = 2

    def check_trailing_whitespace_ending(self, line: str, i: int) -> None:
        """Check that there is no trailing white-space."""
        # exclude \f (formfeed) from the rstrip
        stripped_line = line.rstrip("\t\n\r\v ")
        if line[len(stripped_line) :] not in ("\n", "\r\n"):
            self.add_message(
                "trailing-whitespace",
                line=i,
                col_offset=len(stripped_line),
                confidence=HIGH,
            )

    def check_line_length(self, line: str, i: int, checker_off: bool) -> None:
        """Check that the line length is less than the authorized value."""
        max_chars = self.linter.config.max_line_length
        ignore_long_line = self.linter.config.ignore_long_lines
        line = line.rstrip()
        if len(line) > max_chars and not ignore_long_line.search(line):
            if checker_off:
                self.linter.add_ignored_message("line-too-long", i)
            else:
                self.add_message("line-too-long", line=i, args=(len(line), max_chars))

    @staticmethod
    def remove_pylint_option_from_lines(options_pattern_obj: Match[str]) -> str:
        """Remove the `# pylint ...` pattern from lines."""
        lines = options_pattern_obj.string
        purged_lines = (
            lines[: options_pattern_obj.start(1)].rstrip()
            + lines[options_pattern_obj.end(1) :]
        )
        return purged_lines

    @staticmethod
    def is_line_length_check_activated(pylint_pattern_match_object: Match[str]) -> bool:
        """Return True if the line length check is activated."""
        try:
            for pragma in parse_pragma(pylint_pattern_match_object.group(2)):
                if pragma.action == "disable" and "line-too-long" in pragma.messages:
                    return False
        except PragmaParserError:
            # Printing useful information dealing with this error is done in the lint package
            pass
        return True

    @staticmethod
    def specific_splitlines(lines: str) -> list[str]:
        """Split lines according to universal newlines except those in a specific
        sets.
        """
        unsplit_ends = {
            "\x0b",  # synonym of \v
            "\x0c",  # synonym of \f
            "\x1c",
            "\x1d",
            "\x1e",
            "\x85",
            "\u2028",
            "\u2029",
        }
        res: list[str] = []
        buffer = ""
        for atomic_line in lines.splitlines(True):
            if atomic_line[-1] not in unsplit_ends:
                res.append(buffer + atomic_line)
                buffer = ""
            else:
                buffer += atomic_line
        return res

    def check_lines(
        self, tokens: TokenWrapper, line_start: int, lines: str, lineno: int
    ) -> None:
        """Check given lines for potential messages.

        Check if lines have:
        - a final newline
        - no trailing white-space
        - less than a maximum number of characters
        """
        # we're first going to do a rough check whether any lines in this set
        # go over the line limit. If none of them do, then we don't need to
        # parse out the pylint options later on and can just assume that these
        # lines are clean

        # we'll also handle the line ending check here to avoid double-iteration
        # unless the line lengths are suspect

        max_chars = self.linter.config.max_line_length

        split_lines = self.specific_splitlines(lines)

        for offset, line in enumerate(split_lines):
            if not line.endswith("\n"):
                self.add_message("missing-final-newline", line=lineno + offset)
                continue
            # We don't test for trailing whitespaces in strings
            # See https://github.com/pylint-dev/pylint/issues/6936
            # and https://github.com/pylint-dev/pylint/issues/3822
            if tokens.type(line_start) != tokenize.STRING:
                self.check_trailing_whitespace_ending(line, lineno + offset)

        # This check is purposefully simple and doesn't rstrip since this is running
        # on every line you're checking it's advantageous to avoid doing a lot of work
        potential_line_length_warning = any(
            len(line) > max_chars for line in split_lines
        )

        # if there were no lines passing the max_chars config, we don't bother
        # running the full line check (as we've met an even more strict condition)
        if not potential_line_length_warning:
            return

        # Line length check may be deactivated through `pylint: disable` comment
        mobj = OPTION_PO.search(lines)
        checker_off = False
        if mobj:
            if not self.is_line_length_check_activated(mobj):
                checker_off = True
            # The 'pylint: disable whatever' should not be taken into account for line length count
            lines = self.remove_pylint_option_from_lines(mobj)

        # here we re-run specific_splitlines since we have filtered out pylint options above
        for offset, line in enumerate(self.specific_splitlines(lines)):
            self.check_line_length(line, lineno + offset, checker_off)

    def check_indent_level(self, string: str, expected: int, line_num: int) -> None:
        """Return the indent level of the string."""
        indent = self.linter.config.indent_string
        if indent == "\\t":  # \t is not interpreted in the configuration file
            indent = "\t"
        level = 0
        unit_size = len(indent)
        while string[:unit_size] == indent:
            string = string[unit_size:]
            level += 1
        suppl = ""
        while string and string[0] in " \t":
            suppl += string[0]
            string = string[1:]
        if level != expected or suppl:
            i_type = "spaces"
            if indent[0] == "\t":
                i_type = "tabs"
            self.add_message(
                "bad-indentation",
                line=line_num,
                args=(level * unit_size + len(suppl), i_type, expected * unit_size),
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(FormatChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/format.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Imports checkers for Python code."""

from __future__ import annotations

import collections
import copy
import os
import sys
from collections import defaultdict
from collections.abc import ItemsView, Sequence
from functools import cached_property
from typing import TYPE_CHECKING, Any, Dict, List, Union

import astroid
from astroid import nodes
from astroid.nodes._base_nodes import ImportNode

from pylint.checkers import BaseChecker, DeprecatedMixin
from pylint.checkers.utils import (
    get_import_name,
    in_type_checking_block,
    is_from_fallback_block,
    is_module_ignored,
    is_sys_guard,
    node_ignores_exception,
)
from pylint.exceptions import EmptyReportError
from pylint.graph import DotBackend, get_cycles
from pylint.interfaces import HIGH
from pylint.reporters.ureports.nodes import Paragraph, Section, VerbatimText
from pylint.typing import MessageDefinitionTuple
from pylint.utils import IsortDriver
from pylint.utils.linterstats import LinterStats

if TYPE_CHECKING:
    from pylint.lint import PyLinter


# The dictionary with Any should actually be a _ImportTree again
# but mypy doesn't support recursive types yet
_ImportTree = Dict[str, Union[List[Dict[str, Any]], List[str]]]

DEPRECATED_MODULES = {
    (0, 0, 0): {"tkinter.tix", "fpectl"},
    (3, 2, 0): {"optparse"},
    (3, 3, 0): {"xml.etree.cElementTree"},
    (3, 4, 0): {"imp"},
    (3, 5, 0): {"formatter"},
    (3, 6, 0): {"asynchat", "asyncore", "smtpd"},
    (3, 7, 0): {"macpath"},
    (3, 9, 0): {"lib2to3", "parser", "symbol", "binhex"},
    (3, 10, 0): {"distutils", "typing.io", "typing.re"},
    (3, 11, 0): {
        "aifc",
        "audioop",
        "cgi",
        "cgitb",
        "chunk",
        "crypt",
        "imghdr",
        "msilib",
        "mailcap",
        "nis",
        "nntplib",
        "ossaudiodev",
        "pipes",
        "sndhdr",
        "spwd",
        "sunau",
        "sre_compile",
        "sre_constants",
        "sre_parse",
        "telnetlib",
        "uu",
        "xdrlib",
    },
}


def _get_first_import(
    node: ImportNode,
    context: nodes.LocalsDictNodeNG,
    name: str,
    base: str | None,
    level: int | None,
    alias: str | None,
) -> tuple[nodes.Import | nodes.ImportFrom | None, str | None]:
    """Return the node where [base.]<name> is imported or None if not found."""
    fullname = f"{base}.{name}" if base else name

    first = None
    found = False
    msg = "reimported"

    for first in context.body:
        if first is node:
            continue
        if first.scope() is node.scope() and first.fromlineno > node.fromlineno:
            continue
        if isinstance(first, nodes.Import):
            if any(fullname == iname[0] for iname in first.names):
                found = True
                break
            for imported_name, imported_alias in first.names:
                if not imported_alias and imported_name == alias:
                    found = True
                    msg = "shadowed-import"
                    break
            if found:
                break
        elif isinstance(first, nodes.ImportFrom):
            if level == first.level:
                for imported_name, imported_alias in first.names:
                    if fullname == f"{first.modname}.{imported_name}":
                        found = True
                        break
                    if (
                        name != "*"
                        and name == imported_name
                        and not (alias or imported_alias)
                    ):
                        found = True
                        break
                    if not imported_alias and imported_name == alias:
                        found = True
                        msg = "shadowed-import"
                        break
                if found:
                    break
    if found and not astroid.are_exclusive(first, node):
        return first, msg
    return None, None


def _ignore_import_failure(
    node: ImportNode,
    modname: str,
    ignored_modules: Sequence[str],
) -> bool:
    if is_module_ignored(modname, ignored_modules):
        return True

    # Ignore import failure if part of guarded import block
    # I.e. `sys.version_info` or `typing.TYPE_CHECKING`
    if in_type_checking_block(node):
        return True
    if isinstance(node.parent, nodes.If) and is_sys_guard(node.parent):
        return True

    return node_ignores_exception(node, ImportError)


# utilities to represents import dependencies as tree and dot graph ###########


def _make_tree_defs(mod_files_list: ItemsView[str, set[str]]) -> _ImportTree:
    """Get a list of 2-uple (module, list_of_files_which_import_this_module),
    it will return a dictionary to represent this as a tree.
    """
    tree_defs: _ImportTree = {}
    for mod, files in mod_files_list:
        node: list[_ImportTree | list[str]] = [tree_defs, []]
        for prefix in mod.split("."):
            assert isinstance(node[0], dict)
            node = node[0].setdefault(prefix, ({}, []))  # type: ignore[arg-type,assignment]
        assert isinstance(node[1], list)
        node[1].extend(files)
    return tree_defs


def _repr_tree_defs(data: _ImportTree, indent_str: str | None = None) -> str:
    """Return a string which represents imports as a tree."""
    lines = []
    nodes_items = data.items()
    for i, (mod, (sub, files)) in enumerate(sorted(nodes_items, key=lambda x: x[0])):
        files_list = "" if not files else f"({','.join(sorted(files))})"
        if indent_str is None:
            lines.append(f"{mod} {files_list}")
            sub_indent_str = "  "
        else:
            lines.append(rf"{indent_str}\-{mod} {files_list}")
            if i == len(nodes_items) - 1:
                sub_indent_str = f"{indent_str}  "
            else:
                sub_indent_str = f"{indent_str}| "
        if sub and isinstance(sub, dict):
            lines.append(_repr_tree_defs(sub, sub_indent_str))
    return "\n".join(lines)


def _dependencies_graph(filename: str, dep_info: dict[str, set[str]]) -> str:
    """Write dependencies as a dot (graphviz) file."""
    done = {}
    printer = DotBackend(os.path.splitext(os.path.basename(filename))[0], rankdir="LR")
    printer.emit('URL="." node[shape="box"]')
    for modname, dependencies in sorted(dep_info.items()):
        sorted_dependencies = sorted(dependencies)
        done[modname] = 1
        printer.emit_node(modname)
        for depmodname in sorted_dependencies:
            if depmodname not in done:
                done[depmodname] = 1
                printer.emit_node(depmodname)
    for depmodname, dependencies in sorted(dep_info.items()):
        for modname in sorted(dependencies):
            printer.emit_edge(modname, depmodname)
    return printer.generate(filename)


def _make_graph(
    filename: str, dep_info: dict[str, set[str]], sect: Section, gtype: str
) -> None:
    """Generate a dependencies graph and add some information about it in the
    report's section.
    """
    outputfile = _dependencies_graph(filename, dep_info)
    sect.append(Paragraph((f"{gtype}imports graph has been written to {outputfile}",)))


# the import checker itself ###################################################

MSGS: dict[str, MessageDefinitionTuple] = {
    "E0401": (
        "Unable to import %s",
        "import-error",
        "Used when pylint has been unable to import a module.",
        {"old_names": [("F0401", "old-import-error")]},
    ),
    "E0402": (
        "Attempted relative import beyond top-level package",
        "relative-beyond-top-level",
        "Used when a relative import tries to access too many levels "
        "in the current package.",
    ),
    "R0401": (
        "Cyclic import (%s)",
        "cyclic-import",
        "Used when a cyclic import between two or more modules is detected.",
    ),
    "R0402": (
        "Use 'from %s import %s' instead",
        "consider-using-from-import",
        "Emitted when a submodule of a package is imported and "
        "aliased with the same name, "
        "e.g., instead of ``import concurrent.futures as futures`` use "
        "``from concurrent import futures``.",
    ),
    "W0401": (
        "Wildcard import %s",
        "wildcard-import",
        "Used when `from module import *` is detected.",
    ),
    "W0404": (
        "Reimport %r (imported line %s)",
        "reimported",
        "Used when a module is imported more than once.",
    ),
    "W0406": (
        "Module import itself",
        "import-self",
        "Used when a module is importing itself.",
    ),
    "W0407": (
        "Prefer importing %r instead of %r",
        "preferred-module",
        "Used when a module imported has a preferred replacement module.",
    ),
    "W0410": (
        "__future__ import is not the first non docstring statement",
        "misplaced-future",
        "Python 2.5 and greater require __future__ import to be the "
        "first non docstring statement in the module.",
    ),
    "C0410": (
        "Multiple imports on one line (%s)",
        "multiple-imports",
        "Used when import statement importing multiple modules is detected.",
    ),
    "C0411": (
        "%s should be placed before %s",
        "wrong-import-order",
        "Used when PEP8 import order is not respected (standard imports "
        "first, then third-party libraries, then local imports).",
    ),
    "C0412": (
        "Imports from package %s are not grouped",
        "ungrouped-imports",
        "Used when imports are not grouped by packages.",
    ),
    "C0413": (
        'Import "%s" should be placed at the top of the module',
        "wrong-import-position",
        "Used when code and imports are mixed.",
    ),
    "C0414": (
        "Import alias does not rename original package",
        "useless-import-alias",
        "Used when an import alias is same as original package, "
        "e.g., using import numpy as numpy instead of import numpy as np.",
    ),
    "C0415": (
        "Import outside toplevel (%s)",
        "import-outside-toplevel",
        "Used when an import statement is used anywhere other than the module "
        "toplevel. Move this import to the top of the file.",
    ),
    "W0416": (
        "Shadowed %r (imported line %s)",
        "shadowed-import",
        "Used when a module is aliased with a name that shadows another import.",
    ),
}


DEFAULT_STANDARD_LIBRARY = ()
DEFAULT_KNOWN_THIRD_PARTY = ("enchant",)
DEFAULT_PREFERRED_MODULES = ()


class ImportsChecker(DeprecatedMixin, BaseChecker):
    """BaseChecker for import statements.

    Checks for
    * external modules dependencies
    * relative / wildcard imports
    * cyclic imports
    * uses of deprecated modules
    * uses of modules instead of preferred modules
    """

    name = "imports"
    msgs = {**DeprecatedMixin.DEPRECATED_MODULE_MESSAGE, **MSGS}
    default_deprecated_modules = ()

    options = (
        (
            "deprecated-modules",
            {
                "default": default_deprecated_modules,
                "type": "csv",
                "metavar": "<modules>",
                "help": "Deprecated modules which should not be used,"
                " separated by a comma.",
            },
        ),
        (
            "preferred-modules",
            {
                "default": DEFAULT_PREFERRED_MODULES,
                "type": "csv",
                "metavar": "<module:preferred-module>",
                "help": "Couples of modules and preferred modules,"
                " separated by a comma.",
            },
        ),
        (
            "import-graph",
            {
                "default": "",
                "type": "path",
                "metavar": "<file.gv>",
                "help": "Output a graph (.gv or any supported image format) of"
                " all (i.e. internal and external) dependencies to the given file"
                " (report RP0402 must not be disabled).",
            },
        ),
        (
            "ext-import-graph",
            {
                "default": "",
                "type": "path",
                "metavar": "<file.gv>",
                "help": "Output a graph (.gv or any supported image format)"
                " of external dependencies to the given file"
                " (report RP0402 must not be disabled).",
            },
        ),
        (
            "int-import-graph",
            {
                "default": "",
                "type": "path",
                "metavar": "<file.gv>",
                "help": "Output a graph (.gv or any supported image format)"
                " of internal dependencies to the given file"
                " (report RP0402 must not be disabled).",
            },
        ),
        (
            "known-standard-library",
            {
                "default": DEFAULT_STANDARD_LIBRARY,
                "type": "csv",
                "metavar": "<modules>",
                "help": "Force import order to recognize a module as part of "
                "the standard compatibility libraries.",
            },
        ),
        (
            "known-third-party",
            {
                "default": DEFAULT_KNOWN_THIRD_PARTY,
                "type": "csv",
                "metavar": "<modules>",
                "help": "Force import order to recognize a module as part of "
                "a third party library.",
            },
        ),
        (
            "allow-any-import-level",
            {
                "default": (),
                "type": "csv",
                "metavar": "<modules>",
                "help": (
                    "List of modules that can be imported at any level, not just "
                    "the top level one."
                ),
            },
        ),
        (
            "allow-wildcard-with-all",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Allow wildcard imports from modules that define __all__.",
            },
        ),
        (
            "allow-reexport-from-package",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Allow explicit reexports by alias from a package __init__.",
            },
        ),
    )

    def __init__(self, linter: PyLinter) -> None:
        BaseChecker.__init__(self, linter)
        self.import_graph: defaultdict[str, set[str]] = defaultdict(set)
        self._imports_stack: list[tuple[ImportNode, str]] = []
        self._first_non_import_node = None
        self._module_pkg: dict[
            Any, Any
        ] = {}  # mapping of modules to the pkg they belong in
        self._allow_any_import_level: set[Any] = set()
        self.reports = (
            ("RP0401", "External dependencies", self._report_external_dependencies),
            ("RP0402", "Modules dependencies graph", self._report_dependencies_graph),
        )
        self._excluded_edges: defaultdict[str, set[str]] = defaultdict(set)

    def open(self) -> None:
        """Called before visiting project (i.e set of modules)."""
        self.linter.stats.dependencies = {}
        self.linter.stats = self.linter.stats
        self.import_graph = defaultdict(set)
        self._module_pkg = {}  # mapping of modules to the pkg they belong in
        self._current_module_package = False
        self._ignored_modules: Sequence[str] = self.linter.config.ignored_modules
        # Build a mapping {'module': 'preferred-module'}
        self.preferred_modules = dict(
            module.split(":")
            for module in self.linter.config.preferred_modules
            if ":" in module
        )
        self._allow_any_import_level = set(self.linter.config.allow_any_import_level)
        self._allow_reexport_package = self.linter.config.allow_reexport_from_package

    def _import_graph_without_ignored_edges(self) -> defaultdict[str, set[str]]:
        filtered_graph = copy.deepcopy(self.import_graph)
        for node in filtered_graph:
            filtered_graph[node].difference_update(self._excluded_edges[node])
        return filtered_graph

    def close(self) -> None:
        """Called before visiting project (i.e set of modules)."""
        if self.linter.is_message_enabled("cyclic-import"):
            graph = self._import_graph_without_ignored_edges()
            vertices = list(graph)
            for cycle in get_cycles(graph, vertices=vertices):
                self.add_message("cyclic-import", args=" -> ".join(cycle))

    def get_map_data(
        self,
    ) -> tuple[defaultdict[str, set[str]], defaultdict[str, set[str]]]:
        if self.linter.is_message_enabled("cyclic-import"):
            return (self.import_graph, self._excluded_edges)
        return (defaultdict(set), defaultdict(set))

    def reduce_map_data(
        self,
        linter: PyLinter,
        data: list[tuple[defaultdict[str, set[str]], defaultdict[str, set[str]]]],
    ) -> None:
        if self.linter.is_message_enabled("cyclic-import"):
            self.import_graph = defaultdict(set)
            self._excluded_edges = defaultdict(set)
            for to_update in data:
                graph, excluded_edges = to_update
                self.import_graph.update(graph)
                self._excluded_edges.update(excluded_edges)

            self.close()

    def deprecated_modules(self) -> set[str]:
        """Callback returning the deprecated modules."""
        # First get the modules the user indicated
        all_deprecated_modules = set(self.linter.config.deprecated_modules)
        # Now get the hard-coded ones from the stdlib
        for since_vers, mod_set in DEPRECATED_MODULES.items():
            if since_vers <= sys.version_info:
                all_deprecated_modules = all_deprecated_modules.union(mod_set)
        return all_deprecated_modules

    def visit_module(self, node: nodes.Module) -> None:
        """Store if current module is a package, i.e. an __init__ file."""
        self._current_module_package = node.package

    def visit_import(self, node: nodes.Import) -> None:
        """Triggered when an import statement is seen."""
        self._check_reimport(node)
        self._check_import_as_rename(node)
        self._check_toplevel(node)

        names = [name for name, _ in node.names]
        if len(names) >= 2:
            self.add_message("multiple-imports", args=", ".join(names), node=node)

        for name in names:
            self.check_deprecated_module(node, name)
            self._check_preferred_module(node, name)
            imported_module = self._get_imported_module(node, name)
            if isinstance(node.parent, nodes.Module):
                # Allow imports nested
                self._check_position(node)
            if isinstance(node.scope(), nodes.Module):
                self._record_import(node, imported_module)

            if imported_module is None:
                continue

            self._add_imported_module(node, imported_module.name)

    def visit_importfrom(self, node: nodes.ImportFrom) -> None:
        """Triggered when a from statement is seen."""
        basename = node.modname
        imported_module = self._get_imported_module(node, basename)
        absolute_name = get_import_name(node, basename)

        self._check_import_as_rename(node)
        self._check_misplaced_future(node)
        self.check_deprecated_module(node, absolute_name)
        self._check_preferred_module(node, basename)
        self._check_wildcard_imports(node, imported_module)
        self._check_same_line_imports(node)
        self._check_reimport(node, basename=basename, level=node.level)
        self._check_toplevel(node)

        if isinstance(node.parent, nodes.Module):
            # Allow imports nested
            self._check_position(node)
        if isinstance(node.scope(), nodes.Module):
            self._record_import(node, imported_module)
        if imported_module is None:
            return
        for name, _ in node.names:
            if name != "*":
                self._add_imported_module(node, f"{imported_module.name}.{name}")
            else:
                self._add_imported_module(node, imported_module.name)

    def leave_module(self, node: nodes.Module) -> None:
        # Check imports are grouped by category (standard, 3rd party, local)
        std_imports, ext_imports, loc_imports = self._check_imports_order(node)

        # Check that imports are grouped by package within a given category
        met_import: set[str] = set()  # set for 'import x' style
        met_from: set[str] = set()  # set for 'from x import y' style
        current_package = None
        for import_node, import_name in std_imports + ext_imports + loc_imports:
            met = met_from if isinstance(import_node, nodes.ImportFrom) else met_import
            package, _, _ = import_name.partition(".")
            if (
                current_package
                and current_package != package
                and package in met
                and not in_type_checking_block(import_node)
                and not (
                    isinstance(import_node.parent, nodes.If)
                    and is_sys_guard(import_node.parent)
                )
            ):
                self.add_message("ungrouped-imports", node=import_node, args=package)
            current_package = package
            if not self.linter.is_message_enabled(
                "ungrouped-imports", import_node.fromlineno
            ):
                continue
            met.add(package)

        self._imports_stack = []
        self._first_non_import_node = None

    def compute_first_non_import_node(
        self,
        node: nodes.If
        | nodes.Expr
        | nodes.Comprehension
        | nodes.IfExp
        | nodes.Assign
        | nodes.AssignAttr
        | nodes.Try,
    ) -> None:
        # if the node does not contain an import instruction, and if it is the
        # first node of the module, keep a track of it (all the import positions
        # of the module will be compared to the position of this first
        # instruction)
        if self._first_non_import_node:
            return
        if not isinstance(node.parent, nodes.Module):
            return
        if isinstance(node, nodes.Try) and any(
            node.nodes_of_class((nodes.Import, nodes.ImportFrom))
        ):
            return
        if isinstance(node, nodes.Assign):
            # Add compatibility for module level dunder names
            # https://www.python.org/dev/peps/pep-0008/#module-level-dunder-names
            valid_targets = [
                isinstance(target, nodes.AssignName)
                and target.name.startswith("__")
                and target.name.endswith("__")
                for target in node.targets
            ]
            if all(valid_targets):
                return
        self._first_non_import_node = node

    visit_try = (
        visit_assignattr
    ) = (
        visit_assign
    ) = (
        visit_ifexp
    ) = visit_comprehension = visit_expr = visit_if = compute_first_non_import_node

    def visit_functiondef(
        self, node: nodes.FunctionDef | nodes.While | nodes.For | nodes.ClassDef
    ) -> None:
        # If it is the first non import instruction of the module, record it.
        if self._first_non_import_node:
            return

        # Check if the node belongs to an `If` or a `Try` block. If they
        # contain imports, skip recording this node.
        if not isinstance(node.parent.scope(), nodes.Module):
            return

        root = node
        while not isinstance(root.parent, nodes.Module):
            root = root.parent

        if isinstance(root, (nodes.If, nodes.Try)):
            if any(root.nodes_of_class((nodes.Import, nodes.ImportFrom))):
                return

        self._first_non_import_node = node

    visit_classdef = visit_for = visit_while = visit_functiondef

    def _check_misplaced_future(self, node: nodes.ImportFrom) -> None:
        basename = node.modname
        if basename == "__future__":
            # check if this is the first non-docstring statement in the module
            prev = node.previous_sibling()
            if prev:
                # consecutive future statements are possible
                if not (
                    isinstance(prev, nodes.ImportFrom) and prev.modname == "__future__"
                ):
                    self.add_message("misplaced-future", node=node)
            return

    def _check_same_line_imports(self, node: nodes.ImportFrom) -> None:
        # Detect duplicate imports on the same line.
        names = (name for name, _ in node.names)
        counter = collections.Counter(names)
        for name, count in counter.items():
            if count > 1:
                self.add_message("reimported", node=node, args=(name, node.fromlineno))

    def _check_position(self, node: ImportNode) -> None:
        """Check `node` import or importfrom node position is correct.

        Send a message  if `node` comes before another instruction
        """
        # if a first non-import instruction has already been encountered,
        # it means the import comes after it and therefore is not well placed
        if self._first_non_import_node:
            if self.linter.is_message_enabled(
                "wrong-import-position", self._first_non_import_node.fromlineno
            ):
                self.add_message(
                    "wrong-import-position", node=node, args=node.as_string()
                )
            else:
                self.linter.add_ignored_message(
                    "wrong-import-position", node.fromlineno, node
                )

    def _record_import(
        self,
        node: ImportNode,
        importedmodnode: nodes.Module | None,
    ) -> None:
        """Record the package `node` imports from."""
        if isinstance(node, nodes.ImportFrom):
            importedname = node.modname
        else:
            importedname = importedmodnode.name if importedmodnode else None
        if not importedname:
            importedname = node.names[0][0].split(".")[0]

        if isinstance(node, nodes.ImportFrom) and (node.level or 0) >= 1:
            # We need the importedname with first point to detect local package
            # Example of node:
            #  'from .my_package1 import MyClass1'
            #  the output should be '.my_package1' instead of 'my_package1'
            # Example of node:
            #  'from . import my_package2'
            #  the output should be '.my_package2' instead of '{pyfile}'
            importedname = "." + importedname

        self._imports_stack.append((node, importedname))

    @staticmethod
    def _is_fallback_import(
        node: ImportNode, imports: list[tuple[ImportNode, str]]
    ) -> bool:
        imports = [import_node for (import_node, _) in imports]
        return any(astroid.are_exclusive(import_node, node) for import_node in imports)

    # pylint: disable = too-many-statements
    def _check_imports_order(
        self, _module_node: nodes.Module
    ) -> tuple[
        list[tuple[ImportNode, str]],
        list[tuple[ImportNode, str]],
        list[tuple[ImportNode, str]],
    ]:
        """Checks imports of module `node` are grouped by category.

        Imports must follow this order: standard, 3rd party, local
        """
        std_imports: list[tuple[ImportNode, str]] = []
        third_party_imports: list[tuple[ImportNode, str]] = []
        first_party_imports: list[tuple[ImportNode, str]] = []
        # need of a list that holds third or first party ordered import
        external_imports: list[tuple[ImportNode, str]] = []
        local_imports: list[tuple[ImportNode, str]] = []
        third_party_not_ignored: list[tuple[ImportNode, str]] = []
        first_party_not_ignored: list[tuple[ImportNode, str]] = []
        local_not_ignored: list[tuple[ImportNode, str]] = []
        isort_driver = IsortDriver(self.linter.config)
        for node, modname in self._imports_stack:
            if modname.startswith("."):
                package = "." + modname.split(".")[1]
            else:
                package = modname.split(".")[0]
            nested = not isinstance(node.parent, nodes.Module)
            ignore_for_import_order = not self.linter.is_message_enabled(
                "wrong-import-order", node.fromlineno
            )
            import_category = isort_driver.place_module(package)
            node_and_package_import = (node, package)
            if import_category in {"FUTURE", "STDLIB"}:
                std_imports.append(node_and_package_import)
                wrong_import = (
                    third_party_not_ignored
                    or first_party_not_ignored
                    or local_not_ignored
                )
                if self._is_fallback_import(node, wrong_import):
                    continue
                if wrong_import and not nested:
                    self.add_message(
                        "wrong-import-order",
                        node=node,
                        args=(
                            f'standard import "{node.as_string()}"',
                            f'"{wrong_import[0][0].as_string()}"',
                        ),
                    )
            elif import_category == "THIRDPARTY":
                third_party_imports.append(node_and_package_import)
                external_imports.append(node_and_package_import)
                if not nested:
                    if not ignore_for_import_order:
                        third_party_not_ignored.append(node_and_package_import)
                    else:
                        self.linter.add_ignored_message(
                            "wrong-import-order", node.fromlineno, node
                        )
                wrong_import = first_party_not_ignored or local_not_ignored
                if wrong_import and not nested:
                    self.add_message(
                        "wrong-import-order",
                        node=node,
                        args=(
                            f'third party import "{node.as_string()}"',
                            f'"{wrong_import[0][0].as_string()}"',
                        ),
                    )
            elif import_category == "FIRSTPARTY":
                first_party_imports.append(node_and_package_import)
                external_imports.append(node_and_package_import)
                if not nested:
                    if not ignore_for_import_order:
                        first_party_not_ignored.append(node_and_package_import)
                    else:
                        self.linter.add_ignored_message(
                            "wrong-import-order", node.fromlineno, node
                        )
                wrong_import = local_not_ignored
                if wrong_import and not nested:
                    self.add_message(
                        "wrong-import-order",
                        node=node,
                        args=(
                            f'first party import "{node.as_string()}"',
                            f'"{wrong_import[0][0].as_string()}"',
                        ),
                    )
            elif import_category == "LOCALFOLDER":
                local_imports.append((node, package))
                if not nested:
                    if not ignore_for_import_order:
                        local_not_ignored.append((node, package))
                    else:
                        self.linter.add_ignored_message(
                            "wrong-import-order", node.fromlineno, node
                        )
        return std_imports, external_imports, local_imports

    def _get_imported_module(
        self, importnode: ImportNode, modname: str
    ) -> nodes.Module | None:
        try:
            return importnode.do_import_module(modname)
        except astroid.TooManyLevelsError:
            if _ignore_import_failure(importnode, modname, self._ignored_modules):
                return None
            self.add_message("relative-beyond-top-level", node=importnode)
        except astroid.AstroidSyntaxError as exc:
            message = f"Cannot import {modname!r} due to '{exc.error}'"
            self.add_message(
                "syntax-error", line=importnode.lineno, args=message, confidence=HIGH
            )

        except astroid.AstroidBuildingError:
            if not self.linter.is_message_enabled("import-error"):
                return None
            if _ignore_import_failure(importnode, modname, self._ignored_modules):
                return None
            if (
                not self.linter.config.analyse_fallback_blocks
                and is_from_fallback_block(importnode)
            ):
                return None

            dotted_modname = get_import_name(importnode, modname)
            self.add_message("import-error", args=repr(dotted_modname), node=importnode)
        except Exception as e:  # pragma: no cover
            raise astroid.AstroidError from e
        return None

    def _add_imported_module(self, node: ImportNode, importedmodname: str) -> None:
        """Notify an imported module, used to analyze dependencies."""
        module_file = node.root().file
        context_name = node.root().name
        base = os.path.splitext(os.path.basename(module_file))[0]

        try:
            importedmodname = astroid.modutils.get_module_part(
                importedmodname, module_file
            )
        except ImportError:
            pass

        if context_name == importedmodname:
            self.add_message("import-self", node=node)

        elif not astroid.modutils.is_stdlib_module(importedmodname):
            # if this is not a package __init__ module
            if base != "__init__" and context_name not in self._module_pkg:
                # record the module's parent, or the module itself if this is
                # a top level module, as the package it belongs to
                self._module_pkg[context_name] = context_name.rsplit(".", 1)[0]

            # handle dependencies
            dependencies_stat: dict[str, set[str]] = self.linter.stats.dependencies
            importedmodnames = dependencies_stat.setdefault(importedmodname, set())
            if context_name not in importedmodnames:
                importedmodnames.add(context_name)

            # update import graph
            self.import_graph[context_name].add(importedmodname)
            if not self.linter.is_message_enabled(
                "cyclic-import", line=node.lineno
            ) or in_type_checking_block(node):
                self._excluded_edges[context_name].add(importedmodname)

    def _check_preferred_module(self, node: ImportNode, mod_path: str) -> None:
        """Check if the module has a preferred replacement."""

        mod_compare = [mod_path]
        # build a comparison list of possible names using importfrom
        if isinstance(node, astroid.nodes.node_classes.ImportFrom):
            mod_compare = [f"{node.modname}.{name[0]}" for name in node.names]

        # find whether there are matches with the import vs preferred_modules keys
        matches = [
            k
            for k in self.preferred_modules
            for mod in mod_compare
            # exact match
            if k == mod
            # checks for base module matches
            or k in mod.split(".")[0]
        ]

        # if we have matches, add message
        if matches:
            self.add_message(
                "preferred-module",
                node=node,
                args=(self.preferred_modules[matches[0]], matches[0]),
            )

    def _check_import_as_rename(self, node: ImportNode) -> None:
        names = node.names
        for name in names:
            if not all(name):
                return

            splitted_packages = name[0].rsplit(".", maxsplit=1)
            import_name = splitted_packages[-1]
            aliased_name = name[1]
            if import_name != aliased_name:
                continue

            if len(splitted_packages) == 1 and (
                self._allow_reexport_package is False
                or self._current_module_package is False
            ):
                self.add_message("useless-import-alias", node=node, confidence=HIGH)
            elif len(splitted_packages) == 2:
                self.add_message(
                    "consider-using-from-import",
                    node=node,
                    args=(splitted_packages[0], import_name),
                )

    def _check_reimport(
        self,
        node: ImportNode,
        basename: str | None = None,
        level: int | None = None,
    ) -> None:
        """Check if a module with the same name is already imported or aliased."""
        if not self.linter.is_message_enabled(
            "reimported"
        ) and not self.linter.is_message_enabled("shadowed-import"):
            return

        frame = node.frame()
        root = node.root()
        contexts = [(frame, level)]
        if root is not frame:
            contexts.append((root, None))

        for known_context, known_level in contexts:
            for name, alias in node.names:
                first, msg = _get_first_import(
                    node, known_context, name, basename, known_level, alias
                )
                if first is not None and msg is not None:
                    name = name if msg == "reimported" else alias
                    self.add_message(
                        msg, node=node, args=(name, first.fromlineno), confidence=HIGH
                    )

    def _report_external_dependencies(
        self, sect: Section, _: LinterStats, _dummy: LinterStats | None
    ) -> None:
        """Return a verbatim layout for displaying dependencies."""
        dep_info = _make_tree_defs(self._external_dependencies_info.items())
        if not dep_info:
            raise EmptyReportError()
        tree_str = _repr_tree_defs(dep_info)
        sect.append(VerbatimText(tree_str))

    def _report_dependencies_graph(
        self, sect: Section, _: LinterStats, _dummy: LinterStats | None
    ) -> None:
        """Write dependencies as a dot (graphviz) file."""
        dep_info = self.linter.stats.dependencies
        if not dep_info or not (
            self.linter.config.import_graph
            or self.linter.config.ext_import_graph
            or self.linter.config.int_import_graph
        ):
            raise EmptyReportError()
        filename = self.linter.config.import_graph
        if filename:
            _make_graph(filename, dep_info, sect, "")
        filename = self.linter.config.ext_import_graph
        if filename:
            _make_graph(filename, self._external_dependencies_info, sect, "external ")
        filename = self.linter.config.int_import_graph
        if filename:
            _make_graph(filename, self._internal_dependencies_info, sect, "internal ")

    def _filter_dependencies_graph(self, internal: bool) -> defaultdict[str, set[str]]:
        """Build the internal or the external dependency graph."""
        graph: defaultdict[str, set[str]] = defaultdict(set)
        for importee, importers in self.linter.stats.dependencies.items():
            for importer in importers:
                package = self._module_pkg.get(importer, importer)
                is_inside = importee.startswith(package)
                if is_inside and internal or not is_inside and not internal:
                    graph[importee].add(importer)
        return graph

    @cached_property
    def _external_dependencies_info(self) -> defaultdict[str, set[str]]:
        """Return cached external dependencies information or build and
        cache them.
        """
        return self._filter_dependencies_graph(internal=False)

    @cached_property
    def _internal_dependencies_info(self) -> defaultdict[str, set[str]]:
        """Return cached internal dependencies information or build and
        cache them.
        """
        return self._filter_dependencies_graph(internal=True)

    def _check_wildcard_imports(
        self, node: nodes.ImportFrom, imported_module: nodes.Module | None
    ) -> None:
        if node.root().package:
            # Skip the check if in __init__.py issue #2026
            return

        wildcard_import_is_allowed = self._wildcard_import_is_allowed(imported_module)
        for name, _ in node.names:
            if name == "*" and not wildcard_import_is_allowed:
                self.add_message("wildcard-import", args=node.modname, node=node)

    def _wildcard_import_is_allowed(self, imported_module: nodes.Module | None) -> bool:
        return (
            self.linter.config.allow_wildcard_with_all
            and imported_module is not None
            and "__all__" in imported_module.locals
        )

    def _check_toplevel(self, node: ImportNode) -> None:
        """Check whether the import is made outside the module toplevel."""
        # If the scope of the import is a module, then obviously it is
        # not outside the module toplevel.
        if isinstance(node.scope(), nodes.Module):
            return

        module_names = [
            f"{node.modname}.{name[0]}"
            if isinstance(node, nodes.ImportFrom)
            else name[0]
            for name in node.names
        ]

        # Get the full names of all the imports that are only allowed at the module level
        scoped_imports = [
            name for name in module_names if name not in self._allow_any_import_level
        ]

        if scoped_imports:
            self.add_message(
                "import-outside-toplevel", args=", ".join(scoped_imports), node=node
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(ImportsChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/imports.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from itertools import zip_longest
from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class LambdaExpressionChecker(BaseChecker):
    """Check for unnecessary usage of lambda expressions."""

    name = "lambda-expressions"
    msgs = {
        "C3001": (
            "Lambda expression assigned to a variable. "
            'Define a function using the "def" keyword instead.',
            "unnecessary-lambda-assignment",
            "Used when a lambda expression is assigned to variable "
            'rather than defining a standard function with the "def" keyword.',
        ),
        "C3002": (
            "Lambda expression called directly. Execute the expression inline instead.",
            "unnecessary-direct-lambda-call",
            "Used when a lambda expression is directly called "
            "rather than executing its contents inline.",
        ),
    }
    options = ()

    def visit_assign(self, node: nodes.Assign) -> None:
        """Check if lambda expression is assigned to a variable."""
        if isinstance(node.targets[0], nodes.AssignName) and isinstance(
            node.value, nodes.Lambda
        ):
            self.add_message(
                "unnecessary-lambda-assignment",
                node=node.value,
                confidence=HIGH,
            )
        elif isinstance(node.targets[0], nodes.Tuple) and isinstance(
            node.value, (nodes.Tuple, nodes.List)
        ):
            # Iterate over tuple unpacking assignment elements and
            # see if any lambdas are assigned to a variable.
            # N.B. We may encounter W0632 (unbalanced-tuple-unpacking)
            # and still need to flag the lambdas that are being assigned.
            for lhs_elem, rhs_elem in zip_longest(
                node.targets[0].elts, node.value.elts
            ):
                if lhs_elem is None or rhs_elem is None:
                    # unbalanced tuple unpacking. stop checking.
                    break
                if isinstance(lhs_elem, nodes.AssignName) and isinstance(
                    rhs_elem, nodes.Lambda
                ):
                    self.add_message(
                        "unnecessary-lambda-assignment",
                        node=rhs_elem,
                        confidence=HIGH,
                    )

    def visit_namedexpr(self, node: nodes.NamedExpr) -> None:
        if isinstance(node.target, nodes.AssignName) and isinstance(
            node.value, nodes.Lambda
        ):
            self.add_message(
                "unnecessary-lambda-assignment",
                node=node.value,
                confidence=HIGH,
            )

    def visit_call(self, node: nodes.Call) -> None:
        """Check if lambda expression is called directly."""
        if isinstance(node.func, nodes.Lambda):
            self.add_message(
                "unnecessary-direct-lambda-call",
                node=node,
                confidence=HIGH,
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(LambdaExpressionChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/lambda_expressions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checker for use of Python logging."""

from __future__ import annotations

import string
from typing import TYPE_CHECKING, Literal

import astroid
from astroid import bases, nodes
from astroid.typing import InferenceResult

from pylint import checkers
from pylint.checkers import utils
from pylint.checkers.utils import infer_all
from pylint.typing import MessageDefinitionTuple

if TYPE_CHECKING:
    from pylint.lint import PyLinter

MSGS: dict[
    str, MessageDefinitionTuple
] = {  # pylint: disable=consider-using-namedtuple-or-dataclass
    "W1201": (
        "Use %s formatting in logging functions",
        "logging-not-lazy",
        "Used when a logging statement has a call form of "
        '"logging.<logging method>(format_string % (format_args...))". '
        "Use another type of string formatting instead. "
        "You can use % formatting but leave interpolation to "
        "the logging function by passing the parameters as arguments. "
        "If logging-fstring-interpolation is disabled then "
        "you can use fstring formatting. "
        "If logging-format-interpolation is disabled then "
        "you can use str.format.",
    ),
    "W1202": (
        "Use %s formatting in logging functions",
        "logging-format-interpolation",
        "Used when a logging statement has a call form of "
        '"logging.<logging method>(format_string.format(format_args...))". '
        "Use another type of string formatting instead. "
        "You can use % formatting but leave interpolation to "
        "the logging function by passing the parameters as arguments. "
        "If logging-fstring-interpolation is disabled then "
        "you can use fstring formatting. "
        "If logging-not-lazy is disabled then "
        "you can use % formatting as normal.",
    ),
    "W1203": (
        "Use %s formatting in logging functions",
        "logging-fstring-interpolation",
        "Used when a logging statement has a call form of "
        '"logging.<logging method>(f"...")".'
        "Use another type of string formatting instead. "
        "You can use % formatting but leave interpolation to "
        "the logging function by passing the parameters as arguments. "
        "If logging-format-interpolation is disabled then "
        "you can use str.format. "
        "If logging-not-lazy is disabled then "
        "you can use % formatting as normal.",
    ),
    "E1200": (
        "Unsupported logging format character %r (%#02x) at index %d",
        "logging-unsupported-format",
        "Used when an unsupported format character is used in a logging "
        "statement format string.",
    ),
    "E1201": (
        "Logging format string ends in middle of conversion specifier",
        "logging-format-truncated",
        "Used when a logging statement format string terminates before "
        "the end of a conversion specifier.",
    ),
    "E1205": (
        "Too many arguments for logging format string",
        "logging-too-many-args",
        "Used when a logging format string is given too many arguments.",
    ),
    "E1206": (
        "Not enough arguments for logging format string",
        "logging-too-few-args",
        "Used when a logging format string is given too few arguments.",
    ),
}


CHECKED_CONVENIENCE_FUNCTIONS = {
    "critical",
    "debug",
    "error",
    "exception",
    "fatal",
    "info",
    "warn",
    "warning",
}

MOST_COMMON_FORMATTING = frozenset(["%s", "%d", "%f", "%r"])


def is_method_call(
    func: bases.BoundMethod, types: tuple[str, ...] = (), methods: tuple[str, ...] = ()
) -> bool:
    """Determines if a BoundMethod node represents a method call.

    Args:
      func: The BoundMethod AST node to check.
      types: Optional sequence of caller type names to restrict check.
      methods: Optional sequence of method names to restrict check.

    Returns:
      true if the node represents a method call for the given type and
      method names, False otherwise.
    """
    return (
        isinstance(func, astroid.BoundMethod)
        and isinstance(func.bound, astroid.Instance)
        and (func.bound.name in types if types else True)
        and (func.name in methods if methods else True)
    )


class LoggingChecker(checkers.BaseChecker):
    """Checks use of the logging module."""

    name = "logging"
    msgs = MSGS

    options = (
        (
            "logging-modules",
            {
                "default": ("logging",),
                "type": "csv",
                "metavar": "<comma separated list>",
                "help": "Logging modules to check that the string format "
                "arguments are in logging function parameter format.",
            },
        ),
        (
            "logging-format-style",
            {
                "default": "old",
                "type": "choice",
                "metavar": "<old (%) or new ({)>",
                "choices": ["old", "new"],
                "help": "The type of string formatting that logging methods do. "
                "`old` means using % formatting, `new` is for `{}` formatting.",
            },
        ),
    )

    def visit_module(self, _: nodes.Module) -> None:
        """Clears any state left in this checker from last module checked."""
        # The code being checked can just as easily "import logging as foo",
        # so it is necessary to process the imports and store in this field
        # what name the logging module is actually given.
        self._logging_names: set[str] = set()
        logging_mods = self.linter.config.logging_modules

        self._format_style = self.linter.config.logging_format_style

        self._logging_modules = set(logging_mods)
        self._from_imports = {}
        for logging_mod in logging_mods:
            parts = logging_mod.rsplit(".", 1)
            if len(parts) > 1:
                self._from_imports[parts[0]] = parts[1]

    def visit_importfrom(self, node: nodes.ImportFrom) -> None:
        """Checks to see if a module uses a non-Python logging module."""
        try:
            logging_name = self._from_imports[node.modname]
            for module, as_name in node.names:
                if module == logging_name:
                    self._logging_names.add(as_name or module)
        except KeyError:
            pass

    def visit_import(self, node: nodes.Import) -> None:
        """Checks to see if this module uses Python's built-in logging."""
        for module, as_name in node.names:
            if module in self._logging_modules:
                self._logging_names.add(as_name or module)

    def visit_call(self, node: nodes.Call) -> None:
        """Checks calls to logging methods."""

        def is_logging_name() -> bool:
            return (
                isinstance(node.func, nodes.Attribute)
                and isinstance(node.func.expr, nodes.Name)
                and node.func.expr.name in self._logging_names
            )

        def is_logger_class() -> tuple[bool, str | None]:
            for inferred in infer_all(node.func):
                if isinstance(inferred, astroid.BoundMethod):
                    parent = inferred._proxied.parent
                    if isinstance(parent, nodes.ClassDef) and (
                        parent.qname() == "logging.Logger"
                        or any(
                            ancestor.qname() == "logging.Logger"
                            for ancestor in parent.ancestors()
                        )
                    ):
                        return True, inferred._proxied.name
            return False, None

        if is_logging_name():
            name = node.func.attrname
        else:
            result, name = is_logger_class()
            if not result:
                return
        self._check_log_method(node, name)

    def _check_log_method(self, node: nodes.Call, name: str) -> None:
        """Checks calls to logging.log(level, format, *format_args)."""
        if name == "log":
            if node.starargs or node.kwargs or len(node.args) < 2:
                # Either a malformed call, star args, or double-star args. Beyond
                # the scope of this checker.
                return
            format_pos: Literal[0, 1] = 1
        elif name in CHECKED_CONVENIENCE_FUNCTIONS:
            if node.starargs or node.kwargs or not node.args:
                # Either no args, star args, or double-star args. Beyond the
                # scope of this checker.
                return
            format_pos = 0
        else:
            return

        format_arg = node.args[format_pos]
        if isinstance(format_arg, nodes.BinOp):
            binop = format_arg
            emit = binop.op == "%"
            if binop.op == "+" and not self._is_node_explicit_str_concatenation(binop):
                total_number_of_strings = sum(
                    1
                    for operand in (binop.left, binop.right)
                    if self._is_operand_literal_str(utils.safe_infer(operand))
                )
                emit = total_number_of_strings > 0
            if emit:
                self.add_message(
                    "logging-not-lazy",
                    node=node,
                    args=(self._helper_string(node),),
                )
        elif isinstance(format_arg, nodes.Call):
            self._check_call_func(format_arg)
        elif isinstance(format_arg, nodes.Const):
            self._check_format_string(node, format_pos)
        elif isinstance(format_arg, nodes.JoinedStr):
            if str_formatting_in_f_string(format_arg):
                return
            self.add_message(
                "logging-fstring-interpolation",
                node=node,
                args=(self._helper_string(node),),
            )

    def _helper_string(self, node: nodes.Call) -> str:
        """Create a string that lists the valid types of formatting for this node."""
        valid_types = ["lazy %"]

        if not self.linter.is_message_enabled(
            "logging-fstring-formatting", node.fromlineno
        ):
            valid_types.append("fstring")
        if not self.linter.is_message_enabled(
            "logging-format-interpolation", node.fromlineno
        ):
            valid_types.append(".format()")
        if not self.linter.is_message_enabled("logging-not-lazy", node.fromlineno):
            valid_types.append("%")

        return " or ".join(valid_types)

    @staticmethod
    def _is_operand_literal_str(operand: InferenceResult | None) -> bool:
        """Return True if the operand in argument is a literal string."""
        return isinstance(operand, nodes.Const) and operand.name == "str"

    @staticmethod
    def _is_node_explicit_str_concatenation(node: nodes.NodeNG) -> bool:
        """Return True if the node represents an explicitly concatenated string."""
        if not isinstance(node, nodes.BinOp):
            return False
        return (
            LoggingChecker._is_operand_literal_str(node.left)
            or LoggingChecker._is_node_explicit_str_concatenation(node.left)
        ) and (
            LoggingChecker._is_operand_literal_str(node.right)
            or LoggingChecker._is_node_explicit_str_concatenation(node.right)
        )

    def _check_call_func(self, node: nodes.Call) -> None:
        """Checks that function call is not format_string.format()."""
        func = utils.safe_infer(node.func)
        types = ("str", "unicode")
        methods = ("format",)
        if (
            isinstance(func, astroid.BoundMethod)
            and is_method_call(func, types, methods)
            and not is_complex_format_str(func.bound)
        ):
            self.add_message(
                "logging-format-interpolation",
                node=node,
                args=(self._helper_string(node),),
            )

    def _check_format_string(self, node: nodes.Call, format_arg: Literal[0, 1]) -> None:
        """Checks that format string tokens match the supplied arguments.

        Args:
          node: AST node to be checked.
          format_arg: Index of the format string in the node arguments.
        """
        num_args = _count_supplied_tokens(node.args[format_arg + 1 :])
        if not num_args:
            # If no args were supplied the string is not interpolated and can contain
            # formatting characters - it's used verbatim. Don't check any further.
            return

        format_string = node.args[format_arg].value
        required_num_args = 0
        if isinstance(format_string, bytes):
            format_string = format_string.decode()
        if isinstance(format_string, str):
            try:
                if self._format_style == "old":
                    keyword_args, required_num_args, _, _ = utils.parse_format_string(
                        format_string
                    )
                    if keyword_args:
                        # Keyword checking on logging strings is complicated by
                        # special keywords - out of scope.
                        return
                elif self._format_style == "new":
                    (
                        keyword_arguments,
                        implicit_pos_args,
                        explicit_pos_args,
                    ) = utils.parse_format_method_string(format_string)

                    keyword_args_cnt = len(
                        {k for k, _ in keyword_arguments if not isinstance(k, int)}
                    )
                    required_num_args = (
                        keyword_args_cnt + implicit_pos_args + explicit_pos_args
                    )
            except utils.UnsupportedFormatCharacter as ex:
                char = format_string[ex.index]
                self.add_message(
                    "logging-unsupported-format",
                    node=node,
                    args=(char, ord(char), ex.index),
                )
                return
            except utils.IncompleteFormatString:
                self.add_message("logging-format-truncated", node=node)
                return
        if num_args > required_num_args:
            self.add_message("logging-too-many-args", node=node)
        elif num_args < required_num_args:
            self.add_message("logging-too-few-args", node=node)


def is_complex_format_str(node: nodes.NodeNG) -> bool:
    """Return whether the node represents a string with complex formatting specs."""
    inferred = utils.safe_infer(node)
    if inferred is None or not (
        isinstance(inferred, nodes.Const) and isinstance(inferred.value, str)
    ):
        return True
    try:
        parsed = list(string.Formatter().parse(inferred.value))
    except ValueError:
        # This format string is invalid
        return False
    return any(format_spec for (_, _, format_spec, _) in parsed)


def _count_supplied_tokens(args: list[nodes.NodeNG]) -> int:
    """Counts the number of tokens in an args list.

    The Python log functions allow for special keyword arguments: func,
    exc_info and extra. To handle these cases correctly, we only count
    arguments that aren't keywords.

    Args:
      args: AST nodes that are arguments for a log format string.

    Returns:
      Number of AST nodes that aren't keywords.
    """
    return sum(1 for arg in args if not isinstance(arg, nodes.Keyword))


def str_formatting_in_f_string(node: nodes.JoinedStr) -> bool:
    """Determine whether the node represents an f-string with string formatting.

    For example: `f'Hello %s'`
    """
    # Check "%" presence first for performance.
    return any(
        "%" in val.value and any(x in val.value for x in MOST_COMMON_FORMATTING)
        for val in node.values
        if isinstance(val, nodes.Const)
    )


def register(linter: PyLinter) -> None:
    linter.register_checker(LoggingChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/logging.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Variables checkers for Python code."""

from __future__ import annotations

from typing import TYPE_CHECKING

import astroid
from astroid import arguments, bases, nodes

from pylint.checkers import BaseChecker, utils
from pylint.interfaces import INFERENCE

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class MethodArgsChecker(BaseChecker):
    """BaseChecker for method_args.

    Checks for
    * missing-timeout
    * positional-only-arguments-expected
    """

    name = "method_args"
    msgs = {
        "W3101": (
            "Missing timeout argument for method '%s' can cause your program to hang indefinitely",
            "missing-timeout",
            "Used when a method needs a 'timeout' parameter in order to avoid waiting "
            "for a long time. If no timeout is specified explicitly the default value "
            "is used. For example for 'requests' the program will never time out "
            "(i.e. hang indefinitely).",
        ),
        "E3102": (
            "`%s()` got some positional-only arguments passed as keyword arguments: %s",
            "positional-only-arguments-expected",
            "Emitted when positional-only arguments have been passed as keyword arguments. "
            "Remove the keywords for the affected arguments in the function call.",
            {"minversion": (3, 8)},
        ),
    }
    options = (
        (
            "timeout-methods",
            {
                "default": (
                    "requests.api.delete",
                    "requests.api.get",
                    "requests.api.head",
                    "requests.api.options",
                    "requests.api.patch",
                    "requests.api.post",
                    "requests.api.put",
                    "requests.api.request",
                ),
                "type": "csv",
                "metavar": "<comma separated list>",
                "help": "List of qualified names (i.e., library.method) which require a timeout parameter "
                "e.g. 'requests.api.get,requests.api.post'",
            },
        ),
    )

    @utils.only_required_for_messages(
        "missing-timeout", "positional-only-arguments-expected"
    )
    def visit_call(self, node: nodes.Call) -> None:
        self._check_missing_timeout(node)
        self._check_positional_only_arguments_expected(node)

    def _check_missing_timeout(self, node: nodes.Call) -> None:
        """Check if the call needs a timeout parameter based on package.func_name
        configured in config.timeout_methods.

        Package uses inferred node in order to know the package imported.
        """
        inferred = utils.safe_infer(node.func)
        call_site = arguments.CallSite.from_call(node)
        if (
            inferred
            and not call_site.has_invalid_keywords()
            and isinstance(
                inferred, (nodes.FunctionDef, nodes.ClassDef, bases.UnboundMethod)
            )
            and inferred.qname() in self.linter.config.timeout_methods
        ):
            keyword_arguments = [keyword.arg for keyword in node.keywords]
            keyword_arguments.extend(call_site.keyword_arguments)
            if "timeout" not in keyword_arguments:
                self.add_message(
                    "missing-timeout",
                    node=node,
                    args=(node.func.as_string(),),
                    confidence=INFERENCE,
                )

    def _check_positional_only_arguments_expected(self, node: nodes.Call) -> None:
        """Check if positional only arguments have been passed as keyword arguments by
        inspecting its method definition.
        """
        inferred_func = utils.safe_infer(node.func)
        while isinstance(inferred_func, (astroid.BoundMethod, astroid.UnboundMethod)):
            inferred_func = inferred_func._proxied
        if not (
            isinstance(inferred_func, (nodes.FunctionDef))
            and inferred_func.args.posonlyargs
        ):
            return
        if inferred_func.args.kwarg:
            return
        pos_args = [a.name for a in inferred_func.args.posonlyargs]
        kws = [k.arg for k in node.keywords if k.arg in pos_args]
        if not kws:
            return

        self.add_message(
            "positional-only-arguments-expected",
            node=node,
            args=(node.func.as_string(), ", ".join(f"'{k}'" for k in kws)),
            confidence=INFERENCE,
        )


def register(linter: PyLinter) -> None:
    linter.register_checker(MethodArgsChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/method_args.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Check source code is ascii only or has an encoding declaration (PEP 263)."""

from __future__ import annotations

import re
import tokenize
from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseRawFileChecker, BaseTokenChecker
from pylint.typing import ManagedMessage

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class ByIdManagedMessagesChecker(BaseRawFileChecker):

    """Checks for messages that are enabled or disabled by id instead of symbol."""

    name = "miscellaneous"
    msgs = {
        "I0023": (
            "%s",
            "use-symbolic-message-instead",
            "Used when a message is enabled or disabled by id.",
            {"default_enabled": False},
        )
    }
    options = ()

    def _clear_by_id_managed_msgs(self) -> None:
        self.linter._by_id_managed_msgs.clear()

    def _get_by_id_managed_msgs(self) -> list[ManagedMessage]:
        return self.linter._by_id_managed_msgs

    def process_module(self, node: nodes.Module) -> None:
        """Inspect the source file to find messages activated or deactivated by id."""
        managed_msgs = self._get_by_id_managed_msgs()
        for mod_name, msgid, symbol, lineno, is_disabled in managed_msgs:
            if mod_name == node.name:
                verb = "disable" if is_disabled else "enable"
                txt = f"'{msgid}' is cryptic: use '# pylint: {verb}={symbol}' instead"
                self.add_message("use-symbolic-message-instead", line=lineno, args=txt)
        self._clear_by_id_managed_msgs()


class EncodingChecker(BaseTokenChecker, BaseRawFileChecker):

    """BaseChecker for encoding issues.

    Checks for:
    * warning notes in the code like FIXME, XXX
    * encoding issues.
    """

    # configuration section name
    name = "miscellaneous"
    msgs = {
        "W0511": (
            "%s",
            "fixme",
            "Used when a warning note as FIXME or XXX is detected.",
        )
    }

    options = (
        (
            "notes",
            {
                "type": "csv",
                "metavar": "<comma separated values>",
                "default": ("FIXME", "XXX", "TODO"),
                "help": (
                    "List of note tags to take in consideration, "
                    "separated by a comma."
                ),
            },
        ),
        (
            "notes-rgx",
            {
                "type": "string",
                "metavar": "<regexp>",
                "help": "Regular expression of note tags to take in consideration.",
                "default": "",
            },
        ),
    )

    def open(self) -> None:
        super().open()

        notes = "|".join(re.escape(note) for note in self.linter.config.notes)
        if self.linter.config.notes_rgx:
            regex_string = rf"#\s*({notes}|{self.linter.config.notes_rgx})(?=(:|\s|\Z))"
        else:
            regex_string = rf"#\s*({notes})(?=(:|\s|\Z))"

        self._fixme_pattern = re.compile(regex_string, re.I)

    def _check_encoding(
        self, lineno: int, line: bytes, file_encoding: str
    ) -> str | None:
        try:
            return line.decode(file_encoding)
        except UnicodeDecodeError:
            pass
        except LookupError:
            if (
                line.startswith(b"#")
                and "coding" in str(line)
                and file_encoding in str(line)
            ):
                msg = f"Cannot decode using encoding '{file_encoding}', bad encoding"
                self.add_message("syntax-error", line=lineno, args=msg)
        return None

    def process_module(self, node: nodes.Module) -> None:
        """Inspect the source file to find encoding problem."""
        encoding = node.file_encoding if node.file_encoding else "ascii"

        with node.stream() as stream:
            for lineno, line in enumerate(stream):
                self._check_encoding(lineno + 1, line, encoding)

    def process_tokens(self, tokens: list[tokenize.TokenInfo]) -> None:
        """Inspect the source to find fixme problems."""
        if not self.linter.config.notes:
            return
        for token_info in tokens:
            if token_info.type != tokenize.COMMENT:
                continue
            comment_text = token_info.string[1:].lstrip()  # trim '#' and white-spaces
            if self._fixme_pattern.search("#" + comment_text.lower()):
                self.add_message(
                    "fixme",
                    col_offset=token_info.start[1] + 1,
                    args=comment_text,
                    line=token_info.start[0],
                )


def register(linter: PyLinter) -> None:
    linter.register_checker(EncodingChecker(linter))
    linter.register_checker(ByIdManagedMessagesChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/misc.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint import checkers, interfaces
from pylint.checkers import utils

if TYPE_CHECKING:
    from pylint.lint import PyLinter


_LIST_MODIFIER_METHODS = {"append", "remove"}
_SET_MODIFIER_METHODS = {"add", "remove"}


class ModifiedIterationChecker(checkers.BaseChecker):
    """Checks for modified iterators in for loops iterations.

    Currently supports `for` loops for Sets, Dictionaries and Lists.
    """

    name = "modified_iteration"

    msgs = {
        "W4701": (
            "Iterated list '%s' is being modified inside for loop body, consider iterating through a copy of it "
            "instead.",
            "modified-iterating-list",
            "Emitted when items are added or removed to a list being iterated through. "
            "Doing so can result in unexpected behaviour, that is why it is preferred to use a copy of the list.",
        ),
        "E4702": (
            "Iterated dict '%s' is being modified inside for loop body, iterate through a copy of it instead.",
            "modified-iterating-dict",
            "Emitted when items are added or removed to a dict being iterated through. "
            "Doing so raises a RuntimeError.",
        ),
        "E4703": (
            "Iterated set '%s' is being modified inside for loop body, iterate through a copy of it instead.",
            "modified-iterating-set",
            "Emitted when items are added or removed to a set being iterated through. "
            "Doing so raises a RuntimeError.",
        ),
    }

    options = ()

    @utils.only_required_for_messages(
        "modified-iterating-list", "modified-iterating-dict", "modified-iterating-set"
    )
    def visit_for(self, node: nodes.For) -> None:
        iter_obj = node.iter
        for body_node in node.body:
            self._modified_iterating_check_on_node_and_children(body_node, iter_obj)

    def _modified_iterating_check_on_node_and_children(
        self, body_node: nodes.NodeNG, iter_obj: nodes.NodeNG
    ) -> None:
        """See if node or any of its children raises modified iterating messages."""
        self._modified_iterating_check(body_node, iter_obj)
        for child in body_node.get_children():
            self._modified_iterating_check_on_node_and_children(child, iter_obj)

    def _modified_iterating_check(
        self, node: nodes.NodeNG, iter_obj: nodes.NodeNG
    ) -> None:
        msg_id = None
        if isinstance(node, nodes.Delete) and any(
            self._deleted_iteration_target_cond(t, iter_obj) for t in node.targets
        ):
            inferred = utils.safe_infer(iter_obj)
            if isinstance(inferred, nodes.List):
                msg_id = "modified-iterating-list"
            elif isinstance(inferred, nodes.Dict):
                msg_id = "modified-iterating-dict"
            elif isinstance(inferred, nodes.Set):
                msg_id = "modified-iterating-set"
        elif not isinstance(iter_obj, (nodes.Name, nodes.Attribute)):
            pass
        elif self._modified_iterating_list_cond(node, iter_obj):
            msg_id = "modified-iterating-list"
        elif self._modified_iterating_dict_cond(node, iter_obj):
            msg_id = "modified-iterating-dict"
        elif self._modified_iterating_set_cond(node, iter_obj):
            msg_id = "modified-iterating-set"
        if msg_id:
            self.add_message(
                msg_id,
                node=node,
                args=(iter_obj.repr_name(),),
                confidence=interfaces.INFERENCE,
            )

    @staticmethod
    def _is_node_expr_that_calls_attribute_name(node: nodes.NodeNG) -> bool:
        return (
            isinstance(node, nodes.Expr)
            and isinstance(node.value, nodes.Call)
            and isinstance(node.value.func, nodes.Attribute)
            and isinstance(node.value.func.expr, nodes.Name)
        )

    @staticmethod
    def _common_cond_list_set(
        node: nodes.Expr,
        iter_obj: nodes.Name | nodes.Attribute,
        infer_val: nodes.List | nodes.Set,
    ) -> bool:
        iter_obj_name = (
            iter_obj.attrname
            if isinstance(iter_obj, nodes.Attribute)
            else iter_obj.name
        )
        return (infer_val == utils.safe_infer(iter_obj)) and (  # type: ignore[no-any-return]
            node.value.func.expr.name == iter_obj_name
        )

    @staticmethod
    def _is_node_assigns_subscript_name(node: nodes.NodeNG) -> bool:
        return isinstance(node, nodes.Assign) and (
            isinstance(node.targets[0], nodes.Subscript)
            and (isinstance(node.targets[0].value, nodes.Name))
        )

    def _modified_iterating_list_cond(
        self, node: nodes.NodeNG, iter_obj: nodes.Name | nodes.Attribute
    ) -> bool:
        if not self._is_node_expr_that_calls_attribute_name(node):
            return False
        infer_val = utils.safe_infer(node.value.func.expr)
        if not isinstance(infer_val, nodes.List):
            return False
        return (
            self._common_cond_list_set(node, iter_obj, infer_val)
            and node.value.func.attrname in _LIST_MODIFIER_METHODS
        )

    def _modified_iterating_dict_cond(
        self, node: nodes.NodeNG, iter_obj: nodes.Name | nodes.Attribute
    ) -> bool:
        if not self._is_node_assigns_subscript_name(node):
            return False
        # Do not emit when merely updating the same key being iterated
        if (
            isinstance(iter_obj, nodes.Name)
            and iter_obj.name == node.targets[0].value.name
            and isinstance(iter_obj.parent.target, nodes.AssignName)
            and isinstance(node.targets[0].slice, nodes.Name)
            and iter_obj.parent.target.name == node.targets[0].slice.name
        ):
            return False
        infer_val = utils.safe_infer(node.targets[0].value)
        if not isinstance(infer_val, nodes.Dict):
            return False
        if infer_val != utils.safe_infer(iter_obj):
            return False
        if isinstance(iter_obj, nodes.Attribute):
            iter_obj_name = iter_obj.attrname
        else:
            iter_obj_name = iter_obj.name
        return node.targets[0].value.name == iter_obj_name  # type: ignore[no-any-return]

    def _modified_iterating_set_cond(
        self, node: nodes.NodeNG, iter_obj: nodes.Name | nodes.Attribute
    ) -> bool:
        if not self._is_node_expr_that_calls_attribute_name(node):
            return False
        infer_val = utils.safe_infer(node.value.func.expr)
        if not isinstance(infer_val, nodes.Set):
            return False
        return (
            self._common_cond_list_set(node, iter_obj, infer_val)
            and node.value.func.attrname in _SET_MODIFIER_METHODS
        )

    def _deleted_iteration_target_cond(
        self, node: nodes.DelName, iter_obj: nodes.NodeNG
    ) -> bool:
        if not isinstance(node, nodes.DelName):
            return False
        if not isinstance(iter_obj.parent, nodes.For):
            return False
        if not isinstance(
            iter_obj.parent.target, (nodes.AssignName, nodes.BaseContainer)
        ):
            return False
        return any(
            t == node.name
            for t in utils.find_assigned_names_recursive(iter_obj.parent.target)
        )


def register(linter: PyLinter) -> None:
    linter.register_checker(ModifiedIterationChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/modified_iterating_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Check for use of nested min/max functions."""

from __future__ import annotations

import copy
from typing import TYPE_CHECKING

from astroid import nodes, objects
from astroid.const import Context

from pylint.checkers import BaseChecker
from pylint.checkers.utils import only_required_for_messages, safe_infer
from pylint.interfaces import INFERENCE

if TYPE_CHECKING:
    from pylint.lint import PyLinter

DICT_TYPES = (
    objects.DictValues,
    objects.DictKeys,
    objects.DictItems,
    nodes.node_classes.Dict,
)


class NestedMinMaxChecker(BaseChecker):
    """Multiple nested min/max calls on the same line will raise multiple messages.

    This behaviour is intended as it would slow down the checker to check
    for nested call with minimal benefits.
    """

    FUNC_NAMES = ("builtins.min", "builtins.max")

    name = "nested_min_max"
    msgs = {
        "W3301": (
            "Do not use nested call of '%s'; it's possible to do '%s' instead",
            "nested-min-max",
            "Nested calls ``min(1, min(2, 3))`` can be rewritten as ``min(1, 2, 3)``.",
        )
    }

    @classmethod
    def is_min_max_call(cls, node: nodes.NodeNG) -> bool:
        if not isinstance(node, nodes.Call):
            return False

        inferred = safe_infer(node.func)
        return (
            isinstance(inferred, nodes.FunctionDef)
            and inferred.qname() in cls.FUNC_NAMES
        )

    @classmethod
    def get_redundant_calls(cls, node: nodes.Call) -> list[nodes.Call]:
        return [
            arg
            for arg in node.args
            if cls.is_min_max_call(arg) and arg.func.name == node.func.name
        ]

    @only_required_for_messages("nested-min-max")
    def visit_call(self, node: nodes.Call) -> None:
        if not self.is_min_max_call(node):
            return

        redundant_calls = self.get_redundant_calls(node)
        if not redundant_calls:
            return

        fixed_node = copy.copy(node)
        while len(redundant_calls) > 0:
            for i, arg in enumerate(fixed_node.args):
                # Exclude any calls with generator expressions as there is no
                # clear better suggestion for them.
                if isinstance(arg, nodes.Call) and any(
                    isinstance(a, nodes.GeneratorExp) for a in arg.args
                ):
                    return

                if arg in redundant_calls:
                    fixed_node.args = (
                        fixed_node.args[:i] + arg.args + fixed_node.args[i + 1 :]
                    )
                    break

            redundant_calls = self.get_redundant_calls(fixed_node)

        for idx, arg in enumerate(fixed_node.args):
            if not isinstance(arg, nodes.Const):
                inferred = safe_infer(arg)
                if isinstance(
                    inferred, (nodes.List, nodes.Tuple, nodes.Set, *DICT_TYPES)
                ):
                    splat_node = nodes.Starred(
                        ctx=Context.Load,
                        lineno=inferred.lineno,
                        col_offset=0,
                        parent=nodes.NodeNG(
                            lineno=None,
                            col_offset=None,
                            end_lineno=None,
                            end_col_offset=None,
                            parent=None,
                        ),
                        end_lineno=0,
                        end_col_offset=0,
                    )
                    splat_node.value = arg
                    fixed_node.args = (
                        fixed_node.args[:idx]
                        + [splat_node]
                        + fixed_node.args[idx + 1 : idx]
                    )

        self.add_message(
            "nested-min-max",
            node=node,
            args=(node.func.name, fixed_node.as_string()),
            confidence=INFERENCE,
        )


def register(linter: PyLinter) -> None:
    linter.register_checker(NestedMinMaxChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/nested_min_max.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Check for new / old style related problems."""

from __future__ import annotations

from typing import TYPE_CHECKING

import astroid
from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import (
    has_known_bases,
    node_frame_class,
    only_required_for_messages,
)
from pylint.typing import MessageDefinitionTuple

if TYPE_CHECKING:
    from pylint.lint import PyLinter

MSGS: dict[str, MessageDefinitionTuple] = {
    "E1003": (
        "Bad first argument %r given to super()",
        "bad-super-call",
        "Used when another argument than the current class is given as "
        "first argument of the super builtin.",
    )
}


class NewStyleConflictChecker(BaseChecker):
    """Checks for usage of new style capabilities on old style classes and
    other new/old styles conflicts problems.

    * use of property, __slots__, super
    * "super" usage
    """

    # configuration section name
    name = "newstyle"
    # messages
    msgs = MSGS
    # configuration options
    options = ()

    @only_required_for_messages("bad-super-call")
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        """Check use of super."""
        # ignore actual functions or method within a new style class
        if not node.is_method():
            return
        klass = node.parent.frame()
        for stmt in node.nodes_of_class(nodes.Call):
            if node_frame_class(stmt) != node_frame_class(node):
                # Don't look down in other scopes.
                continue

            expr = stmt.func
            if not isinstance(expr, nodes.Attribute):
                continue

            call = expr.expr
            # skip the test if using super
            if not (
                isinstance(call, nodes.Call)
                and isinstance(call.func, nodes.Name)
                and call.func.name == "super"
            ):
                continue

            # super should not be used on an old style class
            if klass.newstyle or not has_known_bases(klass):
                # super first arg should not be the class
                if not call.args:
                    continue

                # calling super(type(self), self) can lead to recursion loop
                # in derived classes
                arg0 = call.args[0]
                if (
                    isinstance(arg0, nodes.Call)
                    and isinstance(arg0.func, nodes.Name)
                    and arg0.func.name == "type"
                ):
                    self.add_message("bad-super-call", node=call, args=("type",))
                    continue

                # calling super(self.__class__, self) can lead to recursion loop
                # in derived classes
                if (
                    len(call.args) >= 2
                    and isinstance(call.args[1], nodes.Name)
                    and call.args[1].name == "self"
                    and isinstance(arg0, nodes.Attribute)
                    and arg0.attrname == "__class__"
                ):
                    self.add_message(
                        "bad-super-call", node=call, args=("self.__class__",)
                    )
                    continue

                try:
                    supcls = call.args and next(call.args[0].infer(), None)
                except astroid.InferenceError:
                    continue

                # If the supcls is in the ancestors of klass super can be used to skip
                # a step in the mro() and get a method from a higher parent
                if klass is not supcls and all(i != supcls for i in klass.ancestors()):
                    name = None
                    # if supcls is not Uninferable, then supcls was inferred
                    # and use its name. Otherwise, try to look
                    # for call.args[0].name
                    if supcls:
                        name = supcls.name
                    elif call.args and hasattr(call.args[0], "name"):
                        name = call.args[0].name
                    if name:
                        self.add_message("bad-super-call", node=call, args=(name,))

    visit_asyncfunctiondef = visit_functiondef


def register(linter: PyLinter) -> None:
    linter.register_checker(NewStyleConflictChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/newstyle.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""All alphanumeric unicode character are allowed in Python but due
to similarities in how they look they can be confused.

See: https://peps.python.org/pep-0672/#confusing-features

The following checkers are intended to make users are aware of these issues.
"""

from __future__ import annotations

from astroid import nodes

from pylint import constants, interfaces, lint
from pylint.checkers import base_checker, utils

NON_ASCII_HELP = (
    "Used when the name contains at least one non-ASCII unicode character. "
    "See https://peps.python.org/pep-0672/#confusing-features"
    " for a background why this could be bad. \n"
    "If your programming guideline defines that you are programming in "
    "English, then there should be no need for non ASCII characters in "
    "Python Names. If not you can simply disable this check."
)


class NonAsciiNameChecker(base_checker.BaseChecker):
    """A strict name checker only allowing ASCII.

    Note: This check only checks Names, so it ignores the content of
          docstrings and comments!
    """

    msgs = {
        "C2401": (
            '%s name "%s" contains a non-ASCII character, consider renaming it.',
            "non-ascii-name",
            NON_ASCII_HELP,
            {"old_names": [("C0144", "old-non-ascii-name")]},
        ),
        # First %s will always be "file"
        "W2402": (
            '%s name "%s" contains a non-ASCII character.',
            "non-ascii-file-name",
            (
                # Some = PyCharm at the time of writing didn't display the non_ascii_name_loł
                # files. That's also why this is a warning and not only a convention!
                "Under python 3.5, PEP 3131 allows non-ascii identifiers, but not non-ascii file names."
                "Since Python 3.5, even though Python supports UTF-8 files, some editors or tools "
                "don't."
            ),
        ),
        # First %s will always be "module"
        "C2403": (
            '%s name "%s" contains a non-ASCII character, use an ASCII-only alias for import.',
            "non-ascii-module-import",
            NON_ASCII_HELP,
        ),
    }

    name = "NonASCII-Checker"

    def _check_name(self, node_type: str, name: str | None, node: nodes.NodeNG) -> None:
        """Check whether a name is using non-ASCII characters."""

        if name is None:
            # For some nodes i.e. *kwargs from a dict, the name will be empty
            return

        if not str(name).isascii():
            type_label = constants.HUMAN_READABLE_TYPES[node_type]
            args = (type_label.capitalize(), name)

            msg = "non-ascii-name"

            # Some node types have customized messages
            if node_type == "file":
                msg = "non-ascii-file-name"
            elif node_type == "module":
                msg = "non-ascii-module-import"

            self.add_message(msg, node=node, args=args, confidence=interfaces.HIGH)

    @utils.only_required_for_messages("non-ascii-name", "non-ascii-file-name")
    def visit_module(self, node: nodes.Module) -> None:
        self._check_name("file", node.name.split(".")[-1], node)

    @utils.only_required_for_messages("non-ascii-name")
    def visit_functiondef(
        self, node: nodes.FunctionDef | nodes.AsyncFunctionDef
    ) -> None:
        self._check_name("function", node.name, node)

        # Check argument names
        arguments = node.args

        # Check position only arguments
        if arguments.posonlyargs:
            for pos_only_arg in arguments.posonlyargs:
                self._check_name("argument", pos_only_arg.name, pos_only_arg)

        # Check "normal" arguments
        if arguments.args:
            for arg in arguments.args:
                self._check_name("argument", arg.name, arg)

        # Check key word only arguments
        if arguments.kwonlyargs:
            for kwarg in arguments.kwonlyargs:
                self._check_name("argument", kwarg.name, kwarg)

    visit_asyncfunctiondef = visit_functiondef

    @utils.only_required_for_messages("non-ascii-name")
    def visit_global(self, node: nodes.Global) -> None:
        for name in node.names:
            self._check_name("const", name, node)

    @utils.only_required_for_messages("non-ascii-name")
    def visit_assignname(self, node: nodes.AssignName) -> None:
        """Check module level assigned names."""
        # The NameChecker from which this Checker originates knows a lot of different
        # versions of variables, i.e. constants, inline variables etc.
        # To simplify we use only `variable` here, as we don't need to apply different
        # rules to different types of variables.
        frame = node.frame()

        if isinstance(frame, nodes.FunctionDef):
            if node.parent in frame.body:
                # Only perform the check if the assignment was done in within the body
                # of the function (and not the function parameter definition
                # (will be handled in visit_functiondef)
                # or within a decorator (handled in visit_call)
                self._check_name("variable", node.name, node)
        elif isinstance(frame, nodes.ClassDef):
            self._check_name("attr", node.name, node)
        else:
            # Possibilities here:
            # - isinstance(node.assign_type(), nodes.Comprehension) == inlinevar
            # - isinstance(frame, nodes.Module) == variable (constant?)
            # - some other kind of assignment missed but still most likely a variable
            self._check_name("variable", node.name, node)

    @utils.only_required_for_messages("non-ascii-name")
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        self._check_name("class", node.name, node)
        for attr, anodes in node.instance_attrs.items():
            if not any(node.instance_attr_ancestors(attr)):
                self._check_name("attr", attr, anodes[0])

    def _check_module_import(self, node: nodes.ImportFrom | nodes.Import) -> None:
        for module_name, alias in node.names:
            name = alias or module_name
            self._check_name("module", name, node)

    @utils.only_required_for_messages("non-ascii-name", "non-ascii-module-import")
    def visit_import(self, node: nodes.Import) -> None:
        self._check_module_import(node)

    @utils.only_required_for_messages("non-ascii-name", "non-ascii-module-import")
    def visit_importfrom(self, node: nodes.ImportFrom) -> None:
        self._check_module_import(node)

    @utils.only_required_for_messages("non-ascii-name")
    def visit_call(self, node: nodes.Call) -> None:
        """Check if the used keyword args are correct."""
        for keyword in node.keywords:
            self._check_name("argument", keyword.arg, keyword)


def register(linter: lint.PyLinter) -> None:
    linter.register_checker(NonAsciiNameChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/non_ascii_names.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import tokenize
from typing import TYPE_CHECKING, Any, Literal, cast

from pylint.checkers import BaseTokenChecker
from pylint.reporters.ureports.nodes import Paragraph, Section, Table, Text
from pylint.utils import LinterStats, diff_string

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def report_raw_stats(
    sect: Section,
    stats: LinterStats,
    old_stats: LinterStats | None,
) -> None:
    """Calculate percentage of code / doc / comment / empty."""
    total_lines = stats.code_type_count["total"]
    sect.insert(0, Paragraph([Text(f"{total_lines} lines have been analyzed\n")]))
    lines = ["type", "number", "%", "previous", "difference"]
    for node_type in ("code", "docstring", "comment", "empty"):
        node_type = cast(Literal["code", "docstring", "comment", "empty"], node_type)
        total = stats.code_type_count[node_type]
        percent = float(total * 100) / total_lines if total_lines else None
        old = old_stats.code_type_count[node_type] if old_stats else None
        diff_str = diff_string(old, total) if old else None
        lines += [
            node_type,
            str(total),
            f"{percent:.2f}" if percent is not None else "NC",
            str(old) if old else "NC",
            diff_str if diff_str else "NC",
        ]
    sect.append(Table(children=lines, cols=5, rheaders=1))


class RawMetricsChecker(BaseTokenChecker):
    """Checker that provides raw metrics instead of checking anything.

    Provides:
    * total number of lines
    * total number of code lines
    * total number of docstring lines
    * total number of comments lines
    * total number of empty lines
    """

    # configuration section name
    name = "metrics"
    # configuration options
    options = ()
    # messages
    msgs: Any = {}
    # reports
    reports = (("RP0701", "Raw metrics", report_raw_stats),)

    def open(self) -> None:
        """Init statistics."""
        self.linter.stats.reset_code_count()

    def process_tokens(self, tokens: list[tokenize.TokenInfo]) -> None:
        """Update stats."""
        i = 0
        tokens = list(tokens)
        while i < len(tokens):
            i, lines_number, line_type = get_type(tokens, i)
            self.linter.stats.code_type_count["total"] += lines_number
            self.linter.stats.code_type_count[line_type] += lines_number


JUNK = (tokenize.NL, tokenize.INDENT, tokenize.NEWLINE, tokenize.ENDMARKER)


def get_type(
    tokens: list[tokenize.TokenInfo], start_index: int
) -> tuple[int, int, Literal["code", "docstring", "comment", "empty"]]:
    """Return the line type : docstring, comment, code, empty."""
    i = start_index
    start = tokens[i][2]
    pos = start
    line_type = None
    while i < len(tokens) and tokens[i][2][0] == start[0]:
        tok_type = tokens[i][0]
        pos = tokens[i][3]
        if line_type is None:
            if tok_type == tokenize.STRING:
                line_type = "docstring"
            elif tok_type == tokenize.COMMENT:
                line_type = "comment"
            elif tok_type in JUNK:
                pass
            else:
                line_type = "code"
        i += 1
    if line_type is None:
        line_type = "empty"
    elif i < len(tokens) and tokens[i][0] == tokenize.NEWLINE:
        i += 1
    # Mypy fails to infer the literal of line_type
    return i, pos[0] - start[0] + 1, line_type  # type: ignore[return-value]


def register(linter: PyLinter) -> None:
    linter.register_checker(RawMetricsChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/raw_metrics.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Looks for code which can be refactored."""

from __future__ import annotations

from typing import TYPE_CHECKING

from pylint.checkers.refactoring.implicit_booleaness_checker import (
    ImplicitBooleanessChecker,
)
from pylint.checkers.refactoring.not_checker import NotChecker
from pylint.checkers.refactoring.recommendation_checker import RecommendationChecker
from pylint.checkers.refactoring.refactoring_checker import RefactoringChecker

if TYPE_CHECKING:
    from pylint.lint import PyLinter

__all__ = [
    "ImplicitBooleanessChecker",
    "NotChecker",
    "RecommendationChecker",
    "RefactoringChecker",
]


def register(linter: PyLinter) -> None:
    linter.register_checker(RefactoringChecker(linter))
    linter.register_checker(NotChecker(linter))
    linter.register_checker(RecommendationChecker(linter))
    linter.register_checker(ImplicitBooleanessChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/refactoring/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import itertools

import astroid
from astroid import bases, nodes, util

from pylint import checkers
from pylint.checkers import utils
from pylint.interfaces import HIGH, INFERENCE


def _is_constant_zero(node: str | nodes.NodeNG) -> bool:
    # We have to check that node.value is not False because node.value == 0 is True
    # when node.value is False
    return (
        isinstance(node, astroid.Const) and node.value == 0 and node.value is not False
    )


class ImplicitBooleanessChecker(checkers.BaseChecker):
    """Checks for incorrect usage of comparisons or len() inside conditions.

    Incorrect usage of len()
    Pep8 states:
    For sequences, (strings, lists, tuples), use the fact that empty sequences are false.

        Yes: if not seq:
             if seq:

        No: if len(seq):
            if not len(seq):

    Problems detected:
    * if len(sequence):
    * if not len(sequence):
    * elif len(sequence):
    * elif not len(sequence):
    * while len(sequence):
    * while not len(sequence):
    * assert len(sequence):
    * assert not len(sequence):
    * bool(len(sequence))

    Incorrect usage of empty literal sequences; (), [], {},

    For empty sequences, (dicts, lists, tuples), use the fact that empty sequences are false.

        Yes: if variable:
             if not variable

        No: if variable == empty_literal:
            if variable != empty_literal:

    Problems detected:
    * comparison such as variable == empty_literal:
    * comparison such as variable != empty_literal:
    """

    name = "refactoring"
    msgs = {
        "C1802": (
            "Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty",
            "use-implicit-booleaness-not-len",
            "Empty sequences are considered false in a boolean context. You can either"
            " remove the call to 'len' (``if not x``) or compare the length against a"
            " scalar (``if len(x) > 1``).",
            {"old_names": [("C1801", "len-as-condition")]},
        ),
        "C1803": (
            '"%s" can be simplified to "%s", if it is strictly a sequence, as an empty %s is falsey',
            "use-implicit-booleaness-not-comparison",
            "Empty sequences are considered false in a boolean context. Following this"
            " check blindly in weakly typed code base can create hard to debug issues."
            " If the value can be something else that is falsey but not a sequence (for"
            " example ``None``, an empty string, or ``0``) the code will not be "
            "equivalent.",
        ),
        "C1804": (
            '"%s" can be simplified to "%s", if it is striclty a string, as an empty string is falsey',
            "use-implicit-booleaness-not-comparison-to-string",
            "Empty string are considered false in a boolean context. Following this"
            " check blindly in weakly typed code base can create hard to debug issues."
            " If the value can be something else that is falsey but not a string (for"
            " example ``None``, an empty sequence, or ``0``) the code will not be "
            "equivalent.",
            {
                "default_enabled": False,
                "old_names": [("C1901", "compare-to-empty-string")],
            },
        ),
        "C1805": (
            '"%s" can be simplified to "%s", if it is strictly an int, as 0 is falsey',
            "use-implicit-booleaness-not-comparison-to-zero",
            "0 is considered false in a boolean context. Following this"
            " check blindly in weakly typed code base can create hard to debug issues."
            " If the value can be something else that is falsey but not an int (for"
            " example ``None``, an empty string, or an empty sequence) the code will not be "
            "equivalent.",
            {"default_enabled": False, "old_names": [("C2001", "compare-to-zero")]},
        ),
    }

    options = ()
    _operators = {"!=", "==", "is not", "is"}

    @utils.only_required_for_messages("use-implicit-booleaness-not-len")
    def visit_call(self, node: nodes.Call) -> None:
        # a len(S) call is used inside a test condition
        # could be if, while, assert or if expression statement
        # e.g. `if len(S):`
        if not utils.is_call_of_name(node, "len"):
            return
        # the len() call could also be nested together with other
        # boolean operations, e.g. `if z or len(x):`
        parent = node.parent
        while isinstance(parent, nodes.BoolOp):
            parent = parent.parent
        # we're finally out of any nested boolean operations so check if
        # this len() call is part of a test condition
        if not utils.is_test_condition(node, parent):
            return
        len_arg = node.args[0]
        generator_or_comprehension = (
            nodes.ListComp,
            nodes.SetComp,
            nodes.DictComp,
            nodes.GeneratorExp,
        )
        if isinstance(len_arg, generator_or_comprehension):
            # The node is a generator or comprehension as in len([x for x in ...])
            self.add_message(
                "use-implicit-booleaness-not-len",
                node=node,
                confidence=HIGH,
            )
            return
        try:
            instance = next(len_arg.infer())
        except astroid.InferenceError:
            # Probably undefined-variable, abort check
            return
        mother_classes = self.base_names_of_instance(instance)
        affected_by_pep8 = any(
            t in mother_classes for t in ("str", "tuple", "list", "set")
        )
        if "range" in mother_classes or (
            affected_by_pep8 and not self.instance_has_bool(instance)
        ):
            self.add_message(
                "use-implicit-booleaness-not-len",
                node=node,
                confidence=INFERENCE,
            )

    @staticmethod
    def instance_has_bool(class_def: nodes.ClassDef) -> bool:
        try:
            class_def.getattr("__bool__")
            return True
        except astroid.AttributeInferenceError:
            ...
        return False

    @utils.only_required_for_messages("use-implicit-booleaness-not-len")
    def visit_unaryop(self, node: nodes.UnaryOp) -> None:
        """`not len(S)` must become `not S` regardless if the parent block is a test
        condition or something else (boolean expression) e.g. `if not len(S):`.
        """
        if (
            isinstance(node, nodes.UnaryOp)
            and node.op == "not"
            and utils.is_call_of_name(node.operand, "len")
        ):
            self.add_message(
                "use-implicit-booleaness-not-len", node=node, confidence=HIGH
            )

    @utils.only_required_for_messages(
        "use-implicit-booleaness-not-comparison",
        "use-implicit-booleaness-not-comparison-to-string",
        "use-implicit-booleaness-not-comparison-to-zero",
    )
    def visit_compare(self, node: nodes.Compare) -> None:
        if self.linter.is_message_enabled("use-implicit-booleaness-not-comparison"):
            self._check_use_implicit_booleaness_not_comparison(node)
        if self.linter.is_message_enabled(
            "use-implicit-booleaness-not-comparison-to-zero"
        ) or self.linter.is_message_enabled(
            "use-implicit-booleaness-not-comparison-to-str"
        ):
            self._check_compare_to_str_or_zero(node)

    def _check_compare_to_str_or_zero(self, node: nodes.Compare) -> None:
        # note: astroid.Compare has the left most operand in node.left
        # while the rest are a list of tuples in node.ops
        # the format of the tuple is ('compare operator sign', node)
        # here we squash everything into `ops` to make it easier for processing later
        ops: list[tuple[str, nodes.NodeNG]] = [("", node.left), *node.ops]
        iter_ops = iter(ops)
        all_ops = list(itertools.chain(*iter_ops))
        for ops_idx in range(len(all_ops) - 2):
            op_2 = all_ops[ops_idx + 1]
            if op_2 not in self._operators:
                continue
            op_1 = all_ops[ops_idx]
            op_3 = all_ops[ops_idx + 2]
            error_detected = False
            if self.linter.is_message_enabled(
                "use-implicit-booleaness-not-comparison-to-zero"
            ):
                # 0 ?? X
                if _is_constant_zero(op_1):
                    error_detected = True
                    op = op_3
                # X ?? 0
                elif _is_constant_zero(op_3):
                    error_detected = True
                    op = op_1
                if error_detected:
                    original = f"{op_1.as_string()} {op_2} {op_3.as_string()}"
                    suggestion = (
                        op.as_string()
                        if op_2 in {"!=", "is not"}
                        else f"not {op.as_string()}"
                    )
                    self.add_message(
                        "use-implicit-booleaness-not-comparison-to-zero",
                        args=(original, suggestion),
                        node=node,
                        confidence=HIGH,
                    )
                    error_detected = False
            if self.linter.is_message_enabled(
                "use-implicit-booleaness-not-comparison-to-str"
            ):
                node_name = ""
                # x ?? ""
                if utils.is_empty_str_literal(op_1):
                    error_detected = True
                    node_name = op_3.as_string()
                # '' ?? X
                elif utils.is_empty_str_literal(op_3):
                    error_detected = True
                    node_name = op_1.as_string()
                if error_detected:
                    suggestion = (
                        f"not {node_name}" if op_2 in {"==", "is"} else node_name
                    )
                    self.add_message(
                        "use-implicit-booleaness-not-comparison-to-string",
                        args=(node.as_string(), suggestion),
                        node=node,
                        confidence=HIGH,
                    )

    def _check_use_implicit_booleaness_not_comparison(
        self, node: nodes.Compare
    ) -> None:
        """Check for left side and right side of the node for empty literals."""
        is_left_empty_literal = utils.is_base_container(
            node.left
        ) or utils.is_empty_dict_literal(node.left)

        # Check both left-hand side and right-hand side for literals
        for operator, comparator in node.ops:
            is_right_empty_literal = utils.is_base_container(
                comparator
            ) or utils.is_empty_dict_literal(comparator)
            # Using Exclusive OR (XOR) to compare between two side.
            # If two sides are both literal, it should be different error.
            if is_right_empty_literal ^ is_left_empty_literal:
                # set target_node to opposite side of literal
                target_node = node.left if is_right_empty_literal else comparator
                literal_node = comparator if is_right_empty_literal else node.left
                # Infer node to check
                target_instance = utils.safe_infer(target_node)
                if target_instance is None:
                    continue
                mother_classes = self.base_names_of_instance(target_instance)
                is_base_comprehension_type = any(
                    t in mother_classes for t in ("tuple", "list", "dict", "set")
                )

                # Only time we bypass check is when target_node is not inherited by
                # collection literals and have its own __bool__ implementation.
                if not is_base_comprehension_type and self.instance_has_bool(
                    target_instance
                ):
                    continue

                # No need to check for operator when visiting compare node
                if operator in {"==", "!=", ">=", ">", "<=", "<"}:
                    self.add_message(
                        "use-implicit-booleaness-not-comparison",
                        args=self._implicit_booleaness_message_args(
                            literal_node, operator, target_node
                        ),
                        node=node,
                        confidence=HIGH,
                    )

    def _get_node_description(self, node: nodes.NodeNG) -> str:
        return {
            nodes.List: "list",
            nodes.Tuple: "tuple",
            nodes.Dict: "dict",
            nodes.Const: "str",
        }.get(type(node), "iterable")

    def _implicit_booleaness_message_args(
        self, literal_node: nodes.NodeNG, operator: str, target_node: nodes.NodeNG
    ) -> tuple[str, str, str]:
        """Helper to get the right message for "use-implicit-booleaness-not-comparison"."""
        description = self._get_node_description(literal_node)
        collection_literal = {
            "list": "[]",
            "tuple": "()",
            "dict": "{}",
        }.get(description, "iterable")
        instance_name = "x"
        if isinstance(target_node, nodes.Call) and target_node.func:
            instance_name = f"{target_node.func.as_string()}(...)"
        elif isinstance(target_node, (nodes.Attribute, nodes.Name)):
            instance_name = target_node.as_string()
        original_comparison = f"{instance_name} {operator} {collection_literal}"
        suggestion = f"{instance_name}" if operator == "!=" else f"not {instance_name}"
        return original_comparison, suggestion, description

    @staticmethod
    def base_names_of_instance(
        node: util.UninferableBase | bases.Instance,
    ) -> list[str]:
        """Return all names inherited by a class instance or those returned by a
        function.

        The inherited names include 'object'.
        """
        if isinstance(node, bases.Instance):
            return [node.name] + [x.name for x in node.ancestors()]
        return []

back/venvmsg/Lib/site-packages/pylint/checkers/refactoring/implicit_booleaness_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

import astroid
from astroid import nodes

from pylint import checkers
from pylint.checkers import utils


class NotChecker(checkers.BaseChecker):
    """Checks for too many not in comparison expressions.

    - "not not" should trigger a warning
    - "not" followed by a comparison should trigger a warning
    """

    msgs = {
        "C0117": (
            'Consider changing "%s" to "%s"',
            "unnecessary-negation",
            "Used when a boolean expression contains an unneeded negation, "
            "e.g. when two negation operators cancel each other out.",
            {"old_names": [("C0113", "unneeded-not")]},
        )
    }
    name = "refactoring"
    reverse_op = {
        "<": ">=",
        "<=": ">",
        ">": "<=",
        ">=": "<",
        "==": "!=",
        "!=": "==",
        "in": "not in",
        "is": "is not",
    }
    # sets are not ordered, so for example "not set(LEFT_VALS) <= set(RIGHT_VALS)" is
    # not equivalent to "set(LEFT_VALS) > set(RIGHT_VALS)"
    skipped_nodes = (nodes.Set,)
    # 'builtins' py3, '__builtin__' py2
    skipped_classnames = [f"builtins.{qname}" for qname in ("set", "frozenset")]

    @utils.only_required_for_messages("unnecessary-negation")
    def visit_unaryop(self, node: nodes.UnaryOp) -> None:
        if node.op != "not":
            return
        operand = node.operand

        if isinstance(operand, nodes.UnaryOp) and operand.op == "not":
            self.add_message(
                "unnecessary-negation",
                node=node,
                args=(node.as_string(), operand.operand.as_string()),
            )
        elif isinstance(operand, nodes.Compare):
            left = operand.left
            # ignore multiple comparisons
            if len(operand.ops) > 1:
                return
            operator, right = operand.ops[0]
            if operator not in self.reverse_op:
                return
            # Ignore __ne__ as function of __eq__
            frame = node.frame()
            if frame.name == "__ne__" and operator == "==":
                return
            for _type in (utils.node_type(left), utils.node_type(right)):
                if not _type:
                    return
                if isinstance(_type, self.skipped_nodes):
                    return
                if (
                    isinstance(_type, astroid.Instance)
                    and _type.qname() in self.skipped_classnames
                ):
                    return
            suggestion = (
                f"{left.as_string()} {self.reverse_op[operator]} {right.as_string()}"
            )
            self.add_message(
                "unnecessary-negation", node=node, args=(node.as_string(), suggestion)
            )

back/venvmsg/Lib/site-packages/pylint/checkers/refactoring/not_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import astroid
from astroid import nodes

from pylint import checkers
from pylint.checkers import utils
from pylint.interfaces import HIGH, INFERENCE


class RecommendationChecker(checkers.BaseChecker):
    name = "refactoring"
    msgs = {
        "C0200": (
            "Consider using enumerate instead of iterating with range and len",
            "consider-using-enumerate",
            "Emitted when code that iterates with range and len is "
            "encountered. Such code can be simplified by using the "
            "enumerate builtin.",
        ),
        "C0201": (
            "Consider iterating the dictionary directly instead of calling .keys()",
            "consider-iterating-dictionary",
            "Emitted when the keys of a dictionary are iterated through the ``.keys()`` "
            "method or when ``.keys()`` is used for a membership check. "
            "It is enough to iterate through the dictionary itself, "
            "``for key in dictionary``. For membership checks, "
            "``if key in dictionary`` is faster.",
        ),
        "C0206": (
            "Consider iterating with .items()",
            "consider-using-dict-items",
            "Emitted when iterating over the keys of a dictionary and accessing the "
            "value by index lookup. "
            "Both the key and value can be accessed by iterating using the .items() "
            "method of the dictionary instead.",
        ),
        "C0207": (
            "Use %s instead",
            "use-maxsplit-arg",
            "Emitted when accessing only the first or last element of str.split(). "
            "The first and last element can be accessed by using "
            "str.split(sep, maxsplit=1)[0] or str.rsplit(sep, maxsplit=1)[-1] "
            "instead.",
        ),
        "C0208": (
            "Use a sequence type when iterating over values",
            "use-sequence-for-iteration",
            "When iterating over values, sequence types (e.g., ``lists``, ``tuples``, ``ranges``) "
            "are more efficient than ``sets``.",
        ),
        "C0209": (
            "Formatting a regular string which could be an f-string",
            "consider-using-f-string",
            "Used when we detect a string that is being formatted with format() or % "
            "which could potentially be an f-string. The use of f-strings is preferred. "
            "Requires Python 3.6 and ``py-version >= 3.6``.",
        ),
    }

    def open(self) -> None:
        py_version = self.linter.config.py_version
        self._py36_plus = py_version >= (3, 6)

    @staticmethod
    def _is_builtin(node: nodes.NodeNG, function: str) -> bool:
        inferred = utils.safe_infer(node)
        if not inferred:
            return False
        return utils.is_builtin_object(inferred) and inferred.name == function

    @utils.only_required_for_messages(
        "consider-iterating-dictionary", "use-maxsplit-arg"
    )
    def visit_call(self, node: nodes.Call) -> None:
        self._check_consider_iterating_dictionary(node)
        self._check_use_maxsplit_arg(node)

    def _check_consider_iterating_dictionary(self, node: nodes.Call) -> None:
        if not isinstance(node.func, nodes.Attribute):
            return
        if node.func.attrname != "keys":
            return

        if isinstance(node.parent, nodes.BinOp) and node.parent.op in {"&", "|", "^"}:
            return

        comp_ancestor = utils.get_node_first_ancestor_of_type(node, nodes.Compare)
        if (
            isinstance(node.parent, (nodes.For, nodes.Comprehension))
            or comp_ancestor
            and any(
                op
                for op, comparator in comp_ancestor.ops
                if op in {"in", "not in"}
                and (comparator in node.node_ancestors() or comparator is node)
            )
        ):
            inferred = utils.safe_infer(node.func)
            if not isinstance(inferred, astroid.BoundMethod) or not isinstance(
                inferred.bound, nodes.Dict
            ):
                return
            self.add_message(
                "consider-iterating-dictionary", node=node, confidence=INFERENCE
            )

    def _check_use_maxsplit_arg(self, node: nodes.Call) -> None:
        """Add message when accessing first or last elements of a str.split() or
        str.rsplit().
        """

        # Check if call is split() or rsplit()
        if not (
            isinstance(node.func, nodes.Attribute)
            and node.func.attrname in {"split", "rsplit"}
            and isinstance(utils.safe_infer(node.func), astroid.BoundMethod)
        ):
            return
        inferred_expr = utils.safe_infer(node.func.expr)
        if isinstance(inferred_expr, astroid.Instance) and any(
            inferred_expr.nodes_of_class(nodes.ClassDef)
        ):
            return

        confidence = HIGH
        try:
            sep = utils.get_argument_from_call(node, 0, "sep")
        except utils.NoSuchArgumentError:
            sep = utils.infer_kwarg_from_call(node, keyword="sep")
            confidence = INFERENCE
            if not sep:
                return

        try:
            # Ignore if maxsplit arg has been set
            utils.get_argument_from_call(node, 1, "maxsplit")
            return
        except utils.NoSuchArgumentError:
            if utils.infer_kwarg_from_call(node, keyword="maxsplit"):
                return

        if isinstance(node.parent, nodes.Subscript):
            try:
                subscript_value = utils.get_subscript_const_value(node.parent).value
            except utils.InferredTypeError:
                return

            # Check for cases where variable (Name) subscripts may be mutated within a loop
            if isinstance(node.parent.slice, nodes.Name):
                # Check if loop present within the scope of the node
                scope = node.scope()
                for loop_node in scope.nodes_of_class((nodes.For, nodes.While)):
                    if not loop_node.parent_of(node):
                        continue

                    # Check if var is mutated within loop (Assign/AugAssign)
                    for assignment_node in loop_node.nodes_of_class(nodes.AugAssign):
                        if node.parent.slice.name == assignment_node.target.name:
                            return
                    for assignment_node in loop_node.nodes_of_class(nodes.Assign):
                        if node.parent.slice.name in [
                            n.name for n in assignment_node.targets
                        ]:
                            return

            if subscript_value in (-1, 0):
                fn_name = node.func.attrname
                new_fn = "rsplit" if subscript_value == -1 else "split"
                new_name = (
                    node.func.as_string().rsplit(fn_name, maxsplit=1)[0]
                    + new_fn
                    + f"({sep.as_string()}, maxsplit=1)[{subscript_value}]"
                )
                self.add_message(
                    "use-maxsplit-arg",
                    node=node,
                    args=(new_name,),
                    confidence=confidence,
                )

    @utils.only_required_for_messages(
        "consider-using-enumerate",
        "consider-using-dict-items",
        "use-sequence-for-iteration",
    )
    def visit_for(self, node: nodes.For) -> None:
        self._check_consider_using_enumerate(node)
        self._check_consider_using_dict_items(node)
        self._check_use_sequence_for_iteration(node)

    def _check_consider_using_enumerate(self, node: nodes.For) -> None:
        """Emit a convention whenever range and len are used for indexing."""
        # Verify that we have a `range([start], len(...), [stop])` call and
        # that the object which is iterated is used as a subscript in the
        # body of the for.

        # Is it a proper range call?
        if not isinstance(node.iter, nodes.Call):
            return
        if not self._is_builtin(node.iter.func, "range"):
            return
        if not node.iter.args:
            return
        is_constant_zero = (
            isinstance(node.iter.args[0], nodes.Const) and node.iter.args[0].value == 0
        )
        if len(node.iter.args) == 2 and not is_constant_zero:
            return
        if len(node.iter.args) > 2:
            return

        # Is it a proper len call?
        if not isinstance(node.iter.args[-1], nodes.Call):
            return
        second_func = node.iter.args[-1].func
        if not self._is_builtin(second_func, "len"):
            return
        len_args = node.iter.args[-1].args
        if not len_args or len(len_args) != 1:
            return
        iterating_object = len_args[0]
        if isinstance(iterating_object, nodes.Name):
            expected_subscript_val_type = nodes.Name
        elif isinstance(iterating_object, nodes.Attribute):
            expected_subscript_val_type = nodes.Attribute
        else:
            return
        # If we're defining __iter__ on self, enumerate won't work
        scope = node.scope()
        if (
            isinstance(iterating_object, nodes.Name)
            and iterating_object.name == "self"
            and scope.name == "__iter__"
        ):
            return

        # Verify that the body of the for loop uses a subscript
        # with the object that was iterated. This uses some heuristics
        # in order to make sure that the same object is used in the
        # for body.
        for child in node.body:
            for subscript in child.nodes_of_class(nodes.Subscript):
                if not isinstance(subscript.value, expected_subscript_val_type):
                    continue

                value = subscript.slice
                if not isinstance(value, nodes.Name):
                    continue
                if subscript.value.scope() != node.scope():
                    # Ignore this subscript if it's not in the same
                    # scope. This means that in the body of the for
                    # loop, another scope was created, where the same
                    # name for the iterating object was used.
                    continue
                if value.name == node.target.name and (
                    isinstance(subscript.value, nodes.Name)
                    and iterating_object.name == subscript.value.name
                    or isinstance(subscript.value, nodes.Attribute)
                    and iterating_object.attrname == subscript.value.attrname
                ):
                    self.add_message("consider-using-enumerate", node=node)
                    return

    def _check_consider_using_dict_items(self, node: nodes.For) -> None:
        """Add message when accessing dict values by index lookup."""
        # Verify that we have a .keys() call and
        # that the object which is iterated is used as a subscript in the
        # body of the for.

        iterating_object_name = utils.get_iterating_dictionary_name(node)
        if iterating_object_name is None:
            return

        # Verify that the body of the for loop uses a subscript
        # with the object that was iterated. This uses some heuristics
        # in order to make sure that the same object is used in the
        # for body.
        for child in node.body:
            for subscript in child.nodes_of_class(nodes.Subscript):
                if not isinstance(subscript.value, (nodes.Name, nodes.Attribute)):
                    continue

                value = subscript.slice
                if (
                    not isinstance(value, nodes.Name)
                    or value.name != node.target.name
                    or iterating_object_name != subscript.value.as_string()
                ):
                    continue
                last_definition_lineno = value.lookup(value.name)[1][-1].lineno
                if last_definition_lineno > node.lineno:
                    # Ignore this subscript if it has been redefined after
                    # the for loop. This checks for the line number using .lookup()
                    # to get the line number where the iterating object was last
                    # defined and compare that to the for loop's line number
                    continue
                if (
                    isinstance(subscript.parent, nodes.Assign)
                    and subscript in subscript.parent.targets
                    or isinstance(subscript.parent, nodes.AugAssign)
                    and subscript == subscript.parent.target
                ):
                    # Ignore this subscript if it is the target of an assignment
                    # Early termination as dict index lookup is necessary
                    return

                self.add_message("consider-using-dict-items", node=node)
                return

    @utils.only_required_for_messages(
        "consider-using-dict-items",
        "use-sequence-for-iteration",
    )
    def visit_comprehension(self, node: nodes.Comprehension) -> None:
        self._check_consider_using_dict_items_comprehension(node)
        self._check_use_sequence_for_iteration(node)

    def _check_consider_using_dict_items_comprehension(
        self, node: nodes.Comprehension
    ) -> None:
        """Add message when accessing dict values by index lookup."""
        iterating_object_name = utils.get_iterating_dictionary_name(node)
        if iterating_object_name is None:
            return

        for child in node.parent.get_children():
            for subscript in child.nodes_of_class(nodes.Subscript):
                if not isinstance(subscript.value, (nodes.Name, nodes.Attribute)):
                    continue

                value = subscript.slice
                if (
                    not isinstance(value, nodes.Name)
                    or value.name != node.target.name
                    or iterating_object_name != subscript.value.as_string()
                ):
                    continue

                self.add_message("consider-using-dict-items", node=node)
                return

    def _check_use_sequence_for_iteration(
        self, node: nodes.For | nodes.Comprehension
    ) -> None:
        """Check if code iterates over an in-place defined set.

        Sets using `*` are not considered in-place.
        """
        if isinstance(node.iter, nodes.Set) and not any(
            utils.has_starred_node_recursive(node)
        ):
            self.add_message(
                "use-sequence-for-iteration", node=node.iter, confidence=HIGH
            )

    @utils.only_required_for_messages("consider-using-f-string")
    def visit_const(self, node: nodes.Const) -> None:
        if self._py36_plus:
            # f-strings require Python 3.6
            if node.pytype() == "builtins.str" and not isinstance(
                node.parent, nodes.JoinedStr
            ):
                self._detect_replacable_format_call(node)

    def _detect_replacable_format_call(self, node: nodes.Const) -> None:
        """Check whether a string is used in a call to format() or '%' and whether it
        can be replaced by an f-string.
        """
        if (
            isinstance(node.parent, nodes.Attribute)
            and node.parent.attrname == "format"
        ):
            # Don't warn on referencing / assigning .format without calling it
            if not isinstance(node.parent.parent, nodes.Call):
                return

            if node.parent.parent.args:
                for arg in node.parent.parent.args:
                    # If star expressions with more than 1 element are being used
                    if isinstance(arg, nodes.Starred):
                        inferred = utils.safe_infer(arg.value)
                        if (
                            isinstance(inferred, astroid.List)
                            and len(inferred.elts) > 1
                        ):
                            return
                    # Backslashes can't be in f-string expressions
                    if "\\" in arg.as_string():
                        return

            elif node.parent.parent.keywords:
                keyword_args = [
                    i[0] for i in utils.parse_format_method_string(node.value)[0]
                ]
                for keyword in node.parent.parent.keywords:
                    # If keyword is used multiple times
                    if keyword_args.count(keyword.arg) > 1:
                        return

                    keyword = utils.safe_infer(keyword.value)

                    # If lists of more than one element are being unpacked
                    if isinstance(keyword, nodes.Dict):
                        if len(keyword.items) > 1 and len(keyword_args) > 1:
                            return

            # If all tests pass, then raise message
            self.add_message(
                "consider-using-f-string",
                node=node,
                line=node.lineno,
                col_offset=node.col_offset,
            )

        elif isinstance(node.parent, nodes.BinOp) and node.parent.op == "%":
            # Backslashes can't be in f-string expressions
            if "\\" in node.parent.right.as_string():
                return

            # If % applied to another type than str, it's modulo and can't be replaced by formatting
            if not hasattr(node.parent.left, "value") or not isinstance(
                node.parent.left.value, str
            ):
                return

            # Brackets can be inconvenient in f-string expressions
            if "{" in node.parent.left.value or "}" in node.parent.left.value:
                return

            inferred_right = utils.safe_infer(node.parent.right)

            # If dicts or lists of length > 1 are used
            if isinstance(inferred_right, nodes.Dict):
                if len(inferred_right.items) > 1:
                    return
            elif isinstance(inferred_right, nodes.List):
                if len(inferred_right.elts) > 1:
                    return

            # If all tests pass, then raise message
            self.add_message(
                "consider-using-f-string",
                node=node,
                line=node.lineno,
                col_offset=node.col_offset,
            )

back/venvmsg/Lib/site-packages/pylint/checkers/refactoring/recommendation_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import collections
import copy
import itertools
import tokenize
from collections.abc import Iterator
from functools import cached_property, reduce
from re import Pattern
from typing import TYPE_CHECKING, Any, NamedTuple, Union, cast

import astroid
from astroid import bases, nodes
from astroid.util import UninferableBase

from pylint import checkers
from pylint.checkers import utils
from pylint.checkers.base.basic_error_checker import _loop_exits_early
from pylint.checkers.utils import node_frame_class
from pylint.interfaces import HIGH, INFERENCE, Confidence

if TYPE_CHECKING:
    from pylint.lint import PyLinter


NodesWithNestedBlocks = Union[nodes.Try, nodes.While, nodes.For, nodes.If]

KNOWN_INFINITE_ITERATORS = {"itertools.count", "itertools.cycle"}
BUILTIN_EXIT_FUNCS = frozenset(("quit", "exit"))
CALLS_THAT_COULD_BE_REPLACED_BY_WITH = frozenset(
    (
        "threading.lock.acquire",
        "threading._RLock.acquire",
        "threading.Semaphore.acquire",
        "multiprocessing.managers.BaseManager.start",
        "multiprocessing.managers.SyncManager.start",
    )
)
CALLS_RETURNING_CONTEXT_MANAGERS = frozenset(
    (
        "_io.open",  # regular 'open()' call
        "pathlib.Path.open",
        "codecs.open",
        "urllib.request.urlopen",
        "tempfile.NamedTemporaryFile",
        "tempfile.SpooledTemporaryFile",
        "tempfile.TemporaryDirectory",
        "tempfile.TemporaryFile",
        "zipfile.ZipFile",
        "zipfile.PyZipFile",
        "zipfile.ZipFile.open",
        "zipfile.PyZipFile.open",
        "tarfile.TarFile",
        "tarfile.TarFile.open",
        "multiprocessing.context.BaseContext.Pool",
        "subprocess.Popen",
    )
)


def _if_statement_is_always_returning(
    if_node: nodes.If, returning_node_class: nodes.NodeNG
) -> bool:
    return any(isinstance(node, returning_node_class) for node in if_node.body)


def _except_statement_is_always_returning(
    node: nodes.Try, returning_node_class: nodes.NodeNG
) -> bool:
    """Detect if all except statements return."""
    return all(
        any(isinstance(child, returning_node_class) for child in handler.body)
        for handler in node.handlers
    )


def _is_trailing_comma(tokens: list[tokenize.TokenInfo], index: int) -> bool:
    """Check if the given token is a trailing comma.

    :param tokens: Sequence of modules tokens
    :type tokens: list[tokenize.TokenInfo]
    :param int index: Index of token under check in tokens
    :returns: True if the token is a comma which trails an expression
    :rtype: bool
    """
    token = tokens[index]
    if token.exact_type != tokenize.COMMA:
        return False
    # Must have remaining tokens on the same line such as NEWLINE
    left_tokens = itertools.islice(tokens, index + 1, None)

    more_tokens_on_line = False
    for remaining_token in left_tokens:
        if remaining_token.start[0] == token.start[0]:
            more_tokens_on_line = True
            # If one of the remaining same line tokens is not NEWLINE or COMMENT
            # the comma is not trailing.
            if remaining_token.type not in (tokenize.NEWLINE, tokenize.COMMENT):
                return False

    if not more_tokens_on_line:
        return False

    def get_curline_index_start() -> int:
        """Get the index denoting the start of the current line."""
        for subindex, token in enumerate(reversed(tokens[:index])):
            # See Lib/tokenize.py and Lib/token.py in cpython for more info
            if token.type == tokenize.NEWLINE:
                return index - subindex
        return 0

    curline_start = get_curline_index_start()
    expected_tokens = {"return", "yield"}
    return any(
        "=" in prevtoken.string or prevtoken.string in expected_tokens
        for prevtoken in tokens[curline_start:index]
    )


def _is_inside_context_manager(node: nodes.Call) -> bool:
    frame = node.frame()
    if not isinstance(
        frame, (nodes.FunctionDef, astroid.BoundMethod, astroid.UnboundMethod)
    ):
        return False
    return frame.name == "__enter__" or utils.decorated_with(
        frame, "contextlib.contextmanager"
    )


def _is_a_return_statement(node: nodes.Call) -> bool:
    frame = node.frame()
    for parent in node.node_ancestors():
        if parent is frame:
            break
        if isinstance(parent, nodes.Return):
            return True
    return False


def _is_part_of_with_items(node: nodes.Call) -> bool:
    """Checks if one of the node's parents is a ``nodes.With`` node and that the node
    itself is located somewhere under its ``items``.
    """
    frame = node.frame()
    current = node
    while current != frame:
        if isinstance(current, nodes.With):
            items_start = current.items[0][0].lineno
            items_end = current.items[-1][0].tolineno
            return items_start <= node.lineno <= items_end  # type: ignore[no-any-return]
        current = current.parent
    return False


def _will_be_released_automatically(node: nodes.Call) -> bool:
    """Checks if a call that could be used in a ``with`` statement is used in an
    alternative construct which would ensure that its __exit__ method is called.
    """
    callables_taking_care_of_exit = frozenset(
        (
            "contextlib._BaseExitStack.enter_context",
            "contextlib.ExitStack.enter_context",  # necessary for Python 3.6 compatibility
        )
    )
    if not isinstance(node.parent, nodes.Call):
        return False
    func = utils.safe_infer(node.parent.func)
    if not func:
        return False
    return func.qname() in callables_taking_care_of_exit


def _is_part_of_assignment_target(node: nodes.NodeNG) -> bool:
    """Check whether use of a variable is happening as part of the left-hand
    side of an assignment.

    This requires recursive checking, because destructuring assignment can have
    arbitrarily nested tuples and lists to unpack.
    """
    if isinstance(node.parent, nodes.Assign):
        return node in node.parent.targets

    if isinstance(node.parent, nodes.AugAssign):
        return node == node.parent.target  # type: ignore[no-any-return]

    if isinstance(node.parent, (nodes.Tuple, nodes.List)):
        return _is_part_of_assignment_target(node.parent)

    return False


class ConsiderUsingWithStack(NamedTuple):
    """Stack for objects that may potentially trigger a R1732 message
    if they are not used in a ``with`` block later on.
    """

    module_scope: dict[str, nodes.NodeNG] = {}
    class_scope: dict[str, nodes.NodeNG] = {}
    function_scope: dict[str, nodes.NodeNG] = {}

    def __iter__(self) -> Iterator[dict[str, nodes.NodeNG]]:
        yield from (self.function_scope, self.class_scope, self.module_scope)

    def get_stack_for_frame(
        self, frame: nodes.FunctionDef | nodes.ClassDef | nodes.Module
    ) -> dict[str, nodes.NodeNG]:
        """Get the stack corresponding to the scope of the given frame."""
        if isinstance(frame, nodes.FunctionDef):
            return self.function_scope
        if isinstance(frame, nodes.ClassDef):
            return self.class_scope
        return self.module_scope

    def clear_all(self) -> None:
        """Convenience method to clear all stacks."""
        for stack in self:
            stack.clear()


class RefactoringChecker(checkers.BaseTokenChecker):
    """Looks for code which can be refactored.

    This checker also mixes the astroid and the token approaches
    in order to create knowledge about whether an "else if" node
    is a true "else if" node, or an "elif" node.
    """

    name = "refactoring"

    msgs = {
        "R1701": (
            "Consider merging these isinstance calls to isinstance(%s, (%s))",
            "consider-merging-isinstance",
            "Used when multiple consecutive isinstance calls can be merged into one.",
        ),
        "R1706": (
            "Consider using ternary (%s)",
            "consider-using-ternary",
            "Used when one of known pre-python 2.5 ternary syntax is used.",
        ),
        "R1709": (
            "Boolean expression may be simplified to %s",
            "simplify-boolean-expression",
            "Emitted when redundant pre-python 2.5 ternary syntax is used.",
        ),
        "R1726": (
            'Boolean condition "%s" may be simplified to "%s"',
            "simplifiable-condition",
            "Emitted when a boolean condition is able to be simplified.",
        ),
        "R1727": (
            "Boolean condition '%s' will always evaluate to '%s'",
            "condition-evals-to-constant",
            "Emitted when a boolean condition can be simplified to a constant value.",
        ),
        "R1702": (
            "Too many nested blocks (%s/%s)",
            "too-many-nested-blocks",
            "Used when a function or a method has too many nested "
            "blocks. This makes the code less understandable and "
            "maintainable.",
            {"old_names": [("R0101", "old-too-many-nested-blocks")]},
        ),
        "R1703": (
            "The if statement can be replaced with %s",
            "simplifiable-if-statement",
            "Used when an if statement can be replaced with 'bool(test)'.",
            {"old_names": [("R0102", "old-simplifiable-if-statement")]},
        ),
        "R1704": (
            "Redefining argument with the local name %r",
            "redefined-argument-from-local",
            "Used when a local name is redefining an argument, which might "
            "suggest a potential error. This is taken in account only for "
            "a handful of name binding operations, such as for iteration, "
            "with statement assignment and exception handler assignment.",
        ),
        "R1705": (
            'Unnecessary "%s" after "return", %s',
            "no-else-return",
            "Used in order to highlight an unnecessary block of "
            "code following an if containing a return statement. "
            "As such, it will warn when it encounters an else "
            "following a chain of ifs, all of them containing a "
            "return statement.",
        ),
        "R1707": (
            "Disallow trailing comma tuple",
            "trailing-comma-tuple",
            "In Python, a tuple is actually created by the comma symbol, "
            "not by the parentheses. Unfortunately, one can actually create a "
            "tuple by misplacing a trailing comma, which can lead to potential "
            "weird bugs in your code. You should always use parentheses "
            "explicitly for creating a tuple.",
        ),
        "R1708": (
            "Do not raise StopIteration in generator, use return statement instead",
            "stop-iteration-return",
            "According to PEP479, the raise of StopIteration to end the loop of "
            "a generator may lead to hard to find bugs. This PEP specify that "
            "raise StopIteration has to be replaced by a simple return statement",
        ),
        "R1710": (
            "Either all return statements in a function should return an expression, "
            "or none of them should.",
            "inconsistent-return-statements",
            "According to PEP8, if any return statement returns an expression, "
            "any return statements where no value is returned should explicitly "
            "state this as return None, and an explicit return statement "
            "should be present at the end of the function (if reachable)",
        ),
        "R1711": (
            "Useless return at end of function or method",
            "useless-return",
            'Emitted when a single "return" or "return None" statement is found '
            "at the end of function or method definition. This statement can safely be "
            "removed because Python will implicitly return None",
        ),
        "R1712": (
            "Consider using tuple unpacking for swapping variables",
            "consider-swap-variables",
            "You do not have to use a temporary variable in order to "
            'swap variables. Using "tuple unpacking" to directly swap '
            "variables makes the intention more clear.",
        ),
        "R1713": (
            "Consider using str.join(sequence) for concatenating "
            "strings from an iterable",
            "consider-using-join",
            "Using str.join(sequence) is faster, uses less memory "
            "and increases readability compared to for-loop iteration.",
        ),
        "R1714": (
            "Consider merging these comparisons with 'in' by using '%s %sin (%s)'."
            " Use a set instead if elements are hashable.",
            "consider-using-in",
            "To check if a variable is equal to one of many values, "
            'combine the values into a set or tuple and check if the variable is contained "in" it '
            "instead of checking for equality against each of the values. "
            "This is faster and less verbose.",
        ),
        "R1715": (
            "Consider using dict.get for getting values from a dict "
            "if a key is present or a default if not",
            "consider-using-get",
            "Using the builtin dict.get for getting a value from a dictionary "
            "if a key is present or a default if not, is simpler and considered "
            "more idiomatic, although sometimes a bit slower",
        ),
        "R1716": (
            "Simplify chained comparison between the operands",
            "chained-comparison",
            "This message is emitted when pylint encounters boolean operation like "
            '"a < b and b < c", suggesting instead to refactor it to "a < b < c"',
        ),
        "R1717": (
            "Consider using a dictionary comprehension",
            "consider-using-dict-comprehension",
            "Emitted when we detect the creation of a dictionary "
            "using the dict() callable and a transient list. "
            "Although there is nothing syntactically wrong with this code, "
            "it is hard to read and can be simplified to a dict comprehension. "
            "Also it is faster since you don't need to create another "
            "transient list",
        ),
        "R1718": (
            "Consider using a set comprehension",
            "consider-using-set-comprehension",
            "Although there is nothing syntactically wrong with this code, "
            "it is hard to read and can be simplified to a set comprehension. "
            "Also it is faster since you don't need to create another "
            "transient list",
        ),
        "R1719": (
            "The if expression can be replaced with %s",
            "simplifiable-if-expression",
            "Used when an if expression can be replaced with 'bool(test)' "
            "or simply 'test' if the boolean cast is implicit.",
        ),
        "R1720": (
            'Unnecessary "%s" after "raise", %s',
            "no-else-raise",
            "Used in order to highlight an unnecessary block of "
            "code following an if containing a raise statement. "
            "As such, it will warn when it encounters an else "
            "following a chain of ifs, all of them containing a "
            "raise statement.",
        ),
        "R1721": (
            "Unnecessary use of a comprehension, use %s instead.",
            "unnecessary-comprehension",
            "Instead of using an identity comprehension, "
            "consider using the list, dict or set constructor. "
            "It is faster and simpler.",
        ),
        "R1722": (
            "Consider using 'sys.exit' instead",
            "consider-using-sys-exit",
            "Contrary to 'exit()' or 'quit()', 'sys.exit' does not rely on the "
            "site module being available (as the 'sys' module is always available).",
        ),
        "R1723": (
            'Unnecessary "%s" after "break", %s',
            "no-else-break",
            "Used in order to highlight an unnecessary block of "
            "code following an if containing a break statement. "
            "As such, it will warn when it encounters an else "
            "following a chain of ifs, all of them containing a "
            "break statement.",
        ),
        "R1724": (
            'Unnecessary "%s" after "continue", %s',
            "no-else-continue",
            "Used in order to highlight an unnecessary block of "
            "code following an if containing a continue statement. "
            "As such, it will warn when it encounters an else "
            "following a chain of ifs, all of them containing a "
            "continue statement.",
        ),
        "R1725": (
            "Consider using Python 3 style super() without arguments",
            "super-with-arguments",
            "Emitted when calling the super() builtin with the current class "
            "and instance. On Python 3 these arguments are the default and they can be omitted.",
        ),
        "R1728": (
            "Consider using a generator instead '%s(%s)'",
            "consider-using-generator",
            "If your container can be large using "
            "a generator will bring better performance.",
        ),
        "R1729": (
            "Use a generator instead '%s(%s)'",
            "use-a-generator",
            "Comprehension inside of 'any', 'all', 'max', 'min' or 'sum' is unnecessary. "
            "A generator would be sufficient and faster.",
        ),
        "R1730": (
            "Consider using '%s' instead of unnecessary if block",
            "consider-using-min-builtin",
            "Using the min builtin instead of a conditional improves readability and conciseness.",
        ),
        "R1731": (
            "Consider using '%s' instead of unnecessary if block",
            "consider-using-max-builtin",
            "Using the max builtin instead of a conditional improves readability and conciseness.",
        ),
        "R1732": (
            "Consider using 'with' for resource-allocating operations",
            "consider-using-with",
            "Emitted if a resource-allocating assignment or call may be replaced by a 'with' block. "
            "By using 'with' the release of the allocated resources is ensured even in the case "
            "of an exception.",
        ),
        "R1733": (
            "Unnecessary dictionary index lookup, use '%s' instead",
            "unnecessary-dict-index-lookup",
            "Emitted when iterating over the dictionary items (key-item pairs) and accessing the "
            "value by index lookup. "
            "The value can be accessed directly instead.",
        ),
        "R1734": (
            "Consider using [] instead of list()",
            "use-list-literal",
            "Emitted when using list() to create an empty list instead of the literal []. "
            "The literal is faster as it avoids an additional function call.",
        ),
        "R1735": (
            "Consider using '%s' instead of a call to 'dict'.",
            "use-dict-literal",
            "Emitted when using dict() to create a dictionary instead of a literal '{ ... }'. "
            "The literal is faster as it avoids an additional function call.",
        ),
        "R1736": (
            "Unnecessary list index lookup, use '%s' instead",
            "unnecessary-list-index-lookup",
            "Emitted when iterating over an enumeration and accessing the "
            "value by index lookup. "
            "The value can be accessed directly instead.",
        ),
    }
    options = (
        (
            "max-nested-blocks",
            {
                "default": 5,
                "type": "int",
                "metavar": "<int>",
                "help": "Maximum number of nested blocks for function / method body",
            },
        ),
        (
            "never-returning-functions",
            {
                "default": ("sys.exit", "argparse.parse_error"),
                "type": "csv",
                "metavar": "<members names>",
                "help": "Complete name of functions that never returns. When checking "
                "for inconsistent-return-statements if a never returning function is "
                "called then it will be considered as an explicit return statement "
                "and no message will be printed.",
            },
        ),
    )

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._return_nodes: dict[str, list[nodes.Return]] = {}
        self._consider_using_with_stack = ConsiderUsingWithStack()
        self._init()
        self._never_returning_functions: set[str] = set()

    def _init(self) -> None:
        self._nested_blocks: list[NodesWithNestedBlocks] = []
        self._elifs: list[tuple[int, int]] = []
        self._reported_swap_nodes: set[nodes.NodeNG] = set()
        self._can_simplify_bool_op: bool = False
        self._consider_using_with_stack.clear_all()

    def open(self) -> None:
        # do this in open since config not fully initialized in __init__
        self._never_returning_functions = set(
            self.linter.config.never_returning_functions
        )

    @cached_property
    def _dummy_rgx(self) -> Pattern[str]:
        return self.linter.config.dummy_variables_rgx  # type: ignore[no-any-return]

    @staticmethod
    def _is_bool_const(node: nodes.Return | nodes.Assign) -> bool:
        return isinstance(node.value, nodes.Const) and isinstance(
            node.value.value, bool
        )

    def _is_actual_elif(self, node: nodes.If | nodes.Try) -> bool:
        """Check if the given node is an actual elif.

        This is a problem we're having with the builtin ast module,
        which splits `elif` branches into a separate if statement.
        Unfortunately we need to know the exact type in certain
        cases.
        """
        if isinstance(node.parent, nodes.If):
            orelse = node.parent.orelse
            # current if node must directly follow an "else"
            if orelse and orelse == [node]:
                if (node.lineno, node.col_offset) in self._elifs:
                    return True
        return False

    def _check_simplifiable_if(self, node: nodes.If) -> None:
        """Check if the given if node can be simplified.

        The if statement can be reduced to a boolean expression
        in some cases. For instance, if there are two branches
        and both of them return a boolean value that depends on
        the result of the statement's test, then this can be reduced
        to `bool(test)` without losing any functionality.
        """

        if self._is_actual_elif(node):
            # Not interested in if statements with multiple branches.
            return
        if len(node.orelse) != 1 or len(node.body) != 1:
            return

        # Check if both branches can be reduced.
        first_branch = node.body[0]
        else_branch = node.orelse[0]
        if isinstance(first_branch, nodes.Return):
            if not isinstance(else_branch, nodes.Return):
                return
            first_branch_is_bool = self._is_bool_const(first_branch)
            else_branch_is_bool = self._is_bool_const(else_branch)
            reduced_to = "'return bool(test)'"
        elif isinstance(first_branch, nodes.Assign):
            if not isinstance(else_branch, nodes.Assign):
                return

            # Check if we assign to the same value
            first_branch_targets = [
                target.name
                for target in first_branch.targets
                if isinstance(target, nodes.AssignName)
            ]
            else_branch_targets = [
                target.name
                for target in else_branch.targets
                if isinstance(target, nodes.AssignName)
            ]
            if not first_branch_targets or not else_branch_targets:
                return
            if sorted(first_branch_targets) != sorted(else_branch_targets):
                return

            first_branch_is_bool = self._is_bool_const(first_branch)
            else_branch_is_bool = self._is_bool_const(else_branch)
            reduced_to = "'var = bool(test)'"
        else:
            return

        if not first_branch_is_bool or not else_branch_is_bool:
            return
        if not first_branch.value.value:
            # This is a case that can't be easily simplified and
            # if it can be simplified, it will usually result in a
            # code that's harder to understand and comprehend.
            # Let's take for instance `arg and arg <= 3`. This could theoretically be
            # reduced to `not arg or arg > 3`, but the net result is that now the
            # condition is harder to understand, because it requires understanding of
            # an extra clause:
            #   * first, there is the negation of truthness with `not arg`
            #   * the second clause is `arg > 3`, which occurs when arg has a
            #     a truth value, but it implies that `arg > 3` is equivalent
            #     with `arg and arg > 3`, which means that the user must
            #     think about this assumption when evaluating `arg > 3`.
            #     The original form is easier to grasp.
            return

        self.add_message("simplifiable-if-statement", node=node, args=(reduced_to,))

    def process_tokens(self, tokens: list[tokenize.TokenInfo]) -> None:
        # Process tokens and look for 'if' or 'elif'
        for index, token in enumerate(tokens):
            token_string = token[1]
            if token_string == "elif":
                # AST exists by the time process_tokens is called, so
                # it's safe to assume tokens[index+1] exists.
                # tokens[index+1][2] is the elif's position as
                # reported by CPython and PyPy,
                # token[2] is the actual position and also is
                # reported by IronPython.
                self._elifs.extend([token[2], tokens[index + 1][2]])
            elif self.linter.is_message_enabled(
                "trailing-comma-tuple"
            ) and _is_trailing_comma(tokens, index):
                self.add_message("trailing-comma-tuple", line=token.start[0])

    @utils.only_required_for_messages("consider-using-with")
    def leave_module(self, _: nodes.Module) -> None:
        # check for context managers that have been created but not used
        self._emit_consider_using_with_if_needed(
            self._consider_using_with_stack.module_scope
        )
        self._init()

    @utils.only_required_for_messages("too-many-nested-blocks", "no-else-return")
    def visit_try(self, node: nodes.Try) -> None:
        self._check_nested_blocks(node)

        self._check_superfluous_else_return(node)
        self._check_superfluous_else_raise(node)

    visit_while = visit_try

    def _check_redefined_argument_from_local(self, name_node: nodes.AssignName) -> None:
        if self._dummy_rgx and self._dummy_rgx.match(name_node.name):
            return
        if not name_node.lineno:
            # Unknown position, maybe it is a manually built AST?
            return

        scope = name_node.scope()
        if not isinstance(scope, nodes.FunctionDef):
            return

        for defined_argument in scope.args.nodes_of_class(
            nodes.AssignName, skip_klass=(nodes.Lambda,)
        ):
            if defined_argument.name == name_node.name:
                self.add_message(
                    "redefined-argument-from-local",
                    node=name_node,
                    args=(name_node.name,),
                )

    @utils.only_required_for_messages(
        "redefined-argument-from-local",
        "too-many-nested-blocks",
        "unnecessary-dict-index-lookup",
        "unnecessary-list-index-lookup",
    )
    def visit_for(self, node: nodes.For) -> None:
        self._check_nested_blocks(node)
        self._check_unnecessary_dict_index_lookup(node)
        self._check_unnecessary_list_index_lookup(node)

        for name in node.target.nodes_of_class(nodes.AssignName):
            self._check_redefined_argument_from_local(name)

    @utils.only_required_for_messages("redefined-argument-from-local")
    def visit_excepthandler(self, node: nodes.ExceptHandler) -> None:
        if node.name and isinstance(node.name, nodes.AssignName):
            self._check_redefined_argument_from_local(node.name)

    @utils.only_required_for_messages(
        "redefined-argument-from-local", "consider-using-with"
    )
    def visit_with(self, node: nodes.With) -> None:
        for var, names in node.items:
            if isinstance(var, nodes.Name):
                for stack in self._consider_using_with_stack:
                    # We don't need to restrict the stacks we search to the current scope and
                    # outer scopes, as e.g. the function_scope stack will be empty when we
                    # check a ``with`` on the class level.
                    if var.name in stack:
                        del stack[var.name]
                        break
            if not names:
                continue
            for name in names.nodes_of_class(nodes.AssignName):
                self._check_redefined_argument_from_local(name)

    def _check_superfluous_else(
        self,
        node: nodes.If | nodes.Try,
        msg_id: str,
        returning_node_class: nodes.NodeNG,
    ) -> None:
        if isinstance(node, nodes.Try) and node.finalbody:
            # Not interested in try/except/else/finally statements.
            return

        if not node.orelse:
            # Not interested in if/try statements without else.
            return

        if self._is_actual_elif(node):
            # Not interested in elif nodes; only if
            return

        if (
            isinstance(node, nodes.If)
            and _if_statement_is_always_returning(node, returning_node_class)
        ) or (
            isinstance(node, nodes.Try)
            and not node.finalbody
            and _except_statement_is_always_returning(node, returning_node_class)
        ):
            orelse = node.orelse[0]
            if (orelse.lineno, orelse.col_offset) in self._elifs:
                args = ("elif", 'remove the leading "el" from "elif"')
            else:
                args = ("else", 'remove the "else" and de-indent the code inside it')
            self.add_message(msg_id, node=node, args=args, confidence=HIGH)

    def _check_superfluous_else_return(self, node: nodes.If) -> None:
        return self._check_superfluous_else(
            node, msg_id="no-else-return", returning_node_class=nodes.Return
        )

    def _check_superfluous_else_raise(self, node: nodes.If) -> None:
        return self._check_superfluous_else(
            node, msg_id="no-else-raise", returning_node_class=nodes.Raise
        )

    def _check_superfluous_else_break(self, node: nodes.If) -> None:
        return self._check_superfluous_else(
            node, msg_id="no-else-break", returning_node_class=nodes.Break
        )

    def _check_superfluous_else_continue(self, node: nodes.If) -> None:
        return self._check_superfluous_else(
            node, msg_id="no-else-continue", returning_node_class=nodes.Continue
        )

    @staticmethod
    def _type_and_name_are_equal(node_a: Any, node_b: Any) -> bool:
        if isinstance(node_a, nodes.Name) and isinstance(node_b, nodes.Name):
            return node_a.name == node_b.name  # type: ignore[no-any-return]
        if isinstance(node_a, nodes.AssignName) and isinstance(
            node_b, nodes.AssignName
        ):
            return node_a.name == node_b.name  # type: ignore[no-any-return]
        if isinstance(node_a, nodes.Const) and isinstance(node_b, nodes.Const):
            return node_a.value == node_b.value  # type: ignore[no-any-return]
        return False

    def _is_dict_get_block(self, node: nodes.If) -> bool:
        # "if <compare node>"
        if not isinstance(node.test, nodes.Compare):
            return False

        # Does not have a single statement in the guard's body
        if len(node.body) != 1:
            return False

        # Look for a single variable assignment on the LHS and a subscript on RHS
        stmt = node.body[0]
        if not (
            isinstance(stmt, nodes.Assign)
            and len(node.body[0].targets) == 1
            and isinstance(node.body[0].targets[0], nodes.AssignName)
            and isinstance(stmt.value, nodes.Subscript)
        ):
            return False

        # The subscript's slice needs to be the same as the test variable.
        slice_value = stmt.value.slice
        if not (
            self._type_and_name_are_equal(stmt.value.value, node.test.ops[0][1])
            and self._type_and_name_are_equal(slice_value, node.test.left)
        ):
            return False

        # The object needs to be a dictionary instance
        return isinstance(utils.safe_infer(node.test.ops[0][1]), nodes.Dict)

    def _check_consider_get(self, node: nodes.If) -> None:
        if_block_ok = self._is_dict_get_block(node)
        if if_block_ok and not node.orelse:
            self.add_message("consider-using-get", node=node)
        elif (
            if_block_ok
            and len(node.orelse) == 1
            and isinstance(node.orelse[0], nodes.Assign)
            and self._type_and_name_are_equal(
                node.orelse[0].targets[0], node.body[0].targets[0]
            )
            and len(node.orelse[0].targets) == 1
        ):
            self.add_message("consider-using-get", node=node)

    @utils.only_required_for_messages(
        "too-many-nested-blocks",
        "simplifiable-if-statement",
        "no-else-return",
        "no-else-raise",
        "no-else-break",
        "no-else-continue",
        "consider-using-get",
        "consider-using-min-builtin",
        "consider-using-max-builtin",
    )
    def visit_if(self, node: nodes.If) -> None:
        self._check_simplifiable_if(node)
        self._check_nested_blocks(node)
        self._check_superfluous_else_return(node)
        self._check_superfluous_else_raise(node)
        self._check_superfluous_else_break(node)
        self._check_superfluous_else_continue(node)
        self._check_consider_get(node)
        self._check_consider_using_min_max_builtin(node)

    # pylint: disable = too-many-branches
    def _check_consider_using_min_max_builtin(self, node: nodes.If) -> None:
        """Check if the given if node can be refactored as a min/max python builtin."""
        if self._is_actual_elif(node) or node.orelse:
            # Not interested in if statements with multiple branches.
            return

        if len(node.body) != 1:
            return

        body = node.body[0]
        # Check if condition can be reduced.
        if not hasattr(body, "targets") or len(body.targets) != 1:
            return

        target = body.targets[0]
        if not (
            isinstance(node.test, nodes.Compare)
            and not isinstance(target, nodes.Subscript)
            and not isinstance(node.test.left, nodes.Subscript)
            and isinstance(body, nodes.Assign)
        ):
            return

        # Check that the assignation is on the same variable.
        if hasattr(node.test.left, "name"):
            left_operand = node.test.left.name
        elif hasattr(node.test.left, "attrname"):
            left_operand = node.test.left.attrname
        else:
            return

        if hasattr(target, "name"):
            target_assignation = target.name
        elif hasattr(target, "attrname"):
            target_assignation = target.attrname
        else:
            return

        if not (left_operand == target_assignation):
            return

        if len(node.test.ops) > 1:
            return

        if not isinstance(body.value, (nodes.Name, nodes.Const)):
            return

        operator, right_statement = node.test.ops[0]
        if isinstance(body.value, nodes.Name):
            body_value = body.value.name
        else:
            body_value = body.value.value

        if isinstance(right_statement, nodes.Name):
            right_statement_value = right_statement.name
        elif isinstance(right_statement, nodes.Const):
            right_statement_value = right_statement.value
        else:
            return

        # Verify the right part of the statement is the same.
        if right_statement_value != body_value:
            return

        if operator in {"<", "<="}:
            reduced_to = (
                f"{target_assignation} = max({target_assignation}, {body_value})"
            )
            self.add_message(
                "consider-using-max-builtin", node=node, args=(reduced_to,)
            )
        elif operator in {">", ">="}:
            reduced_to = (
                f"{target_assignation} = min({target_assignation}, {body_value})"
            )
            self.add_message(
                "consider-using-min-builtin", node=node, args=(reduced_to,)
            )

    @utils.only_required_for_messages("simplifiable-if-expression")
    def visit_ifexp(self, node: nodes.IfExp) -> None:
        self._check_simplifiable_ifexp(node)

    def _check_simplifiable_ifexp(self, node: nodes.IfExp) -> None:
        if not isinstance(node.body, nodes.Const) or not isinstance(
            node.orelse, nodes.Const
        ):
            return

        if not isinstance(node.body.value, bool) or not isinstance(
            node.orelse.value, bool
        ):
            return

        if isinstance(node.test, nodes.Compare):
            test_reduced_to = "test"
        else:
            test_reduced_to = "bool(test)"

        if (node.body.value, node.orelse.value) == (True, False):
            reduced_to = f"'{test_reduced_to}'"
        elif (node.body.value, node.orelse.value) == (False, True):
            reduced_to = "'not test'"
        else:
            return

        self.add_message("simplifiable-if-expression", node=node, args=(reduced_to,))

    @utils.only_required_for_messages(
        "too-many-nested-blocks",
        "inconsistent-return-statements",
        "useless-return",
        "consider-using-with",
    )
    def leave_functiondef(self, node: nodes.FunctionDef) -> None:
        # check left-over nested blocks stack
        self._emit_nested_blocks_message_if_needed(self._nested_blocks)
        # new scope = reinitialize the stack of nested blocks
        self._nested_blocks = []
        # check consistent return statements
        self._check_consistent_returns(node)
        # check for single return or return None at the end
        self._check_return_at_the_end(node)
        self._return_nodes[node.name] = []
        # check for context managers that have been created but not used
        self._emit_consider_using_with_if_needed(
            self._consider_using_with_stack.function_scope
        )
        self._consider_using_with_stack.function_scope.clear()

    @utils.only_required_for_messages("consider-using-with")
    def leave_classdef(self, _: nodes.ClassDef) -> None:
        # check for context managers that have been created but not used
        self._emit_consider_using_with_if_needed(
            self._consider_using_with_stack.class_scope
        )
        self._consider_using_with_stack.class_scope.clear()

    @utils.only_required_for_messages("stop-iteration-return")
    def visit_raise(self, node: nodes.Raise) -> None:
        self._check_stop_iteration_inside_generator(node)

    def _check_stop_iteration_inside_generator(self, node: nodes.Raise) -> None:
        """Check if an exception of type StopIteration is raised inside a generator."""
        frame = node.frame()
        if not isinstance(frame, nodes.FunctionDef) or not frame.is_generator():
            return
        if utils.node_ignores_exception(node, StopIteration):
            return
        if not node.exc:
            return
        exc = utils.safe_infer(node.exc)
        if not exc or not isinstance(exc, (bases.Instance, nodes.ClassDef)):
            return
        if self._check_exception_inherit_from_stopiteration(exc):
            self.add_message("stop-iteration-return", node=node, confidence=INFERENCE)

    @staticmethod
    def _check_exception_inherit_from_stopiteration(
        exc: nodes.ClassDef | bases.Instance,
    ) -> bool:
        """Return True if the exception node in argument inherit from StopIteration."""
        stopiteration_qname = f"{utils.EXCEPTIONS_MODULE}.StopIteration"
        return any(_class.qname() == stopiteration_qname for _class in exc.mro())

    def _check_consider_using_comprehension_constructor(self, node: nodes.Call) -> None:
        if (
            isinstance(node.func, nodes.Name)
            and node.args
            and isinstance(node.args[0], nodes.ListComp)
        ):
            if node.func.name == "dict":
                element = node.args[0].elt
                if isinstance(element, nodes.Call):
                    return

                # If we have an `IfExp` here where both the key AND value
                # are different, then don't raise the issue. See #5588
                if (
                    isinstance(element, nodes.IfExp)
                    and isinstance(element.body, (nodes.Tuple, nodes.List))
                    and len(element.body.elts) == 2
                    and isinstance(element.orelse, (nodes.Tuple, nodes.List))
                    and len(element.orelse.elts) == 2
                ):
                    key1, value1 = element.body.elts
                    key2, value2 = element.orelse.elts
                    if (
                        key1.as_string() != key2.as_string()
                        and value1.as_string() != value2.as_string()
                    ):
                        return

                message_name = "consider-using-dict-comprehension"
                self.add_message(message_name, node=node)
            elif node.func.name == "set":
                message_name = "consider-using-set-comprehension"
                self.add_message(message_name, node=node)

    def _check_consider_using_generator(self, node: nodes.Call) -> None:
        # 'any', 'all', definitely should use generator, while 'list', 'tuple',
        # 'sum', 'max', and 'min' need to be considered first
        # See https://github.com/pylint-dev/pylint/pull/3309#discussion_r576683109
        # https://github.com/pylint-dev/pylint/pull/6595#issuecomment-1125704244
        # and https://peps.python.org/pep-0289/
        checked_call = ["any", "all", "sum", "max", "min", "list", "tuple"]
        if (
            isinstance(node, nodes.Call)
            and node.func
            and isinstance(node.func, nodes.Name)
            and node.func.name in checked_call
        ):
            # functions in checked_calls take exactly one positional argument
            # check whether the argument is list comprehension
            if len(node.args) == 1 and isinstance(node.args[0], nodes.ListComp):
                # remove square brackets '[]'
                inside_comp = node.args[0].as_string()[1:-1]
                if node.keywords:
                    inside_comp = f"({inside_comp})"
                    inside_comp += ", "
                    inside_comp += ", ".join(kw.as_string() for kw in node.keywords)
                call_name = node.func.name
                if call_name in {"any", "all"}:
                    self.add_message(
                        "use-a-generator",
                        node=node,
                        args=(call_name, inside_comp),
                    )
                else:
                    self.add_message(
                        "consider-using-generator",
                        node=node,
                        args=(call_name, inside_comp),
                    )

    @utils.only_required_for_messages(
        "stop-iteration-return",
        "consider-using-dict-comprehension",
        "consider-using-set-comprehension",
        "consider-using-sys-exit",
        "super-with-arguments",
        "consider-using-generator",
        "consider-using-with",
        "use-list-literal",
        "use-dict-literal",
        "use-a-generator",
    )
    def visit_call(self, node: nodes.Call) -> None:
        self._check_raising_stopiteration_in_generator_next_call(node)
        self._check_consider_using_comprehension_constructor(node)
        self._check_quit_exit_call(node)
        self._check_super_with_arguments(node)
        self._check_consider_using_generator(node)
        self._check_consider_using_with(node)
        self._check_use_list_literal(node)
        self._check_use_dict_literal(node)

    @staticmethod
    def _has_exit_in_scope(scope: nodes.LocalsDictNodeNG) -> bool:
        exit_func = scope.locals.get("exit")
        return bool(
            exit_func and isinstance(exit_func[0], (nodes.ImportFrom, nodes.Import))
        )

    def _check_quit_exit_call(self, node: nodes.Call) -> None:
        if isinstance(node.func, nodes.Name) and node.func.name in BUILTIN_EXIT_FUNCS:
            # If we have `exit` imported from `sys` in the current or global scope,
            # exempt this instance.
            local_scope = node.scope()
            if self._has_exit_in_scope(local_scope) or self._has_exit_in_scope(
                node.root()
            ):
                return
            self.add_message("consider-using-sys-exit", node=node, confidence=HIGH)

    def _check_super_with_arguments(self, node: nodes.Call) -> None:
        if not isinstance(node.func, nodes.Name) or node.func.name != "super":
            return

        if (
            len(node.args) != 2
            or not all(isinstance(arg, nodes.Name) for arg in node.args)
            or node.args[1].name != "self"
            or (frame_class := node_frame_class(node)) is None
            or node.args[0].name != frame_class.name
        ):
            return

        self.add_message("super-with-arguments", node=node)

    def _check_raising_stopiteration_in_generator_next_call(
        self, node: nodes.Call
    ) -> None:
        """Check if a StopIteration exception is raised by the call to next function.

        If the next value has a default value, then do not add message.

        :param node: Check to see if this Call node is a next function
        :type node: :class:`nodes.Call`
        """

        def _looks_like_infinite_iterator(param: nodes.NodeNG) -> bool:
            inferred = utils.safe_infer(param)
            if isinstance(inferred, bases.Instance):
                return inferred.qname() in KNOWN_INFINITE_ITERATORS
            return False

        if isinstance(node.func, nodes.Attribute):
            # A next() method, which is now what we want.
            return

        if len(node.args) == 0:
            # handle case when builtin.next is called without args.
            # see https://github.com/pylint-dev/pylint/issues/7828
            return

        inferred = utils.safe_infer(node.func)

        if (
            isinstance(inferred, nodes.FunctionDef)
            and inferred.qname() == "builtins.next"
        ):
            frame = node.frame()
            # The next builtin can only have up to two
            # positional arguments and no keyword arguments
            has_sentinel_value = len(node.args) > 1
            if (
                isinstance(frame, nodes.FunctionDef)
                and frame.is_generator()
                and not has_sentinel_value
                and not utils.node_ignores_exception(node, StopIteration)
                and not _looks_like_infinite_iterator(node.args[0])
            ):
                self.add_message(
                    "stop-iteration-return", node=node, confidence=INFERENCE
                )

    def _check_nested_blocks(
        self,
        node: NodesWithNestedBlocks,
    ) -> None:
        """Update and check the number of nested blocks."""
        # only check block levels inside functions or methods
        if not isinstance(node.scope(), nodes.FunctionDef):
            return
        # messages are triggered on leaving the nested block. Here we save the
        # stack in case the current node isn't nested in the previous one
        nested_blocks = self._nested_blocks[:]
        if node.parent == node.scope():
            self._nested_blocks = [node]
        else:
            # go through ancestors from the most nested to the less
            for ancestor_node in reversed(self._nested_blocks):
                if ancestor_node == node.parent:
                    break
                self._nested_blocks.pop()
            # if the node is an elif, this should not be another nesting level
            if isinstance(node, nodes.If) and self._is_actual_elif(node):
                if self._nested_blocks:
                    self._nested_blocks.pop()
            self._nested_blocks.append(node)

        # send message only once per group of nested blocks
        if len(nested_blocks) > len(self._nested_blocks):
            self._emit_nested_blocks_message_if_needed(nested_blocks)

    def _emit_nested_blocks_message_if_needed(
        self, nested_blocks: list[NodesWithNestedBlocks]
    ) -> None:
        if len(nested_blocks) > self.linter.config.max_nested_blocks:
            self.add_message(
                "too-many-nested-blocks",
                node=nested_blocks[0],
                args=(len(nested_blocks), self.linter.config.max_nested_blocks),
            )

    def _emit_consider_using_with_if_needed(
        self, stack: dict[str, nodes.NodeNG]
    ) -> None:
        for node in stack.values():
            self.add_message("consider-using-with", node=node)

    @staticmethod
    def _duplicated_isinstance_types(node: nodes.BoolOp) -> dict[str, set[str]]:
        """Get the duplicated types from the underlying isinstance calls.

        :param nodes.BoolOp node: Node which should contain a bunch of isinstance calls.
        :returns: Dictionary of the comparison objects from the isinstance calls,
                  to duplicate values from consecutive calls.
        :rtype: dict
        """
        duplicated_objects: set[str] = set()
        all_types: collections.defaultdict[str, set[str]] = collections.defaultdict(set)

        for call in node.values:
            if not isinstance(call, nodes.Call) or len(call.args) != 2:
                continue

            inferred = utils.safe_infer(call.func)
            if not inferred or not utils.is_builtin_object(inferred):
                continue

            if inferred.name != "isinstance":
                continue

            isinstance_object = call.args[0].as_string()
            isinstance_types = call.args[1]

            if isinstance_object in all_types:
                duplicated_objects.add(isinstance_object)

            if isinstance(isinstance_types, nodes.Tuple):
                elems = [
                    class_type.as_string() for class_type in isinstance_types.itered()
                ]
            else:
                elems = [isinstance_types.as_string()]
            all_types[isinstance_object].update(elems)

        # Remove all keys which not duplicated
        return {
            key: value for key, value in all_types.items() if key in duplicated_objects
        }

    def _check_consider_merging_isinstance(self, node: nodes.BoolOp) -> None:
        """Check isinstance calls which can be merged together."""
        if node.op != "or":
            return

        first_args = self._duplicated_isinstance_types(node)
        for duplicated_name, class_names in first_args.items():
            names = sorted(name for name in class_names)
            self.add_message(
                "consider-merging-isinstance",
                node=node,
                args=(duplicated_name, ", ".join(names)),
            )

    def _check_consider_using_in(self, node: nodes.BoolOp) -> None:
        allowed_ops = {"or": "==", "and": "!="}

        if node.op not in allowed_ops or len(node.values) < 2:
            return

        for value in node.values:
            if (
                not isinstance(value, nodes.Compare)
                or len(value.ops) != 1
                or value.ops[0][0] not in allowed_ops[node.op]
            ):
                return
            for comparable in value.left, value.ops[0][1]:
                if isinstance(comparable, nodes.Call):
                    return

        # Gather variables and values from comparisons
        variables, values = [], []
        for value in node.values:
            variable_set = set()
            for comparable in value.left, value.ops[0][1]:
                if isinstance(comparable, (nodes.Name, nodes.Attribute)):
                    variable_set.add(comparable.as_string())
                values.append(comparable.as_string())
            variables.append(variable_set)

        # Look for (common-)variables that occur in all comparisons
        common_variables = reduce(lambda a, b: a.intersection(b), variables)

        if not common_variables:
            return

        # Gather information for the suggestion
        common_variable = sorted(list(common_variables))[0]
        values = list(collections.OrderedDict.fromkeys(values))
        values.remove(common_variable)
        values_string = ", ".join(values) if len(values) != 1 else values[0] + ","
        maybe_not = "" if node.op == "or" else "not "
        self.add_message(
            "consider-using-in",
            node=node,
            args=(common_variable, maybe_not, values_string),
            confidence=HIGH,
        )

    def _check_chained_comparison(self, node: nodes.BoolOp) -> None:
        """Check if there is any chained comparison in the expression.

        Add a refactoring message if a boolOp contains comparison like a < b and b < c,
        which can be chained as a < b < c.

        Care is taken to avoid simplifying a < b < c and b < d.
        """
        if node.op != "and" or len(node.values) < 2:
            return

        def _find_lower_upper_bounds(
            comparison_node: nodes.Compare,
            uses: collections.defaultdict[str, dict[str, set[nodes.Compare]]],
        ) -> None:
            left_operand = comparison_node.left
            for operator, right_operand in comparison_node.ops:
                for operand in (left_operand, right_operand):
                    value = None
                    if isinstance(operand, nodes.Name):
                        value = operand.name
                    elif isinstance(operand, nodes.Const):
                        value = operand.value

                    if value is None:
                        continue

                    if operator in {"<", "<="}:
                        if operand is left_operand:
                            uses[value]["lower_bound"].add(comparison_node)
                        elif operand is right_operand:
                            uses[value]["upper_bound"].add(comparison_node)
                    elif operator in {">", ">="}:
                        if operand is left_operand:
                            uses[value]["upper_bound"].add(comparison_node)
                        elif operand is right_operand:
                            uses[value]["lower_bound"].add(comparison_node)
                left_operand = right_operand

        uses: collections.defaultdict[
            str, dict[str, set[nodes.Compare]]
        ] = collections.defaultdict(
            lambda: {"lower_bound": set(), "upper_bound": set()}
        )
        for comparison_node in node.values:
            if isinstance(comparison_node, nodes.Compare):
                _find_lower_upper_bounds(comparison_node, uses)

        for bounds in uses.values():
            num_shared = len(bounds["lower_bound"].intersection(bounds["upper_bound"]))
            num_lower_bounds = len(bounds["lower_bound"])
            num_upper_bounds = len(bounds["upper_bound"])
            if num_shared < num_lower_bounds and num_shared < num_upper_bounds:
                self.add_message("chained-comparison", node=node)
                break

    @staticmethod
    def _apply_boolean_simplification_rules(
        operator: str, values: list[nodes.NodeNG]
    ) -> list[nodes.NodeNG]:
        """Removes irrelevant values or returns short-circuiting values.

        This function applies the following two rules:
        1) an OR expression with True in it will always be true, and the
           reverse for AND

        2) False values in OR expressions are only relevant if all values are
           false, and the reverse for AND
        """
        simplified_values: list[nodes.NodeNG] = []

        for subnode in values:
            inferred_bool = None
            if not next(subnode.nodes_of_class(nodes.Name), False):
                inferred = utils.safe_infer(subnode)
                if inferred:
                    inferred_bool = inferred.bool_value()

            if not isinstance(inferred_bool, bool):
                simplified_values.append(subnode)
            elif (operator == "or") == inferred_bool:
                return [subnode]

        return simplified_values or [nodes.Const(operator == "and")]

    def _simplify_boolean_operation(self, bool_op: nodes.BoolOp) -> nodes.BoolOp:
        """Attempts to simplify a boolean operation.

        Recursively applies simplification on the operator terms,
        and keeps track of whether reductions have been made.
        """
        children = list(bool_op.get_children())
        intermediate = [
            self._simplify_boolean_operation(child)
            if isinstance(child, nodes.BoolOp)
            else child
            for child in children
        ]
        result = self._apply_boolean_simplification_rules(bool_op.op, intermediate)
        if len(result) < len(children):
            self._can_simplify_bool_op = True
        if len(result) == 1:
            return result[0]
        simplified_bool_op = copy.copy(bool_op)
        simplified_bool_op.postinit(result)
        return simplified_bool_op

    def _check_simplifiable_condition(self, node: nodes.BoolOp) -> None:
        """Check if a boolean condition can be simplified.

        Variables will not be simplified, even if the value can be inferred,
        and expressions like '3 + 4' will remain expanded.
        """
        if not utils.is_test_condition(node):
            return

        self._can_simplify_bool_op = False
        simplified_expr = self._simplify_boolean_operation(node)

        if not self._can_simplify_bool_op:
            return

        if not next(simplified_expr.nodes_of_class(nodes.Name), False):
            self.add_message(
                "condition-evals-to-constant",
                node=node,
                args=(node.as_string(), simplified_expr.as_string()),
            )
        else:
            self.add_message(
                "simplifiable-condition",
                node=node,
                args=(node.as_string(), simplified_expr.as_string()),
            )

    @utils.only_required_for_messages(
        "consider-merging-isinstance",
        "consider-using-in",
        "chained-comparison",
        "simplifiable-condition",
        "condition-evals-to-constant",
    )
    def visit_boolop(self, node: nodes.BoolOp) -> None:
        self._check_consider_merging_isinstance(node)
        self._check_consider_using_in(node)
        self._check_chained_comparison(node)
        self._check_simplifiable_condition(node)

    @staticmethod
    def _is_simple_assignment(node: nodes.NodeNG | None) -> bool:
        return (
            isinstance(node, nodes.Assign)
            and len(node.targets) == 1
            and isinstance(node.targets[0], nodes.AssignName)
            and isinstance(node.value, nodes.Name)
        )

    def _check_swap_variables(self, node: nodes.Return | nodes.Assign) -> None:
        if not node.next_sibling() or not node.next_sibling().next_sibling():
            return
        assignments = [node, node.next_sibling(), node.next_sibling().next_sibling()]
        if not all(self._is_simple_assignment(node) for node in assignments):
            return
        if any(node in self._reported_swap_nodes for node in assignments):
            return
        left = [node.targets[0].name for node in assignments]
        right = [node.value.name for node in assignments]
        if left[0] == right[-1] and left[1:] == right[:-1]:
            self._reported_swap_nodes.update(assignments)
            message = "consider-swap-variables"
            self.add_message(message, node=node)

    @utils.only_required_for_messages(
        "simplify-boolean-expression",
        "consider-using-ternary",
        "consider-swap-variables",
        "consider-using-with",
    )
    def visit_assign(self, node: nodes.Assign) -> None:
        self._append_context_managers_to_stack(node)
        self.visit_return(node)  # remaining checks are identical as for return nodes

    @utils.only_required_for_messages(
        "simplify-boolean-expression",
        "consider-using-ternary",
        "consider-swap-variables",
    )
    def visit_return(self, node: nodes.Return | nodes.Assign) -> None:
        self._check_swap_variables(node)
        if self._is_and_or_ternary(node.value):
            cond, truth_value, false_value = self._and_or_ternary_arguments(node.value)
        else:
            return

        if all(
            isinstance(value, nodes.Compare) for value in (truth_value, false_value)
        ):
            return

        inferred_truth_value = utils.safe_infer(truth_value, compare_constants=True)
        if inferred_truth_value is None or isinstance(
            inferred_truth_value, UninferableBase
        ):
            return
        truth_boolean_value = inferred_truth_value.bool_value()

        if truth_boolean_value is False:
            message = "simplify-boolean-expression"
            suggestion = false_value.as_string()
        else:
            message = "consider-using-ternary"
            suggestion = f"{truth_value.as_string()} if {cond.as_string()} else {false_value.as_string()}"
        self.add_message(message, node=node, args=(suggestion,), confidence=INFERENCE)

    def _append_context_managers_to_stack(self, node: nodes.Assign) -> None:
        if _is_inside_context_manager(node):
            # if we are inside a context manager itself, we assume that it will handle
            # the resource management itself.
            return
        if isinstance(node.targets[0], (nodes.Tuple, nodes.List, nodes.Set)):
            assignees = node.targets[0].elts
            value = utils.safe_infer(node.value)
            if value is None or not hasattr(value, "elts"):
                # We cannot deduce what values are assigned, so we have to skip this
                return
            values = value.elts
        else:
            assignees = [node.targets[0]]
            values = [node.value]
        if any(isinstance(n, UninferableBase) for n in (assignees, values)):
            return
        for assignee, value in zip(assignees, values):
            if not isinstance(value, nodes.Call):
                continue
            inferred = utils.safe_infer(value.func)
            if (
                not inferred
                or inferred.qname() not in CALLS_RETURNING_CONTEXT_MANAGERS
                or not isinstance(assignee, (nodes.AssignName, nodes.AssignAttr))
            ):
                continue
            stack = self._consider_using_with_stack.get_stack_for_frame(node.frame())
            varname = (
                assignee.name
                if isinstance(assignee, nodes.AssignName)
                else assignee.attrname
            )
            if varname in stack:
                existing_node = stack[varname]
                if astroid.are_exclusive(node, existing_node):
                    # only one of the two assignments can be executed at runtime, thus it is fine
                    stack[varname] = value
                    continue
                # variable was redefined before it was used in a ``with`` block
                self.add_message(
                    "consider-using-with",
                    node=existing_node,
                )
            stack[varname] = value

    def _check_consider_using_with(self, node: nodes.Call) -> None:
        if _is_inside_context_manager(node) or _is_a_return_statement(node):
            # If we are inside a context manager itself, we assume that it will handle the
            # resource management itself.
            # If the node is a child of a return, we assume that the caller knows he is getting
            # a context manager he should use properly (i.e. in a ``with``).
            return
        if (
            node
            in self._consider_using_with_stack.get_stack_for_frame(
                node.frame()
            ).values()
        ):
            # the result of this call was already assigned to a variable and will be
            # checked when leaving the scope.
            return
        inferred = utils.safe_infer(node.func)
        if not inferred or not isinstance(
            inferred, (nodes.FunctionDef, nodes.ClassDef, bases.UnboundMethod)
        ):
            return
        could_be_used_in_with = (
            # things like ``lock.acquire()``
            inferred.qname() in CALLS_THAT_COULD_BE_REPLACED_BY_WITH
            or (
                # things like ``open("foo")`` which are not already inside a ``with`` statement
                inferred.qname() in CALLS_RETURNING_CONTEXT_MANAGERS
                and not _is_part_of_with_items(node)
            )
        )
        if could_be_used_in_with and not _will_be_released_automatically(node):
            self.add_message("consider-using-with", node=node)

    def _check_use_list_literal(self, node: nodes.Call) -> None:
        """Check if empty list is created by using the literal []."""
        if node.as_string() == "list()":
            inferred = utils.safe_infer(node.func)
            if isinstance(inferred, nodes.ClassDef) and not node.args:
                if inferred.qname() == "builtins.list":
                    self.add_message("use-list-literal", node=node)

    def _check_use_dict_literal(self, node: nodes.Call) -> None:
        """Check if dict is created by using the literal {}."""
        if not isinstance(node.func, astroid.Name) or node.func.name != "dict":
            return
        inferred = utils.safe_infer(node.func)
        if (
            isinstance(inferred, nodes.ClassDef)
            and inferred.qname() == "builtins.dict"
            and not node.args
        ):
            self.add_message(
                "use-dict-literal",
                args=(self._dict_literal_suggestion(node),),
                node=node,
                confidence=INFERENCE,
            )

    @staticmethod
    def _dict_literal_suggestion(node: nodes.Call) -> str:
        """Return a suggestion of reasonable length."""
        elements: list[str] = []
        for keyword in node.keywords:
            if len(", ".join(elements)) >= 64:
                break
            if keyword not in node.kwargs:
                elements.append(f'"{keyword.arg}": {keyword.value.as_string()}')
        for keyword in node.kwargs:
            if len(", ".join(elements)) >= 64:
                break
            elements.append(f"**{keyword.value.as_string()}")
        suggestion = ", ".join(elements)
        return f"{{{suggestion}{', ... '  if len(suggestion) > 64 else ''}}}"

    @staticmethod
    def _name_to_concatenate(node: nodes.NodeNG) -> str | None:
        """Try to extract the name used in a concatenation loop."""
        if isinstance(node, nodes.Name):
            return cast("str | None", node.name)
        if not isinstance(node, nodes.JoinedStr):
            return None

        values = [
            value for value in node.values if isinstance(value, nodes.FormattedValue)
        ]
        if len(values) != 1 or not isinstance(values[0].value, nodes.Name):
            return None
        return cast("str | None", values[0].value.name)

    def _check_consider_using_join(self, aug_assign: nodes.AugAssign) -> None:
        """We start with the augmented assignment and work our way upwards.

        Names of variables for nodes if match successful:
        result = ''  # assign
        for number in ['1', '2', '3']  # for_loop
            result += number  # aug_assign
        """
        for_loop = aug_assign.parent
        if not isinstance(for_loop, nodes.For) or len(for_loop.body) > 1:
            return
        assign = for_loop.previous_sibling()
        if not isinstance(assign, nodes.Assign):
            return
        result_assign_names = {
            target.name
            for target in assign.targets
            if isinstance(target, nodes.AssignName)
        }

        is_concat_loop = (
            aug_assign.op == "+="
            and isinstance(aug_assign.target, nodes.AssignName)
            and len(for_loop.body) == 1
            and aug_assign.target.name in result_assign_names
            and isinstance(assign.value, nodes.Const)
            and isinstance(assign.value.value, str)
            and self._name_to_concatenate(aug_assign.value) == for_loop.target.name
        )
        if is_concat_loop:
            self.add_message("consider-using-join", node=aug_assign)

    @utils.only_required_for_messages("consider-using-join")
    def visit_augassign(self, node: nodes.AugAssign) -> None:
        self._check_consider_using_join(node)

    @utils.only_required_for_messages(
        "unnecessary-comprehension",
        "unnecessary-dict-index-lookup",
        "unnecessary-list-index-lookup",
    )
    def visit_comprehension(self, node: nodes.Comprehension) -> None:
        self._check_unnecessary_comprehension(node)
        self._check_unnecessary_dict_index_lookup(node)
        self._check_unnecessary_list_index_lookup(node)

    def _check_unnecessary_comprehension(self, node: nodes.Comprehension) -> None:
        if (
            isinstance(node.parent, nodes.GeneratorExp)
            or len(node.ifs) != 0
            or len(node.parent.generators) != 1
            or node.is_async
        ):
            return

        if (
            isinstance(node.parent, nodes.DictComp)
            and isinstance(node.parent.key, nodes.Name)
            and isinstance(node.parent.value, nodes.Name)
            and isinstance(node.target, nodes.Tuple)
            and all(isinstance(elt, nodes.AssignName) for elt in node.target.elts)
        ):
            expr_list = [node.parent.key.name, node.parent.value.name]
            target_list = [elt.name for elt in node.target.elts]

        elif isinstance(node.parent, (nodes.ListComp, nodes.SetComp)):
            expr = node.parent.elt
            if isinstance(expr, nodes.Name):
                expr_list = expr.name
            elif isinstance(expr, nodes.Tuple):
                if any(not isinstance(elt, nodes.Name) for elt in expr.elts):
                    return
                expr_list = [elt.name for elt in expr.elts]
            else:
                expr_list = []
            target = node.parent.generators[0].target
            target_list = (
                target.name
                if isinstance(target, nodes.AssignName)
                else (
                    [
                        elt.name
                        for elt in target.elts
                        if isinstance(elt, nodes.AssignName)
                    ]
                    if isinstance(target, nodes.Tuple)
                    else []
                )
            )
        else:
            return
        if expr_list == target_list and expr_list:
            args: tuple[str] | None = None
            inferred = utils.safe_infer(node.iter)
            if isinstance(node.parent, nodes.DictComp) and isinstance(
                inferred, astroid.objects.DictItems
            ):
                args = (f"{node.iter.func.expr.as_string()}",)
            elif (
                isinstance(node.parent, nodes.ListComp)
                and isinstance(inferred, nodes.List)
            ) or (
                isinstance(node.parent, nodes.SetComp)
                and isinstance(inferred, nodes.Set)
            ):
                args = (f"{node.iter.as_string()}",)
            if args:
                self.add_message(
                    "unnecessary-comprehension", node=node.parent, args=args
                )
                return

            if isinstance(node.parent, nodes.DictComp):
                func = "dict"
            elif isinstance(node.parent, nodes.ListComp):
                func = "list"
            elif isinstance(node.parent, nodes.SetComp):
                func = "set"
            else:
                return

            self.add_message(
                "unnecessary-comprehension",
                node=node.parent,
                args=(f"{func}({node.iter.as_string()})",),
            )

    @staticmethod
    def _is_and_or_ternary(node: nodes.NodeNG | None) -> bool:
        """Returns true if node is 'condition and true_value or false_value' form.

        All of: condition, true_value and false_value should not be a complex boolean expression
        """
        return (
            isinstance(node, nodes.BoolOp)
            and node.op == "or"
            and len(node.values) == 2
            and isinstance(node.values[0], nodes.BoolOp)
            and not isinstance(node.values[1], nodes.BoolOp)
            and node.values[0].op == "and"
            and not isinstance(node.values[0].values[1], nodes.BoolOp)
            and len(node.values[0].values) == 2
        )

    @staticmethod
    def _and_or_ternary_arguments(
        node: nodes.BoolOp,
    ) -> tuple[nodes.NodeNG, nodes.NodeNG, nodes.NodeNG]:
        false_value = node.values[1]
        condition, true_value = node.values[0].values
        return condition, true_value, false_value

    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        self._return_nodes[node.name] = list(
            node.nodes_of_class(nodes.Return, skip_klass=nodes.FunctionDef)
        )

    def _check_consistent_returns(self, node: nodes.FunctionDef) -> None:
        """Check that all return statements inside a function are consistent.

        Return statements are consistent if:
            - all returns are explicit and if there is no implicit return;
            - all returns are empty and if there is, possibly, an implicit return.

        Args:
            node (nodes.FunctionDef): the function holding the return statements.
        """
        # explicit return statements are those with a not None value
        explicit_returns = [
            _node for _node in self._return_nodes[node.name] if _node.value is not None
        ]
        if not explicit_returns:
            return
        if len(explicit_returns) == len(
            self._return_nodes[node.name]
        ) and self._is_node_return_ended(node):
            return
        self.add_message("inconsistent-return-statements", node=node)

    def _is_if_node_return_ended(self, node: nodes.If) -> bool:
        """Check if the If node ends with an explicit return statement.

        Args:
            node (nodes.If): If node to be checked.

        Returns:
            bool: True if the node ends with an explicit statement, False otherwise.
        """
        # Do not check if inner function definition are return ended.
        is_if_returning = any(
            self._is_node_return_ended(_ifn)
            for _ifn in node.body
            if not isinstance(_ifn, nodes.FunctionDef)
        )
        if not node.orelse:
            # If there is not orelse part then the if statement is returning if :
            # - there is at least one return statement in its siblings;
            # - the if body is itself returning.
            if not self._has_return_in_siblings(node):
                return False
            return is_if_returning
        # If there is an orelse part then both if body and orelse part should return.
        is_orelse_returning = any(
            self._is_node_return_ended(_ore)
            for _ore in node.orelse
            if not isinstance(_ore, nodes.FunctionDef)
        )
        return is_if_returning and is_orelse_returning

    def _is_raise_node_return_ended(self, node: nodes.Raise) -> bool:
        """Check if the Raise node ends with an explicit return statement.

        Args:
            node (nodes.Raise): Raise node to be checked.

        Returns:
            bool: True if the node ends with an explicit statement, False otherwise.
        """
        # a Raise statement doesn't need to end with a return statement
        # but if the exception raised is handled, then the handler has to
        # ends with a return statement
        if not node.exc:
            # Ignore bare raises
            return True
        if not utils.is_node_inside_try_except(node):
            # If the raise statement is not inside a try/except statement
            # then the exception is raised and cannot be caught. No need
            # to infer it.
            return True
        exc = utils.safe_infer(node.exc)
        if (
            exc is None
            or isinstance(exc, UninferableBase)
            or not hasattr(exc, "pytype")
        ):
            return False
        exc_name = exc.pytype().split(".")[-1]
        handlers = utils.get_exception_handlers(node, exc_name)
        handlers = list(handlers) if handlers is not None else []
        if handlers:
            # among all the handlers handling the exception at least one
            # must end with a return statement
            return any(self._is_node_return_ended(_handler) for _handler in handlers)
        # if no handlers handle the exception then it's ok
        return True

    def _is_node_return_ended(self, node: nodes.NodeNG) -> bool:
        """Check if the node ends with an explicit return statement.

        Args:
            node (nodes.NodeNG): node to be checked.

        Returns:
            bool: True if the node ends with an explicit statement, False otherwise.
        """
        # Recursion base case
        if isinstance(node, nodes.Return):
            return True
        if isinstance(node, nodes.Call):
            try:
                funcdef_node = node.func.inferred()[0]
                if self._is_function_def_never_returning(funcdef_node):
                    return True
            except astroid.InferenceError:
                pass
        if isinstance(node, nodes.While):
            # A while-loop is considered return-ended if it has a
            # truthy test and no break statements
            return (node.test.bool_value() and not _loop_exits_early(node)) or any(
                self._is_node_return_ended(child) for child in node.orelse
            )
        if isinstance(node, nodes.Raise):
            return self._is_raise_node_return_ended(node)
        if isinstance(node, nodes.If):
            return self._is_if_node_return_ended(node)
        if isinstance(node, nodes.Try):
            handlers = {
                _child
                for _child in node.get_children()
                if isinstance(_child, nodes.ExceptHandler)
            }
            all_but_handler = set(node.get_children()) - handlers
            return any(
                self._is_node_return_ended(_child) for _child in all_but_handler
            ) and all(self._is_node_return_ended(_child) for _child in handlers)
        if (
            isinstance(node, nodes.Assert)
            and isinstance(node.test, nodes.Const)
            and not node.test.value
        ):
            # consider assert False as a return node
            return True
        # recurses on the children of the node
        return any(self._is_node_return_ended(_child) for _child in node.get_children())

    @staticmethod
    def _has_return_in_siblings(node: nodes.NodeNG) -> bool:
        """Returns True if there is at least one return in the node's siblings."""
        next_sibling = node.next_sibling()
        while next_sibling:
            if isinstance(next_sibling, nodes.Return):
                return True
            next_sibling = next_sibling.next_sibling()
        return False

    def _is_function_def_never_returning(
        self, node: nodes.FunctionDef | astroid.BoundMethod
    ) -> bool:
        """Return True if the function never returns, False otherwise.

        Args:
            node (nodes.FunctionDef or astroid.BoundMethod): function definition node to be analyzed.

        Returns:
            bool: True if the function never returns, False otherwise.
        """
        if isinstance(node, (nodes.FunctionDef, astroid.BoundMethod)) and node.returns:
            return (
                isinstance(node.returns, nodes.Attribute)
                and node.returns.attrname == "NoReturn"
                or isinstance(node.returns, nodes.Name)
                and node.returns.name == "NoReturn"
            )
        try:
            return node.qname() in self._never_returning_functions
        except (TypeError, AttributeError):
            return False

    def _check_return_at_the_end(self, node: nodes.FunctionDef) -> None:
        """Check for presence of a *single* return statement at the end of a
        function.

        "return" or "return None" are useless because None is the
        default return type if they are missing.

        NOTE: produces a message only if there is a single return statement
        in the function body. Otherwise _check_consistent_returns() is called!
        Per its implementation and PEP8 we can have a "return None" at the end
        of the function body if there are other return statements before that!
        """
        if len(self._return_nodes[node.name]) > 1:
            return
        if len(node.body) <= 1:
            return

        last = node.body[-1]
        if isinstance(last, nodes.Return):
            # e.g. "return"
            if last.value is None:
                self.add_message("useless-return", node=node)
            # return None"
            elif isinstance(last.value, nodes.Const) and (last.value.value is None):
                self.add_message("useless-return", node=node)

    def _check_unnecessary_dict_index_lookup(
        self, node: nodes.For | nodes.Comprehension
    ) -> None:
        """Add message when accessing dict values by index lookup."""
        # Verify that we have an .items() call and
        # that the object which is iterated is used as a subscript in the
        # body of the for.
        # Is it a proper items call?
        if (
            isinstance(node.iter, nodes.Call)
            and isinstance(node.iter.func, nodes.Attribute)
            and node.iter.func.attrname == "items"
        ):
            inferred = utils.safe_infer(node.iter.func)
            if not isinstance(inferred, astroid.BoundMethod):
                return
            iterating_object_name = node.iter.func.expr.as_string()

            # Store potential violations. These will only be reported if we don't
            # discover any writes to the collection during the loop.
            messages = []

            # Verify that the body of the for loop uses a subscript
            # with the object that was iterated. This uses some heuristics
            # in order to make sure that the same object is used in the
            # for body.

            children = (
                node.body
                if isinstance(node, nodes.For)
                else list(node.parent.get_children())
            )

            # Check if there are any for / while loops within the loop in question;
            # If so, we will be more conservative about reporting errors as we
            # can't yet do proper control flow analysis to be sure when
            # reassignment will affect us
            nested_loops = itertools.chain.from_iterable(
                child.nodes_of_class((nodes.For, nodes.While)) for child in children
            )
            has_nested_loops = next(nested_loops, None) is not None

            for child in children:
                for subscript in child.nodes_of_class(nodes.Subscript):
                    if not isinstance(subscript.value, (nodes.Name, nodes.Attribute)):
                        continue

                    value = subscript.slice

                    if isinstance(node, nodes.For) and _is_part_of_assignment_target(
                        subscript
                    ):
                        # Ignore this subscript if it is the target of an assignment
                        # Early termination; after reassignment dict index lookup will be necessary
                        return

                    if isinstance(subscript.parent, nodes.Delete):
                        # Ignore this subscript if it's used with the delete keyword
                        return

                    # Case where .items is assigned to k,v (i.e., for k, v in d.items())
                    if isinstance(value, nodes.Name):
                        if (
                            not isinstance(node.target, nodes.Tuple)
                            # Ignore 1-tuples: for k, in d.items()
                            or len(node.target.elts) < 2
                            or value.name != node.target.elts[0].name
                            or iterating_object_name != subscript.value.as_string()
                        ):
                            continue

                        if (
                            isinstance(node, nodes.For)
                            and value.lookup(value.name)[1][-1].lineno > node.lineno
                        ):
                            # Ignore this subscript if it has been redefined after
                            # the for loop. This checks for the line number using .lookup()
                            # to get the line number where the iterating object was last
                            # defined and compare that to the for loop's line number
                            continue

                        if has_nested_loops:
                            messages.append(
                                {
                                    "node": subscript,
                                    "variable": node.target.elts[1].as_string(),
                                }
                            )
                        else:
                            self.add_message(
                                "unnecessary-dict-index-lookup",
                                node=subscript,
                                args=(node.target.elts[1].as_string(),),
                            )

                    # Case where .items is assigned to single var (i.e., for item in d.items())
                    elif isinstance(value, nodes.Subscript):
                        if (
                            not isinstance(node.target, nodes.AssignName)
                            or not isinstance(value.value, nodes.Name)
                            or node.target.name != value.value.name
                            or iterating_object_name != subscript.value.as_string()
                        ):
                            continue

                        if (
                            isinstance(node, nodes.For)
                            and value.value.lookup(value.value.name)[1][-1].lineno
                            > node.lineno
                        ):
                            # Ignore this subscript if it has been redefined after
                            # the for loop. This checks for the line number using .lookup()
                            # to get the line number where the iterating object was last
                            # defined and compare that to the for loop's line number
                            continue

                        # check if subscripted by 0 (key)
                        inferred = utils.safe_infer(value.slice)
                        if not isinstance(inferred, nodes.Const) or inferred.value != 0:
                            continue

                        if has_nested_loops:
                            messages.append(
                                {
                                    "node": subscript,
                                    "variable": "1".join(
                                        value.as_string().rsplit("0", maxsplit=1)
                                    ),
                                }
                            )
                        else:
                            self.add_message(
                                "unnecessary-dict-index-lookup",
                                node=subscript,
                                args=(
                                    "1".join(value.as_string().rsplit("0", maxsplit=1)),
                                ),
                            )

            for message in messages:
                self.add_message(
                    "unnecessary-dict-index-lookup",
                    node=message["node"],
                    args=(message["variable"],),
                )

    def _check_unnecessary_list_index_lookup(
        self, node: nodes.For | nodes.Comprehension
    ) -> None:
        if (
            not isinstance(node.iter, nodes.Call)
            or not isinstance(node.iter.func, nodes.Name)
            or not node.iter.func.name == "enumerate"
        ):
            return

        preliminary_confidence = HIGH
        try:
            iterable_arg = utils.get_argument_from_call(
                node.iter, position=0, keyword="iterable"
            )
        except utils.NoSuchArgumentError:
            iterable_arg = utils.infer_kwarg_from_call(node.iter, keyword="iterable")
            preliminary_confidence = INFERENCE

        if not isinstance(iterable_arg, nodes.Name):
            return

        if not isinstance(node.target, nodes.Tuple) or len(node.target.elts) < 2:
            # enumerate() result is being assigned without destructuring
            return

        if not isinstance(node.target.elts[1], nodes.AssignName):
            # The value is not being assigned to a single variable, e.g. being
            # destructured, so we can't necessarily use it.
            return

        has_start_arg, confidence = self._enumerate_with_start(node)
        if has_start_arg:
            # enumerate is being called with start arg/kwarg so resulting index lookup
            # is not redundant, hence we should not report an error.
            return

        # Preserve preliminary_confidence if it was INFERENCE
        confidence = (
            preliminary_confidence
            if preliminary_confidence == INFERENCE
            else confidence
        )

        iterating_object_name = iterable_arg.name
        value_variable = node.target.elts[1]

        # Store potential violations. These will only be reported if we don't
        # discover any writes to the collection during the loop.
        bad_nodes = []

        children = (
            node.body
            if isinstance(node, nodes.For)
            else list(node.parent.get_children())
        )

        # Check if there are any for / while loops within the loop in question;
        # If so, we will be more conservative about reporting errors as we
        # can't yet do proper control flow analysis to be sure when
        # reassignment will affect us
        nested_loops = itertools.chain.from_iterable(
            child.nodes_of_class((nodes.For, nodes.While)) for child in children
        )
        has_nested_loops = next(nested_loops, None) is not None

        # Check if there are any if statements within the loop in question;
        # If so, we will be more conservative about reporting errors as we
        # can't yet do proper control flow analysis to be sure when
        # reassignment will affect us
        if_statements = itertools.chain.from_iterable(
            child.nodes_of_class(nodes.If) for child in children
        )
        has_if_statements = next(if_statements, None) is not None

        for child in children:
            for subscript in child.nodes_of_class(nodes.Subscript):
                if isinstance(node, nodes.For) and _is_part_of_assignment_target(
                    subscript
                ):
                    # Ignore this subscript if it is the target of an assignment
                    # Early termination; after reassignment index lookup will be necessary
                    return

                if isinstance(subscript.parent, nodes.Delete):
                    # Ignore this subscript if it's used with the delete keyword
                    return

                index = subscript.slice
                if isinstance(index, nodes.Name):
                    if (
                        index.name != node.target.elts[0].name
                        or iterating_object_name != subscript.value.as_string()
                    ):
                        continue

                    if (
                        isinstance(node, nodes.For)
                        and index.lookup(index.name)[1][-1].lineno > node.lineno
                    ):
                        # Ignore this subscript if it has been redefined after
                        # the for loop.
                        continue

                    if (
                        isinstance(node, nodes.For)
                        and index.lookup(value_variable.name)[1][-1].lineno
                        > node.lineno
                    ):
                        # The variable holding the value from iteration has been
                        # reassigned on a later line, so it can't be used.
                        continue

                    if has_nested_loops:
                        # Have found a likely issue, but since there are nested
                        # loops we don't want to report this unless we get to the
                        # end of the loop without updating the collection
                        bad_nodes.append(subscript)
                    elif has_if_statements:
                        continue
                    else:
                        self.add_message(
                            "unnecessary-list-index-lookup",
                            node=subscript,
                            args=(node.target.elts[1].name,),
                            confidence=confidence,
                        )

        for subscript in bad_nodes:
            self.add_message(
                "unnecessary-list-index-lookup",
                node=subscript,
                args=(node.target.elts[1].name,),
                confidence=confidence,
            )

    def _enumerate_with_start(
        self, node: nodes.For | nodes.Comprehension
    ) -> tuple[bool, Confidence]:
        """Check presence of `start` kwarg or second argument to enumerate.

        For example:

        `enumerate([1,2,3], start=1)`
        `enumerate([1,2,3], 1)`

        If `start` is assigned to `0`, the default value, this is equivalent to
        not calling `enumerate` with start.
        """
        confidence = HIGH

        if len(node.iter.args) > 1:
            # We assume the second argument to `enumerate` is the `start` int arg.
            # It's a reasonable assumption for now as it's the only possible argument:
            # https://docs.python.org/3/library/functions.html#enumerate
            start_arg = node.iter.args[1]
            start_val, confidence = self._get_start_value(start_arg)
            if start_val is None:
                return False, confidence
            return not start_val == 0, confidence

        for keyword in node.iter.keywords:
            if keyword.arg == "start":
                start_val, confidence = self._get_start_value(keyword.value)
                if start_val is None:
                    return False, confidence
                return not start_val == 0, confidence

        return False, confidence

    def _get_start_value(self, node: nodes.NodeNG) -> tuple[int | None, Confidence]:
        if (
            isinstance(node, (nodes.Name, nodes.Call, nodes.Attribute))
            or isinstance(node, nodes.UnaryOp)
            and isinstance(node.operand, (nodes.Attribute, nodes.Name))
        ):
            inferred = utils.safe_infer(node)
            start_val = inferred.value if inferred else None
            return start_val, INFERENCE
        if isinstance(node, nodes.UnaryOp):
            return node.operand.value, HIGH
        if isinstance(node, nodes.Const):
            return node.value, HIGH
        return None, HIGH

back/venvmsg/Lib/site-packages/pylint/checkers/refactoring/refactoring_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""A similarities / code duplication command line tool and pylint checker.

The algorithm is based on comparing the hash value of n successive lines of a file.
First the files are read and any line that doesn't fulfill requirement are removed
(comments, docstrings...)

Those stripped lines are stored in the LineSet class which gives access to them.
Then each index of the stripped lines collection is associated with the hash of n
successive entries of the stripped lines starting at the current index (n is the
minimum common lines option).

The common hashes between both linesets are then looked for. If there are matches, then
the match indices in both linesets are stored and associated with the corresponding
couples (start line number/end line number) in both files.

This association is then post-processed to handle the case of successive matches. For
example if the minimum common lines setting is set to four, then the hashes are
computed with four lines. If one of match indices couple (12, 34) is the
successor of another one (11, 33) then it means that there are in fact five lines which
are common.

Once post-processed the values of association table are the result looked for, i.e.
start and end lines numbers of common lines in both files.
"""

from __future__ import annotations

import argparse
import copy
import functools
import itertools
import operator
import re
import sys
import warnings
from collections import defaultdict
from collections.abc import Callable, Generator, Iterable, Sequence
from getopt import getopt
from io import BufferedIOBase, BufferedReader, BytesIO
from itertools import chain, groupby
from typing import (
    TYPE_CHECKING,
    Any,
    Dict,
    List,
    NamedTuple,
    NewType,
    NoReturn,
    TextIO,
    Tuple,
    Union,
)

import astroid
from astroid import nodes

from pylint.checkers import BaseChecker, BaseRawFileChecker, table_lines_from_stats
from pylint.reporters.ureports.nodes import Section, Table
from pylint.typing import MessageDefinitionTuple, Options
from pylint.utils import LinterStats, decoding_stream

if TYPE_CHECKING:
    from pylint.lint import PyLinter

DEFAULT_MIN_SIMILARITY_LINE = 4

REGEX_FOR_LINES_WITH_CONTENT = re.compile(r".*\w+")

# Index defines a location in a LineSet stripped lines collection
Index = NewType("Index", int)

# LineNumber defines a location in a LinesSet real lines collection (the whole file lines)
LineNumber = NewType("LineNumber", int)


# LineSpecifs holds characteristics of a line in a file
class LineSpecifs(NamedTuple):
    line_number: LineNumber
    text: str


# Links LinesChunk object to the starting indices (in lineset's stripped lines)
# of the different chunk of lines that are used to compute the hash
HashToIndex_T = Dict["LinesChunk", List[Index]]

# Links index in the lineset's stripped lines to the real lines in the file
IndexToLines_T = Dict[Index, "SuccessiveLinesLimits"]

# The types the streams read by pylint can take. Originating from astroid.nodes.Module.stream() and open()
STREAM_TYPES = Union[TextIO, BufferedReader, BytesIO]


class CplSuccessiveLinesLimits:
    """Holds a SuccessiveLinesLimits object for each checked file and counts the number
    of common lines between both stripped lines collections extracted from both files.
    """

    __slots__ = ("first_file", "second_file", "effective_cmn_lines_nb")

    def __init__(
        self,
        first_file: SuccessiveLinesLimits,
        second_file: SuccessiveLinesLimits,
        effective_cmn_lines_nb: int,
    ) -> None:
        self.first_file = first_file
        self.second_file = second_file
        self.effective_cmn_lines_nb = effective_cmn_lines_nb


# Links the indices to the starting line in both lineset's stripped lines to
# the start and end lines in both files
CplIndexToCplLines_T = Dict["LineSetStartCouple", CplSuccessiveLinesLimits]


class LinesChunk:
    """The LinesChunk object computes and stores the hash of some consecutive stripped
    lines of a lineset.
    """

    __slots__ = ("_fileid", "_index", "_hash")

    def __init__(self, fileid: str, num_line: int, *lines: Iterable[str]) -> None:
        self._fileid: str = fileid
        """The name of the file from which the LinesChunk object is generated."""

        self._index: Index = Index(num_line)
        """The index in the stripped lines that is the starting of consecutive
        lines.
        """

        self._hash: int = sum(hash(lin) for lin in lines)
        """The hash of some consecutive lines."""

    def __eq__(self, o: Any) -> bool:
        if not isinstance(o, LinesChunk):
            return NotImplemented
        return self._hash == o._hash

    def __hash__(self) -> int:
        return self._hash

    def __repr__(self) -> str:
        return (
            f"<LinesChunk object for file {self._fileid} ({self._index}, {self._hash})>"
        )

    def __str__(self) -> str:
        return (
            f"LinesChunk object for file {self._fileid}, starting at line {self._index} \n"
            f"Hash is {self._hash}"
        )


class SuccessiveLinesLimits:
    """A class to handle the numbering of begin and end of successive lines.

    :note: Only the end line number can be updated.
    """

    __slots__ = ("_start", "_end")

    def __init__(self, start: LineNumber, end: LineNumber) -> None:
        self._start: LineNumber = start
        self._end: LineNumber = end

    @property
    def start(self) -> LineNumber:
        return self._start

    @property
    def end(self) -> LineNumber:
        return self._end

    @end.setter
    def end(self, value: LineNumber) -> None:
        self._end = value

    def __repr__(self) -> str:
        return f"<SuccessiveLinesLimits <{self._start};{self._end}>>"


class LineSetStartCouple(NamedTuple):
    """Indices in both linesets that mark the beginning of successive lines."""

    fst_lineset_index: Index
    snd_lineset_index: Index

    def __repr__(self) -> str:
        return (
            f"<LineSetStartCouple <{self.fst_lineset_index};{self.snd_lineset_index}>>"
        )

    def __eq__(self, other: Any) -> bool:
        if not isinstance(other, LineSetStartCouple):
            return NotImplemented
        return (
            self.fst_lineset_index == other.fst_lineset_index
            and self.snd_lineset_index == other.snd_lineset_index
        )

    def __hash__(self) -> int:
        return hash(self.fst_lineset_index) + hash(self.snd_lineset_index)

    def increment(self, value: Index) -> LineSetStartCouple:
        return LineSetStartCouple(
            Index(self.fst_lineset_index + value),
            Index(self.snd_lineset_index + value),
        )


LinesChunkLimits_T = Tuple["LineSet", LineNumber, LineNumber]


def hash_lineset(
    lineset: LineSet, min_common_lines: int = DEFAULT_MIN_SIMILARITY_LINE
) -> tuple[HashToIndex_T, IndexToLines_T]:
    """Return two dicts.

    The first associates the hash of successive stripped lines of a lineset
    to the indices of the starting lines.
    The second dict, associates the index of the starting line in the lineset's stripped lines to the
    couple [start, end] lines number in the corresponding file.

    :param lineset: lineset object (i.e the lines in a file)
    :param min_common_lines: number of successive lines that are used to compute the hash
    :return: a dict linking hashes to corresponding start index and a dict that links this
             index to the start and end lines in the file
    """
    hash2index = defaultdict(list)
    index2lines = {}
    # Comments, docstring and other specific patterns maybe excluded -> call to stripped_lines
    # to get only what is desired
    lines = tuple(x.text for x in lineset.stripped_lines)
    # Need different iterators on same lines but each one is shifted 1 from the precedent
    shifted_lines = [iter(lines[i:]) for i in range(min_common_lines)]

    for i, *succ_lines in enumerate(zip(*shifted_lines)):
        start_linenumber = LineNumber(lineset.stripped_lines[i].line_number)
        try:
            end_linenumber = lineset.stripped_lines[i + min_common_lines].line_number
        except IndexError:
            end_linenumber = LineNumber(lineset.stripped_lines[-1].line_number + 1)

        index = Index(i)
        index2lines[index] = SuccessiveLinesLimits(
            start=start_linenumber, end=end_linenumber
        )

        l_c = LinesChunk(lineset.name, index, *succ_lines)
        hash2index[l_c].append(index)

    return hash2index, index2lines


def remove_successive(all_couples: CplIndexToCplLines_T) -> None:
    """Removes all successive entries in the dictionary in argument.

    :param all_couples: collection that has to be cleaned up from successive entries.
                        The keys are couples of indices that mark the beginning of common entries
                        in both linesets. The values have two parts. The first one is the couple
                        of starting and ending line numbers of common successive lines in the first file.
                        The second part is the same for the second file.

    For example consider the following dict:

    >>> all_couples
    {(11, 34): ([5, 9], [27, 31]),
     (23, 79): ([15, 19], [45, 49]),
     (12, 35): ([6, 10], [28, 32])}

    There are two successive keys (11, 34) and (12, 35).
    It means there are two consecutive similar chunks of lines in both files.
    Thus remove last entry and update the last line numbers in the first entry

    >>> remove_successive(all_couples)
    >>> all_couples
    {(11, 34): ([5, 10], [27, 32]),
     (23, 79): ([15, 19], [45, 49])}
    """
    couple: LineSetStartCouple
    for couple in tuple(all_couples.keys()):
        to_remove = []
        test = couple.increment(Index(1))
        while test in all_couples:
            all_couples[couple].first_file.end = all_couples[test].first_file.end
            all_couples[couple].second_file.end = all_couples[test].second_file.end
            all_couples[couple].effective_cmn_lines_nb += 1
            to_remove.append(test)
            test = test.increment(Index(1))

        for target in to_remove:
            try:
                all_couples.pop(target)
            except KeyError:
                pass


def filter_noncode_lines(
    ls_1: LineSet,
    stindex_1: Index,
    ls_2: LineSet,
    stindex_2: Index,
    common_lines_nb: int,
) -> int:
    """Return the effective number of common lines between lineset1
    and lineset2 filtered from non code lines.

    That is to say the number of common successive stripped
    lines except those that do not contain code (for example
    a line with only an ending parenthesis)

    :param ls_1: first lineset
    :param stindex_1: first lineset starting index
    :param ls_2: second lineset
    :param stindex_2: second lineset starting index
    :param common_lines_nb: number of common successive stripped lines before being filtered from non code lines
    :return: the number of common successive stripped lines that contain code
    """
    stripped_l1 = [
        lspecif.text
        for lspecif in ls_1.stripped_lines[stindex_1 : stindex_1 + common_lines_nb]
        if REGEX_FOR_LINES_WITH_CONTENT.match(lspecif.text)
    ]
    stripped_l2 = [
        lspecif.text
        for lspecif in ls_2.stripped_lines[stindex_2 : stindex_2 + common_lines_nb]
        if REGEX_FOR_LINES_WITH_CONTENT.match(lspecif.text)
    ]
    return sum(sline_1 == sline_2 for sline_1, sline_2 in zip(stripped_l1, stripped_l2))


class Commonality(NamedTuple):
    cmn_lines_nb: int
    fst_lset: LineSet
    fst_file_start: LineNumber
    fst_file_end: LineNumber
    snd_lset: LineSet
    snd_file_start: LineNumber
    snd_file_end: LineNumber


class Similar:
    """Finds copy-pasted lines of code in a project."""

    def __init__(
        self,
        min_lines: int = DEFAULT_MIN_SIMILARITY_LINE,
        ignore_comments: bool = False,
        ignore_docstrings: bool = False,
        ignore_imports: bool = False,
        ignore_signatures: bool = False,
    ) -> None:
        # If we run in pylint mode we link the namespace objects
        if isinstance(self, BaseChecker):
            self.namespace = self.linter.config
        else:
            self.namespace = argparse.Namespace()

        self.namespace.min_similarity_lines = min_lines
        self.namespace.ignore_comments = ignore_comments
        self.namespace.ignore_docstrings = ignore_docstrings
        self.namespace.ignore_imports = ignore_imports
        self.namespace.ignore_signatures = ignore_signatures
        self.linesets: list[LineSet] = []

    def append_stream(
        self, streamid: str, stream: STREAM_TYPES, encoding: str | None = None
    ) -> None:
        """Append a file to search for similarities."""
        if isinstance(stream, BufferedIOBase):
            if encoding is None:
                raise ValueError
            readlines = decoding_stream(stream, encoding).readlines
        else:
            # hint parameter is incorrectly typed as non-optional
            readlines = stream.readlines  # type: ignore[assignment]

        try:
            lines = readlines()
        except UnicodeDecodeError:
            lines = []

        self.linesets.append(
            LineSet(
                streamid,
                lines,
                self.namespace.ignore_comments,
                self.namespace.ignore_docstrings,
                self.namespace.ignore_imports,
                self.namespace.ignore_signatures,
                line_enabled_callback=self.linter._is_one_message_enabled
                if hasattr(self, "linter")
                else None,
            )
        )

    def run(self) -> None:
        """Start looking for similarities and display results on stdout."""
        if self.namespace.min_similarity_lines == 0:
            return
        self._display_sims(self._compute_sims())

    def _compute_sims(self) -> list[tuple[int, set[LinesChunkLimits_T]]]:
        """Compute similarities in appended files."""
        no_duplicates: dict[int, list[set[LinesChunkLimits_T]]] = defaultdict(list)

        for commonality in self._iter_sims():
            num = commonality.cmn_lines_nb
            lineset1 = commonality.fst_lset
            start_line_1 = commonality.fst_file_start
            end_line_1 = commonality.fst_file_end
            lineset2 = commonality.snd_lset
            start_line_2 = commonality.snd_file_start
            end_line_2 = commonality.snd_file_end

            duplicate = no_duplicates[num]
            couples: set[LinesChunkLimits_T]
            for couples in duplicate:
                if (lineset1, start_line_1, end_line_1) in couples or (
                    lineset2,
                    start_line_2,
                    end_line_2,
                ) in couples:
                    break
            else:
                duplicate.append(
                    {
                        (lineset1, start_line_1, end_line_1),
                        (lineset2, start_line_2, end_line_2),
                    }
                )
        sims: list[tuple[int, set[LinesChunkLimits_T]]] = []
        ensembles: list[set[LinesChunkLimits_T]]
        for num, ensembles in no_duplicates.items():
            cpls: set[LinesChunkLimits_T]
            for cpls in ensembles:
                sims.append((num, cpls))
        sims.sort()
        sims.reverse()
        return sims

    def _display_sims(
        self, similarities: list[tuple[int, set[LinesChunkLimits_T]]]
    ) -> None:
        """Display computed similarities on stdout."""
        report = self._get_similarity_report(similarities)
        print(report)

    def _get_similarity_report(
        self, similarities: list[tuple[int, set[LinesChunkLimits_T]]]
    ) -> str:
        """Create a report from similarities."""
        report: str = ""
        duplicated_line_number: int = 0
        for number, couples in similarities:
            report += f"\n{number} similar lines in {len(couples)} files\n"
            couples_l = sorted(couples)
            line_set = start_line = end_line = None
            for line_set, start_line, end_line in couples_l:
                report += f"=={line_set.name}:[{start_line}:{end_line}]\n"
            if line_set:
                for line in line_set._real_lines[start_line:end_line]:
                    report += f"   {line.rstrip()}\n" if line.rstrip() else "\n"
            duplicated_line_number += number * (len(couples_l) - 1)
        total_line_number: int = sum(len(lineset) for lineset in self.linesets)
        report += (
            f"TOTAL lines={total_line_number} "
            f"duplicates={duplicated_line_number} "
            f"percent={duplicated_line_number * 100.0 / total_line_number:.2f}\n"
        )
        return report

    # pylint: disable = too-many-locals
    def _find_common(
        self, lineset1: LineSet, lineset2: LineSet
    ) -> Generator[Commonality, None, None]:
        """Find similarities in the two given linesets.

        This the core of the algorithm. The idea is to compute the hashes of a
        minimal number of successive lines of each lineset and then compare the
        hashes. Every match of such comparison is stored in a dict that links the
        couple of starting indices in both linesets to the couple of corresponding
        starting and ending lines in both files.

        Last regroups all successive couples in a bigger one. It allows to take into
        account common chunk of lines that have more than the minimal number of
        successive lines required.
        """
        hash_to_index_1: HashToIndex_T
        hash_to_index_2: HashToIndex_T
        index_to_lines_1: IndexToLines_T
        index_to_lines_2: IndexToLines_T
        hash_to_index_1, index_to_lines_1 = hash_lineset(
            lineset1, self.namespace.min_similarity_lines
        )
        hash_to_index_2, index_to_lines_2 = hash_lineset(
            lineset2, self.namespace.min_similarity_lines
        )

        hash_1: frozenset[LinesChunk] = frozenset(hash_to_index_1.keys())
        hash_2: frozenset[LinesChunk] = frozenset(hash_to_index_2.keys())

        common_hashes: Iterable[LinesChunk] = sorted(
            hash_1 & hash_2, key=lambda m: hash_to_index_1[m][0]
        )

        # all_couples is a dict that links the couple of indices in both linesets that mark the beginning of
        # successive common lines, to the corresponding starting and ending number lines in both files
        all_couples: CplIndexToCplLines_T = {}

        for c_hash in sorted(common_hashes, key=operator.attrgetter("_index")):
            for indices_in_linesets in itertools.product(
                hash_to_index_1[c_hash], hash_to_index_2[c_hash]
            ):
                index_1 = indices_in_linesets[0]
                index_2 = indices_in_linesets[1]
                all_couples[
                    LineSetStartCouple(index_1, index_2)
                ] = CplSuccessiveLinesLimits(
                    copy.copy(index_to_lines_1[index_1]),
                    copy.copy(index_to_lines_2[index_2]),
                    effective_cmn_lines_nb=self.namespace.min_similarity_lines,
                )

        remove_successive(all_couples)

        for cml_stripped_l, cmn_l in all_couples.items():
            start_index_1 = cml_stripped_l.fst_lineset_index
            start_index_2 = cml_stripped_l.snd_lineset_index
            nb_common_lines = cmn_l.effective_cmn_lines_nb

            com = Commonality(
                cmn_lines_nb=nb_common_lines,
                fst_lset=lineset1,
                fst_file_start=cmn_l.first_file.start,
                fst_file_end=cmn_l.first_file.end,
                snd_lset=lineset2,
                snd_file_start=cmn_l.second_file.start,
                snd_file_end=cmn_l.second_file.end,
            )

            eff_cmn_nb = filter_noncode_lines(
                lineset1, start_index_1, lineset2, start_index_2, nb_common_lines
            )

            if eff_cmn_nb > self.namespace.min_similarity_lines:
                yield com

    def _iter_sims(self) -> Generator[Commonality, None, None]:
        """Iterate on similarities among all files, by making a Cartesian
        product.
        """
        for idx, lineset in enumerate(self.linesets[:-1]):
            for lineset2 in self.linesets[idx + 1 :]:
                yield from self._find_common(lineset, lineset2)

    def get_map_data(self) -> list[LineSet]:
        """Returns the data we can use for a map/reduce process.

        In this case we are returning this instance's Linesets, that is all file
        information that will later be used for vectorisation.
        """
        return self.linesets

    def combine_mapreduce_data(self, linesets_collection: list[list[LineSet]]) -> None:
        """Reduces and recombines data into a format that we can report on.

        The partner function of get_map_data()
        """
        self.linesets = [line for lineset in linesets_collection for line in lineset]


def stripped_lines(
    lines: Iterable[str],
    ignore_comments: bool,
    ignore_docstrings: bool,
    ignore_imports: bool,
    ignore_signatures: bool,
    line_enabled_callback: Callable[[str, int], bool] | None = None,
) -> list[LineSpecifs]:
    """Return tuples of line/line number/line type with leading/trailing white-space and
    any ignored code features removed.

    :param lines: a collection of lines
    :param ignore_comments: if true, any comment in the lines collection is removed from the result
    :param ignore_docstrings: if true, any line that is a docstring is removed from the result
    :param ignore_imports: if true, any line that is an import is removed from the result
    :param ignore_signatures: if true, any line that is part of a function signature is removed from the result
    :param line_enabled_callback: If called with "R0801" and a line number, a return value of False will disregard
           the line
    :return: the collection of line/line number/line type tuples
    """
    if ignore_imports or ignore_signatures:
        tree = astroid.parse("".join(lines))
    if ignore_imports:
        node_is_import_by_lineno = (
            (node.lineno, isinstance(node, (nodes.Import, nodes.ImportFrom)))
            for node in tree.body
        )
        line_begins_import = {
            lineno: all(is_import for _, is_import in node_is_import_group)
            for lineno, node_is_import_group in groupby(
                node_is_import_by_lineno, key=lambda x: x[0]
            )
        }
        current_line_is_import = False
    if ignore_signatures:

        def _get_functions(
            functions: list[nodes.NodeNG], tree: nodes.NodeNG
        ) -> list[nodes.NodeNG]:
            """Recursively get all functions including nested in the classes from the
            tree.
            """

            for node in tree.body:
                if isinstance(node, (nodes.FunctionDef, nodes.AsyncFunctionDef)):
                    functions.append(node)

                if isinstance(
                    node,
                    (nodes.ClassDef, nodes.FunctionDef, nodes.AsyncFunctionDef),
                ):
                    _get_functions(functions, node)

            return functions

        functions = _get_functions([], tree)
        signature_lines = set(
            chain(
                *(
                    range(
                        func.lineno,
                        func.body[0].lineno if func.body else func.tolineno + 1,
                    )
                    for func in functions
                )
            )
        )

    strippedlines = []
    docstring = None
    for lineno, line in enumerate(lines, start=1):
        if line_enabled_callback is not None and not line_enabled_callback(
            "R0801", lineno
        ):
            continue
        line = line.strip()
        if ignore_docstrings:
            if not docstring:
                if line.startswith('"""') or line.startswith("'''"):
                    docstring = line[:3]
                    line = line[3:]
                elif line.startswith('r"""') or line.startswith("r'''"):
                    docstring = line[1:4]
                    line = line[4:]
            if docstring:
                if line.endswith(docstring):
                    docstring = None
                line = ""
        if ignore_imports:
            current_line_is_import = line_begins_import.get(
                lineno, current_line_is_import
            )
            if current_line_is_import:
                line = ""
        if ignore_comments:
            line = line.split("#", 1)[0].strip()
        if ignore_signatures and lineno in signature_lines:
            line = ""
        if line:
            strippedlines.append(
                LineSpecifs(text=line, line_number=LineNumber(lineno - 1))
            )
    return strippedlines


@functools.total_ordering
class LineSet:
    """Holds and indexes all the lines of a single source file.

    Allows for correspondence between real lines of the source file and stripped ones, which
    are the real ones from which undesired patterns have been removed.
    """

    def __init__(
        self,
        name: str,
        lines: list[str],
        ignore_comments: bool = False,
        ignore_docstrings: bool = False,
        ignore_imports: bool = False,
        ignore_signatures: bool = False,
        line_enabled_callback: Callable[[str, int], bool] | None = None,
    ) -> None:
        self.name = name
        self._real_lines = lines
        self._stripped_lines = stripped_lines(
            lines,
            ignore_comments,
            ignore_docstrings,
            ignore_imports,
            ignore_signatures,
            line_enabled_callback=line_enabled_callback,
        )

    def __str__(self) -> str:
        return f"<Lineset for {self.name}>"

    def __len__(self) -> int:
        return len(self._real_lines)

    def __getitem__(self, index: int) -> LineSpecifs:
        return self._stripped_lines[index]

    def __lt__(self, other: LineSet) -> bool:
        return self.name < other.name

    def __hash__(self) -> int:
        return id(self)

    def __eq__(self, other: Any) -> bool:
        if not isinstance(other, LineSet):
            return False
        return self.__dict__ == other.__dict__

    @property
    def stripped_lines(self) -> list[LineSpecifs]:
        return self._stripped_lines

    @property
    def real_lines(self) -> list[str]:
        return self._real_lines


MSGS: dict[str, MessageDefinitionTuple] = {
    "R0801": (
        "Similar lines in %s files\n%s",
        "duplicate-code",
        "Indicates that a set of similar lines has been detected "
        "among multiple file. This usually means that the code should "
        "be refactored to avoid this duplication.",
    )
}


def report_similarities(
    sect: Section,
    stats: LinterStats,
    old_stats: LinterStats | None,
) -> None:
    """Make a layout with some stats about duplication."""
    lines = ["", "now", "previous", "difference"]
    lines += table_lines_from_stats(stats, old_stats, "duplicated_lines")
    sect.append(Table(children=lines, cols=4, rheaders=1, cheaders=1))


# wrapper to get a pylint checker from the similar class
class SimilarChecker(BaseRawFileChecker, Similar):
    """Checks for similarities and duplicated code.

    This computation may be memory / CPU intensive, so you
    should disable it if you experience some problems.
    """

    # configuration section name
    name = "similarities"
    # messages
    msgs = MSGS
    # configuration options
    # for available dict keys/values see the optik parser 'add_option' method
    options: Options = (
        (
            "min-similarity-lines",
            {
                "default": DEFAULT_MIN_SIMILARITY_LINE,
                "type": "int",
                "metavar": "<int>",
                "help": "Minimum lines number of a similarity.",
            },
        ),
        (
            "ignore-comments",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Comments are removed from the similarity computation",
            },
        ),
        (
            "ignore-docstrings",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Docstrings are removed from the similarity computation",
            },
        ),
        (
            "ignore-imports",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Imports are removed from the similarity computation",
            },
        ),
        (
            "ignore-signatures",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Signatures are removed from the similarity computation",
            },
        ),
    )
    # reports
    reports = (("RP0801", "Duplication", report_similarities),)

    def __init__(self, linter: PyLinter) -> None:
        BaseRawFileChecker.__init__(self, linter)
        Similar.__init__(
            self,
            min_lines=self.linter.config.min_similarity_lines,
            ignore_comments=self.linter.config.ignore_comments,
            ignore_docstrings=self.linter.config.ignore_docstrings,
            ignore_imports=self.linter.config.ignore_imports,
            ignore_signatures=self.linter.config.ignore_signatures,
        )

    def open(self) -> None:
        """Init the checkers: reset linesets and statistics information."""
        self.linesets = []
        self.linter.stats.reset_duplicated_lines()

    def process_module(self, node: nodes.Module) -> None:
        """Process a module.

        the module's content is accessible via the stream object

        stream must implement the readlines method
        """
        if self.linter.current_name is None:
            # TODO: 3.0 Fix current_name
            warnings.warn(
                (
                    "In pylint 3.0 the current_name attribute of the linter object should be a string. "
                    "If unknown it should be initialized as an empty string."
                ),
                DeprecationWarning,
                stacklevel=2,
            )
        with node.stream() as stream:
            self.append_stream(self.linter.current_name, stream, node.file_encoding)

    def close(self) -> None:
        """Compute and display similarities on closing (i.e. end of parsing)."""
        total = sum(len(lineset) for lineset in self.linesets)
        duplicated = 0
        stats = self.linter.stats
        for num, couples in self._compute_sims():
            msg = []
            lineset = start_line = end_line = None
            for lineset, start_line, end_line in couples:
                msg.append(f"=={lineset.name}:[{start_line}:{end_line}]")
            msg.sort()

            if lineset:
                for line in lineset.real_lines[start_line:end_line]:
                    msg.append(line.rstrip())

            self.add_message("R0801", args=(len(couples), "\n".join(msg)))
            duplicated += num * (len(couples) - 1)
        stats.nb_duplicated_lines += int(duplicated)
        stats.percent_duplicated_lines += float(total and duplicated * 100.0 / total)

    def get_map_data(self) -> list[LineSet]:
        """Passthru override."""
        return Similar.get_map_data(self)

    def reduce_map_data(self, linter: PyLinter, data: list[list[LineSet]]) -> None:
        """Reduces and recombines data into a format that we can report on.

        The partner function of get_map_data()

        Calls self.close() to actually calculate and report duplicate code.
        """
        Similar.combine_mapreduce_data(self, linesets_collection=data)
        self.close()


def register(linter: PyLinter) -> None:
    linter.register_checker(SimilarChecker(linter))


def usage(status: int = 0) -> NoReturn:
    """Display command line usage information."""
    print("finds copy pasted blocks in a set of files")
    print()
    print(
        "Usage: symilar [-d|--duplicates min_duplicated_lines] \
[-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] [--ignore-signatures] file1..."
    )
    sys.exit(status)


def Run(argv: Sequence[str] | None = None) -> NoReturn:
    """Standalone command line access point."""
    if argv is None:
        argv = sys.argv[1:]

    s_opts = "hdi"
    l_opts = [
        "help",
        "duplicates=",
        "ignore-comments",
        "ignore-imports",
        "ignore-docstrings",
        "ignore-signatures",
    ]
    min_lines = DEFAULT_MIN_SIMILARITY_LINE
    ignore_comments = False
    ignore_docstrings = False
    ignore_imports = False
    ignore_signatures = False
    opts, args = getopt(list(argv), s_opts, l_opts)
    for opt, val in opts:
        if opt in {"-d", "--duplicates"}:
            min_lines = int(val)
        elif opt in {"-h", "--help"}:
            usage()
        elif opt in {"-i", "--ignore-comments"}:
            ignore_comments = True
        elif opt in {"--ignore-docstrings"}:
            ignore_docstrings = True
        elif opt in {"--ignore-imports"}:
            ignore_imports = True
        elif opt in {"--ignore-signatures"}:
            ignore_signatures = True
    if not args:
        usage(1)
    sim = Similar(
        min_lines, ignore_comments, ignore_docstrings, ignore_imports, ignore_signatures
    )
    for filename in args:
        with open(filename, encoding="utf-8") as stream:
            sim.append_stream(filename, stream)
    sim.run()
    sys.exit(0)


if __name__ == "__main__":
    Run()

back/venvmsg/Lib/site-packages/pylint/checkers/similar.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checker for spelling errors in comments and docstrings."""

from __future__ import annotations

import re
import tokenize
from re import Pattern
from typing import TYPE_CHECKING, Any, Literal

from astroid import nodes

from pylint.checkers import BaseTokenChecker
from pylint.checkers.utils import only_required_for_messages

if TYPE_CHECKING:
    from pylint.lint import PyLinter

try:
    import enchant
    from enchant.tokenize import (
        Chunker,
        EmailFilter,
        Filter,
        URLFilter,
        WikiWordFilter,
        get_tokenizer,
    )

    PYENCHANT_AVAILABLE = True
except ImportError:  # pragma: no cover
    enchant = None
    PYENCHANT_AVAILABLE = False

    class EmailFilter:  # type: ignore[no-redef]
        ...

    class URLFilter:  # type: ignore[no-redef]
        ...

    class WikiWordFilter:  # type: ignore[no-redef]
        ...

    class Filter:  # type: ignore[no-redef]
        def _skip(self, word: str) -> bool:
            raise NotImplementedError

    class Chunker:  # type: ignore[no-redef]
        pass

    def get_tokenizer(
        tag: str | None = None,  # pylint: disable=unused-argument
        chunkers: list[Chunker] | None = None,  # pylint: disable=unused-argument
        filters: list[Filter] | None = None,  # pylint: disable=unused-argument
    ) -> Filter:
        return Filter()


def _get_enchant_dicts() -> list[tuple[Any, enchant.ProviderDesc]]:
    # Broker().list_dicts() is not typed in enchant, but it does return tuples
    return enchant.Broker().list_dicts() if PYENCHANT_AVAILABLE else []  # type: ignore[no-any-return]


def _get_enchant_dict_choices(
    inner_enchant_dicts: list[tuple[Any, enchant.ProviderDesc]]
) -> list[str]:
    return [""] + [d[0] for d in inner_enchant_dicts]


def _get_enchant_dict_help(
    inner_enchant_dicts: list[tuple[Any, enchant.ProviderDesc]],
    pyenchant_available: bool,
) -> str:
    if inner_enchant_dicts:
        dict_as_str = [f"{d[0]} ({d[1].name})" for d in inner_enchant_dicts]
        enchant_help = f"Available dictionaries: {', '.join(dict_as_str)}"
    else:
        enchant_help = "No available dictionaries : You need to install "
        if not pyenchant_available:
            enchant_help += "both the python package and "
        enchant_help += "the system dependency for enchant to work"
    return f"Spelling dictionary name. {enchant_help}."


enchant_dicts = _get_enchant_dicts()


class WordsWithDigitsFilter(Filter):  # type: ignore[misc]
    """Skips words with digits."""

    def _skip(self, word: str) -> bool:
        return any(char.isdigit() for char in word)


class WordsWithUnderscores(Filter):  # type: ignore[misc]
    """Skips words with underscores.

    They are probably function parameter names.
    """

    def _skip(self, word: str) -> bool:
        return "_" in word


class RegExFilter(Filter):  # type: ignore[misc]
    """Parent class for filters using regular expressions.

    This filter skips any words the match the expression
    assigned to the class attribute ``_pattern``.
    """

    _pattern: Pattern[str]

    def _skip(self, word: str) -> bool:
        return bool(self._pattern.match(word))


class CamelCasedWord(RegExFilter):
    r"""Filter skipping over camelCasedWords.
    This filter skips any words matching the following regular expression:

           ^([a-z]\w+[A-Z]+\w+)

    That is, any words that are camelCasedWords.
    """
    _pattern = re.compile(r"^([a-z]+(\d|[A-Z])(?:\w+)?)")


class SphinxDirectives(RegExFilter):
    r"""Filter skipping over Sphinx Directives.
    This filter skips any words matching the following regular expression:

           ^(:([a-z]+)){1,2}:`([^`]+)(`)?

    That is, for example, :class:`BaseQuery`
    """
    # The final ` in the pattern is optional because enchant strips it out
    _pattern = re.compile(r"^(:([a-z]+)){1,2}:`([^`]+)(`)?")


class ForwardSlashChunker(Chunker):  # type: ignore[misc]
    """This chunker allows splitting words like 'before/after' into 'before' and
    'after'.
    """

    _text: str

    def next(self) -> tuple[str, int]:
        while True:
            if not self._text:
                raise StopIteration()
            if "/" not in self._text:
                text = self._text
                self._offset = 0
                self._text = ""
                return text, 0
            pre_text, post_text = self._text.split("/", 1)
            self._text = post_text
            self._offset = 0
            if (
                not pre_text
                or not post_text
                or not pre_text[-1].isalpha()
                or not post_text[0].isalpha()
            ):
                self._text = ""
                self._offset = 0
                return f"{pre_text}/{post_text}", 0
            return pre_text, 0

    def _next(self) -> tuple[str, Literal[0]]:
        while True:
            if "/" not in self._text:
                return self._text, 0
            pre_text, post_text = self._text.split("/", 1)
            if not pre_text or not post_text:
                break
            if not pre_text[-1].isalpha() or not post_text[0].isalpha():
                raise StopIteration()
            self._text = pre_text + " " + post_text
        raise StopIteration()


CODE_FLANKED_IN_BACKTICK_REGEX = re.compile(r"(\s|^)(`{1,2})([^`]+)(\2)([^`]|$)")


def _strip_code_flanked_in_backticks(line: str) -> str:
    """Alter line so code flanked in back-ticks is ignored.

    Pyenchant automatically strips back-ticks when parsing tokens,
    so this cannot be done at the individual filter level.
    """

    def replace_code_but_leave_surrounding_characters(match_obj: re.Match[str]) -> str:
        return match_obj.group(1) + match_obj.group(5)

    return CODE_FLANKED_IN_BACKTICK_REGEX.sub(
        replace_code_but_leave_surrounding_characters, line
    )


class SpellingChecker(BaseTokenChecker):
    """Check spelling in comments and docstrings."""

    name = "spelling"
    msgs = {
        "C0401": (
            "Wrong spelling of a word '%s' in a comment:\n%s\n"
            "%s\nDid you mean: '%s'?",
            "wrong-spelling-in-comment",
            "Used when a word in comment is not spelled correctly.",
        ),
        "C0402": (
            "Wrong spelling of a word '%s' in a docstring:\n%s\n"
            "%s\nDid you mean: '%s'?",
            "wrong-spelling-in-docstring",
            "Used when a word in docstring is not spelled correctly.",
        ),
        "C0403": (
            "Invalid characters %r in a docstring",
            "invalid-characters-in-docstring",
            "Used when a word in docstring cannot be checked by enchant.",
        ),
    }
    options = (
        (
            "spelling-dict",
            {
                "default": "",
                "type": "choice",
                "metavar": "<dict name>",
                "choices": _get_enchant_dict_choices(enchant_dicts),
                "help": _get_enchant_dict_help(enchant_dicts, PYENCHANT_AVAILABLE),
            },
        ),
        (
            "spelling-ignore-words",
            {
                "default": "",
                "type": "string",
                "metavar": "<comma separated words>",
                "help": "List of comma separated words that should not be checked.",
            },
        ),
        (
            "spelling-private-dict-file",
            {
                "default": "",
                "type": "path",
                "metavar": "<path to file>",
                "help": "A path to a file that contains the private "
                "dictionary; one word per line.",
            },
        ),
        (
            "spelling-store-unknown-words",
            {
                "default": "n",
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Tells whether to store unknown words to the "
                "private dictionary (see the "
                "--spelling-private-dict-file option) instead of "
                "raising a message.",
            },
        ),
        (
            "max-spelling-suggestions",
            {
                "default": 4,
                "type": "int",
                "metavar": "N",
                "help": "Limits count of emitted suggestions for spelling mistakes.",
            },
        ),
        (
            "spelling-ignore-comment-directives",
            {
                "default": "fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:",
                "type": "string",
                "metavar": "<comma separated words>",
                "help": "List of comma separated words that should be considered "
                "directives if they appear at the beginning of a comment "
                "and should not be checked.",
            },
        ),
    )

    def open(self) -> None:
        self.initialized = False
        if not PYENCHANT_AVAILABLE:
            return
        dict_name = self.linter.config.spelling_dict
        if not dict_name:
            return

        self.ignore_list = [
            w.strip() for w in self.linter.config.spelling_ignore_words.split(",")
        ]
        # "param" appears in docstring in param description and
        # "pylint" appears in comments in pylint pragmas.
        self.ignore_list.extend(["param", "pylint"])

        self.ignore_comment_directive_list = [
            w.strip()
            for w in self.linter.config.spelling_ignore_comment_directives.split(",")
        ]

        if self.linter.config.spelling_private_dict_file:
            self.spelling_dict = enchant.DictWithPWL(
                dict_name, self.linter.config.spelling_private_dict_file
            )
        else:
            self.spelling_dict = enchant.Dict(dict_name)

        if self.linter.config.spelling_store_unknown_words:
            self.unknown_words: set[str] = set()

        self.tokenizer = get_tokenizer(
            dict_name,
            chunkers=[ForwardSlashChunker],
            filters=[
                EmailFilter,
                URLFilter,
                WikiWordFilter,
                WordsWithDigitsFilter,
                WordsWithUnderscores,
                CamelCasedWord,
                SphinxDirectives,
            ],
        )
        self.initialized = True

    # pylint: disable = too-many-statements
    def _check_spelling(self, msgid: str, line: str, line_num: int) -> None:
        original_line = line
        try:
            # The mypy warning is caught by the except statement
            initial_space = re.search(r"^\s+", line).regs[0][1]  # type: ignore[union-attr]
        except (IndexError, AttributeError):
            initial_space = 0
        if line.strip().startswith("#") and "docstring" not in msgid:
            line = line.strip()[1:]
            # A ``Filter`` cannot determine if the directive is at the beginning of a line,
            #   nor determine if a colon is present or not (``pyenchant`` strips trailing colons).
            #   So implementing this here.
            for iter_directive in self.ignore_comment_directive_list:
                if line.startswith(" " + iter_directive):
                    line = line[(len(iter_directive) + 1) :]
                    break
            starts_with_comment = True
        else:
            starts_with_comment = False

        line = _strip_code_flanked_in_backticks(line)

        for word, word_start_at in self.tokenizer(line.strip()):
            word_start_at += initial_space
            lower_cased_word = word.casefold()

            # Skip words from ignore list.
            if word in self.ignore_list or lower_cased_word in self.ignore_list:
                continue

            # Strip starting u' from unicode literals and r' from raw strings.
            if word.startswith(("u'", 'u"', "r'", 'r"')) and len(word) > 2:
                word = word[2:]
                lower_cased_word = lower_cased_word[2:]

            # If it is a known word, then continue.
            try:
                if self.spelling_dict.check(lower_cased_word):
                    # The lower cased version of word passed spell checking
                    continue

                # If we reached this far, it means there was a spelling mistake.
                # Let's retry with the original work because 'unicode' is a
                # spelling mistake but 'Unicode' is not
                if self.spelling_dict.check(word):
                    continue
            except enchant.errors.Error:
                self.add_message(
                    "invalid-characters-in-docstring", line=line_num, args=(word,)
                )
                continue

            # Store word to private dict or raise a message.
            if self.linter.config.spelling_store_unknown_words:
                if lower_cased_word not in self.unknown_words:
                    with open(
                        self.linter.config.spelling_private_dict_file,
                        "a",
                        encoding="utf-8",
                    ) as f:
                        f.write(f"{lower_cased_word}\n")
                    self.unknown_words.add(lower_cased_word)
            else:
                # Present up to N suggestions.
                suggestions = self.spelling_dict.suggest(word)
                del suggestions[self.linter.config.max_spelling_suggestions :]
                line_segment = line[word_start_at:]
                match = re.search(rf"(\W|^)({word})(\W|$)", line_segment)
                if match:
                    # Start position of second group in regex.
                    col = match.regs[2][0]
                else:
                    col = line_segment.index(word)
                col += word_start_at
                if starts_with_comment:
                    col += 1
                indicator = (" " * col) + ("^" * len(word))
                all_suggestion = "' or '".join(suggestions)
                args = (word, original_line, indicator, f"'{all_suggestion}'")
                self.add_message(msgid, line=line_num, args=args)

    def process_tokens(self, tokens: list[tokenize.TokenInfo]) -> None:
        if not self.initialized:
            return

        # Process tokens and look for comments.
        for tok_type, token, (start_row, _), _, _ in tokens:
            if tok_type == tokenize.COMMENT:
                if start_row == 1 and token.startswith("#!/"):
                    # Skip shebang lines
                    continue
                if token.startswith("# pylint:"):
                    # Skip pylint enable/disable comments
                    continue
                if token.startswith("# type: "):
                    # Skip python 2 type comments and mypy type ignore comments
                    # mypy do not support additional text in type comments
                    continue
                self._check_spelling("wrong-spelling-in-comment", token, start_row)

    @only_required_for_messages("wrong-spelling-in-docstring")
    def visit_module(self, node: nodes.Module) -> None:
        self._check_docstring(node)

    @only_required_for_messages("wrong-spelling-in-docstring")
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        self._check_docstring(node)

    @only_required_for_messages("wrong-spelling-in-docstring")
    def visit_functiondef(
        self, node: nodes.FunctionDef | nodes.AsyncFunctionDef
    ) -> None:
        self._check_docstring(node)

    visit_asyncfunctiondef = visit_functiondef

    def _check_docstring(
        self,
        node: nodes.FunctionDef
        | nodes.AsyncFunctionDef
        | nodes.ClassDef
        | nodes.Module,
    ) -> None:
        """Check if the node has any spelling errors."""
        if not self.initialized:
            return
        if not node.doc_node:
            return
        start_line = node.lineno + 1
        # Go through lines of docstring
        for idx, line in enumerate(node.doc_node.value.splitlines()):
            self._check_spelling("wrong-spelling-in-docstring", line, start_line + idx)


def register(linter: PyLinter) -> None:
    linter.register_checker(SpellingChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/spelling.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checkers for various standard library functions."""

from __future__ import annotations

import sys
from collections.abc import Iterable
from typing import TYPE_CHECKING, Any, Dict, Set, Tuple

import astroid
from astroid import nodes, util
from astroid.typing import InferenceResult

from pylint import interfaces
from pylint.checkers import BaseChecker, DeprecatedMixin, utils
from pylint.interfaces import HIGH, INFERENCE
from pylint.typing import MessageDefinitionTuple

if TYPE_CHECKING:
    from pylint.lint import PyLinter

DeprecationDict = Dict[Tuple[int, int, int], Set[str]]

OPEN_FILES_MODE = ("open", "file")
OPEN_FILES_FUNCS = (*OPEN_FILES_MODE, "read_text", "write_text")
UNITTEST_CASE = "unittest.case"
THREADING_THREAD = "threading.Thread"
COPY_COPY = "copy.copy"
OS_ENVIRON = "os._Environ"
ENV_GETTERS = ("os.getenv",)
SUBPROCESS_POPEN = "subprocess.Popen"
SUBPROCESS_RUN = "subprocess.run"
OPEN_MODULE = {"_io", "pathlib"}
DEBUG_BREAKPOINTS = ("builtins.breakpoint", "sys.breakpointhook", "pdb.set_trace")
LRU_CACHE = {
    "functools.lru_cache",  # Inferred for @lru_cache
    "functools._lru_cache_wrapper.wrapper",  # Inferred for @lru_cache() on >= Python 3.8
    "functools.lru_cache.decorating_function",  # Inferred for @lru_cache() on <= Python 3.7
}
NON_INSTANCE_METHODS = {"builtins.staticmethod", "builtins.classmethod"}


# For modules, see ImportsChecker

DEPRECATED_ARGUMENTS: dict[
    tuple[int, int, int], dict[str, tuple[tuple[int | None, str], ...]]
] = {
    (0, 0, 0): {
        "int": ((None, "x"),),
        "bool": ((None, "x"),),
        "float": ((None, "x"),),
    },
    (3, 8, 0): {
        "asyncio.tasks.sleep": ((None, "loop"),),
        "asyncio.tasks.gather": ((None, "loop"),),
        "asyncio.tasks.shield": ((None, "loop"),),
        "asyncio.tasks.wait_for": ((None, "loop"),),
        "asyncio.tasks.wait": ((None, "loop"),),
        "asyncio.tasks.as_completed": ((None, "loop"),),
        "asyncio.subprocess.create_subprocess_exec": ((None, "loop"),),
        "asyncio.subprocess.create_subprocess_shell": ((4, "loop"),),
        "gettext.translation": ((5, "codeset"),),
        "gettext.install": ((2, "codeset"),),
        "functools.partialmethod": ((None, "func"),),
        "weakref.finalize": ((None, "func"), (None, "obj")),
        "profile.Profile.runcall": ((None, "func"),),
        "cProfile.Profile.runcall": ((None, "func"),),
        "bdb.Bdb.runcall": ((None, "func"),),
        "trace.Trace.runfunc": ((None, "func"),),
        "curses.wrapper": ((None, "func"),),
        "unittest.case.TestCase.addCleanup": ((None, "function"),),
        "concurrent.futures.thread.ThreadPoolExecutor.submit": ((None, "fn"),),
        "concurrent.futures.process.ProcessPoolExecutor.submit": ((None, "fn"),),
        "contextlib._BaseExitStack.callback": ((None, "callback"),),
        "contextlib.AsyncExitStack.push_async_callback": ((None, "callback"),),
        "multiprocessing.managers.Server.create": ((None, "c"), (None, "typeid")),
        "multiprocessing.managers.SharedMemoryServer.create": (
            (None, "c"),
            (None, "typeid"),
        ),
    },
    (3, 9, 0): {"random.Random.shuffle": ((1, "random"),)},
    (3, 12, 0): {
        "coroutine.throw": ((1, "value"), (2, "traceback")),
        "shutil.rmtree": ((2, "onerror"),),
    },
}

DEPRECATED_DECORATORS: DeprecationDict = {
    (3, 8, 0): {"asyncio.coroutine"},
    (3, 3, 0): {
        "abc.abstractclassmethod",
        "abc.abstractstaticmethod",
        "abc.abstractproperty",
    },
    (3, 4, 0): {"importlib.util.module_for_loader"},
}


DEPRECATED_METHODS: dict[int, DeprecationDict] = {
    0: {
        (0, 0, 0): {
            "cgi.parse_qs",
            "cgi.parse_qsl",
            "ctypes.c_buffer",
            "distutils.command.register.register.check_metadata",
            "distutils.command.sdist.sdist.check_metadata",
            "tkinter.Misc.tk_menuBar",
            "tkinter.Menu.tk_bindForTraversal",
        }
    },
    2: {
        (2, 6, 0): {
            "commands.getstatus",
            "os.popen2",
            "os.popen3",
            "os.popen4",
            "macostools.touched",
        },
        (2, 7, 0): {
            "unittest.case.TestCase.assertEquals",
            "unittest.case.TestCase.assertNotEquals",
            "unittest.case.TestCase.assertAlmostEquals",
            "unittest.case.TestCase.assertNotAlmostEquals",
            "unittest.case.TestCase.assert_",
            "xml.etree.ElementTree.Element.getchildren",
            "xml.etree.ElementTree.Element.getiterator",
            "xml.etree.ElementTree.XMLParser.getiterator",
            "xml.etree.ElementTree.XMLParser.doctype",
        },
    },
    3: {
        (3, 0, 0): {
            "inspect.getargspec",
            "failUnlessEqual",
            "assertEquals",
            "failIfEqual",
            "assertNotEquals",
            "failUnlessAlmostEqual",
            "assertAlmostEquals",
            "failIfAlmostEqual",
            "assertNotAlmostEquals",
            "failUnless",
            "assert_",
            "failUnlessRaises",
            "failIf",
            "assertRaisesRegexp",
            "assertRegexpMatches",
            "assertNotRegexpMatches",
        },
        (3, 1, 0): {
            "base64.encodestring",
            "base64.decodestring",
            "ntpath.splitunc",
            "os.path.splitunc",
            "os.stat_float_times",
            "turtle.RawTurtle.settiltangle",
        },
        (3, 2, 0): {
            "cgi.escape",
            "configparser.RawConfigParser.readfp",
            "xml.etree.ElementTree.Element.getchildren",
            "xml.etree.ElementTree.Element.getiterator",
            "xml.etree.ElementTree.XMLParser.getiterator",
            "xml.etree.ElementTree.XMLParser.doctype",
        },
        (3, 3, 0): {
            "inspect.getmoduleinfo",
            "logging.warn",
            "logging.Logger.warn",
            "logging.LoggerAdapter.warn",
            "nntplib._NNTPBase.xpath",
            "platform.popen",
            "sqlite3.OptimizedUnicode",
            "time.clock",
        },
        (3, 4, 0): {
            "importlib.find_loader",
            "importlib.abc.Loader.load_module",
            "importlib.abc.Loader.module_repr",
            "importlib.abc.PathEntryFinder.find_loader",
            "importlib.abc.PathEntryFinder.find_module",
            "plistlib.readPlist",
            "plistlib.writePlist",
            "plistlib.readPlistFromBytes",
            "plistlib.writePlistToBytes",
        },
        (3, 4, 4): {"asyncio.tasks.async"},
        (3, 5, 0): {
            "fractions.gcd",
            "inspect.formatargspec",
            "inspect.getcallargs",
            "platform.linux_distribution",
            "platform.dist",
        },
        (3, 6, 0): {
            "importlib._bootstrap_external.FileLoader.load_module",
            "_ssl.RAND_pseudo_bytes",
        },
        (3, 7, 0): {
            "sys.set_coroutine_wrapper",
            "sys.get_coroutine_wrapper",
            "aifc.openfp",
            "threading.Thread.isAlive",
            "asyncio.Task.current_task",
            "asyncio.Task.all_task",
            "locale.format",
            "ssl.wrap_socket",
            "ssl.match_hostname",
            "sunau.openfp",
            "wave.openfp",
        },
        (3, 8, 0): {
            "gettext.lgettext",
            "gettext.ldgettext",
            "gettext.lngettext",
            "gettext.ldngettext",
            "gettext.bind_textdomain_codeset",
            "gettext.NullTranslations.output_charset",
            "gettext.NullTranslations.set_output_charset",
            "threading.Thread.isAlive",
        },
        (3, 9, 0): {
            "binascii.b2a_hqx",
            "binascii.a2b_hqx",
            "binascii.rlecode_hqx",
            "binascii.rledecode_hqx",
        },
        (3, 10, 0): {
            "_sqlite3.enable_shared_cache",
            "importlib.abc.Finder.find_module",
            "pathlib.Path.link_to",
            "zipimport.zipimporter.load_module",
            "zipimport.zipimporter.find_module",
            "zipimport.zipimporter.find_loader",
            "threading.currentThread",
            "threading.activeCount",
            "threading.Condition.notifyAll",
            "threading.Event.isSet",
            "threading.Thread.setName",
            "threading.Thread.getName",
            "threading.Thread.isDaemon",
            "threading.Thread.setDaemon",
            "cgi.log",
        },
        (3, 11, 0): {
            "locale.getdefaultlocale",
            "locale.resetlocale",
            "re.template",
            "unittest.findTestCases",
            "unittest.makeSuite",
            "unittest.getTestCaseNames",
            "unittest.TestLoader.loadTestsFromModule",
            "unittest.TestLoader.loadTestsFromTestCase",
            "unittest.TestLoader.getTestCaseNames",
        },
        (3, 12, 0): {
            "builtins.bool.__invert__",
            "datetime.datetime.utcfromtimestamp",
            "datetime.datetime.utcnow",
            "xml.etree.ElementTree.Element.__bool__",
        },
    },
}


DEPRECATED_CLASSES: dict[tuple[int, int, int], dict[str, set[str]]] = {
    (3, 2, 0): {
        "configparser": {
            "LegacyInterpolation",
            "SafeConfigParser",
        },
    },
    (3, 3, 0): {
        "importlib.abc": {
            "Finder",
        },
        "pkgutil": {
            "ImpImporter",
            "ImpLoader",
        },
        "collections": {
            "Awaitable",
            "Coroutine",
            "AsyncIterable",
            "AsyncIterator",
            "AsyncGenerator",
            "Hashable",
            "Iterable",
            "Iterator",
            "Generator",
            "Reversible",
            "Sized",
            "Container",
            "Callable",
            "Collection",
            "Set",
            "MutableSet",
            "Mapping",
            "MutableMapping",
            "MappingView",
            "KeysView",
            "ItemsView",
            "ValuesView",
            "Sequence",
            "MutableSequence",
            "ByteString",
        },
    },
    (3, 9, 0): {
        "smtpd": {
            "MailmanProxy",
        }
    },
    (3, 11, 0): {
        "typing": {
            "Text",
        },
        "webbrowser": {
            "MacOSX",
        },
    },
    (3, 12, 0): {
        "typing": {
            "Hashable",
            "Sized",
        },
    },
}


def _check_mode_str(mode: Any) -> bool:
    # check type
    if not isinstance(mode, str):
        return False
    # check syntax
    modes = set(mode)
    _mode = "rwatb+Ux"
    creating = "x" in modes
    if modes - set(_mode) or len(mode) > len(modes):
        return False
    # check logic
    reading = "r" in modes
    writing = "w" in modes
    appending = "a" in modes
    text = "t" in modes
    binary = "b" in modes
    if "U" in modes:
        if writing or appending or creating:
            return False
        reading = True
    if text and binary:
        return False
    total = reading + writing + appending + creating
    if total > 1:
        return False
    if not (reading or writing or appending or creating):
        return False
    return True


class StdlibChecker(DeprecatedMixin, BaseChecker):
    name = "stdlib"

    msgs: dict[str, MessageDefinitionTuple] = {
        **DeprecatedMixin.DEPRECATED_METHOD_MESSAGE,
        **DeprecatedMixin.DEPRECATED_ARGUMENT_MESSAGE,
        **DeprecatedMixin.DEPRECATED_CLASS_MESSAGE,
        **DeprecatedMixin.DEPRECATED_DECORATOR_MESSAGE,
        "W1501": (
            '"%s" is not a valid mode for open.',
            "bad-open-mode",
            "Python supports: r, w, a[, x] modes with b, +, "
            "and U (only with r) options. "
            "See https://docs.python.org/3/library/functions.html#open",
        ),
        "W1502": (
            "Using datetime.time in a boolean context.",
            "boolean-datetime",
            "Using datetime.time in a boolean context can hide "
            "subtle bugs when the time they represent matches "
            "midnight UTC. This behaviour was fixed in Python 3.5. "
            "See https://bugs.python.org/issue13936 for reference.",
            {"maxversion": (3, 5)},
        ),
        "W1503": (
            "Redundant use of %s with constant value %r",
            "redundant-unittest-assert",
            "The first argument of assertTrue and assertFalse is "
            "a condition. If a constant is passed as parameter, that "
            "condition will be always true. In this case a warning "
            "should be emitted.",
        ),
        "W1506": (
            "threading.Thread needs the target function",
            "bad-thread-instantiation",
            "The warning is emitted when a threading.Thread class "
            "is instantiated without the target function being passed as a kwarg or as a second argument. "
            "By default, the first parameter is the group param, not the target param.",
        ),
        "W1507": (
            "Using copy.copy(os.environ). Use os.environ.copy() instead.",
            "shallow-copy-environ",
            "os.environ is not a dict object but proxy object, so "
            "shallow copy has still effects on original object. "
            "See https://bugs.python.org/issue15373 for reference.",
        ),
        "E1507": (
            "%s does not support %s type argument",
            "invalid-envvar-value",
            "Env manipulation functions support only string type arguments. "
            "See https://docs.python.org/3/library/os.html#os.getenv.",
        ),
        "E1519": (
            "singledispatch decorator should not be used with methods, "
            "use singledispatchmethod instead.",
            "singledispatch-method",
            "singledispatch should decorate functions and not class/instance methods. "
            "Use singledispatchmethod for those cases.",
        ),
        "E1520": (
            "singledispatchmethod decorator should not be used with functions, "
            "use singledispatch instead.",
            "singledispatchmethod-function",
            "singledispatchmethod should decorate class/instance methods and not functions. "
            "Use singledispatch for those cases.",
        ),
        "W1508": (
            "%s default type is %s. Expected str or None.",
            "invalid-envvar-default",
            "Env manipulation functions return None or str values. "
            "Supplying anything different as a default may cause bugs. "
            "See https://docs.python.org/3/library/os.html#os.getenv.",
        ),
        "W1509": (
            "Using preexec_fn keyword which may be unsafe in the presence "
            "of threads",
            "subprocess-popen-preexec-fn",
            "The preexec_fn parameter is not safe to use in the presence "
            "of threads in your application. The child process could "
            "deadlock before exec is called. If you must use it, keep it "
            "trivial! Minimize the number of libraries you call into. "
            "See https://docs.python.org/3/library/subprocess.html#popen-constructor",
        ),
        "W1510": (
            "'subprocess.run' used without explicitly defining the value for 'check'.",
            "subprocess-run-check",
            "The ``check`` keyword  is set to False by default. It means the process "
            "launched by ``subprocess.run`` can exit with a non-zero exit code and "
            "fail silently. It's better to set it explicitly to make clear what the "
            "error-handling behavior is.",
        ),
        "W1514": (
            "Using open without explicitly specifying an encoding",
            "unspecified-encoding",
            "It is better to specify an encoding when opening documents. "
            "Using the system default implicitly can create problems on other operating systems. "
            "See https://peps.python.org/pep-0597/",
        ),
        "W1515": (
            "Leaving functions creating breakpoints in production code is not recommended",
            "forgotten-debug-statement",
            "Calls to breakpoint(), sys.breakpointhook() and pdb.set_trace() should be removed "
            "from code that is not actively being debugged.",
        ),
        "W1518": (
            "'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self'",
            "method-cache-max-size-none",
            "By decorating a method with lru_cache or cache the 'self' argument will be linked to "
            "the function and therefore never garbage collected. Unless your instance "
            "will never need to be garbage collected (singleton) it is recommended to refactor "
            "code to avoid this pattern or add a maxsize to the cache. "
            "The default value for maxsize is 128.",
            {
                "old_names": [
                    ("W1516", "lru-cache-decorating-method"),
                    ("W1517", "cache-max-size-none"),
                ]
            },
        ),
    }

    def __init__(self, linter: PyLinter) -> None:
        BaseChecker.__init__(self, linter)
        self._deprecated_methods: set[str] = set()
        self._deprecated_arguments: dict[str, tuple[tuple[int | None, str], ...]] = {}
        self._deprecated_classes: dict[str, set[str]] = {}
        self._deprecated_decorators: set[str] = set()

        for since_vers, func_list in DEPRECATED_METHODS[sys.version_info[0]].items():
            if since_vers <= sys.version_info:
                self._deprecated_methods.update(func_list)
        for since_vers, args_list in DEPRECATED_ARGUMENTS.items():
            if since_vers <= sys.version_info:
                self._deprecated_arguments.update(args_list)
        for since_vers, class_list in DEPRECATED_CLASSES.items():
            if since_vers <= sys.version_info:
                self._deprecated_classes.update(class_list)
        for since_vers, decorator_list in DEPRECATED_DECORATORS.items():
            if since_vers <= sys.version_info:
                self._deprecated_decorators.update(decorator_list)
        # Modules are checked by the ImportsChecker, because the list is
        # synced with the config argument deprecated-modules

    def _check_bad_thread_instantiation(self, node: nodes.Call) -> None:
        func_kwargs = {key.arg for key in node.keywords}
        if "target" in func_kwargs:
            return

        if len(node.args) < 2 and (not node.kwargs or "target" not in func_kwargs):
            self.add_message(
                "bad-thread-instantiation", node=node, confidence=interfaces.HIGH
            )

    def _check_for_preexec_fn_in_popen(self, node: nodes.Call) -> None:
        if node.keywords:
            for keyword in node.keywords:
                if keyword.arg == "preexec_fn":
                    self.add_message("subprocess-popen-preexec-fn", node=node)

    def _check_for_check_kw_in_run(self, node: nodes.Call) -> None:
        kwargs = {keyword.arg for keyword in (node.keywords or ())}
        if "check" not in kwargs:
            self.add_message("subprocess-run-check", node=node, confidence=INFERENCE)

    def _check_shallow_copy_environ(self, node: nodes.Call) -> None:
        confidence = HIGH
        try:
            arg = utils.get_argument_from_call(node, position=0, keyword="x")
        except utils.NoSuchArgumentError:
            arg = utils.infer_kwarg_from_call(node, keyword="x")
            if not arg:
                return
            confidence = INFERENCE
        try:
            inferred_args = arg.inferred()
        except astroid.InferenceError:
            return
        for inferred in inferred_args:
            if inferred.qname() == OS_ENVIRON:
                self.add_message(
                    "shallow-copy-environ", node=node, confidence=confidence
                )
                break

    @utils.only_required_for_messages(
        "bad-open-mode",
        "redundant-unittest-assert",
        "deprecated-method",
        "deprecated-argument",
        "bad-thread-instantiation",
        "shallow-copy-environ",
        "invalid-envvar-value",
        "invalid-envvar-default",
        "subprocess-popen-preexec-fn",
        "subprocess-run-check",
        "deprecated-class",
        "unspecified-encoding",
        "forgotten-debug-statement",
    )
    def visit_call(self, node: nodes.Call) -> None:
        """Visit a Call node."""
        self.check_deprecated_class_in_call(node)
        for inferred in utils.infer_all(node.func):
            if isinstance(inferred, util.UninferableBase):
                continue
            if inferred.root().name in OPEN_MODULE:
                open_func_name: str | None = None
                if isinstance(node.func, nodes.Name):
                    open_func_name = node.func.name
                if isinstance(node.func, nodes.Attribute):
                    open_func_name = node.func.attrname
                if open_func_name in OPEN_FILES_FUNCS:
                    self._check_open_call(node, inferred.root().name, open_func_name)
            elif inferred.root().name == UNITTEST_CASE:
                self._check_redundant_assert(node, inferred)
            elif isinstance(inferred, nodes.ClassDef):
                if inferred.qname() == THREADING_THREAD:
                    self._check_bad_thread_instantiation(node)
                elif inferred.qname() == SUBPROCESS_POPEN:
                    self._check_for_preexec_fn_in_popen(node)
            elif isinstance(inferred, nodes.FunctionDef):
                name = inferred.qname()
                if name == COPY_COPY:
                    self._check_shallow_copy_environ(node)
                elif name in ENV_GETTERS:
                    self._check_env_function(node, inferred)
                elif name == SUBPROCESS_RUN:
                    self._check_for_check_kw_in_run(node)
                elif name in DEBUG_BREAKPOINTS:
                    self.add_message("forgotten-debug-statement", node=node)
            self.check_deprecated_method(node, inferred)

    @utils.only_required_for_messages("boolean-datetime")
    def visit_unaryop(self, node: nodes.UnaryOp) -> None:
        if node.op == "not":
            self._check_datetime(node.operand)

    @utils.only_required_for_messages("boolean-datetime")
    def visit_if(self, node: nodes.If) -> None:
        self._check_datetime(node.test)

    @utils.only_required_for_messages("boolean-datetime")
    def visit_ifexp(self, node: nodes.IfExp) -> None:
        self._check_datetime(node.test)

    @utils.only_required_for_messages("boolean-datetime")
    def visit_boolop(self, node: nodes.BoolOp) -> None:
        for value in node.values:
            self._check_datetime(value)

    @utils.only_required_for_messages(
        "method-cache-max-size-none",
        "singledispatch-method",
        "singledispatchmethod-function",
    )
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        if node.decorators and isinstance(node.parent, nodes.ClassDef):
            self._check_lru_cache_decorators(node)
            self._check_dispatch_decorators(node)

    def _check_lru_cache_decorators(self, node: nodes.FunctionDef) -> None:
        """Check if instance methods are decorated with functools.lru_cache."""
        if any(utils.is_enum(ancestor) for ancestor in node.parent.ancestors()):
            # method of class inheriting from Enum is exempt from this check.
            return

        lru_cache_nodes: list[nodes.NodeNG] = []
        for d_node in node.decorators.nodes:
            # pylint: disable = too-many-try-statements
            try:
                for infered_node in d_node.infer():
                    q_name = infered_node.qname()
                    if q_name in NON_INSTANCE_METHODS:
                        return

                    # Check if there is a maxsize argument set to None in the call
                    if q_name in LRU_CACHE and isinstance(d_node, nodes.Call):
                        try:
                            arg = utils.get_argument_from_call(
                                d_node, position=0, keyword="maxsize"
                            )
                        except utils.NoSuchArgumentError:
                            arg = utils.infer_kwarg_from_call(d_node, "maxsize")

                        if not isinstance(arg, nodes.Const) or arg.value is not None:
                            break

                        lru_cache_nodes.append(d_node)
                        break

                    if q_name == "functools.cache":
                        lru_cache_nodes.append(d_node)
                        break
            except astroid.InferenceError:
                pass
        for lru_cache_node in lru_cache_nodes:
            self.add_message(
                "method-cache-max-size-none",
                node=lru_cache_node,
                confidence=interfaces.INFERENCE,
            )

    def _check_dispatch_decorators(self, node: nodes.FunctionDef) -> None:
        decorators_map: dict[str, tuple[nodes.NodeNG, interfaces.Confidence]] = {}

        for decorator in node.decorators.nodes:
            if isinstance(decorator, nodes.Name) and decorator.name:
                decorators_map[decorator.name] = (decorator, interfaces.HIGH)
            elif utils.is_registered_in_singledispatch_function(node):
                decorators_map["singledispatch"] = (decorator, interfaces.INFERENCE)
            elif utils.is_registered_in_singledispatchmethod_function(node):
                decorators_map["singledispatchmethod"] = (
                    decorator,
                    interfaces.INFERENCE,
                )

        if "singledispatch" in decorators_map and "classmethod" in decorators_map:
            self.add_message(
                "singledispatch-method",
                node=decorators_map["singledispatch"][0],
                confidence=decorators_map["singledispatch"][1],
            )
        elif (
            "singledispatchmethod" in decorators_map
            and "staticmethod" in decorators_map
        ):
            self.add_message(
                "singledispatchmethod-function",
                node=decorators_map["singledispatchmethod"][0],
                confidence=decorators_map["singledispatchmethod"][1],
            )

    def _check_redundant_assert(self, node: nodes.Call, infer: InferenceResult) -> None:
        if (
            isinstance(infer, astroid.BoundMethod)
            and node.args
            and isinstance(node.args[0], nodes.Const)
            and infer.name in {"assertTrue", "assertFalse"}
        ):
            self.add_message(
                "redundant-unittest-assert",
                args=(infer.name, node.args[0].value),
                node=node,
            )

    def _check_datetime(self, node: nodes.NodeNG) -> None:
        """Check that a datetime was inferred, if so, emit boolean-datetime warning."""
        try:
            inferred = next(node.infer())
        except astroid.InferenceError:
            return
        if isinstance(inferred, astroid.Instance) and inferred.qname() in {
            "_pydatetime.time",
            "datetime.time",
        }:
            self.add_message("boolean-datetime", node=node)

    def _check_open_call(
        self, node: nodes.Call, open_module: str, func_name: str
    ) -> None:
        """Various checks for an open call."""
        mode_arg = None
        confidence = HIGH
        try:
            if open_module == "_io":
                mode_arg = utils.get_argument_from_call(
                    node, position=1, keyword="mode"
                )
            elif open_module == "pathlib":
                mode_arg = utils.get_argument_from_call(
                    node, position=0, keyword="mode"
                )
        except utils.NoSuchArgumentError:
            mode_arg = utils.infer_kwarg_from_call(node, keyword="mode")
            if mode_arg:
                confidence = INFERENCE

        if mode_arg:
            mode_arg = utils.safe_infer(mode_arg)
            if (
                func_name in OPEN_FILES_MODE
                and isinstance(mode_arg, nodes.Const)
                and not _check_mode_str(mode_arg.value)
            ):
                self.add_message(
                    "bad-open-mode",
                    node=node,
                    args=mode_arg.value or str(mode_arg.value),
                    confidence=confidence,
                )

        if (
            not mode_arg
            or isinstance(mode_arg, nodes.Const)
            and (not mode_arg.value or "b" not in str(mode_arg.value))
        ):
            confidence = HIGH
            try:
                if open_module == "pathlib":
                    if node.func.attrname == "read_text":
                        encoding_arg = utils.get_argument_from_call(
                            node, position=0, keyword="encoding"
                        )
                    elif node.func.attrname == "write_text":
                        encoding_arg = utils.get_argument_from_call(
                            node, position=1, keyword="encoding"
                        )
                    else:
                        encoding_arg = utils.get_argument_from_call(
                            node, position=2, keyword="encoding"
                        )
                else:
                    encoding_arg = utils.get_argument_from_call(
                        node, position=3, keyword="encoding"
                    )
            except utils.NoSuchArgumentError:
                encoding_arg = utils.infer_kwarg_from_call(node, keyword="encoding")
                if encoding_arg:
                    confidence = INFERENCE
                else:
                    self.add_message(
                        "unspecified-encoding", node=node, confidence=confidence
                    )

            if encoding_arg:
                encoding_arg = utils.safe_infer(encoding_arg)

                if isinstance(encoding_arg, nodes.Const) and encoding_arg.value is None:
                    self.add_message(
                        "unspecified-encoding", node=node, confidence=confidence
                    )

    def _check_env_function(self, node: nodes.Call, infer: nodes.FunctionDef) -> None:
        env_name_kwarg = "key"
        env_value_kwarg = "default"
        if node.keywords:
            kwargs = {keyword.arg: keyword.value for keyword in node.keywords}
        else:
            kwargs = None
        if node.args:
            env_name_arg = node.args[0]
        elif kwargs and env_name_kwarg in kwargs:
            env_name_arg = kwargs[env_name_kwarg]
        else:
            env_name_arg = None

        if env_name_arg:
            self._check_invalid_envvar_value(
                node=node,
                message="invalid-envvar-value",
                call_arg=utils.safe_infer(env_name_arg),
                infer=infer,
                allow_none=False,
            )

        if len(node.args) == 2:
            env_value_arg = node.args[1]
        elif kwargs and env_value_kwarg in kwargs:
            env_value_arg = kwargs[env_value_kwarg]
        else:
            env_value_arg = None

        if env_value_arg:
            self._check_invalid_envvar_value(
                node=node,
                infer=infer,
                message="invalid-envvar-default",
                call_arg=utils.safe_infer(env_value_arg),
                allow_none=True,
            )

    def _check_invalid_envvar_value(
        self,
        node: nodes.Call,
        infer: nodes.FunctionDef,
        message: str,
        call_arg: InferenceResult | None,
        allow_none: bool,
    ) -> None:
        if call_arg is None or isinstance(call_arg, util.UninferableBase):
            return

        name = infer.qname()
        if isinstance(call_arg, nodes.Const):
            emit = False
            if call_arg.value is None:
                emit = not allow_none
            elif not isinstance(call_arg.value, str):
                emit = True
            if emit:
                self.add_message(message, node=node, args=(name, call_arg.pytype()))
        else:
            self.add_message(message, node=node, args=(name, call_arg.pytype()))

    def deprecated_methods(self) -> set[str]:
        return self._deprecated_methods

    def deprecated_arguments(self, method: str) -> tuple[tuple[int | None, str], ...]:
        return self._deprecated_arguments.get(method, ())

    def deprecated_classes(self, module: str) -> Iterable[str]:
        return self._deprecated_classes.get(module, ())

    def deprecated_decorators(self) -> Iterable[str]:
        return self._deprecated_decorators


def register(linter: PyLinter) -> None:
    linter.register_checker(StdlibChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/stdlib.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checker for string formatting operations."""

from __future__ import annotations

import collections
import re
import tokenize
from collections import Counter
from collections.abc import Iterable, Sequence
from typing import TYPE_CHECKING, Literal

import astroid
from astroid import bases, nodes, util
from astroid.typing import SuccessfulInferenceResult

from pylint.checkers import BaseChecker, BaseRawFileChecker, BaseTokenChecker, utils
from pylint.checkers.utils import only_required_for_messages
from pylint.interfaces import HIGH
from pylint.typing import MessageDefinitionTuple

if TYPE_CHECKING:
    from pylint.lint import PyLinter


_AST_NODE_STR_TYPES = ("__builtin__.unicode", "__builtin__.str", "builtins.str")
# Prefixes for both strings and bytes literals per
# https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
_PREFIXES = {
    "r",
    "u",
    "R",
    "U",
    "f",
    "F",
    "fr",
    "Fr",
    "fR",
    "FR",
    "rf",
    "rF",
    "Rf",
    "RF",
    "b",
    "B",
    "br",
    "Br",
    "bR",
    "BR",
    "rb",
    "rB",
    "Rb",
    "RB",
}
_PAREN_IGNORE_TOKEN_TYPES = (
    tokenize.NEWLINE,
    tokenize.NL,
    tokenize.COMMENT,
)
SINGLE_QUOTED_REGEX = re.compile(f"({'|'.join(_PREFIXES)})?'''")
DOUBLE_QUOTED_REGEX = re.compile(f"({'|'.join(_PREFIXES)})?\"\"\"")
QUOTE_DELIMITER_REGEX = re.compile(f"({'|'.join(_PREFIXES)})?(\"|')", re.DOTALL)

MSGS: dict[
    str, MessageDefinitionTuple
] = {  # pylint: disable=consider-using-namedtuple-or-dataclass
    "E1300": (
        "Unsupported format character %r (%#02x) at index %d",
        "bad-format-character",
        "Used when an unsupported format character is used in a format string.",
    ),
    "E1301": (
        "Format string ends in middle of conversion specifier",
        "truncated-format-string",
        "Used when a format string terminates before the end of a "
        "conversion specifier.",
    ),
    "E1302": (
        "Mixing named and unnamed conversion specifiers in format string",
        "mixed-format-string",
        "Used when a format string contains both named (e.g. '%(foo)d') "
        "and unnamed (e.g. '%d') conversion specifiers.  This is also "
        "used when a named conversion specifier contains * for the "
        "minimum field width and/or precision.",
    ),
    "E1303": (
        "Expected mapping for format string, not %s",
        "format-needs-mapping",
        "Used when a format string that uses named conversion specifiers "
        "is used with an argument that is not a mapping.",
    ),
    "W1300": (
        "Format string dictionary key should be a string, not %s",
        "bad-format-string-key",
        "Used when a format string that uses named conversion specifiers "
        "is used with a dictionary whose keys are not all strings.",
    ),
    "W1301": (
        "Unused key %r in format string dictionary",
        "unused-format-string-key",
        "Used when a format string that uses named conversion specifiers "
        "is used with a dictionary that contains keys not required by the "
        "format string.",
    ),
    "E1304": (
        "Missing key %r in format string dictionary",
        "missing-format-string-key",
        "Used when a format string that uses named conversion specifiers "
        "is used with a dictionary that doesn't contain all the keys "
        "required by the format string.",
    ),
    "E1305": (
        "Too many arguments for format string",
        "too-many-format-args",
        "Used when a format string that uses unnamed conversion "
        "specifiers is given too many arguments.",
    ),
    "E1306": (
        "Not enough arguments for format string",
        "too-few-format-args",
        "Used when a format string that uses unnamed conversion "
        "specifiers is given too few arguments",
    ),
    "E1307": (
        "Argument %r does not match format type %r",
        "bad-string-format-type",
        "Used when a type required by format string "
        "is not suitable for actual argument type",
    ),
    "E1310": (
        "Suspicious argument in %s.%s call",
        "bad-str-strip-call",
        "The argument to a str.{l,r,}strip call contains a duplicate character,",
    ),
    "W1302": (
        "Invalid format string",
        "bad-format-string",
        "Used when a PEP 3101 format string is invalid.",
    ),
    "W1303": (
        "Missing keyword argument %r for format string",
        "missing-format-argument-key",
        "Used when a PEP 3101 format string that uses named fields "
        "doesn't receive one or more required keywords.",
    ),
    "W1304": (
        "Unused format argument %r",
        "unused-format-string-argument",
        "Used when a PEP 3101 format string that uses named "
        "fields is used with an argument that "
        "is not required by the format string.",
    ),
    "W1305": (
        "Format string contains both automatic field numbering "
        "and manual field specification",
        "format-combined-specification",
        "Used when a PEP 3101 format string contains both automatic "
        "field numbering (e.g. '{}') and manual field "
        "specification (e.g. '{0}').",
    ),
    "W1306": (
        "Missing format attribute %r in format specifier %r",
        "missing-format-attribute",
        "Used when a PEP 3101 format string uses an "
        "attribute specifier ({0.length}), but the argument "
        "passed for formatting doesn't have that attribute.",
    ),
    "W1307": (
        "Using invalid lookup key %r in format specifier %r",
        "invalid-format-index",
        "Used when a PEP 3101 format string uses a lookup specifier "
        "({a[1]}), but the argument passed for formatting "
        "doesn't contain or doesn't have that key as an attribute.",
    ),
    "W1308": (
        "Duplicate string formatting argument %r, consider passing as named argument",
        "duplicate-string-formatting-argument",
        "Used when we detect that a string formatting is "
        "repeating an argument instead of using named string arguments",
    ),
    "W1309": (
        "Using an f-string that does not have any interpolated variables",
        "f-string-without-interpolation",
        "Used when we detect an f-string that does not use any interpolation variables, "
        "in which case it can be either a normal string or a bug in the code.",
    ),
    "W1310": (
        "Using formatting for a string that does not have any interpolated variables",
        "format-string-without-interpolation",
        "Used when we detect a string that does not have any interpolation variables, "
        "in which case it can be either a normal string without formatting or a bug in the code.",
    ),
}

OTHER_NODES = (
    nodes.Const,
    nodes.List,
    nodes.Lambda,
    nodes.FunctionDef,
    nodes.ListComp,
    nodes.SetComp,
    nodes.GeneratorExp,
)


def get_access_path(key: str | Literal[0], parts: list[tuple[bool, str]]) -> str:
    """Given a list of format specifiers, returns
    the final access path (e.g. a.b.c[0][1]).
    """
    path = []
    for is_attribute, specifier in parts:
        if is_attribute:
            path.append(f".{specifier}")
        else:
            path.append(f"[{specifier!r}]")
    return str(key) + "".join(path)


def arg_matches_format_type(
    arg_type: SuccessfulInferenceResult, format_type: str
) -> bool:
    if format_type in "sr":
        # All types can be printed with %s and %r
        return True
    if isinstance(arg_type, astroid.Instance):
        arg_type = arg_type.pytype()
        if arg_type == "builtins.str":
            return format_type == "c"
        if arg_type == "builtins.float":
            return format_type in "deEfFgGn%"
        if arg_type == "builtins.int":
            # Integers allow all types
            return True
        return False
    return True


class StringFormatChecker(BaseChecker):
    """Checks string formatting operations to ensure that the format string
    is valid and the arguments match the format string.
    """

    name = "string"
    msgs = MSGS

    # pylint: disable = too-many-branches, too-many-locals, too-many-statements
    @only_required_for_messages(
        "bad-format-character",
        "truncated-format-string",
        "mixed-format-string",
        "bad-format-string-key",
        "missing-format-string-key",
        "unused-format-string-key",
        "bad-string-format-type",
        "format-needs-mapping",
        "too-many-format-args",
        "too-few-format-args",
        "format-string-without-interpolation",
    )
    def visit_binop(self, node: nodes.BinOp) -> None:
        if node.op != "%":
            return
        left = node.left
        args = node.right

        if not (isinstance(left, nodes.Const) and isinstance(left.value, str)):
            return
        format_string = left.value
        try:
            (
                required_keys,
                required_num_args,
                required_key_types,
                required_arg_types,
            ) = utils.parse_format_string(format_string)
        except utils.UnsupportedFormatCharacter as exc:
            formatted = format_string[exc.index]
            self.add_message(
                "bad-format-character",
                node=node,
                args=(formatted, ord(formatted), exc.index),
            )
            return
        except utils.IncompleteFormatString:
            self.add_message("truncated-format-string", node=node)
            return
        if not required_keys and not required_num_args:
            self.add_message("format-string-without-interpolation", node=node)
            return
        if required_keys and required_num_args:
            # The format string uses both named and unnamed format
            # specifiers.
            self.add_message("mixed-format-string", node=node)
        elif required_keys:
            # The format string uses only named format specifiers.
            # Check that the RHS of the % operator is a mapping object
            # that contains precisely the set of keys required by the
            # format string.
            if isinstance(args, nodes.Dict):
                keys = set()
                unknown_keys = False
                for k, _ in args.items:
                    if isinstance(k, nodes.Const):
                        key = k.value
                        if isinstance(key, str):
                            keys.add(key)
                        else:
                            self.add_message(
                                "bad-format-string-key", node=node, args=key
                            )
                    else:
                        # One of the keys was something other than a
                        # constant.  Since we can't tell what it is,
                        # suppress checks for missing keys in the
                        # dictionary.
                        unknown_keys = True
                if not unknown_keys:
                    for key in required_keys:
                        if key not in keys:
                            self.add_message(
                                "missing-format-string-key", node=node, args=key
                            )
                for key in keys:
                    if key not in required_keys:
                        self.add_message(
                            "unused-format-string-key", node=node, args=key
                        )
                for key, arg in args.items:
                    if not isinstance(key, nodes.Const):
                        continue
                    format_type = required_key_types.get(key.value, None)
                    arg_type = utils.safe_infer(arg)
                    if (
                        format_type is not None
                        and arg_type
                        and not isinstance(arg_type, util.UninferableBase)
                        and not arg_matches_format_type(arg_type, format_type)
                    ):
                        self.add_message(
                            "bad-string-format-type",
                            node=node,
                            args=(arg_type.pytype(), format_type),
                        )
            elif isinstance(args, (OTHER_NODES, nodes.Tuple)):
                type_name = type(args).__name__
                self.add_message("format-needs-mapping", node=node, args=type_name)
            # else:
            # The RHS of the format specifier is a name or
            # expression.  It may be a mapping object, so
            # there's nothing we can check.
        else:
            # The format string uses only unnamed format specifiers.
            # Check that the number of arguments passed to the RHS of
            # the % operator matches the number required by the format
            # string.
            args_elts = []
            if isinstance(args, nodes.Tuple):
                rhs_tuple = utils.safe_infer(args)
                num_args = None
                if isinstance(rhs_tuple, nodes.BaseContainer):
                    args_elts = rhs_tuple.elts
                    num_args = len(args_elts)
            elif isinstance(args, (OTHER_NODES, (nodes.Dict, nodes.DictComp))):
                args_elts = [args]
                num_args = 1
            elif isinstance(args, nodes.Name):
                inferred = utils.safe_infer(args)
                if isinstance(inferred, nodes.Tuple):
                    # The variable is a tuple, so we need to get the elements
                    # from it for further inspection
                    args_elts = inferred.elts
                    num_args = len(args_elts)
                elif isinstance(inferred, nodes.Const):
                    args_elts = [inferred]
                    num_args = 1
                else:
                    num_args = None
            else:
                # The RHS of the format specifier is an expression.
                # It could be a tuple of unknown size, so
                # there's nothing we can check.
                num_args = None
            if num_args is not None:
                if num_args > required_num_args:
                    self.add_message("too-many-format-args", node=node)
                elif num_args < required_num_args:
                    self.add_message("too-few-format-args", node=node)
                for arg, format_type in zip(args_elts, required_arg_types):
                    if not arg:
                        continue
                    arg_type = utils.safe_infer(arg)
                    if (
                        arg_type
                        and not isinstance(arg_type, util.UninferableBase)
                        and not arg_matches_format_type(arg_type, format_type)
                    ):
                        self.add_message(
                            "bad-string-format-type",
                            node=node,
                            args=(arg_type.pytype(), format_type),
                        )

    @only_required_for_messages("f-string-without-interpolation")
    def visit_joinedstr(self, node: nodes.JoinedStr) -> None:
        self._check_interpolation(node)

    def _check_interpolation(self, node: nodes.JoinedStr) -> None:
        if isinstance(node.parent, nodes.FormattedValue):
            return
        for value in node.values:
            if isinstance(value, nodes.FormattedValue):
                return
        self.add_message("f-string-without-interpolation", node=node)

    def visit_call(self, node: nodes.Call) -> None:
        func = utils.safe_infer(node.func)
        if (
            isinstance(func, astroid.BoundMethod)
            and isinstance(func.bound, astroid.Instance)
            and func.bound.name in {"str", "unicode", "bytes"}
        ):
            if func.name in {"strip", "lstrip", "rstrip"} and node.args:
                arg = utils.safe_infer(node.args[0])
                if not isinstance(arg, nodes.Const) or not isinstance(arg.value, str):
                    return
                if len(arg.value) != len(set(arg.value)):
                    self.add_message(
                        "bad-str-strip-call",
                        node=node,
                        args=(func.bound.name, func.name),
                    )
            elif func.name == "format":
                self._check_new_format(node, func)

    def _detect_vacuous_formatting(
        self, node: nodes.Call, positional_arguments: list[SuccessfulInferenceResult]
    ) -> None:
        counter = collections.Counter(
            arg.name for arg in positional_arguments if isinstance(arg, nodes.Name)
        )
        for name, count in counter.items():
            if count == 1:
                continue
            self.add_message(
                "duplicate-string-formatting-argument", node=node, args=(name,)
            )

    def _check_new_format(self, node: nodes.Call, func: bases.BoundMethod) -> None:
        """Check the new string formatting."""
        # Skip format nodes which don't have an explicit string on the
        # left side of the format operation.
        # We do this because our inference engine can't properly handle
        # redefinition of the original string.
        # Note that there may not be any left side at all, if the format method
        # has been assigned to another variable. See issue 351. For example:
        #
        #    fmt = 'some string {}'.format
        #    fmt('arg')
        if isinstance(node.func, nodes.Attribute) and not isinstance(
            node.func.expr, nodes.Const
        ):
            return
        if node.starargs or node.kwargs:
            return
        try:
            strnode = next(func.bound.infer())
        except astroid.InferenceError:
            return
        if not (isinstance(strnode, nodes.Const) and isinstance(strnode.value, str)):
            return
        try:
            call_site = astroid.arguments.CallSite.from_call(node)
        except astroid.InferenceError:
            return

        try:
            fields, num_args, manual_pos = utils.parse_format_method_string(
                strnode.value
            )
        except utils.IncompleteFormatString:
            self.add_message("bad-format-string", node=node)
            return

        positional_arguments = call_site.positional_arguments
        named_arguments = call_site.keyword_arguments
        named_fields = {field[0] for field in fields if isinstance(field[0], str)}
        if num_args and manual_pos:
            self.add_message("format-combined-specification", node=node)
            return

        check_args = False
        # Consider "{[0]} {[1]}" as num_args.
        num_args += sum(1 for field in named_fields if not field)
        if named_fields:
            for field in named_fields:
                if field and field not in named_arguments:
                    self.add_message(
                        "missing-format-argument-key", node=node, args=(field,)
                    )
            for field in named_arguments:
                if field not in named_fields:
                    self.add_message(
                        "unused-format-string-argument", node=node, args=(field,)
                    )
            # num_args can be 0 if manual_pos is not.
            num_args = num_args or manual_pos
            if positional_arguments or num_args:
                empty = not all(field for field in named_fields)
                if named_arguments or empty:
                    # Verify the required number of positional arguments
                    # only if the .format got at least one keyword argument.
                    # This means that the format strings accepts both
                    # positional and named fields and we should warn
                    # when one of them is missing or is extra.
                    check_args = True
        else:
            check_args = True
        if check_args:
            # num_args can be 0 if manual_pos is not.
            num_args = num_args or manual_pos
            if not num_args:
                self.add_message("format-string-without-interpolation", node=node)
                return
            if len(positional_arguments) > num_args:
                self.add_message("too-many-format-args", node=node)
            elif len(positional_arguments) < num_args:
                self.add_message("too-few-format-args", node=node)

        self._detect_vacuous_formatting(node, positional_arguments)
        self._check_new_format_specifiers(node, fields, named_arguments)

    # pylint: disable = too-many-statements
    def _check_new_format_specifiers(
        self,
        node: nodes.Call,
        fields: list[tuple[str, list[tuple[bool, str]]]],
        named: dict[str, SuccessfulInferenceResult],
    ) -> None:
        """Check attribute and index access in the format
        string ("{0.a}" and "{0[a]}").
        """
        key: Literal[0] | str
        for key, specifiers in fields:
            # Obtain the argument. If it can't be obtained
            # or inferred, skip this check.
            if not key:
                # {[0]} will have an unnamed argument, defaulting
                # to 0. It will not be present in `named`, so use the value
                # 0 for it.
                key = 0
            if isinstance(key, int):
                try:
                    argname = utils.get_argument_from_call(node, key)
                except utils.NoSuchArgumentError:
                    continue
            else:
                if key not in named:
                    continue
                argname = named[key]
            if argname is None or isinstance(argname, util.UninferableBase):
                continue
            try:
                argument = utils.safe_infer(argname)
            except astroid.InferenceError:
                continue
            if not specifiers or not argument:
                # No need to check this key if it doesn't
                # use attribute / item access
                continue
            if argument.parent and isinstance(argument.parent, nodes.Arguments):
                # Ignore any object coming from an argument,
                # because we can't infer its value properly.
                continue
            previous = argument
            parsed: list[tuple[bool, str]] = []
            for is_attribute, specifier in specifiers:
                if isinstance(previous, util.UninferableBase):
                    break
                parsed.append((is_attribute, specifier))
                if is_attribute:
                    try:
                        previous = previous.getattr(specifier)[0]
                    except astroid.NotFoundError:
                        if (
                            hasattr(previous, "has_dynamic_getattr")
                            and previous.has_dynamic_getattr()
                        ):
                            # Don't warn if the object has a custom __getattr__
                            break
                        path = get_access_path(key, parsed)
                        self.add_message(
                            "missing-format-attribute",
                            args=(specifier, path),
                            node=node,
                        )
                        break
                else:
                    warn_error = False
                    if hasattr(previous, "getitem"):
                        try:
                            previous = previous.getitem(nodes.Const(specifier))
                        except (
                            astroid.AstroidIndexError,
                            astroid.AstroidTypeError,
                            astroid.AttributeInferenceError,
                        ):
                            warn_error = True
                        except astroid.InferenceError:
                            break
                        if isinstance(previous, util.UninferableBase):
                            break
                    else:
                        try:
                            # Lookup __getitem__ in the current node,
                            # but skip further checks, because we can't
                            # retrieve the looked object
                            previous.getattr("__getitem__")
                            break
                        except astroid.NotFoundError:
                            warn_error = True
                    if warn_error:
                        path = get_access_path(key, parsed)
                        self.add_message(
                            "invalid-format-index", args=(specifier, path), node=node
                        )
                        break

                try:
                    previous = next(previous.infer())
                except astroid.InferenceError:
                    # can't check further if we can't infer it
                    break


class StringConstantChecker(BaseTokenChecker, BaseRawFileChecker):
    """Check string literals."""

    name = "string"
    msgs = {
        "W1401": (
            "Anomalous backslash in string: '%s'. "
            "String constant might be missing an r prefix.",
            "anomalous-backslash-in-string",
            "Used when a backslash is in a literal string but not as an escape.",
        ),
        "W1402": (
            "Anomalous Unicode escape in byte string: '%s'. "
            "String constant might be missing an r or u prefix.",
            "anomalous-unicode-escape-in-string",
            "Used when an escape like \\u is encountered in a byte "
            "string where it has no effect.",
        ),
        "W1404": (
            "Implicit string concatenation found in %s",
            "implicit-str-concat",
            "String literals are implicitly concatenated in a "
            "literal iterable definition : "
            "maybe a comma is missing ?",
            {"old_names": [("W1403", "implicit-str-concat-in-sequence")]},
        ),
        "W1405": (
            "Quote delimiter %s is inconsistent with the rest of the file",
            "inconsistent-quotes",
            "Quote delimiters are not used consistently throughout a module "
            "(with allowances made for avoiding unnecessary escaping).",
        ),
        "W1406": (
            "The u prefix for strings is no longer necessary in Python >=3.0",
            "redundant-u-string-prefix",
            "Used when we detect a string with a u prefix. These prefixes were necessary "
            "in Python 2 to indicate a string was Unicode, but since Python 3.0 strings "
            "are Unicode by default.",
        ),
    }
    options = (
        (
            "check-str-concat-over-line-jumps",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "This flag controls whether the "
                "implicit-str-concat should generate a warning "
                "on implicit string concatenation in sequences defined over "
                "several lines.",
            },
        ),
        (
            "check-quote-consistency",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "This flag controls whether inconsistent-quotes generates a "
                "warning when the character used as a quote delimiter is used "
                "inconsistently within a module.",
            },
        ),
    )

    # Characters that have a special meaning after a backslash in either
    # Unicode or byte strings.
    ESCAPE_CHARACTERS = "abfnrtvx\n\r\t\\'\"01234567"

    # Characters that have a special meaning after a backslash but only in
    # Unicode strings.
    UNICODE_ESCAPE_CHARACTERS = "uUN"

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self.string_tokens: dict[
            tuple[int, int], tuple[str, tokenize.TokenInfo | None]
        ] = {}
        """Token position -> (token value, next token)."""
        self._parenthesized_string_tokens: dict[tuple[int, int], bool] = {}

    def process_module(self, node: nodes.Module) -> None:
        self._unicode_literals = "unicode_literals" in node.future_imports

    def process_tokens(self, tokens: list[tokenize.TokenInfo]) -> None:
        encoding = "ascii"
        for i, (token_type, token, start, _, line) in enumerate(tokens):
            if token_type == tokenize.ENCODING:
                # this is always the first token processed
                encoding = token
            elif token_type == tokenize.STRING:
                # 'token' is the whole un-parsed token; we can look at the start
                # of it to see whether it's a raw or unicode string etc.
                self.process_string_token(token, start[0], start[1])
                # We figure the next token, ignoring comments & newlines:
                j = i + 1
                while j < len(tokens) and tokens[j].type in (
                    tokenize.NEWLINE,
                    tokenize.NL,
                    tokenize.COMMENT,
                ):
                    j += 1
                next_token = tokens[j] if j < len(tokens) else None
                if encoding != "ascii":
                    # We convert `tokenize` character count into a byte count,
                    # to match with astroid `.col_offset`
                    start = (start[0], len(line[: start[1]].encode(encoding)))
                self.string_tokens[start] = (str_eval(token), next_token)
                is_parenthesized = self._is_initial_string_token(
                    i, tokens
                ) and self._is_parenthesized(i, tokens)
                self._parenthesized_string_tokens[start] = is_parenthesized

        if self.linter.config.check_quote_consistency:
            self.check_for_consistent_string_delimiters(tokens)

    def _is_initial_string_token(
        self, index: int, tokens: Sequence[tokenize.TokenInfo]
    ) -> bool:
        # Must NOT be preceded by a string literal
        prev_token = self._find_prev_token(index, tokens)
        if prev_token and prev_token.type == tokenize.STRING:
            return False
        # Must be followed by a string literal token.
        next_token = self._find_next_token(index, tokens)
        return bool(next_token and next_token.type == tokenize.STRING)

    def _is_parenthesized(self, index: int, tokens: list[tokenize.TokenInfo]) -> bool:
        prev_token = self._find_prev_token(
            index, tokens, ignore=(*_PAREN_IGNORE_TOKEN_TYPES, tokenize.STRING)
        )
        if not prev_token or prev_token.type != tokenize.OP or prev_token[1] != "(":
            return False
        next_token = self._find_next_token(
            index, tokens, ignore=(*_PAREN_IGNORE_TOKEN_TYPES, tokenize.STRING)
        )
        return bool(
            next_token and next_token.type == tokenize.OP and next_token[1] == ")"
        )

    def _find_prev_token(
        self,
        index: int,
        tokens: Sequence[tokenize.TokenInfo],
        *,
        ignore: tuple[int, ...] = _PAREN_IGNORE_TOKEN_TYPES,
    ) -> tokenize.TokenInfo | None:
        i = index - 1
        while i >= 0 and tokens[i].type in ignore:
            i -= 1
        return tokens[i] if i >= 0 else None

    def _find_next_token(
        self,
        index: int,
        tokens: Sequence[tokenize.TokenInfo],
        *,
        ignore: tuple[int, ...] = _PAREN_IGNORE_TOKEN_TYPES,
    ) -> tokenize.TokenInfo | None:
        i = index + 1
        while i < len(tokens) and tokens[i].type in ignore:
            i += 1
        return tokens[i] if i < len(tokens) else None

    @only_required_for_messages("implicit-str-concat")
    def visit_call(self, node: nodes.Call) -> None:
        self.check_for_concatenated_strings(node.args, "call")

    @only_required_for_messages("implicit-str-concat")
    def visit_list(self, node: nodes.List) -> None:
        self.check_for_concatenated_strings(node.elts, "list")

    @only_required_for_messages("implicit-str-concat")
    def visit_set(self, node: nodes.Set) -> None:
        self.check_for_concatenated_strings(node.elts, "set")

    @only_required_for_messages("implicit-str-concat")
    def visit_tuple(self, node: nodes.Tuple) -> None:
        self.check_for_concatenated_strings(node.elts, "tuple")

    def visit_assign(self, node: nodes.Assign) -> None:
        if isinstance(node.value, nodes.Const) and isinstance(node.value.value, str):
            self.check_for_concatenated_strings([node.value], "assignment")

    def check_for_consistent_string_delimiters(
        self, tokens: Iterable[tokenize.TokenInfo]
    ) -> None:
        """Adds a message for each string using inconsistent quote delimiters.

        Quote delimiters are used inconsistently if " and ' are mixed in a module's
        shortstrings without having done so to avoid escaping an internal quote
        character.

        Args:
          tokens: The tokens to be checked against for consistent usage.
        """
        string_delimiters: Counter[str] = collections.Counter()

        # First, figure out which quote character predominates in the module
        for tok_type, token, _, _, _ in tokens:
            if tok_type == tokenize.STRING and _is_quote_delimiter_chosen_freely(token):
                string_delimiters[_get_quote_delimiter(token)] += 1

        if len(string_delimiters) > 1:
            # Ties are broken arbitrarily
            most_common_delimiter = string_delimiters.most_common(1)[0][0]
            for tok_type, token, start, _, _ in tokens:
                if tok_type != tokenize.STRING:
                    continue
                quote_delimiter = _get_quote_delimiter(token)
                if (
                    _is_quote_delimiter_chosen_freely(token)
                    and quote_delimiter != most_common_delimiter
                ):
                    self.add_message(
                        "inconsistent-quotes", line=start[0], args=(quote_delimiter,)
                    )

    def check_for_concatenated_strings(
        self, elements: Sequence[nodes.NodeNG], iterable_type: str
    ) -> None:
        for elt in elements:
            if not (
                isinstance(elt, nodes.Const) and elt.pytype() in _AST_NODE_STR_TYPES
            ):
                continue
            if elt.col_offset < 0:
                # This can happen in case of escaped newlines
                continue
            token_index = (elt.lineno, elt.col_offset)
            if token_index not in self.string_tokens:
                # This may happen with Latin1 encoding
                # cf. https://github.com/pylint-dev/pylint/issues/2610
                continue
            matching_token, next_token = self.string_tokens[token_index]
            # We detect string concatenation: the AST Const is the
            # combination of 2 string tokens
            if (
                matching_token != elt.value
                and next_token is not None
                and next_token.type == tokenize.STRING
            ):
                if next_token.start[0] == elt.lineno or (
                    self.linter.config.check_str_concat_over_line_jumps
                    # Allow implicitly concatenated strings in parens.
                    # See https://github.com/pylint-dev/pylint/issues/8552.
                    and not self._parenthesized_string_tokens.get(
                        (elt.lineno, elt.col_offset)
                    )
                ):
                    self.add_message(
                        "implicit-str-concat",
                        line=elt.lineno,
                        args=(iterable_type,),
                        confidence=HIGH,
                    )

    def process_string_token(self, token: str, start_row: int, start_col: int) -> None:
        quote_char = None
        for _index, char in enumerate(token):
            if char in "'\"":
                quote_char = char
                break
        if quote_char is None:
            return
        # pylint: disable=undefined-loop-variable
        prefix = token[:_index].lower()  # markers like u, b, r.
        after_prefix = token[_index:]
        # pylint: enable=undefined-loop-variable
        # Chop off quotes
        quote_length = (
            3 if after_prefix[:3] == after_prefix[-3:] == 3 * quote_char else 1
        )
        string_body = after_prefix[quote_length:-quote_length]
        # No special checks on raw strings at the moment.
        if "r" not in prefix:
            self.process_non_raw_string_token(
                prefix,
                string_body,
                start_row,
                start_col + len(prefix) + quote_length,
            )

    def process_non_raw_string_token(
        self, prefix: str, string_body: str, start_row: int, string_start_col: int
    ) -> None:
        """Check for bad escapes in a non-raw string.

        prefix: lowercase string of string prefix markers ('ur').
        string_body: the un-parsed body of the string, not including the quote
        marks.
        start_row: line number in the source.
        string_start_col: col number of the string start in the source.
        """
        # Walk through the string; if we see a backslash then escape the next
        # character, and skip over it.  If we see a non-escaped character,
        # alert, and continue.
        #
        # Accept a backslash when it escapes a backslash, or a quote, or
        # end-of-line, or one of the letters that introduce a special escape
        # sequence <https://docs.python.org/reference/lexical_analysis.html>
        #
        index = 0
        while True:
            index = string_body.find("\\", index)
            if index == -1:
                break
            # There must be a next character; having a backslash at the end
            # of the string would be a SyntaxError.
            next_char = string_body[index + 1]
            match = string_body[index : index + 2]
            # The column offset will vary depending on whether the string token
            # is broken across lines. Calculate relative to the nearest line
            # break or relative to the start of the token's line.
            last_newline = string_body.rfind("\n", 0, index)
            if last_newline == -1:
                line = start_row
                col_offset = index + string_start_col
            else:
                line = start_row + string_body.count("\n", 0, index)
                col_offset = index - last_newline - 1
            if next_char in self.UNICODE_ESCAPE_CHARACTERS:
                if "u" in prefix:
                    pass
                elif "b" not in prefix:
                    pass  # unicode by default
                else:
                    self.add_message(
                        "anomalous-unicode-escape-in-string",
                        line=line,
                        args=(match,),
                        col_offset=col_offset,
                    )
            elif next_char not in self.ESCAPE_CHARACTERS:
                self.add_message(
                    "anomalous-backslash-in-string",
                    line=line,
                    args=(match,),
                    col_offset=col_offset,
                )
            # Whether it was a valid escape or not, backslash followed by
            # another character can always be consumed whole: the second
            # character can never be the start of a new backslash escape.
            index += 2

    @only_required_for_messages("redundant-u-string-prefix")
    def visit_const(self, node: nodes.Const) -> None:
        if node.pytype() == "builtins.str" and not isinstance(
            node.parent, nodes.JoinedStr
        ):
            self._detect_u_string_prefix(node)

    def _detect_u_string_prefix(self, node: nodes.Const) -> None:
        """Check whether strings include a 'u' prefix like u'String'."""
        if node.kind == "u":
            self.add_message(
                "redundant-u-string-prefix",
                line=node.lineno,
                col_offset=node.col_offset,
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(StringFormatChecker(linter))
    linter.register_checker(StringConstantChecker(linter))


def str_eval(token: str) -> str:
    """Mostly replicate `ast.literal_eval(token)` manually to avoid any performance hit.

    This supports f-strings, contrary to `ast.literal_eval`.
    We have to support all string literal notations:
    https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
    """
    if token[0:2].lower() in {"fr", "rf"}:
        token = token[2:]
    elif token[0].lower() in {"r", "u", "f"}:
        token = token[1:]
    if token[0:3] in {'"""', "'''"}:
        return token[3:-3]
    return token[1:-1]


def _is_long_string(string_token: str) -> bool:
    """Is this string token a "longstring" (is it triple-quoted)?

    Long strings are triple-quoted as defined in
    https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals

    This function only checks characters up through the open quotes.  Because it's meant
    to be applied only to tokens that represent string literals, it doesn't bother to
    check for close-quotes (demonstrating that the literal is a well-formed string).

    Args:
        string_token: The string token to be parsed.

    Returns:
        A boolean representing whether this token matches a longstring
        regex.
    """
    return bool(
        SINGLE_QUOTED_REGEX.match(string_token)
        or DOUBLE_QUOTED_REGEX.match(string_token)
    )


def _get_quote_delimiter(string_token: str) -> str:
    """Returns the quote character used to delimit this token string.

    This function checks whether the token is a well-formed string.

    Args:
        string_token: The token to be parsed.

    Returns:
        A string containing solely the first quote delimiter character in the
        given string.

    Raises:
      ValueError: No quote delimiter characters are present.
    """
    match = QUOTE_DELIMITER_REGEX.match(string_token)
    if not match:
        raise ValueError(f"string token {string_token} is not a well-formed string")
    return match.group(2)


def _is_quote_delimiter_chosen_freely(string_token: str) -> bool:
    """Was there a non-awkward option for the quote delimiter?

    Args:
        string_token: The quoted string whose delimiters are to be checked.

    Returns:
        Whether there was a choice in this token's quote character that would
        not have involved backslash-escaping an interior quote character.  Long
        strings are excepted from this analysis under the assumption that their
        quote characters are set by policy.
    """
    quote_delimiter = _get_quote_delimiter(string_token)
    unchosen_delimiter = '"' if quote_delimiter == "'" else "'"
    return bool(
        quote_delimiter
        and not _is_long_string(string_token)
        and unchosen_delimiter not in str_eval(string_token)
    )

back/venvmsg/Lib/site-packages/pylint/checkers/strings.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import only_required_for_messages, safe_infer

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class ThreadingChecker(BaseChecker):
    """Checks for threading module.

    - useless with lock - locking used in wrong way that has no effect (with threading.Lock():)
    """

    name = "threading"

    LOCKS = frozenset(
        (
            "threading.Lock",
            "threading.RLock",
            "threading.Condition",
            "threading.Semaphore",
            "threading.BoundedSemaphore",
        )
    )

    msgs = {
        "W2101": (
            "'%s()' directly created in 'with' has no effect",
            "useless-with-lock",
            "Used when a new lock instance is created by using with statement "
            "which has no effect. Instead, an existing instance should be used to acquire lock.",
        ),
    }

    @only_required_for_messages("useless-with-lock")
    def visit_with(self, node: nodes.With) -> None:
        context_managers = (c for c, _ in node.items if isinstance(c, nodes.Call))
        for context_manager in context_managers:
            if isinstance(context_manager, nodes.Call):
                infered_function = safe_infer(context_manager.func)
                if infered_function is None:
                    continue
                qname = infered_function.qname()
                if qname in self.LOCKS:
                    self.add_message("useless-with-lock", node=node, args=qname)


def register(linter: PyLinter) -> None:
    linter.register_checker(ThreadingChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/threading_checker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Try to find more bugs in the code using astroid inference capabilities."""

from __future__ import annotations

import heapq
import itertools
import operator
import re
import shlex
import sys
from collections.abc import Callable, Iterable
from functools import cached_property, singledispatch
from re import Pattern
from typing import TYPE_CHECKING, Any, Literal, Union

import astroid
import astroid.exceptions
import astroid.helpers
from astroid import arguments, bases, nodes, util
from astroid.typing import InferenceResult, SuccessfulInferenceResult

from pylint.checkers import BaseChecker, utils
from pylint.checkers.utils import (
    decorated_with,
    decorated_with_property,
    has_known_bases,
    is_builtin_object,
    is_comprehension,
    is_hashable,
    is_inside_abstract_class,
    is_iterable,
    is_mapping,
    is_module_ignored,
    is_node_in_type_annotation_context,
    is_none,
    is_overload_stub,
    is_postponed_evaluation_enabled,
    is_super,
    node_ignores_exception,
    only_required_for_messages,
    safe_infer,
    supports_delitem,
    supports_getitem,
    supports_membership_test,
    supports_setitem,
)
from pylint.constants import PY310_PLUS
from pylint.interfaces import HIGH, INFERENCE
from pylint.typing import MessageDefinitionTuple

if TYPE_CHECKING:
    from pylint.lint import PyLinter

CallableObjects = Union[
    bases.BoundMethod,
    bases.UnboundMethod,
    nodes.FunctionDef,
    nodes.Lambda,
    nodes.ClassDef,
]

STR_FORMAT = {"builtins.str.format"}
ASYNCIO_COROUTINE = "asyncio.coroutines.coroutine"
BUILTIN_TUPLE = "builtins.tuple"
TYPE_ANNOTATION_NODES_TYPES = (
    nodes.AnnAssign,
    nodes.Arguments,
    nodes.FunctionDef,
)
BUILTINS_IMPLICIT_RETURN_NONE = {
    "builtins.dict": {"clear", "update"},
    "builtins.list": {
        "append",
        "clear",
        "extend",
        "insert",
        "remove",
        "reverse",
        "sort",
    },
    "builtins.set": {
        "add",
        "clear",
        "difference_update",
        "discard",
        "intersection_update",
        "remove",
        "symmetric_difference_update",
        "update",
    },
}


class VERSION_COMPATIBLE_OVERLOAD:
    pass


VERSION_COMPATIBLE_OVERLOAD_SENTINEL = VERSION_COMPATIBLE_OVERLOAD()


def _is_owner_ignored(
    owner: SuccessfulInferenceResult,
    attrname: str | None,
    ignored_classes: Iterable[str],
    ignored_modules: Iterable[str],
) -> bool:
    """Check if the given owner should be ignored.

    This will verify if the owner's module is in *ignored_modules*
    or the owner's module fully qualified name is in *ignored_modules*
    or if the *ignored_modules* contains a pattern which catches
    the fully qualified name of the module.

    Also, similar checks are done for the owner itself, if its name
    matches any name from the *ignored_classes* or if its qualified
    name can be found in *ignored_classes*.
    """
    if is_module_ignored(owner.root().qname(), ignored_modules):
        return True

    # Match against ignored classes.
    ignored_classes = set(ignored_classes)
    qname = owner.qname() if hasattr(owner, "qname") else ""
    return any(ignore in (attrname, qname) for ignore in ignored_classes)


@singledispatch
def _node_names(node: SuccessfulInferenceResult) -> Iterable[str]:
    if not hasattr(node, "locals"):
        return []
    return node.locals.keys()  # type: ignore[no-any-return]


@_node_names.register(nodes.ClassDef)
@_node_names.register(astroid.Instance)
def _(node: nodes.ClassDef | bases.Instance) -> Iterable[str]:
    values = itertools.chain(node.instance_attrs.keys(), node.locals.keys())

    try:
        mro = node.mro()[1:]
    except (NotImplementedError, TypeError, astroid.MroError):
        mro = node.ancestors()

    other_values = [value for cls in mro for value in _node_names(cls)]
    return itertools.chain(values, other_values)


def _string_distance(seq1: str, seq2: str) -> int:
    seq2_length = len(seq2)

    row = [*list(range(1, seq2_length + 1)), 0]
    for seq1_index, seq1_char in enumerate(seq1):
        last_row = row
        row = [0] * seq2_length + [seq1_index + 1]

        for seq2_index, seq2_char in enumerate(seq2):
            row[seq2_index] = min(
                last_row[seq2_index] + 1,
                row[seq2_index - 1] + 1,
                last_row[seq2_index - 1] + (seq1_char != seq2_char),
            )

    return row[seq2_length - 1]


def _similar_names(
    owner: SuccessfulInferenceResult,
    attrname: str | None,
    distance_threshold: int,
    max_choices: int,
) -> list[str]:
    """Given an owner and a name, try to find similar names.

    The similar names are searched given a distance metric and only
    a given number of choices will be returned.
    """
    possible_names: list[tuple[str, int]] = []
    names = _node_names(owner)

    for name in names:
        if name == attrname:
            continue

        distance = _string_distance(attrname or "", name)
        if distance <= distance_threshold:
            possible_names.append((name, distance))

    # Now get back the values with a minimum, up to the given
    # limit or choices.
    picked = [
        name
        for (name, _) in heapq.nsmallest(
            max_choices, possible_names, key=operator.itemgetter(1)
        )
    ]
    return sorted(picked)


def _missing_member_hint(
    owner: SuccessfulInferenceResult,
    attrname: str | None,
    distance_threshold: int,
    max_choices: int,
) -> str:
    names = _similar_names(owner, attrname, distance_threshold, max_choices)
    if not names:
        # No similar name.
        return ""

    names = [repr(name) for name in names]
    if len(names) == 1:
        names_hint = ", ".join(names)
    else:
        names_hint = f"one of {', '.join(names[:-1])} or {names[-1]}"

    return f"; maybe {names_hint}?"


MSGS: dict[str, MessageDefinitionTuple] = {
    "E1101": (
        "%s %r has no %r member%s",
        "no-member",
        "Used when a variable is accessed for a nonexistent member.",
        {"old_names": [("E1103", "maybe-no-member")]},
    ),
    "I1101": (
        "%s %r has no %r member%s, but source is unavailable. Consider "
        "adding this module to extension-pkg-allow-list if you want "
        "to perform analysis based on run-time introspection of living objects.",
        "c-extension-no-member",
        "Used when a variable is accessed for non-existent member of C "
        "extension. Due to unavailability of source static analysis is impossible, "
        "but it may be performed by introspecting living objects in run-time.",
    ),
    "E1102": (
        "%s is not callable",
        "not-callable",
        "Used when an object being called has been inferred to a non "
        "callable object.",
    ),
    "E1111": (
        "Assigning result of a function call, where the function has no return",
        "assignment-from-no-return",
        "Used when an assignment is done on a function call but the "
        "inferred function doesn't return anything.",
    ),
    "E1120": (
        "No value for argument %s in %s call",
        "no-value-for-parameter",
        "Used when a function call passes too few arguments.",
    ),
    "E1121": (
        "Too many positional arguments for %s call",
        "too-many-function-args",
        "Used when a function call passes too many positional arguments.",
    ),
    "E1123": (
        "Unexpected keyword argument %r in %s call",
        "unexpected-keyword-arg",
        "Used when a function call passes a keyword argument that "
        "doesn't correspond to one of the function's parameter names.",
    ),
    "E1124": (
        "Argument %r passed by position and keyword in %s call",
        "redundant-keyword-arg",
        "Used when a function call would result in assigning multiple "
        "values to a function parameter, one value from a positional "
        "argument and one from a keyword argument.",
    ),
    "E1125": (
        "Missing mandatory keyword argument %r in %s call",
        "missing-kwoa",
        (
            "Used when a function call does not pass a mandatory"
            " keyword-only argument."
        ),
    ),
    "E1126": (
        "Sequence index is not an int, slice, or instance with __index__",
        "invalid-sequence-index",
        "Used when a sequence type is indexed with an invalid type. "
        "Valid types are ints, slices, and objects with an __index__ "
        "method.",
    ),
    "E1127": (
        "Slice index is not an int, None, or instance with __index__",
        "invalid-slice-index",
        "Used when a slice index is not an integer, None, or an object "
        "with an __index__ method.",
    ),
    "E1128": (
        "Assigning result of a function call, where the function returns None",
        "assignment-from-none",
        "Used when an assignment is done on a function call but the "
        "inferred function returns nothing but None.",
        {"old_names": [("W1111", "old-assignment-from-none")]},
    ),
    "E1129": (
        "Context manager '%s' doesn't implement __enter__ and __exit__.",
        "not-context-manager",
        "Used when an instance in a with statement doesn't implement "
        "the context manager protocol(__enter__/__exit__).",
    ),
    "E1130": (
        "%s",
        "invalid-unary-operand-type",
        "Emitted when a unary operand is used on an object which does not "
        "support this type of operation.",
    ),
    "E1131": (
        "%s",
        "unsupported-binary-operation",
        "Emitted when a binary arithmetic operation between two "
        "operands is not supported.",
    ),
    "E1132": (
        "Got multiple values for keyword argument %r in function call",
        "repeated-keyword",
        "Emitted when a function call got multiple values for a keyword.",
    ),
    "E1135": (
        "Value '%s' doesn't support membership test",
        "unsupported-membership-test",
        "Emitted when an instance in membership test expression doesn't "
        "implement membership protocol (__contains__/__iter__/__getitem__).",
    ),
    "E1136": (
        "Value '%s' is unsubscriptable",
        "unsubscriptable-object",
        "Emitted when a subscripted value doesn't support subscription "
        "(i.e. doesn't define __getitem__ method or __class_getitem__ for a class).",
    ),
    "E1137": (
        "%r does not support item assignment",
        "unsupported-assignment-operation",
        "Emitted when an object does not support item assignment "
        "(i.e. doesn't define __setitem__ method).",
    ),
    "E1138": (
        "%r does not support item deletion",
        "unsupported-delete-operation",
        "Emitted when an object does not support item deletion "
        "(i.e. doesn't define __delitem__ method).",
    ),
    "E1139": (
        "Invalid metaclass %r used",
        "invalid-metaclass",
        "Emitted whenever we can detect that a class is using, "
        "as a metaclass, something which might be invalid for using as "
        "a metaclass.",
    ),
    "E1141": (
        "Unpacking a dictionary in iteration without calling .items()",
        "dict-iter-missing-items",
        "Emitted when trying to iterate through a dict without calling .items()",
    ),
    "E1142": (
        "'await' should be used within an async function",
        "await-outside-async",
        "Emitted when await is used outside an async function.",
    ),
    "E1143": (
        "'%s' is unhashable and can't be used as a %s in a %s",
        "unhashable-member",
        "Emitted when a dict key or set member is not hashable "
        "(i.e. doesn't define __hash__ method).",
        {"old_names": [("E1140", "unhashable-dict-key")]},
    ),
    "E1144": (
        "Slice step cannot be 0",
        "invalid-slice-step",
        "Used when a slice step is 0 and the object doesn't implement "
        "a custom __getitem__ method.",
    ),
    "W1113": (
        "Keyword argument before variable positional arguments list "
        "in the definition of %s function",
        "keyword-arg-before-vararg",
        "When defining a keyword argument before variable positional arguments, one can "
        "end up in having multiple values passed for the aforementioned parameter in "
        "case the method is called with keyword arguments.",
    ),
    "W1114": (
        "Positional arguments appear to be out of order",
        "arguments-out-of-order",
        "Emitted  when the caller's argument names fully match the parameter "
        "names in the function signature but do not have the same order.",
    ),
    "W1115": (
        "Non-string value assigned to __name__",
        "non-str-assignment-to-dunder-name",
        "Emitted when a non-string value is assigned to __name__",
    ),
    "W1116": (
        "Second argument of isinstance is not a type",
        "isinstance-second-argument-not-valid-type",
        "Emitted when the second argument of an isinstance call is not a type.",
    ),
    "W1117": (
        "%r will be included in %r since a positional-only parameter with this name already exists",
        "kwarg-superseded-by-positional-arg",
        "Emitted when a function is called with a keyword argument that has the "
        "same name as a positional-only parameter and the function contains a "
        "keyword variadic parameter dict.",
    ),
}

# builtin sequence types in Python 2 and 3.
SEQUENCE_TYPES = {
    "str",
    "unicode",
    "list",
    "tuple",
    "bytearray",
    "xrange",
    "range",
    "bytes",
    "memoryview",
}


def _emit_no_member(
    node: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr,
    owner: InferenceResult,
    owner_name: str | None,
    mixin_class_rgx: Pattern[str],
    ignored_mixins: bool = True,
    ignored_none: bool = True,
) -> bool:
    """Try to see if no-member should be emitted for the given owner.

    The following cases are ignored:

        * the owner is a function and it has decorators.
        * the owner is an instance and it has __getattr__, __getattribute__ implemented
        * the module is explicitly ignored from no-member checks
        * the owner is a class and the name can be found in its metaclass.
        * The access node is protected by an except handler, which handles
          AttributeError, Exception or bare except.
        * The node is guarded behind and `IF` or `IFExp` node
    """
    # pylint: disable = too-many-return-statements, too-many-branches
    if node_ignores_exception(node, AttributeError):
        return False
    if ignored_none and isinstance(owner, nodes.Const) and owner.value is None:
        return False
    if is_super(owner) or getattr(owner, "type", None) == "metaclass":
        return False
    if owner_name and ignored_mixins and mixin_class_rgx.match(owner_name):
        return False
    if isinstance(owner, nodes.FunctionDef) and (
        owner.decorators or owner.is_abstract()
    ):
        return False
    if isinstance(owner, (astroid.Instance, nodes.ClassDef)):
        # Issue #2565: Don't ignore enums, as they have a `__getattr__` but it's not
        # invoked at this point.
        try:
            metaclass = owner.metaclass()
        except astroid.MroError:
            pass
        else:
            # Renamed in Python 3.10 to `EnumType`
            if metaclass and metaclass.qname() in {"enum.EnumMeta", "enum.EnumType"}:
                return not _enum_has_attribute(owner, node)
        if owner.has_dynamic_getattr():
            return False
        if not has_known_bases(owner):
            return False

        # Exclude typed annotations, since these might actually exist
        # at some point during the runtime of the program.
        if utils.is_attribute_typed_annotation(owner, node.attrname):
            return False
    if isinstance(owner, astroid.objects.Super):
        # Verify if we are dealing with an invalid Super object.
        # If it is invalid, then there's no point in checking that
        # it has the required attribute. Also, don't fail if the
        # MRO is invalid.
        try:
            owner.super_mro()
        except (astroid.MroError, astroid.SuperError):
            return False
        if not all(has_known_bases(base) for base in owner.type.mro()):
            return False
    if isinstance(owner, nodes.Module):
        try:
            owner.getattr("__getattr__")
            return False
        except astroid.NotFoundError:
            pass
    if owner_name and node.attrname.startswith("_" + owner_name):
        # Test if an attribute has been mangled ('private' attribute)
        unmangled_name = node.attrname.split("_" + owner_name)[-1]
        try:
            if owner.getattr(unmangled_name, context=None) is not None:
                return False
        except astroid.NotFoundError:
            return True

    # Don't emit no-member if guarded behind `IF` or `IFExp`
    #   * Walk up recursively until if statement is found.
    #   * Check if condition can be inferred as `Const`,
    #       would evaluate as `False`,
    #       and whether the node is part of the `body`.
    #   * Continue checking until scope of node is reached.
    scope: nodes.NodeNG = node.scope()
    node_origin: nodes.NodeNG = node
    parent: nodes.NodeNG = node.parent
    while parent != scope:
        if isinstance(parent, (nodes.If, nodes.IfExp)):
            inferred = safe_infer(parent.test)
            if (  # pylint: disable=too-many-boolean-expressions
                isinstance(inferred, nodes.Const)
                and inferred.bool_value() is False
                and (
                    isinstance(parent, nodes.If)
                    and node_origin in parent.body
                    or isinstance(parent, nodes.IfExp)
                    and node_origin == parent.body
                )
            ):
                return False
        node_origin, parent = parent, parent.parent

    return True


def _get_all_attribute_assignments(
    node: nodes.FunctionDef, name: str | None = None
) -> set[str]:
    attributes: set[str] = set()
    for child in node.nodes_of_class((nodes.Assign, nodes.AnnAssign)):
        targets = []
        if isinstance(child, nodes.Assign):
            targets = child.targets
        elif isinstance(child, nodes.AnnAssign):
            targets = [child.target]
        for assign_target in targets:
            if isinstance(assign_target, nodes.Tuple):
                targets.extend(assign_target.elts)
                continue
            if (
                isinstance(assign_target, nodes.AssignAttr)
                and isinstance(assign_target.expr, nodes.Name)
                and (name is None or assign_target.expr.name == name)
            ):
                attributes.add(assign_target.attrname)
    return attributes


def _enum_has_attribute(
    owner: astroid.Instance | nodes.ClassDef, node: nodes.Attribute
) -> bool:
    if isinstance(owner, astroid.Instance):
        enum_def = next(
            (b.parent for b in owner.bases if isinstance(b.parent, nodes.ClassDef)),
            None,
        )

        if enum_def is None:
            # We don't inherit from anything, so try to find the parent
            # class definition and roll with that
            enum_def = node
            while enum_def is not None and not isinstance(enum_def, nodes.ClassDef):
                enum_def = enum_def.parent

        # If this blows, something is clearly wrong
        assert enum_def is not None, "enum_def unexpectedly None"
    else:
        enum_def = owner

    # Find __new__ and __init__
    dunder_new = next((m for m in enum_def.methods() if m.name == "__new__"), None)
    dunder_init = next((m for m in enum_def.methods() if m.name == "__init__"), None)

    enum_attributes: set[str] = set()

    # Find attributes defined in __new__
    if dunder_new:
        # Get the object returned in __new__
        returned_obj_name = next(
            (c.value for c in dunder_new.get_children() if isinstance(c, nodes.Return)),
            None,
        )
        if isinstance(returned_obj_name, nodes.Name):
            # Find all attribute assignments to the returned object
            enum_attributes |= _get_all_attribute_assignments(
                dunder_new, returned_obj_name.name
            )

    # Find attributes defined in __init__
    if dunder_init and dunder_init.body and dunder_init.args:
        # Grab the name referring to `self` from the function def
        enum_attributes |= _get_all_attribute_assignments(
            dunder_init, dunder_init.args.arguments[0].name
        )

    return node.attrname in enum_attributes


def _determine_callable(
    callable_obj: nodes.NodeNG,
) -> tuple[CallableObjects, int, str]:
    # TODO: The typing of the second return variable is actually Literal[0,1]
    # We need typing on astroid.NodeNG.implicit_parameters for this
    # TODO: The typing of the third return variable can be narrowed to a Literal
    # We need typing on astroid.NodeNG.type for this

    # Ordering is important, since BoundMethod is a subclass of UnboundMethod,
    # and Function inherits Lambda.
    parameters = 0
    if hasattr(callable_obj, "implicit_parameters"):
        parameters = callable_obj.implicit_parameters()
    if isinstance(callable_obj, bases.BoundMethod):
        # Bound methods have an extra implicit 'self' argument.
        return callable_obj, parameters, callable_obj.type
    if isinstance(callable_obj, bases.UnboundMethod):
        return callable_obj, parameters, "unbound method"
    if isinstance(callable_obj, nodes.FunctionDef):
        return callable_obj, parameters, callable_obj.type
    if isinstance(callable_obj, nodes.Lambda):
        return callable_obj, parameters, "lambda"
    if isinstance(callable_obj, nodes.ClassDef):
        # Class instantiation, lookup __new__ instead.
        # If we only find object.__new__, we can safely check __init__
        # instead. If __new__ belongs to builtins, then we look
        # again for __init__ in the locals, since we won't have
        # argument information for the builtin __new__ function.
        try:
            # Use the last definition of __new__.
            new = callable_obj.local_attr("__new__")[-1]
        except astroid.NotFoundError:
            new = None

        from_object = new and new.parent.scope().name == "object"
        from_builtins = new and new.root().name in sys.builtin_module_names

        if not new or from_object or from_builtins:
            try:
                # Use the last definition of __init__.
                callable_obj = callable_obj.local_attr("__init__")[-1]
            except astroid.NotFoundError as e:
                raise ValueError from e
        else:
            callable_obj = new

        if not isinstance(callable_obj, nodes.FunctionDef):
            raise ValueError
        # both have an extra implicit 'cls'/'self' argument.
        return callable_obj, parameters, "constructor"

    raise ValueError


def _has_parent_of_type(
    node: nodes.Call,
    node_type: nodes.Keyword | nodes.Starred,
    statement: nodes.Statement,
) -> bool:
    """Check if the given node has a parent of the given type."""
    parent = node.parent
    while not isinstance(parent, node_type) and statement.parent_of(parent):
        parent = parent.parent
    return isinstance(parent, node_type)


def _no_context_variadic_keywords(node: nodes.Call, scope: nodes.Lambda) -> bool:
    statement = node.statement()
    variadics = []

    if (
        isinstance(scope, nodes.Lambda)
        and not isinstance(scope, nodes.FunctionDef)
        or isinstance(statement, nodes.With)
    ):
        variadics = list(node.keywords or []) + node.kwargs
    elif isinstance(statement, (nodes.Return, nodes.Expr, nodes.Assign)) and isinstance(
        statement.value, nodes.Call
    ):
        call = statement.value
        variadics = list(call.keywords or []) + call.kwargs

    return _no_context_variadic(node, scope.args.kwarg, nodes.Keyword, variadics)


def _no_context_variadic_positional(node: nodes.Call, scope: nodes.Lambda) -> bool:
    variadics = node.starargs + node.kwargs
    return _no_context_variadic(node, scope.args.vararg, nodes.Starred, variadics)


def _no_context_variadic(
    node: nodes.Call,
    variadic_name: str | None,
    variadic_type: nodes.Keyword | nodes.Starred,
    variadics: list[nodes.Keyword | nodes.Starred],
) -> bool:
    """Verify if the given call node has variadic nodes without context.

    This is a workaround for handling cases of nested call functions
    which don't have the specific call context at hand.
    Variadic arguments (variable positional arguments and variable
    keyword arguments) are inferred, inherently wrong, by astroid
    as a Tuple, respectively a Dict with empty elements.
    This can lead pylint to believe that a function call receives
    too few arguments.
    """
    scope = node.scope()
    is_in_lambda_scope = not isinstance(scope, nodes.FunctionDef) and isinstance(
        scope, nodes.Lambda
    )
    statement = node.statement()
    for name in statement.nodes_of_class(nodes.Name):
        if name.name != variadic_name:
            continue

        inferred = safe_infer(name)
        if isinstance(inferred, (nodes.List, nodes.Tuple)):
            length = len(inferred.elts)
        elif isinstance(inferred, nodes.Dict):
            length = len(inferred.items)
        else:
            continue

        if is_in_lambda_scope and isinstance(inferred.parent, nodes.Arguments):
            # The statement of the variadic will be the assignment itself,
            # so we need to go the lambda instead
            inferred_statement = inferred.parent.parent
        else:
            inferred_statement = inferred.statement()

        if not length and isinstance(
            inferred_statement, (nodes.Lambda, nodes.FunctionDef)
        ):
            is_in_starred_context = _has_parent_of_type(node, variadic_type, statement)
            used_as_starred_argument = any(
                variadic.value == name or variadic.value.parent_of(name)
                for variadic in variadics
            )
            if is_in_starred_context or used_as_starred_argument:
                return True
    return False


def _is_invalid_metaclass(metaclass: nodes.ClassDef) -> bool:
    try:
        mro = metaclass.mro()
    except (astroid.DuplicateBasesError, astroid.InconsistentMroError):
        return True
    return not any(is_builtin_object(cls) and cls.name == "type" for cls in mro)


def _infer_from_metaclass_constructor(
    cls: nodes.ClassDef, func: nodes.FunctionDef
) -> InferenceResult | None:
    """Try to infer what the given *func* constructor is building.

    :param astroid.FunctionDef func:
        A metaclass constructor. Metaclass definitions can be
        functions, which should accept three arguments, the name of
        the class, the bases of the class and the attributes.
        The function could return anything, but usually it should
        be a proper metaclass.
    :param astroid.ClassDef cls:
        The class for which the *func* parameter should generate
        a metaclass.
    :returns:
        The class generated by the function or None,
        if we couldn't infer it.
    :rtype: astroid.ClassDef
    """
    context = astroid.context.InferenceContext()

    class_bases = nodes.List()
    class_bases.postinit(elts=cls.bases)

    attrs = nodes.Dict(
        lineno=0, col_offset=0, parent=None, end_lineno=0, end_col_offset=0
    )
    local_names = [(name, values[-1]) for name, values in cls.locals.items()]
    attrs.postinit(local_names)

    builder_args = nodes.Tuple()
    builder_args.postinit([cls.name, class_bases, attrs])

    context.callcontext = astroid.context.CallContext(builder_args)
    try:
        inferred = next(func.infer_call_result(func, context), None)
    except astroid.InferenceError:
        return None
    return inferred or None


def _is_c_extension(module_node: InferenceResult) -> bool:
    return (
        isinstance(module_node, nodes.Module)
        and not astroid.modutils.is_stdlib_module(module_node.name)
        and not module_node.fully_defined()
    )


def _is_invalid_isinstance_type(arg: nodes.NodeNG) -> bool:
    # Return True if we are sure that arg is not a type
    if PY310_PLUS and isinstance(arg, nodes.BinOp) and arg.op == "|":
        return any(
            _is_invalid_isinstance_type(elt) and not is_none(elt)
            for elt in (arg.left, arg.right)
        )
    inferred = utils.safe_infer(arg)
    if not inferred:
        # Cannot infer it so skip it.
        return False
    if isinstance(inferred, nodes.Tuple):
        return any(_is_invalid_isinstance_type(elt) for elt in inferred.elts)
    if isinstance(inferred, nodes.ClassDef):
        return False
    if isinstance(inferred, astroid.Instance) and inferred.qname() == BUILTIN_TUPLE:
        return False
    if PY310_PLUS and isinstance(inferred, bases.UnionType):
        return any(
            _is_invalid_isinstance_type(elt) and not is_none(elt)
            for elt in (inferred.left, inferred.right)
        )
    return True


class TypeChecker(BaseChecker):
    """Try to find bugs in the code using type inference."""

    # configuration section name
    name = "typecheck"
    # messages
    msgs = MSGS
    # configuration options
    options = (
        (
            "ignore-on-opaque-inference",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "This flag controls whether pylint should warn about "
                "no-member and similar checks whenever an opaque object "
                "is returned when inferring. The inference can return "
                "multiple potential results while evaluating a Python object, "
                "but some branches might not be evaluated, which results in "
                "partial inference. In that case, it might be useful to still emit "
                "no-member and other checks for the rest of the inferred objects.",
            },
        ),
        (
            "mixin-class-rgx",
            {
                "default": ".*[Mm]ixin",
                "type": "regexp",
                "metavar": "<regexp>",
                "help": "Regex pattern to define which classes are considered mixins.",
            },
        ),
        (
            "ignore-mixin-members",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Tells whether missing members accessed in mixin "
                "class should be ignored. A class is considered mixin if its name matches "
                "the mixin-class-rgx option.",
                "kwargs": {"new_names": ["ignore-checks-for-mixin"]},
            },
        ),
        (
            "ignored-checks-for-mixins",
            {
                "default": [
                    "no-member",
                    "not-async-context-manager",
                    "not-context-manager",
                    "attribute-defined-outside-init",
                ],
                "type": "csv",
                "metavar": "<list of messages names>",
                "help": "List of symbolic message names to ignore for Mixin members.",
            },
        ),
        (
            "ignore-none",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Tells whether to warn about missing members when the owner "
                "of the attribute is inferred to be None.",
            },
        ),
        # the defaults here are *stdlib* names that (almost) always
        # lead to false positives, since their idiomatic use is
        # 'too dynamic' for pylint to grok.
        (
            "ignored-classes",
            {
                "default": (
                    "optparse.Values",
                    "thread._local",
                    "_thread._local",
                    "argparse.Namespace",
                ),
                "type": "csv",
                "metavar": "<members names>",
                "help": "List of class names for which member attributes "
                "should not be checked (useful for classes with "
                "dynamically set attributes). This supports "
                "the use of qualified names.",
            },
        ),
        (
            "generated-members",
            {
                "default": (),
                "type": "string",
                "metavar": "<members names>",
                "help": "List of members which are set dynamically and \
missed by pylint inference system, and so shouldn't trigger E1101 when \
accessed. Python regular expressions are accepted.",
            },
        ),
        (
            "contextmanager-decorators",
            {
                "default": ["contextlib.contextmanager"],
                "type": "csv",
                "metavar": "<decorator names>",
                "help": "List of decorators that produce context managers, "
                "such as contextlib.contextmanager. Add to this list "
                "to register other decorators that produce valid "
                "context managers.",
            },
        ),
        (
            "missing-member-hint-distance",
            {
                "default": 1,
                "type": "int",
                "metavar": "<member hint edit distance>",
                "help": "The minimum edit distance a name should have in order "
                "to be considered a similar match for a missing member name.",
            },
        ),
        (
            "missing-member-max-choices",
            {
                "default": 1,
                "type": "int",
                "metavar": "<member hint max choices>",
                "help": "The total number of similar names that should be taken in "
                "consideration when showing a hint for a missing member.",
            },
        ),
        (
            "missing-member-hint",
            {
                "default": True,
                "type": "yn",
                "metavar": "<missing member hint>",
                "help": "Show a hint with possible names when a member name was not "
                "found. The aspect of finding the hint is based on edit distance.",
            },
        ),
        (
            "signature-mutators",
            {
                "default": [],
                "type": "csv",
                "metavar": "<decorator names>",
                "help": "List of decorators that change the signature of "
                "a decorated function.",
            },
        ),
    )

    def open(self) -> None:
        py_version = self.linter.config.py_version
        self._py310_plus = py_version >= (3, 10)
        self._mixin_class_rgx = self.linter.config.mixin_class_rgx

    @cached_property
    def _suggestion_mode(self) -> bool:
        return self.linter.config.suggestion_mode  # type: ignore[no-any-return]

    @cached_property
    def _compiled_generated_members(self) -> tuple[Pattern[str], ...]:
        # do this lazily since config not fully initialized in __init__
        # generated_members may contain regular expressions
        # (surrounded by quote `"` and followed by a comma `,`)
        # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' =>
        # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}')
        generated_members = self.linter.config.generated_members
        if isinstance(generated_members, str):
            gen = shlex.shlex(generated_members)
            gen.whitespace += ","
            gen.wordchars += r"[]-+\.*?()|"
            generated_members = tuple(tok.strip('"') for tok in gen)
        return tuple(re.compile(exp) for exp in generated_members)

    @only_required_for_messages("keyword-arg-before-vararg")
    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        # check for keyword arg before varargs.

        if node.args.vararg and node.args.defaults:
            # When `positional-only` parameters are present then only
            # `positional-or-keyword` parameters are checked. I.e:
            # >>> def name(pos_only_params, /, pos_or_keyword_params, *args): ...
            if node.args.posonlyargs and not node.args.args:
                return
            self.add_message("keyword-arg-before-vararg", node=node, args=(node.name))

    visit_asyncfunctiondef = visit_functiondef

    @only_required_for_messages("invalid-metaclass")
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        def _metaclass_name(metaclass: InferenceResult) -> str | None:
            # pylint: disable=unidiomatic-typecheck
            if isinstance(metaclass, (nodes.ClassDef, nodes.FunctionDef)):
                return metaclass.name  # type: ignore[no-any-return]
            if type(metaclass) is bases.Instance:
                # Really do mean type, not isinstance, since subclasses of bases.Instance
                # like Const or Dict should use metaclass.as_string below.
                return str(metaclass)
            return metaclass.as_string()  # type: ignore[no-any-return]

        metaclass = node.declared_metaclass()
        if not metaclass:
            return

        if isinstance(metaclass, nodes.FunctionDef):
            # Try to infer the result.
            metaclass = _infer_from_metaclass_constructor(node, metaclass)
            if not metaclass:
                # Don't do anything if we cannot infer the result.
                return

        if isinstance(metaclass, nodes.ClassDef):
            if _is_invalid_metaclass(metaclass):
                self.add_message(
                    "invalid-metaclass", node=node, args=(_metaclass_name(metaclass),)
                )
        else:
            self.add_message(
                "invalid-metaclass", node=node, args=(_metaclass_name(metaclass),)
            )

    def visit_assignattr(self, node: nodes.AssignAttr) -> None:
        if isinstance(node.assign_type(), nodes.AugAssign):
            self.visit_attribute(node)

    def visit_delattr(self, node: nodes.DelAttr) -> None:
        self.visit_attribute(node)

    # pylint: disable = too-many-branches
    @only_required_for_messages("no-member", "c-extension-no-member")
    def visit_attribute(
        self, node: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr
    ) -> None:
        """Check that the accessed attribute exists.

        to avoid too much false positives for now, we'll consider the code as
        correct if a single of the inferred nodes has the accessed attribute.

        function/method, super call and metaclasses are ignored
        """
        if any(
            pattern.match(name)
            for name in (node.attrname, node.as_string())
            for pattern in self._compiled_generated_members
        ):
            return

        if is_postponed_evaluation_enabled(node) and is_node_in_type_annotation_context(
            node
        ):
            return

        try:
            inferred = list(node.expr.infer())
        except astroid.InferenceError:
            return

        # list of (node, nodename) which are missing the attribute
        missingattr: set[tuple[SuccessfulInferenceResult, str | None]] = set()

        non_opaque_inference_results: list[SuccessfulInferenceResult] = [
            owner
            for owner in inferred
            if not isinstance(owner, (nodes.Unknown, util.UninferableBase))
        ]
        if (
            len(non_opaque_inference_results) != len(inferred)
            and self.linter.config.ignore_on_opaque_inference
        ):
            # There is an ambiguity in the inference. Since we can't
            # make sure that we won't emit a false positive, we just stop
            # whenever the inference returns an opaque inference object.
            return
        for owner in non_opaque_inference_results:
            name = getattr(owner, "name", None)
            if _is_owner_ignored(
                owner,
                name,
                self.linter.config.ignored_classes,
                self.linter.config.ignored_modules,
            ):
                continue

            qualname = f"{owner.pytype()}.{node.attrname}"
            if any(
                pattern.match(qualname) for pattern in self._compiled_generated_members
            ):
                return

            try:
                attr_nodes = owner.getattr(node.attrname)
            except AttributeError:
                continue
            except astroid.DuplicateBasesError:
                continue
            except astroid.NotFoundError:
                # This can't be moved before the actual .getattr call,
                # because there can be more values inferred and we are
                # stopping after the first one which has the attribute in question.
                # The problem is that if the first one has the attribute,
                # but we continue to the next values which doesn't have the
                # attribute, then we'll have a false positive.
                # So call this only after the call has been made.
                if not _emit_no_member(
                    node,
                    owner,
                    name,
                    self._mixin_class_rgx,
                    ignored_mixins=(
                        "no-member" in self.linter.config.ignored_checks_for_mixins
                    ),
                    ignored_none=self.linter.config.ignore_none,
                ):
                    continue
                missingattr.add((owner, name))
                continue
            else:
                for attr_node in attr_nodes:
                    attr_parent = attr_node.parent
                    # Skip augmented assignments
                    try:
                        if isinstance(attr_node.statement(), nodes.AugAssign) or (
                            isinstance(attr_parent, nodes.Assign)
                            and utils.is_augmented_assign(attr_parent)[0]
                        ):
                            continue
                    except astroid.exceptions.StatementMissing:
                        break
                    # Skip self-referencing assignments
                    if attr_parent is node.parent:
                        continue
                    break
                else:
                    missingattr.add((owner, name))
                    continue
            # stop on the first found
            break
        else:
            # we have not found any node with the attributes, display the
            # message for inferred nodes
            done = set()
            for owner, name in missingattr:
                if isinstance(owner, astroid.Instance):
                    actual = owner._proxied
                else:
                    actual = owner
                if actual in done:
                    continue
                done.add(actual)

                msg, hint = self._get_nomember_msgid_hint(node, owner)
                self.add_message(
                    msg,
                    node=node,
                    args=(owner.display_type(), name, node.attrname, hint),
                    confidence=INFERENCE,
                )

    def _get_nomember_msgid_hint(
        self,
        node: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr,
        owner: SuccessfulInferenceResult,
    ) -> tuple[Literal["c-extension-no-member", "no-member"], str]:
        suggestions_are_possible = self._suggestion_mode and isinstance(
            owner, nodes.Module
        )
        if suggestions_are_possible and _is_c_extension(owner):
            msg = "c-extension-no-member"
            hint = ""
        else:
            msg = "no-member"
            if self.linter.config.missing_member_hint:
                hint = _missing_member_hint(
                    owner,
                    node.attrname,
                    self.linter.config.missing_member_hint_distance,
                    self.linter.config.missing_member_max_choices,
                )
            else:
                hint = ""
        return msg, hint  # type: ignore[return-value]

    @only_required_for_messages(
        "assignment-from-no-return",
        "assignment-from-none",
        "non-str-assignment-to-dunder-name",
    )
    def visit_assign(self, node: nodes.Assign) -> None:
        """Process assignments in the AST."""

        self._check_assignment_from_function_call(node)
        self._check_dundername_is_string(node)

    def _check_assignment_from_function_call(self, node: nodes.Assign) -> None:
        """When assigning to a function call, check that the function returns a valid
        value.
        """
        if not isinstance(node.value, nodes.Call):
            return

        function_node = safe_infer(node.value.func)
        funcs = (nodes.FunctionDef, astroid.UnboundMethod, astroid.BoundMethod)
        if not isinstance(function_node, funcs):
            return

        # Unwrap to get the actual function node object
        if isinstance(function_node, astroid.BoundMethod) and isinstance(
            function_node._proxied, astroid.UnboundMethod
        ):
            function_node = function_node._proxied._proxied

        # Make sure that it's a valid function that we can analyze.
        # Ordered from less expensive to more expensive checks.
        if (
            not function_node.is_function
            or function_node.decorators
            or self._is_ignored_function(function_node)
        ):
            return

        # Handle builtins such as list.sort() or dict.update()
        if self._is_builtin_no_return(node):
            self.add_message(
                "assignment-from-no-return", node=node, confidence=INFERENCE
            )
            return

        if not function_node.root().fully_defined():
            return

        return_nodes = list(
            function_node.nodes_of_class(nodes.Return, skip_klass=nodes.FunctionDef)
        )
        if not return_nodes:
            self.add_message("assignment-from-no-return", node=node)
        else:
            for ret_node in return_nodes:
                if not (
                    isinstance(ret_node.value, nodes.Const)
                    and ret_node.value.value is None
                    or ret_node.value is None
                ):
                    break
            else:
                self.add_message("assignment-from-none", node=node)

    @staticmethod
    def _is_ignored_function(
        function_node: nodes.FunctionDef | bases.UnboundMethod,
    ) -> bool:
        return (
            isinstance(function_node, nodes.AsyncFunctionDef)
            or utils.is_error(function_node)
            or function_node.is_generator()
            or function_node.is_abstract(pass_is_abstract=False)
        )

    @staticmethod
    def _is_builtin_no_return(node: nodes.Assign) -> bool:
        return (
            isinstance(node.value, nodes.Call)
            and isinstance(node.value.func, nodes.Attribute)
            and bool(inferred := utils.safe_infer(node.value.func.expr))
            and isinstance(inferred, bases.Instance)
            and node.value.func.attrname
            in BUILTINS_IMPLICIT_RETURN_NONE.get(inferred.pytype(), ())
        )

    def _check_dundername_is_string(self, node: nodes.Assign) -> None:
        """Check a string is assigned to self.__name__."""

        # Check the left-hand side of the assignment is <something>.__name__
        lhs = node.targets[0]
        if not isinstance(lhs, nodes.AssignAttr):
            return
        if not lhs.attrname == "__name__":
            return

        # If the right-hand side is not a string
        rhs = node.value
        if isinstance(rhs, nodes.Const) and isinstance(rhs.value, str):
            return
        inferred = utils.safe_infer(rhs)
        if not inferred:
            return
        if not (isinstance(inferred, nodes.Const) and isinstance(inferred.value, str)):
            # Add the message
            self.add_message("non-str-assignment-to-dunder-name", node=node)

    def _check_uninferable_call(self, node: nodes.Call) -> None:
        """Check that the given uninferable Call node does not
        call an actual function.
        """
        if not isinstance(node.func, nodes.Attribute):
            return

        # Look for properties. First, obtain
        # the lhs of the Attribute node and search the attribute
        # there. If that attribute is a property or a subclass of properties,
        # then most likely it's not callable.

        expr = node.func.expr
        klass = safe_infer(expr)
        if not isinstance(klass, astroid.Instance):
            return

        try:
            attrs = klass._proxied.getattr(node.func.attrname)
        except astroid.NotFoundError:
            return

        for attr in attrs:
            if not isinstance(attr, nodes.FunctionDef):
                continue

            # Decorated, see if it is decorated with a property.
            # Also, check the returns and see if they are callable.
            if decorated_with_property(attr):
                try:
                    call_results = list(attr.infer_call_result(node))
                except astroid.InferenceError:
                    continue

                if all(
                    isinstance(return_node, util.UninferableBase)
                    for return_node in call_results
                ):
                    # We were unable to infer return values of the call, skipping
                    continue

                if any(return_node.callable() for return_node in call_results):
                    # Only raise this issue if *all* the inferred values are not callable
                    continue

                self.add_message("not-callable", node=node, args=node.func.as_string())

    def _check_argument_order(
        self,
        node: nodes.Call,
        call_site: arguments.CallSite,
        called: CallableObjects,
        called_param_names: list[str | None],
    ) -> None:
        """Match the supplied argument names against the function parameters.

        Warn if some argument names are not in the same order as they are in
        the function signature.
        """
        # Check for called function being an object instance function
        # If so, ignore the initial 'self' argument in the signature
        try:
            is_classdef = isinstance(called.parent, nodes.ClassDef)
            if is_classdef and called_param_names[0] == "self":
                called_param_names = called_param_names[1:]
        except IndexError:
            return

        try:
            # extract argument names, if they have names
            calling_parg_names = [p.name for p in call_site.positional_arguments]

            # Additionally, get names of keyword arguments to use in a full match
            # against parameters
            calling_kwarg_names = [
                arg.name for arg in call_site.keyword_arguments.values()
            ]
        except AttributeError:
            # the type of arg does not provide a `.name`. In this case we
            # stop checking for out-of-order arguments because it is only relevant
            # for named variables.
            return

        # Don't check for ordering if there is an unmatched arg or param
        arg_set = set(calling_parg_names) | set(calling_kwarg_names)
        param_set = set(called_param_names)
        if arg_set != param_set:
            return

        # Warn based on the equality of argument ordering
        if calling_parg_names != called_param_names[: len(calling_parg_names)]:
            self.add_message("arguments-out-of-order", node=node, args=())

    def _check_isinstance_args(self, node: nodes.Call) -> None:
        if len(node.args) != 2:
            # isinstance called with wrong number of args
            return

        second_arg = node.args[1]
        if _is_invalid_isinstance_type(second_arg):
            self.add_message(
                "isinstance-second-argument-not-valid-type",
                node=node,
                confidence=INFERENCE,
            )

    # pylint: disable = too-many-branches, too-many-locals, too-many-statements
    def visit_call(self, node: nodes.Call) -> None:
        """Check that called functions/methods are inferred to callable objects,
        and that passed arguments match the parameters in the inferred function.
        """
        called = safe_infer(node.func)

        self._check_not_callable(node, called)

        try:
            called, implicit_args, callable_name = _determine_callable(called)
        except ValueError:
            # Any error occurred during determining the function type, most of
            # those errors are handled by different warnings.
            return

        if called.args.args is None:
            if called.name == "isinstance":
                # Verify whether second argument of isinstance is a valid type
                self._check_isinstance_args(node)
            # Built-in functions have no argument information.
            return

        if len(called.argnames()) != len(set(called.argnames())):
            # Duplicate parameter name (see duplicate-argument).  We can't really
            # make sense of the function call in this case, so just return.
            return

        # Build the set of keyword arguments, checking for duplicate keywords,
        # and count the positional arguments.
        call_site = astroid.arguments.CallSite.from_call(node)

        # Warn about duplicated keyword arguments, such as `f=24, **{'f': 24}`
        for keyword in call_site.duplicated_keywords:
            self.add_message("repeated-keyword", node=node, args=(keyword,))

        if call_site.has_invalid_arguments() or call_site.has_invalid_keywords():
            # Can't make sense of this.
            return

        # Has the function signature changed in ways we cannot reliably detect?
        if hasattr(called, "decorators") and decorated_with(
            called, self.linter.config.signature_mutators
        ):
            return

        num_positional_args = len(call_site.positional_arguments)
        keyword_args = list(call_site.keyword_arguments.keys())
        overload_function = is_overload_stub(called)

        # Determine if we don't have a context for our call and we use variadics.
        node_scope = node.scope()
        if isinstance(node_scope, (nodes.Lambda, nodes.FunctionDef)):
            has_no_context_positional_variadic = _no_context_variadic_positional(
                node, node_scope
            )
            has_no_context_keywords_variadic = _no_context_variadic_keywords(
                node, node_scope
            )
        else:
            has_no_context_positional_variadic = (
                has_no_context_keywords_variadic
            ) = False

        # These are coming from the functools.partial implementation in astroid
        already_filled_positionals = getattr(called, "filled_positionals", 0)
        already_filled_keywords = getattr(called, "filled_keywords", {})

        keyword_args += list(already_filled_keywords)
        num_positional_args += implicit_args + already_filled_positionals

        # Decrement `num_positional_args` by 1 when a function call is assigned to a class attribute
        # inside the class where the function is defined.
        # This avoids emitting `too-many-function-args` since `num_positional_args`
        # includes an implicit `self` argument which is not present in `called.args`.
        if (
            isinstance(node.frame(), nodes.ClassDef)
            and isinstance(called, nodes.FunctionDef)
            and called in node.frame().body
            and num_positional_args > 0
            and "builtins.staticmethod" not in called.decoratornames()
        ):
            num_positional_args -= 1

        # Analyze the list of formal parameters.
        args = list(itertools.chain(called.args.posonlyargs or (), called.args.args))
        num_mandatory_parameters = len(args) - len(called.args.defaults)
        parameters: list[tuple[tuple[str | None, nodes.NodeNG | None], bool]] = []
        parameter_name_to_index = {}
        for i, arg in enumerate(args):
            name = arg.name
            parameter_name_to_index[name] = i
            if i >= num_mandatory_parameters:
                defval = called.args.defaults[i - num_mandatory_parameters]
            else:
                defval = None
            parameters.append(((name, defval), False))

        kwparams = {}
        for i, arg in enumerate(called.args.kwonlyargs):
            if isinstance(arg, nodes.Keyword):
                name = arg.arg
            else:
                assert isinstance(arg, nodes.AssignName)
                name = arg.name
            kwparams[name] = [called.args.kw_defaults[i], False]

        self._check_argument_order(
            node, call_site, called, [p[0][0] for p in parameters]
        )

        # 1. Match the positional arguments.
        for i in range(num_positional_args):
            if i < len(parameters):
                parameters[i] = (parameters[i][0], True)
            elif called.args.vararg is not None:
                # The remaining positional arguments get assigned to the *args
                # parameter.
                break
            elif not overload_function:
                # Too many positional arguments.
                self.add_message(
                    "too-many-function-args", node=node, args=(callable_name,)
                )
                break

        # 2. Match the keyword arguments.
        for keyword in keyword_args:
            # Skip if `keyword` is the same name as a positional-only parameter
            # and a `**kwargs` parameter exists.
            if called.args.kwarg and keyword in [
                arg.name for arg in called.args.posonlyargs
            ]:
                self.add_message(
                    "kwarg-superseded-by-positional-arg",
                    node=node,
                    args=(keyword, f"**{called.args.kwarg}"),
                    confidence=HIGH,
                )
                continue
            if keyword in parameter_name_to_index:
                i = parameter_name_to_index[keyword]
                if parameters[i][1]:
                    # Duplicate definition of function parameter.

                    # Might be too hard-coded, but this can actually
                    # happen when using str.format and `self` is passed
                    # by keyword argument, as in `.format(self=self)`.
                    # It's perfectly valid to so, so we're just skipping
                    # it if that's the case.
                    if not (keyword == "self" and called.qname() in STR_FORMAT):
                        self.add_message(
                            "redundant-keyword-arg",
                            node=node,
                            args=(keyword, callable_name),
                        )
                else:
                    parameters[i] = (parameters[i][0], True)
            elif keyword in kwparams:
                if kwparams[keyword][1]:
                    # Duplicate definition of function parameter.
                    self.add_message(
                        "redundant-keyword-arg",
                        node=node,
                        args=(keyword, callable_name),
                    )
                else:
                    kwparams[keyword][1] = True
            elif called.args.kwarg is not None:
                # The keyword argument gets assigned to the **kwargs parameter.
                pass
            elif isinstance(
                called, nodes.FunctionDef
            ) and self._keyword_argument_is_in_all_decorator_returns(called, keyword):
                pass
            elif not overload_function:
                # Unexpected keyword argument.
                self.add_message(
                    "unexpected-keyword-arg", node=node, args=(keyword, callable_name)
                )

        # 3. Match the **kwargs, if any.
        if node.kwargs:
            for i, [(name, _defval), _assigned] in enumerate(parameters):
                # Assume that *kwargs provides values for all remaining
                # unassigned named parameters.
                if name is not None:
                    parameters[i] = (parameters[i][0], True)
                else:
                    # **kwargs can't assign to tuples.
                    pass

        # Check that any parameters without a default have been assigned
        # values.
        for [(name, defval), assigned] in parameters:
            if (defval is None) and not assigned:
                display_name = "<tuple>" if name is None else repr(name)
                if not has_no_context_positional_variadic and not overload_function:
                    self.add_message(
                        "no-value-for-parameter",
                        node=node,
                        args=(display_name, callable_name),
                    )

        for name, val in kwparams.items():
            defval, assigned = val
            if (
                defval is None
                and not assigned
                and not has_no_context_keywords_variadic
                and not overload_function
            ):
                self.add_message(
                    "missing-kwoa",
                    node=node,
                    args=(name, callable_name),
                    confidence=INFERENCE,
                )

    @staticmethod
    def _keyword_argument_is_in_all_decorator_returns(
        func: nodes.FunctionDef, keyword: str
    ) -> bool:
        """Check if the keyword argument exists in all signatures of the
        return values of all decorators of the function.
        """
        if not func.decorators:
            return False

        for decorator in func.decorators.nodes:
            inferred = safe_infer(decorator)

            # If we can't infer the decorator we assume it satisfies consumes
            # the keyword, so we don't raise false positives
            if not inferred:
                return True

            # We only check arguments of function decorators
            if not isinstance(inferred, nodes.FunctionDef):
                return False

            for return_value in inferred.infer_call_result(caller=None):
                # infer_call_result() returns nodes.Const.None for None return values
                # so this also catches non-returning decorators
                if not isinstance(return_value, nodes.FunctionDef):
                    return False

                # If the return value uses a kwarg the keyword will be consumed
                if return_value.args.kwarg:
                    continue

                # Check if the keyword is another type of argument
                if return_value.args.is_argument(keyword):
                    continue

                return False

        return True

    def _check_invalid_sequence_index(self, subscript: nodes.Subscript) -> None:
        # Look for index operations where the parent is a sequence type.
        # If the types can be determined, only allow indices to be int,
        # slice or instances with __index__.
        parent_type = safe_infer(subscript.value)
        if not isinstance(
            parent_type, (nodes.ClassDef, astroid.Instance)
        ) or not has_known_bases(parent_type):
            return None

        # Determine what method on the parent this index will use
        # The parent of this node will be a Subscript, and the parent of that
        # node determines if the Subscript is a get, set, or delete operation.
        if subscript.ctx is astroid.Context.Store:
            methodname = "__setitem__"
        elif subscript.ctx is astroid.Context.Del:
            methodname = "__delitem__"
        else:
            methodname = "__getitem__"

        # Check if this instance's __getitem__, __setitem__, or __delitem__, as
        # appropriate to the statement, is implemented in a builtin sequence
        # type. This way we catch subclasses of sequence types but skip classes
        # that override __getitem__ and which may allow non-integer indices.
        try:
            methods = astroid.interpreter.dunder_lookup.lookup(parent_type, methodname)
            if isinstance(methods, util.UninferableBase):
                return None
            itemmethod = methods[0]
        except (
            astroid.AttributeInferenceError,
            IndexError,
        ):
            return None
        if (
            not isinstance(itemmethod, nodes.FunctionDef)
            or itemmethod.root().name != "builtins"
            or not itemmethod.parent
            or itemmethod.parent.frame().name not in SEQUENCE_TYPES
        ):
            return None

        index_type = safe_infer(subscript.slice)
        if index_type is None or isinstance(index_type, util.UninferableBase):
            return None
        # Constants must be of type int
        if isinstance(index_type, nodes.Const):
            if isinstance(index_type.value, int):
                return None
        # Instance values must be int, slice, or have an __index__ method
        elif isinstance(index_type, astroid.Instance):
            if index_type.pytype() in {"builtins.int", "builtins.slice"}:
                return None
            try:
                index_type.getattr("__index__")
                return None
            except astroid.NotFoundError:
                pass
        elif isinstance(index_type, nodes.Slice):
            # A slice can be present
            # here after inferring the index node, which could
            # be a `slice(...)` call for instance.
            return self._check_invalid_slice_index(index_type)

        # Anything else is an error
        self.add_message("invalid-sequence-index", node=subscript)
        return None

    def _check_not_callable(
        self, node: nodes.Call, inferred_call: nodes.NodeNG | None
    ) -> None:
        """Checks to see if the not-callable message should be emitted.

        Only functions, generators and objects defining __call__ are "callable"
        We ignore instances of descriptors since astroid cannot properly handle them yet
        """
        # Handle uninferable calls
        if not inferred_call or inferred_call.callable():
            self._check_uninferable_call(node)
            return

        if not isinstance(inferred_call, astroid.Instance):
            self.add_message("not-callable", node=node, args=node.func.as_string())
            return

        # Don't emit if we can't make sure this object is callable.
        if not has_known_bases(inferred_call):
            return

        if inferred_call.parent and isinstance(inferred_call.scope(), nodes.ClassDef):
            # Ignore descriptor instances
            if "__get__" in inferred_call.locals:
                return
            # NamedTuple instances are callable
            if inferred_call.qname() == "typing.NamedTuple":
                return

        self.add_message("not-callable", node=node, args=node.func.as_string())

    def _check_invalid_slice_index(self, node: nodes.Slice) -> None:
        # Check the type of each part of the slice
        invalid_slices_nodes: list[nodes.NodeNG] = []
        for index in (node.lower, node.upper, node.step):
            if index is None:
                continue

            index_type = safe_infer(index)
            if index_type is None or isinstance(index_type, util.UninferableBase):
                continue

            # Constants must be of type int or None
            if isinstance(index_type, nodes.Const):
                if isinstance(index_type.value, (int, type(None))):
                    continue
            # Instance values must be of type int, None or an object
            # with __index__
            elif isinstance(index_type, astroid.Instance):
                if index_type.pytype() in {"builtins.int", "builtins.NoneType"}:
                    continue

                try:
                    index_type.getattr("__index__")
                    return
                except astroid.NotFoundError:
                    pass
            invalid_slices_nodes.append(index)

        invalid_slice_step = (
            node.step and isinstance(node.step, nodes.Const) and node.step.value == 0
        )

        if not (invalid_slices_nodes or invalid_slice_step):
            return

        # Anything else is an error, unless the object that is indexed
        # is a custom object, which knows how to handle this kind of slices
        parent = node.parent
        if isinstance(parent, nodes.Subscript):
            inferred = safe_infer(parent.value)
            if inferred is None or isinstance(inferred, util.UninferableBase):
                # Don't know what this is
                return
            known_objects = (
                nodes.List,
                nodes.Dict,
                nodes.Tuple,
                astroid.objects.FrozenSet,
                nodes.Set,
            )
            if not (
                isinstance(inferred, known_objects)
                or isinstance(inferred, nodes.Const)
                and inferred.pytype() in {"builtins.str", "builtins.bytes"}
                or isinstance(inferred, astroid.bases.Instance)
                and inferred.pytype() == "builtins.range"
            ):
                # Might be an instance that knows how to handle this slice object
                return
        for snode in invalid_slices_nodes:
            self.add_message("invalid-slice-index", node=snode)
        if invalid_slice_step:
            self.add_message("invalid-slice-step", node=node.step, confidence=HIGH)

    @only_required_for_messages("not-context-manager")
    def visit_with(self, node: nodes.With) -> None:
        for ctx_mgr, _ in node.items:
            context = astroid.context.InferenceContext()
            inferred = safe_infer(ctx_mgr, context=context)
            if inferred is None or isinstance(inferred, util.UninferableBase):
                continue

            if isinstance(inferred, astroid.bases.Generator):
                # Check if we are dealing with a function decorated
                # with contextlib.contextmanager.
                if decorated_with(
                    inferred.parent, self.linter.config.contextmanager_decorators
                ):
                    continue
                # If the parent of the generator is not the context manager itself,
                # that means that it could have been returned from another
                # function which was the real context manager.
                # The following approach is more of a hack rather than a real
                # solution: walk all the inferred statements for the
                # given *ctx_mgr* and if you find one function scope
                # which is decorated, consider it to be the real
                # manager and give up, otherwise emit not-context-manager.
                # See the test file for not_context_manager for a couple
                # of self explaining tests.

                # Retrieve node from all previously visited nodes in the
                # inference history
                for inferred_path, _ in context.path:
                    if not inferred_path:
                        continue
                    if isinstance(inferred_path, nodes.Call):
                        scope = safe_infer(inferred_path.func)
                    else:
                        scope = inferred_path.scope()
                    if not isinstance(scope, nodes.FunctionDef):
                        continue
                    if decorated_with(
                        scope, self.linter.config.contextmanager_decorators
                    ):
                        break
                else:
                    self.add_message(
                        "not-context-manager", node=node, args=(inferred.name,)
                    )
            else:
                try:
                    inferred.getattr("__enter__")
                    inferred.getattr("__exit__")
                except astroid.NotFoundError:
                    if isinstance(inferred, astroid.Instance):
                        # If we do not know the bases of this class,
                        # just skip it.
                        if not has_known_bases(inferred):
                            continue
                        # Just ignore mixin classes.
                        if (
                            "not-context-manager"
                            in self.linter.config.ignored_checks_for_mixins
                        ):
                            if inferred.name[-5:].lower() == "mixin":
                                continue

                    self.add_message(
                        "not-context-manager", node=node, args=(inferred.name,)
                    )

    @only_required_for_messages("invalid-unary-operand-type")
    def visit_unaryop(self, node: nodes.UnaryOp) -> None:
        """Detect TypeErrors for unary operands."""

        for error in node.type_errors():
            # Let the error customize its output.
            self.add_message("invalid-unary-operand-type", args=str(error), node=node)

    @only_required_for_messages("unsupported-binary-operation")
    def visit_binop(self, node: nodes.BinOp) -> None:
        if node.op == "|":
            self._detect_unsupported_alternative_union_syntax(node)

    def _detect_unsupported_alternative_union_syntax(self, node: nodes.BinOp) -> None:
        """Detect if unsupported alternative Union syntax (PEP 604) was used."""
        if self._py310_plus:  # 310+ supports the new syntax
            return

        if isinstance(
            node.parent, TYPE_ANNOTATION_NODES_TYPES
        ) and not is_postponed_evaluation_enabled(node):
            # Use in type annotations only allowed if
            # postponed evaluation is enabled.
            self._check_unsupported_alternative_union_syntax(node)

        if isinstance(
            node.parent,
            (
                nodes.Assign,
                nodes.Call,
                nodes.Keyword,
                nodes.Dict,
                nodes.Tuple,
                nodes.Set,
                nodes.List,
                nodes.BinOp,
            ),
        ):
            # Check other contexts the syntax might appear, but are invalid.
            # Make sure to filter context if postponed evaluation is enabled
            # and parent is allowed node type.
            allowed_nested_syntax = False
            if is_postponed_evaluation_enabled(node):
                parent_node = node.parent
                while True:
                    if isinstance(parent_node, TYPE_ANNOTATION_NODES_TYPES):
                        allowed_nested_syntax = True
                        break
                    parent_node = parent_node.parent
                    if isinstance(parent_node, nodes.Module):
                        break
            if not allowed_nested_syntax:
                self._check_unsupported_alternative_union_syntax(node)

    def _includes_version_compatible_overload(self, attrs: list[nodes.NodeNG]) -> bool:
        """Check if a set of overloads of an operator includes one that
        can be relied upon for our configured Python version.

        If we are running under a Python 3.10+ runtime but configured for
        pre-3.10 compatibility then Astroid will have inferred the
        existence of __or__ / __ror__ on builtins.type, but these aren't
        available in the configured version of Python.
        """
        is_py310_builtin = all(
            isinstance(attr, (nodes.FunctionDef, astroid.BoundMethod))
            and attr.parent.qname() == "builtins.type"
            for attr in attrs
        )
        return not is_py310_builtin or self._py310_plus

    def _recursive_search_for_classdef_type(
        self, node: nodes.ClassDef, operation: Literal["__or__", "__ror__"]
    ) -> bool | VERSION_COMPATIBLE_OVERLOAD:
        if not isinstance(node, nodes.ClassDef):
            return False
        try:
            attrs = node.getattr(operation)
        except astroid.NotFoundError:
            return True
        if self._includes_version_compatible_overload(attrs):
            return VERSION_COMPATIBLE_OVERLOAD_SENTINEL
        return True

    def _check_unsupported_alternative_union_syntax(self, node: nodes.BinOp) -> None:
        """Check if left or right node is of type `type`.

        If either is, and doesn't support an or operator via a metaclass,
        infer that this is a mistaken attempt to use alternative union
        syntax when not supported.
        """
        msg = "unsupported operand type(s) for |"
        left_obj = astroid.helpers.object_type(node.left)
        right_obj = astroid.helpers.object_type(node.right)
        left_is_type = self._recursive_search_for_classdef_type(left_obj, "__or__")
        if left_is_type is VERSION_COMPATIBLE_OVERLOAD_SENTINEL:
            return
        right_is_type = self._recursive_search_for_classdef_type(right_obj, "__ror__")
        if right_is_type is VERSION_COMPATIBLE_OVERLOAD_SENTINEL:
            return

        if left_is_type or right_is_type:
            self.add_message(
                "unsupported-binary-operation",
                args=msg,
                node=node,
                confidence=INFERENCE,
            )

    # TODO: This check was disabled (by adding the leading underscore)
    # due to false positives several years ago - can we re-enable it?
    # https://github.com/pylint-dev/pylint/issues/6359
    @only_required_for_messages("unsupported-binary-operation")
    def _visit_binop(self, node: nodes.BinOp) -> None:
        """Detect TypeErrors for binary arithmetic operands."""
        self._check_binop_errors(node)

    # TODO: This check was disabled (by adding the leading underscore)
    # due to false positives several years ago - can we re-enable it?
    # https://github.com/pylint-dev/pylint/issues/6359
    @only_required_for_messages("unsupported-binary-operation")
    def _visit_augassign(self, node: nodes.AugAssign) -> None:
        """Detect TypeErrors for augmented binary arithmetic operands."""
        self._check_binop_errors(node)

    def _check_binop_errors(self, node: nodes.BinOp | nodes.AugAssign) -> None:
        for error in node.type_errors():
            # Let the error customize its output.
            if any(
                isinstance(obj, nodes.ClassDef) and not has_known_bases(obj)
                for obj in (error.left_type, error.right_type)
            ):
                continue
            self.add_message("unsupported-binary-operation", args=str(error), node=node)

    def _check_membership_test(self, node: nodes.NodeNG) -> None:
        if is_inside_abstract_class(node):
            return
        if is_comprehension(node):
            return
        inferred = safe_infer(node)
        if inferred is None or isinstance(inferred, util.UninferableBase):
            return
        if not supports_membership_test(inferred):
            self.add_message(
                "unsupported-membership-test", args=node.as_string(), node=node
            )

    @only_required_for_messages("unsupported-membership-test")
    def visit_compare(self, node: nodes.Compare) -> None:
        if len(node.ops) != 1:
            return

        op, right = node.ops[0]
        if op in {"in", "not in"}:
            self._check_membership_test(right)

    @only_required_for_messages("unhashable-member")
    def visit_dict(self, node: nodes.Dict) -> None:
        for k, _ in node.items:
            if not is_hashable(k):
                self.add_message(
                    "unhashable-member",
                    node=k,
                    args=(k.as_string(), "key", "dict"),
                    confidence=INFERENCE,
                )

    @only_required_for_messages("unhashable-member")
    def visit_set(self, node: nodes.Set) -> None:
        for element in node.elts:
            if not is_hashable(element):
                self.add_message(
                    "unhashable-member",
                    node=element,
                    args=(element.as_string(), "member", "set"),
                    confidence=INFERENCE,
                )

    @only_required_for_messages(
        "unsubscriptable-object",
        "unsupported-assignment-operation",
        "unsupported-delete-operation",
        "unhashable-member",
        "invalid-sequence-index",
        "invalid-slice-index",
        "invalid-slice-step",
    )
    def visit_subscript(self, node: nodes.Subscript) -> None:
        self._check_invalid_sequence_index(node)

        supported_protocol: Callable[[Any, Any], bool] | None = None
        if isinstance(node.value, (nodes.ListComp, nodes.DictComp)):
            return

        if isinstance(node.value, nodes.Dict):
            # Assert dict key is hashable
            if not is_hashable(node.slice):
                self.add_message(
                    "unhashable-member",
                    node=node.value,
                    args=(node.slice.as_string(), "key", "dict"),
                    confidence=INFERENCE,
                )

        if node.ctx == astroid.Context.Load:
            supported_protocol = supports_getitem
            msg = "unsubscriptable-object"
        elif node.ctx == astroid.Context.Store:
            supported_protocol = supports_setitem
            msg = "unsupported-assignment-operation"
        elif node.ctx == astroid.Context.Del:
            supported_protocol = supports_delitem
            msg = "unsupported-delete-operation"

        if isinstance(node.value, nodes.SetComp):
            self.add_message(msg, args=node.value.as_string(), node=node.value)
            return

        if is_inside_abstract_class(node):
            return

        inferred = safe_infer(node.value)

        if inferred is None or isinstance(inferred, util.UninferableBase):
            return

        if getattr(inferred, "decorators", None):
            first_decorator = astroid.util.safe_infer(inferred.decorators.nodes[0])
            if isinstance(first_decorator, nodes.ClassDef):
                inferred = first_decorator.instantiate_class()
            else:
                return  # It would be better to handle function
                # decorators, but let's start slow.

        if (
            supported_protocol
            and not supported_protocol(inferred, node)
            and not utils.in_type_checking_block(node)
        ):
            self.add_message(msg, args=node.value.as_string(), node=node.value)

    @only_required_for_messages("dict-items-missing-iter")
    def visit_for(self, node: nodes.For) -> None:
        if not isinstance(node.target, nodes.Tuple):
            # target is not a tuple
            return
        if not len(node.target.elts) == 2:
            # target is not a tuple of two elements
            return

        iterable = node.iter
        if not isinstance(iterable, nodes.Name):
            # it's not a bare variable
            return

        inferred = safe_infer(iterable)
        if not inferred:
            return
        if not isinstance(inferred, nodes.Dict):
            # the iterable is not a dict
            return

        if all(isinstance(i[0], nodes.Tuple) for i in inferred.items):
            # if all keys are tuples
            return

        self.add_message("dict-iter-missing-items", node=node)

    @only_required_for_messages("await-outside-async")
    def visit_await(self, node: nodes.Await) -> None:
        self._check_await_outside_coroutine(node)

    def _check_await_outside_coroutine(self, node: nodes.Await) -> None:
        node_scope = node.scope()
        while not isinstance(node_scope, nodes.Module):
            if isinstance(node_scope, nodes.AsyncFunctionDef):
                return
            if isinstance(node_scope, nodes.FunctionDef):
                break
            node_scope = node_scope.parent.scope()
        self.add_message("await-outside-async", node=node)


class IterableChecker(BaseChecker):
    """Checks for non-iterables used in an iterable context.

    Contexts include:
    - for-statement
    - starargs in function call
    - `yield from`-statement
    - list, dict and set comprehensions
    - generator expressions
    Also checks for non-mappings in function call kwargs.
    """

    name = "typecheck"

    msgs = {
        "E1133": (
            "Non-iterable value %s is used in an iterating context",
            "not-an-iterable",
            "Used when a non-iterable value is used in place where "
            "iterable is expected",
        ),
        "E1134": (
            "Non-mapping value %s is used in a mapping context",
            "not-a-mapping",
            "Used when a non-mapping value is used in place where "
            "mapping is expected",
        ),
    }

    @staticmethod
    def _is_asyncio_coroutine(node: nodes.NodeNG) -> bool:
        if not isinstance(node, nodes.Call):
            return False

        inferred_func = safe_infer(node.func)
        if not isinstance(inferred_func, nodes.FunctionDef):
            return False
        if not inferred_func.decorators:
            return False
        for decorator in inferred_func.decorators.nodes:
            inferred_decorator = safe_infer(decorator)
            if not isinstance(inferred_decorator, nodes.FunctionDef):
                continue
            if inferred_decorator.qname() != ASYNCIO_COROUTINE:
                continue
            return True
        return False

    def _check_iterable(self, node: nodes.NodeNG, check_async: bool = False) -> None:
        if is_inside_abstract_class(node):
            return
        inferred = safe_infer(node)
        if not inferred or is_comprehension(inferred):
            return
        if not is_iterable(inferred, check_async=check_async):
            self.add_message("not-an-iterable", args=node.as_string(), node=node)

    def _check_mapping(self, node: nodes.NodeNG) -> None:
        if is_inside_abstract_class(node):
            return
        if isinstance(node, nodes.DictComp):
            return
        inferred = safe_infer(node)
        if inferred is None or isinstance(inferred, util.UninferableBase):
            return
        if not is_mapping(inferred):
            self.add_message("not-a-mapping", args=node.as_string(), node=node)

    @only_required_for_messages("not-an-iterable")
    def visit_for(self, node: nodes.For) -> None:
        self._check_iterable(node.iter)

    @only_required_for_messages("not-an-iterable")
    def visit_asyncfor(self, node: nodes.AsyncFor) -> None:
        self._check_iterable(node.iter, check_async=True)

    @only_required_for_messages("not-an-iterable")
    def visit_yieldfrom(self, node: nodes.YieldFrom) -> None:
        if self._is_asyncio_coroutine(node.value):
            return
        self._check_iterable(node.value)

    @only_required_for_messages("not-an-iterable", "not-a-mapping")
    def visit_call(self, node: nodes.Call) -> None:
        for stararg in node.starargs:
            self._check_iterable(stararg.value)
        for kwarg in node.kwargs:
            self._check_mapping(kwarg.value)

    @only_required_for_messages("not-an-iterable")
    def visit_listcomp(self, node: nodes.ListComp) -> None:
        for gen in node.generators:
            self._check_iterable(gen.iter, check_async=gen.is_async)

    @only_required_for_messages("not-an-iterable")
    def visit_dictcomp(self, node: nodes.DictComp) -> None:
        for gen in node.generators:
            self._check_iterable(gen.iter, check_async=gen.is_async)

    @only_required_for_messages("not-an-iterable")
    def visit_setcomp(self, node: nodes.SetComp) -> None:
        for gen in node.generators:
            self._check_iterable(gen.iter, check_async=gen.is_async)

    @only_required_for_messages("not-an-iterable")
    def visit_generatorexp(self, node: nodes.GeneratorExp) -> None:
        for gen in node.generators:
            self._check_iterable(gen.iter, check_async=gen.is_async)


def register(linter: PyLinter) -> None:
    linter.register_checker(TypeChecker(linter))
    linter.register_checker(IterableChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/typecheck.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Unicode and some other ASCII characters can be used to create programs that run
much different compared to what a human reader would expect from them.

PEP 672 lists some examples.
See: https://www.python.org/dev/peps/pep-0672/

The following checkers are intended to make users are aware of these issues.
"""

from __future__ import annotations

import codecs
import contextlib
import io
import re
from collections import OrderedDict
from collections.abc import Iterable
from functools import lru_cache
from tokenize import detect_encoding
from typing import NamedTuple, TypeVar

from astroid import nodes

import pylint.interfaces
import pylint.lint
from pylint import checkers

_StrLike = TypeVar("_StrLike", str, bytes)

# Based on:
# https://golangexample.com/go-linter-which-checks-for-dangerous-unicode-character-sequences/
# We use '\u' because it doesn't require a map lookup and is therefore faster
BIDI_UNICODE = [
    "\u202A",  # \N{LEFT-TO-RIGHT EMBEDDING}
    "\u202B",  # \N{RIGHT-TO-LEFT EMBEDDING}
    "\u202C",  # \N{POP DIRECTIONAL FORMATTING}
    "\u202D",  # \N{LEFT-TO-RIGHT OVERRIDE}
    "\u202E",  # \N{RIGHT-TO-LEFT OVERRIDE}
    "\u2066",  # \N{LEFT-TO-RIGHT ISOLATE}
    "\u2067",  # \N{RIGHT-TO-LEFT ISOLATE}
    "\u2068",  # \N{FIRST STRONG ISOLATE}
    "\u2069",  # \N{POP DIRECTIONAL ISOLATE}
    # The following was part of PEP 672:
    # https://www.python.org/dev/peps/pep-0672/
    # so the list above might not be complete
    "\u200F",  # \n{RIGHT-TO-LEFT MARK}
    # We don't use
    #   "\u200E" # \n{LEFT-TO-RIGHT MARK}
    # as this is the default for latin files and can't be used
    # to hide code
]


class _BadChar(NamedTuple):
    """Representation of an ASCII char considered bad."""

    name: str
    unescaped: str
    escaped: str
    code: str
    help_text: str

    def description(self) -> str:
        """Used for the detailed error message description."""
        return (
            f"Invalid unescaped character {self.name}, "
            f'use "{self.escaped}" instead.'
        )

    def human_code(self) -> str:
        """Used to generate the human readable error message."""
        return f"invalid-character-{self.name}"


# Based on https://www.python.org/dev/peps/pep-0672/
BAD_CHARS = [
    _BadChar(
        "backspace",
        "\b",
        "\\b",
        "E2510",
        (
            "Moves the cursor back, so the character after it will overwrite the "
            "character before."
        ),
    ),
    _BadChar(
        "carriage-return",
        "\r",
        "\\r",
        "E2511",
        (
            "Moves the cursor to the start of line, subsequent characters overwrite "
            "the start of the line."
        ),
    ),
    _BadChar(
        "sub",
        "\x1A",
        "\\x1A",
        "E2512",
        (
            'Ctrl+Z "End of text" on Windows. Some programs (such as type) ignore '
            "the rest of the file after it."
        ),
    ),
    _BadChar(
        "esc",
        "\x1B",
        "\\x1B",
        "E2513",
        (
            "Commonly initiates escape codes which allow arbitrary control "
            "of the terminal."
        ),
    ),
    _BadChar(
        "nul",
        "\0",
        "\\0",
        "E2514",
        "Mostly end of input for python.",
    ),
    _BadChar(
        # Zero Width with Space. At the time of writing not accepted by Python.
        # But used in Trojan Source Examples, so still included and tested for.
        "zero-width-space",
        "\u200B",  # \n{ZERO WIDTH SPACE}
        "\\u200B",
        "E2515",
        "Invisible space character could hide real code execution.",
    ),
]
BAD_ASCII_SEARCH_DICT = {char.unescaped: char for char in BAD_CHARS}


def _line_length(line: _StrLike, codec: str) -> int:
    """Get the length of a string like line as displayed in an editor."""
    if isinstance(line, bytes):
        decoded = _remove_bom(line, codec).decode(codec, "replace")
    else:
        decoded = line

    stripped = decoded.rstrip("\n")

    if stripped != decoded:
        stripped = stripped.rstrip("\r")

    return len(stripped)


def _map_positions_to_result(
    line: _StrLike,
    search_dict: dict[_StrLike, _BadChar],
    new_line: _StrLike,
    byte_str_length: int = 1,
) -> dict[int, _BadChar]:
    """Get all occurrences of search dict keys within line.

    Ignores Windows end of line and can handle bytes as well as string.
    Also takes care of encodings for which the length of an encoded code point does not
    default to 8 Bit.
    """

    result: dict[int, _BadChar] = {}

    for search_for, char in search_dict.items():
        if search_for not in line:
            continue

        # Special Handling for Windows '\r\n'
        if char.unescaped == "\r" and line.endswith(new_line):
            ignore_pos = len(line) - 2 * byte_str_length
        else:
            ignore_pos = None

        start = 0
        pos = line.find(search_for, start)
        while pos > 0:
            if pos != ignore_pos:
                # Calculate the column
                col = int(pos / byte_str_length)
                result[col] = char
            start = pos + 1
            pos = line.find(search_for, start)

    return result


UNICODE_BOMS = {
    "utf-8": codecs.BOM_UTF8,
    "utf-16": codecs.BOM_UTF16,
    "utf-32": codecs.BOM_UTF32,
    "utf-16le": codecs.BOM_UTF16_LE,
    "utf-16be": codecs.BOM_UTF16_BE,
    "utf-32le": codecs.BOM_UTF32_LE,
    "utf-32be": codecs.BOM_UTF32_BE,
}
BOM_SORTED_TO_CODEC = OrderedDict(
    # Sorted by length of BOM of each codec
    (UNICODE_BOMS[codec], codec)
    for codec in ("utf-32le", "utf-32be", "utf-8", "utf-16le", "utf-16be")
)

UTF_NAME_REGEX_COMPILED = re.compile(
    "utf[ -]?(8|16|32)[ -]?(le|be|)?(sig)?", flags=re.IGNORECASE
)


def _normalize_codec_name(codec: str) -> str:
    """Make sure the codec name is always given as defined in the BOM dict."""
    return UTF_NAME_REGEX_COMPILED.sub(r"utf-\1\2", codec).lower()


def _remove_bom(encoded: bytes, encoding: str) -> bytes:
    """Remove the bom if given from a line."""
    if encoding not in UNICODE_BOMS:
        return encoded
    bom = UNICODE_BOMS[encoding]
    if encoded.startswith(bom):
        return encoded[len(bom) :]
    return encoded


def _encode_without_bom(string: str, encoding: str) -> bytes:
    """Encode a string but remove the BOM."""
    return _remove_bom(string.encode(encoding), encoding)


def _byte_to_str_length(codec: str) -> int:
    """Return how many byte are usually(!) a character point."""
    if codec.startswith("utf-32"):
        return 4
    if codec.startswith("utf-16"):
        return 2

    return 1


@lru_cache(maxsize=1000)
def _cached_encode_search(string: str, encoding: str) -> bytes:
    """A cached version of encode used for search pattern."""
    return _encode_without_bom(string, encoding)


def _fix_utf16_32_line_stream(steam: Iterable[bytes], codec: str) -> Iterable[bytes]:
    """Handle line ending for UTF16 and UTF32 correctly.

    Currently, Python simply strips the required zeros after \n after the
    line ending. Leading to lines that can't be decoded properly
    """
    if not codec.startswith("utf-16") and not codec.startswith("utf-32"):
        yield from steam
    else:
        # First we get all the bytes in memory
        content = b"".join(line for line in steam)

        new_line = _cached_encode_search("\n", codec)

        # Now we split the line by the real new line in the correct encoding
        # we can't use split as it would strip the \n that we need
        start = 0
        while True:
            pos = content.find(new_line, start)
            if pos >= 0:
                yield content[start : pos + len(new_line)]
            else:
                # Yield the rest and finish
                if content[start:]:
                    yield content[start:]
                break

            start = pos + len(new_line)


def extract_codec_from_bom(first_line: bytes) -> str:
    """Try to extract the codec (unicode only) by checking for the BOM.

    For details about BOM see https://unicode.org/faq/utf_bom.html#BOM

    Args:
        first_line: the first line of a file

    Returns:
        a codec name

    Raises:
        ValueError: if no codec was found
    """
    for bom, codec in BOM_SORTED_TO_CODEC.items():
        if first_line.startswith(bom):
            return codec

    raise ValueError("No BOM found. Could not detect Unicode codec.")


class UnicodeChecker(checkers.BaseRawFileChecker):
    """Check characters that could be used to hide bad code to humans.

    This includes:

    - Bidirectional Unicode (see https://trojansource.codes/)

    - Bad ASCII characters (see PEP672)

        If a programmer requires to use such a character they should use the escaped
        version, that is also much easier to read and does not depend on the editor used.

    The Checker also includes a check that UTF-16 and UTF-32 are not used to encode
    Python files.

    At the time of writing Python supported only UTF-8. See
    https://stackoverflow.com/questions/69897842/ and https://bugs.python.org/issue1503789
    for background.
    """

    name = "unicode_checker"

    msgs = {
        "E2501": (
            # This error will be only displayed to users once Python Supports
            # UTF-16/UTF-32 (if at all)
            "UTF-16 and UTF-32 aren't backward compatible. Use UTF-8 instead",
            "invalid-unicode-codec",
            (
                "For compatibility use UTF-8 instead of UTF-16/UTF-32. "
                "See also https://bugs.python.org/issue1503789 for a history "
                "of this issue. And "
                "https://softwareengineering.stackexchange.com/questions/102205/ "
                "for some possible problems when using UTF-16 for instance."
            ),
        ),
        "E2502": (
            (
                "Contains control characters that can permit obfuscated code "
                "executed differently than displayed"
            ),
            "bidirectional-unicode",
            (
                "bidirectional unicode are typically not displayed characters required "
                "to display right-to-left (RTL) script "
                "(i.e. Chinese, Japanese, Arabic, Hebrew, ...) correctly. "
                "So can you trust this code? "
                "Are you sure it displayed correctly in all editors? "
                "If you did not write it or your language is not RTL,"
                " remove the special characters, as they could be used to trick you into "
                "executing code, "
                "that does something else than what it looks like.\n"
                "More Information:\n"
                "https://en.wikipedia.org/wiki/Bidirectional_text\n"
                "https://trojansource.codes/"
            ),
        ),
        "C2503": (
            "PEP8 recommends UTF-8 as encoding for Python files",
            "bad-file-encoding",
            (
                "PEP8 recommends UTF-8 default encoding for Python files. See "
                "https://peps.python.org/pep-0008/#source-file-encoding"
            ),
        ),
        **{
            bad_char.code: (
                bad_char.description(),
                bad_char.human_code(),
                bad_char.help_text,
            )
            for bad_char in BAD_CHARS
        },
    }

    @staticmethod
    def _is_invalid_codec(codec: str) -> bool:
        return codec.startswith("utf-16") or codec.startswith("utf-32")

    @staticmethod
    def _is_unicode(codec: str) -> bool:
        return codec.startswith("utf")

    @classmethod
    def _find_line_matches(cls, line: bytes, codec: str) -> dict[int, _BadChar]:
        """Find all matches of BAD_CHARS within line.

        Args:
            line: the input
            codec: that will be used to convert line/or search string into

        Return:
            A dictionary with the column offset and the BadASCIIChar
        """
        # We try to decode in Unicode to get the correct column offset
        # if we would use bytes, it could be off because UTF-8 has no fixed length
        try:
            line_search = line.decode(codec, errors="strict")
            search_dict = BAD_ASCII_SEARCH_DICT
            return _map_positions_to_result(line_search, search_dict, "\n")
        except UnicodeDecodeError:
            # If we can't decode properly, we simply use bytes, even so the column offsets
            # might be wrong a bit, but it is still better then nothing
            line_search_byte = line
            search_dict_byte: dict[bytes, _BadChar] = {}
            for char in BAD_CHARS:
                # Some characters might not exist in all encodings
                with contextlib.suppress(UnicodeDecodeError):
                    search_dict_byte[
                        _cached_encode_search(char.unescaped, codec)
                    ] = char

            return _map_positions_to_result(
                line_search_byte,
                search_dict_byte,
                _cached_encode_search("\n", codec),
                byte_str_length=_byte_to_str_length(codec),
            )

    @staticmethod
    def _determine_codec(stream: io.BytesIO) -> tuple[str, int]:
        """Determine the codec from the given stream.

        first tries https://www.python.org/dev/peps/pep-0263/
        and if this fails also checks for BOMs of UTF-16 and UTF-32
        to be future-proof.

        Args:
            stream: The byte stream to analyse

        Returns: A tuple consisting of:
                  - normalized codec name
                  - the line in which the codec was found

        Raises:
            SyntaxError: if failing to detect codec
        """
        try:
            # First try to detect encoding with PEP 263
            # Doesn't work with UTF-16/32 at the time of writing
            # see https://bugs.python.org/issue1503789
            codec, lines = detect_encoding(stream.readline)

            # lines are empty if UTF-8 BOM is found
            codec_definition_line = len(lines) or 1
        except SyntaxError as e:
            # Codec could not be detected by Python, we try manually to check for
            # UTF 16/32 BOMs, which aren't supported by Python at the time of writing.
            # This is only included to be future save and handle these codecs as well
            stream.seek(0)
            try:
                codec = extract_codec_from_bom(stream.readline())
                codec_definition_line = 1
            except ValueError as ve:
                # Failed to detect codec, so the syntax error originated not from
                # UTF16/32 codec usage. So simply raise the error again.
                raise e from ve

        return _normalize_codec_name(codec), codec_definition_line

    def _check_codec(self, codec: str, codec_definition_line: int) -> None:
        """Check validity of the codec."""
        if codec != "utf-8":
            msg = "bad-file-encoding"
            if self._is_invalid_codec(codec):
                msg = "invalid-unicode-codec"
            self.add_message(
                msg,
                # Currently Nodes will lead to crashes of pylint
                # node=node,
                line=codec_definition_line,
                end_lineno=codec_definition_line,
                confidence=pylint.interfaces.HIGH,
                col_offset=None,
                end_col_offset=None,
            )

    def _check_invalid_chars(self, line: bytes, lineno: int, codec: str) -> None:
        """Look for chars considered bad."""
        matches = self._find_line_matches(line, codec)
        for col, char in matches.items():
            self.add_message(
                char.human_code(),
                # Currently Nodes will lead to crashes of pylint
                # node=node,
                line=lineno,
                end_lineno=lineno,
                confidence=pylint.interfaces.HIGH,
                col_offset=col + 1,
                end_col_offset=col + len(char.unescaped) + 1,
            )

    def _check_bidi_chars(self, line: bytes, lineno: int, codec: str) -> None:
        """Look for Bidirectional Unicode, if we use unicode."""
        if not self._is_unicode(codec):
            return
        for dangerous in BIDI_UNICODE:
            if _cached_encode_search(dangerous, codec) in line:
                # Note that we don't add a col_offset on purpose:
                #   Using these unicode characters it depends on the editor
                #   how it displays the location of characters in the line.
                #   So we mark the complete line.
                self.add_message(
                    "bidirectional-unicode",
                    # Currently Nodes will lead to crashes of pylint
                    # node=node,
                    line=lineno,
                    end_lineno=lineno,
                    # We mark the complete line, as bidi controls make it hard
                    # to determine the correct cursor position within an editor
                    col_offset=0,
                    end_col_offset=_line_length(line, codec),
                    confidence=pylint.interfaces.HIGH,
                )
                # We look for bidirectional unicode only once per line
                # as we mark the complete line anyway
                break

    def process_module(self, node: nodes.Module) -> None:
        """Perform the actual check by checking module stream."""
        with node.stream() as stream:
            codec, codec_line = self._determine_codec(stream)
            self._check_codec(codec, codec_line)

            stream.seek(0)

            # Check for invalid content (controls/chars)
            for lineno, line in enumerate(
                _fix_utf16_32_line_stream(stream, codec), start=1
            ):
                if lineno == 1:
                    line = _remove_bom(line, codec)
                self._check_bidi_chars(line, lineno, codec)
                self._check_invalid_chars(line, lineno, codec)


def register(linter: pylint.lint.PyLinter) -> None:
    linter.register_checker(UnicodeChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/unicode.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checker for features used that are not supported by all python versions
indicated by the py-version setting.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import (
    only_required_for_messages,
    safe_infer,
    uninferable_final_decorators,
)

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class UnsupportedVersionChecker(BaseChecker):
    """Checker for features that are not supported by all python versions
    indicated by the py-version setting.
    """

    name = "unsupported_version"
    msgs = {
        "W2601": (
            "F-strings are not supported by all versions included in the py-version setting",
            "using-f-string-in-unsupported-version",
            "Used when the py-version set by the user is lower than 3.6 and pylint encounters "
            "an f-string.",
        ),
        "W2602": (
            "typing.final is not supported by all versions included in the py-version setting",
            "using-final-decorator-in-unsupported-version",
            "Used when the py-version set by the user is lower than 3.8 and pylint encounters "
            "a ``typing.final`` decorator.",
        ),
    }

    def open(self) -> None:
        """Initialize visit variables and statistics."""
        py_version = self.linter.config.py_version
        self._py36_plus = py_version >= (3, 6)
        self._py38_plus = py_version >= (3, 8)

    @only_required_for_messages("using-f-string-in-unsupported-version")
    def visit_joinedstr(self, node: nodes.JoinedStr) -> None:
        """Check f-strings."""
        if not self._py36_plus:
            self.add_message("using-f-string-in-unsupported-version", node=node)

    @only_required_for_messages("using-final-decorator-in-unsupported-version")
    def visit_decorators(self, node: nodes.Decorators) -> None:
        """Check decorators."""
        self._check_typing_final(node)

    def _check_typing_final(self, node: nodes.Decorators) -> None:
        """Add a message when the `typing.final` decorator is used and the
        py-version is lower than 3.8.
        """
        if self._py38_plus:
            return

        decorators = []
        for decorator in node.get_children():
            inferred = safe_infer(decorator)
            if inferred and inferred.qname() == "typing.final":
                decorators.append(decorator)

        for decorator in decorators or uninferable_final_decorators(node):
            self.add_message(
                "using-final-decorator-in-unsupported-version", node=decorator
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(UnsupportedVersionChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/unsupported_version.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Some functions that may be useful for various checkers."""

from __future__ import annotations

import builtins
import fnmatch
import itertools
import numbers
import re
import string
from collections.abc import Iterable, Iterator
from functools import lru_cache, partial
from re import Match
from typing import TYPE_CHECKING, Any, Callable, TypeVar

import _string
import astroid.objects
from astroid import TooManyLevelsError, nodes, util
from astroid.context import InferenceContext
from astroid.exceptions import AstroidError
from astroid.nodes._base_nodes import ImportNode
from astroid.typing import InferenceResult, SuccessfulInferenceResult

if TYPE_CHECKING:
    from functools import _lru_cache_wrapper

    from pylint.checkers import BaseChecker

_NodeT = TypeVar("_NodeT", bound=nodes.NodeNG)
_CheckerT = TypeVar("_CheckerT", bound="BaseChecker")
AstCallbackMethod = Callable[[_CheckerT, _NodeT], None]

COMP_NODE_TYPES = (
    nodes.ListComp,
    nodes.SetComp,
    nodes.DictComp,
    nodes.GeneratorExp,
)
EXCEPTIONS_MODULE = "builtins"
ABC_MODULES = {"abc", "_py_abc"}
ABC_METHODS = {
    "abc.abstractproperty",
    "abc.abstractmethod",
    "abc.abstractclassmethod",
    "abc.abstractstaticmethod",
}
TYPING_PROTOCOLS = frozenset(
    {"typing.Protocol", "typing_extensions.Protocol", ".Protocol"}
)
COMMUTATIVE_OPERATORS = frozenset({"*", "+", "^", "&", "|"})
ITER_METHOD = "__iter__"
AITER_METHOD = "__aiter__"
NEXT_METHOD = "__next__"
GETITEM_METHOD = "__getitem__"
CLASS_GETITEM_METHOD = "__class_getitem__"
SETITEM_METHOD = "__setitem__"
DELITEM_METHOD = "__delitem__"
CONTAINS_METHOD = "__contains__"
KEYS_METHOD = "keys"

# Dictionary which maps the number of expected parameters a
# special method can have to a set of special methods.
# The following keys are used to denote the parameters restrictions:
#
# * None: variable number of parameters
# * number: exactly that number of parameters
# * tuple: these are the odd ones. Basically it means that the function
#          can work with any number of arguments from that tuple,
#          although it's best to implement it in order to accept
#          all of them.
_SPECIAL_METHODS_PARAMS = {
    None: ("__new__", "__init__", "__call__", "__init_subclass__"),
    0: (
        "__del__",
        "__repr__",
        "__str__",
        "__bytes__",
        "__hash__",
        "__bool__",
        "__dir__",
        "__len__",
        "__length_hint__",
        "__iter__",
        "__reversed__",
        "__neg__",
        "__pos__",
        "__abs__",
        "__invert__",
        "__complex__",
        "__int__",
        "__float__",
        "__index__",
        "__trunc__",
        "__floor__",
        "__ceil__",
        "__enter__",
        "__aenter__",
        "__getnewargs_ex__",
        "__getnewargs__",
        "__getstate__",
        "__reduce__",
        "__copy__",
        "__unicode__",
        "__nonzero__",
        "__await__",
        "__aiter__",
        "__anext__",
        "__fspath__",
        "__subclasses__",
    ),
    1: (
        "__format__",
        "__lt__",
        "__le__",
        "__eq__",
        "__ne__",
        "__gt__",
        "__ge__",
        "__getattr__",
        "__getattribute__",
        "__delattr__",
        "__delete__",
        "__instancecheck__",
        "__subclasscheck__",
        "__getitem__",
        "__missing__",
        "__delitem__",
        "__contains__",
        "__add__",
        "__sub__",
        "__mul__",
        "__truediv__",
        "__floordiv__",
        "__rfloordiv__",
        "__mod__",
        "__divmod__",
        "__lshift__",
        "__rshift__",
        "__and__",
        "__xor__",
        "__or__",
        "__radd__",
        "__rsub__",
        "__rmul__",
        "__rtruediv__",
        "__rmod__",
        "__rdivmod__",
        "__rpow__",
        "__rlshift__",
        "__rrshift__",
        "__rand__",
        "__rxor__",
        "__ror__",
        "__iadd__",
        "__isub__",
        "__imul__",
        "__itruediv__",
        "__ifloordiv__",
        "__imod__",
        "__ilshift__",
        "__irshift__",
        "__iand__",
        "__ixor__",
        "__ior__",
        "__ipow__",
        "__setstate__",
        "__reduce_ex__",
        "__deepcopy__",
        "__cmp__",
        "__matmul__",
        "__rmatmul__",
        "__imatmul__",
        "__div__",
    ),
    2: ("__setattr__", "__get__", "__set__", "__setitem__", "__set_name__"),
    3: ("__exit__", "__aexit__"),
    (0, 1): ("__round__",),
    (1, 2): ("__pow__",),
}

SPECIAL_METHODS_PARAMS = {
    name: params
    for params, methods in _SPECIAL_METHODS_PARAMS.items()
    for name in methods
}
PYMETHODS = set(SPECIAL_METHODS_PARAMS)

SUBSCRIPTABLE_CLASSES_PEP585 = frozenset(
    (
        "builtins.tuple",
        "builtins.list",
        "builtins.dict",
        "builtins.set",
        "builtins.frozenset",
        "builtins.type",
        "collections.deque",
        "collections.defaultdict",
        "collections.OrderedDict",
        "collections.Counter",
        "collections.ChainMap",
        "_collections_abc.Awaitable",
        "_collections_abc.Coroutine",
        "_collections_abc.AsyncIterable",
        "_collections_abc.AsyncIterator",
        "_collections_abc.AsyncGenerator",
        "_collections_abc.Iterable",
        "_collections_abc.Iterator",
        "_collections_abc.Generator",
        "_collections_abc.Reversible",
        "_collections_abc.Container",
        "_collections_abc.Collection",
        "_collections_abc.Callable",
        "_collections_abc.Set",
        "_collections_abc.MutableSet",
        "_collections_abc.Mapping",
        "_collections_abc.MutableMapping",
        "_collections_abc.Sequence",
        "_collections_abc.MutableSequence",
        "_collections_abc.ByteString",
        "_collections_abc.MappingView",
        "_collections_abc.KeysView",
        "_collections_abc.ItemsView",
        "_collections_abc.ValuesView",
        "contextlib.AbstractContextManager",
        "contextlib.AbstractAsyncContextManager",
        "re.Pattern",
        "re.Match",
    )
)

SINGLETON_VALUES = {True, False, None}

TERMINATING_FUNCS_QNAMES = frozenset(
    {"_sitebuiltins.Quitter", "sys.exit", "posix._exit", "nt._exit"}
)


class NoSuchArgumentError(Exception):
    pass


class InferredTypeError(Exception):
    pass


def get_all_elements(
    node: nodes.NodeNG,
) -> Iterable[nodes.NodeNG]:
    """Recursively returns all atoms in nested lists and tuples."""
    if isinstance(node, (nodes.Tuple, nodes.List)):
        for child in node.elts:
            yield from get_all_elements(child)
    else:
        yield node


def is_super(node: nodes.NodeNG) -> bool:
    """Return True if the node is referencing the "super" builtin function."""
    if getattr(node, "name", None) == "super" and node.root().name == "builtins":
        return True
    return False


def is_error(node: nodes.FunctionDef) -> bool:
    """Return true if the given function node only raises an exception."""
    return len(node.body) == 1 and isinstance(node.body[0], nodes.Raise)


builtins = builtins.__dict__.copy()  # type: ignore[assignment]
SPECIAL_BUILTINS = ("__builtins__",)  # '__path__', '__file__')


def is_builtin_object(node: nodes.NodeNG) -> bool:
    """Returns True if the given node is an object from the __builtin__ module."""
    return node and node.root().name == "builtins"  # type: ignore[no-any-return]


def is_builtin(name: str) -> bool:
    """Return true if <name> could be considered as a builtin defined by python."""
    return name in builtins or name in SPECIAL_BUILTINS  # type: ignore[operator]


def is_defined_in_scope(
    var_node: nodes.NodeNG,
    varname: str,
    scope: nodes.NodeNG,
) -> bool:
    return defnode_in_scope(var_node, varname, scope) is not None


# pylint: disable = too-many-branches
def defnode_in_scope(
    var_node: nodes.NodeNG,
    varname: str,
    scope: nodes.NodeNG,
) -> nodes.NodeNG | None:
    if isinstance(scope, nodes.If):
        for node in scope.body:
            if isinstance(node, nodes.Nonlocal) and varname in node.names:
                return node
            if isinstance(node, nodes.Assign):
                for target in node.targets:
                    if isinstance(target, nodes.AssignName) and target.name == varname:
                        return target
    elif isinstance(scope, (COMP_NODE_TYPES, nodes.For)):
        for ass_node in scope.nodes_of_class(nodes.AssignName):
            if ass_node.name == varname:
                return ass_node
    elif isinstance(scope, nodes.With):
        for expr, ids in scope.items:
            if expr.parent_of(var_node):
                break
            if ids and isinstance(ids, nodes.AssignName) and ids.name == varname:
                return ids
    elif isinstance(scope, (nodes.Lambda, nodes.FunctionDef)):
        if scope.args.is_argument(varname):
            # If the name is found inside a default value
            # of a function, then let the search continue
            # in the parent's tree.
            if scope.args.parent_of(var_node):
                try:
                    scope.args.default_value(varname)
                    scope = scope.parent
                    defnode = defnode_in_scope(var_node, varname, scope)
                except astroid.NoDefault:
                    pass
                else:
                    return defnode
            return scope
        if getattr(scope, "name", None) == varname:
            return scope
    elif isinstance(scope, nodes.ExceptHandler):
        if isinstance(scope.name, nodes.AssignName):
            ass_node = scope.name
            if ass_node.name == varname:
                return ass_node
    return None


def is_defined_before(var_node: nodes.Name) -> bool:
    """Check if the given variable node is defined before.

    Verify that the variable node is defined by a parent node
    (e.g. if or with) earlier than `var_node`, or is defined by a
    (list, set, dict, or generator comprehension, lambda)
    or in a previous sibling node on the same line
    (statement_defining ; statement_using).
    """
    varname = var_node.name
    for parent in var_node.node_ancestors():
        defnode = defnode_in_scope(var_node, varname, parent)
        if defnode is None:
            continue
        defnode_scope = defnode.scope()
        if isinstance(
            defnode_scope, (*COMP_NODE_TYPES, nodes.Lambda, nodes.FunctionDef)
        ):
            # Avoid the case where var_node_scope is a nested function
            if isinstance(defnode_scope, nodes.FunctionDef):
                var_node_scope = var_node.scope()
                if var_node_scope is not defnode_scope and isinstance(
                    var_node_scope, nodes.FunctionDef
                ):
                    return False
            return True
        if defnode.lineno < var_node.lineno:
            return True
        # `defnode` and `var_node` on the same line
        for defnode_anc in defnode.node_ancestors():
            if defnode_anc.lineno != var_node.lineno:
                continue
            if isinstance(
                defnode_anc,
                (
                    nodes.For,
                    nodes.While,
                    nodes.With,
                    nodes.Try,
                    nodes.ExceptHandler,
                ),
            ):
                return True
    # possibly multiple statements on the same line using semicolon separator
    stmt = var_node.statement()
    _node = stmt.previous_sibling()
    lineno = stmt.fromlineno
    while _node and _node.fromlineno == lineno:
        for assign_node in _node.nodes_of_class(nodes.AssignName):
            if assign_node.name == varname:
                return True
        for imp_node in _node.nodes_of_class((nodes.ImportFrom, nodes.Import)):
            if varname in [name[1] or name[0] for name in imp_node.names]:
                return True
        _node = _node.previous_sibling()
    return False


def is_default_argument(node: nodes.NodeNG, scope: nodes.NodeNG | None = None) -> bool:
    """Return true if the given Name node is used in function or lambda
    default argument's value.
    """
    if not scope:
        scope = node.scope()
    if isinstance(scope, (nodes.FunctionDef, nodes.Lambda)):
        all_defaults = itertools.chain(
            scope.args.defaults, (d for d in scope.args.kw_defaults if d is not None)
        )
        return any(
            default_name_node is node
            for default_node in all_defaults
            for default_name_node in default_node.nodes_of_class(nodes.Name)
        )

    return False


def is_func_decorator(node: nodes.NodeNG) -> bool:
    """Return true if the name is used in function decorator."""
    for parent in node.node_ancestors():
        if isinstance(parent, nodes.Decorators):
            return True
        if parent.is_statement or isinstance(
            parent,
            (
                nodes.Lambda,
                nodes.ComprehensionScope,
                nodes.ListComp,
            ),
        ):
            break
    return False


def is_ancestor_name(frame: nodes.ClassDef, node: nodes.NodeNG) -> bool:
    """Return whether `frame` is an astroid.Class node with `node` in the
    subtree of its bases attribute.
    """
    if not isinstance(frame, nodes.ClassDef):
        return False
    return any(node in base.nodes_of_class(nodes.Name) for base in frame.bases)


def is_being_called(node: nodes.NodeNG) -> bool:
    """Return True if node is the function being called in a Call node."""
    return isinstance(node.parent, nodes.Call) and node.parent.func is node


def assign_parent(node: nodes.NodeNG) -> nodes.NodeNG:
    """Return the higher parent which is not an AssignName, Tuple or List node."""
    while node and isinstance(node, (nodes.AssignName, nodes.Tuple, nodes.List)):
        node = node.parent
    return node


def overrides_a_method(class_node: nodes.ClassDef, name: str) -> bool:
    """Return True if <name> is a method overridden from an ancestor
    which is not the base object class.
    """
    for ancestor in class_node.ancestors():
        if ancestor.name == "object":
            continue
        if name in ancestor and isinstance(ancestor[name], nodes.FunctionDef):
            return True
    return False


def only_required_for_messages(
    *messages: str,
) -> Callable[
    [AstCallbackMethod[_CheckerT, _NodeT]], AstCallbackMethod[_CheckerT, _NodeT]
]:
    """Decorator to store messages that are handled by a checker method as an
    attribute of the function object.

    This information is used by ``ASTWalker`` to decide whether to call the decorated
    method or not. If none of the messages is enabled, the method will be skipped.
    Therefore, the list of messages must be well maintained at all times!
    This decorator only has an effect on ``visit_*`` and ``leave_*`` methods
    of a class inheriting from ``BaseChecker``.
    """

    def store_messages(
        func: AstCallbackMethod[_CheckerT, _NodeT]
    ) -> AstCallbackMethod[_CheckerT, _NodeT]:
        func.checks_msgs = messages  # type: ignore[attr-defined]
        return func

    return store_messages


class IncompleteFormatString(Exception):
    """A format string ended in the middle of a format specifier."""


class UnsupportedFormatCharacter(Exception):
    """A format character in a format string is not one of the supported
    format characters.
    """

    def __init__(self, index: int) -> None:
        super().__init__(index)
        self.index = index


def parse_format_string(
    format_string: str,
) -> tuple[set[str], int, dict[str, str], list[str]]:
    """Parses a format string, returning a tuple (keys, num_args).

    Where 'keys' is the set of mapping keys in the format string, and 'num_args' is the number
    of arguments required by the format string. Raises IncompleteFormatString or
    UnsupportedFormatCharacter if a parse error occurs.
    """
    keys = set()
    key_types = {}
    pos_types = []
    num_args = 0

    def next_char(i: int) -> tuple[int, str]:
        i += 1
        if i == len(format_string):
            raise IncompleteFormatString
        return (i, format_string[i])

    i = 0
    while i < len(format_string):
        char = format_string[i]
        if char == "%":
            i, char = next_char(i)
            # Parse the mapping key (optional).
            key = None
            if char == "(":
                depth = 1
                i, char = next_char(i)
                key_start = i
                while depth != 0:
                    if char == "(":
                        depth += 1
                    elif char == ")":
                        depth -= 1
                    i, char = next_char(i)
                key_end = i - 1
                key = format_string[key_start:key_end]

            # Parse the conversion flags (optional).
            while char in "#0- +":
                i, char = next_char(i)
            # Parse the minimum field width (optional).
            if char == "*":
                num_args += 1
                i, char = next_char(i)
            else:
                while char in string.digits:
                    i, char = next_char(i)
            # Parse the precision (optional).
            if char == ".":
                i, char = next_char(i)
                if char == "*":
                    num_args += 1
                    i, char = next_char(i)
                else:
                    while char in string.digits:
                        i, char = next_char(i)
            # Parse the length modifier (optional).
            if char in "hlL":
                i, char = next_char(i)
            # Parse the conversion type (mandatory).
            flags = "diouxXeEfFgGcrs%a"
            if char not in flags:
                raise UnsupportedFormatCharacter(i)
            if key:
                keys.add(key)
                key_types[key] = char
            elif char != "%":
                num_args += 1
                pos_types.append(char)
        i += 1
    return keys, num_args, key_types, pos_types


def split_format_field_names(
    format_string: str,
) -> tuple[str, Iterable[tuple[bool, str]]]:
    try:
        return _string.formatter_field_name_split(format_string)  # type: ignore[no-any-return]
    except ValueError as e:
        raise IncompleteFormatString() from e


def collect_string_fields(format_string: str) -> Iterable[str | None]:
    """Given a format string, return an iterator
    of all the valid format fields.

    It handles nested fields as well.
    """
    formatter = string.Formatter()
    # pylint: disable = too-many-try-statements
    try:
        parseiterator = formatter.parse(format_string)
        for result in parseiterator:
            if all(item is None for item in result[1:]):
                # not a replacement format
                continue
            name = result[1]
            nested = result[2]
            yield name
            if nested:
                yield from collect_string_fields(nested)
    except ValueError as exc:
        # Probably the format string is invalid.
        if exc.args[0].startswith("cannot switch from manual"):
            # On Jython, parsing a string with both manual
            # and automatic positions will fail with a ValueError,
            # while on CPython it will simply return the fields,
            # the validation being done in the interpreter (?).
            # We're just returning two mixed fields in order
            # to trigger the format-combined-specification check.
            yield ""
            yield "1"
            return
        raise IncompleteFormatString(format_string) from exc


def parse_format_method_string(
    format_string: str,
) -> tuple[list[tuple[str, list[tuple[bool, str]]]], int, int]:
    """Parses a PEP 3101 format string, returning a tuple of
    (keyword_arguments, implicit_pos_args_cnt, explicit_pos_args).

    keyword_arguments is the set of mapping keys in the format string, implicit_pos_args_cnt
    is the number of arguments required by the format string and
    explicit_pos_args is the number of arguments passed with the position.
    """
    keyword_arguments = []
    implicit_pos_args_cnt = 0
    explicit_pos_args = set()
    for name in collect_string_fields(format_string):
        if name and str(name).isdigit():
            explicit_pos_args.add(str(name))
        elif name:
            keyname, fielditerator = split_format_field_names(name)
            if isinstance(keyname, numbers.Number):
                explicit_pos_args.add(str(keyname))
            try:
                keyword_arguments.append((keyname, list(fielditerator)))
            except ValueError as e:
                raise IncompleteFormatString() from e
        else:
            implicit_pos_args_cnt += 1
    return keyword_arguments, implicit_pos_args_cnt, len(explicit_pos_args)


def is_attr_protected(attrname: str) -> bool:
    """Return True if attribute name is protected (start with _ and some other
    details), False otherwise.
    """
    return (
        attrname[0] == "_"
        and attrname != "_"
        and not (attrname.startswith("__") and attrname.endswith("__"))
    )


def node_frame_class(node: nodes.NodeNG) -> nodes.ClassDef | None:
    """Return the class that is wrapping the given node.

    The function returns a class for a method node (or a staticmethod or a
    classmethod), otherwise it returns `None`.
    """
    klass = node.frame()
    nodes_to_check = (
        nodes.NodeNG,
        astroid.UnboundMethod,
        astroid.BaseInstance,
    )
    while (
        klass
        and isinstance(klass, nodes_to_check)
        and not isinstance(klass, nodes.ClassDef)
    ):
        if klass.parent is None:
            return None

        klass = klass.parent.frame()

    return klass


def get_outer_class(class_node: astroid.ClassDef) -> astroid.ClassDef | None:
    """Return the class that is the outer class of given (nested) class_node."""
    parent_klass = class_node.parent.frame()

    return parent_klass if isinstance(parent_klass, astroid.ClassDef) else None


def is_attr_private(attrname: str) -> Match[str] | None:
    """Check that attribute name is private (at least two leading underscores,
    at most one trailing underscore).
    """
    regex = re.compile("^_{2,10}.*[^_]+_?$")
    return regex.match(attrname)


def get_argument_from_call(
    call_node: nodes.Call, position: int | None = None, keyword: str | None = None
) -> nodes.Name:
    """Returns the specified argument from a function call.

    :param nodes.Call call_node: Node representing a function call to check.
    :param int position: position of the argument.
    :param str keyword: the keyword of the argument.

    :returns: The node representing the argument, None if the argument is not found.
    :rtype: nodes.Name
    :raises ValueError: if both position and keyword are None.
    :raises NoSuchArgumentError: if no argument at the provided position or with
    the provided keyword.
    """
    if position is None and keyword is None:
        raise ValueError("Must specify at least one of: position or keyword.")
    if position is not None:
        try:
            return call_node.args[position]
        except IndexError:
            pass
    if keyword and call_node.keywords:
        for arg in call_node.keywords:
            if arg.arg == keyword:
                return arg.value

    raise NoSuchArgumentError


def infer_kwarg_from_call(call_node: nodes.Call, keyword: str) -> nodes.Name | None:
    """Returns the specified argument from a function's kwargs.

    :param nodes.Call call_node: Node representing a function call to check.
    :param str keyword: Name of the argument to be extracted.

    :returns: The node representing the argument, None if the argument is not found.
    :rtype: nodes.Name
    """
    for arg in call_node.kwargs:
        inferred = safe_infer(arg.value)
        if isinstance(inferred, nodes.Dict):
            for item in inferred.items:
                if item[0].value == keyword:
                    return item[1]

    return None


def inherit_from_std_ex(node: nodes.NodeNG | astroid.Instance) -> bool:
    """Return whether the given class node is subclass of
    exceptions.Exception.
    """
    ancestors = node.ancestors() if hasattr(node, "ancestors") else []
    return any(
        ancestor.name in {"Exception", "BaseException"}
        and ancestor.root().name == EXCEPTIONS_MODULE
        for ancestor in itertools.chain([node], ancestors)
    )


def error_of_type(
    handler: nodes.ExceptHandler,
    error_type: str | type[Exception] | tuple[str | type[Exception], ...],
) -> bool:
    """Check if the given exception handler catches
    the given error_type.

    The *handler* parameter is a node, representing an ExceptHandler node.
    The *error_type* can be an exception, such as AttributeError,
    the name of an exception, or it can be a tuple of errors.
    The function will return True if the handler catches any of the
    given errors.
    """

    def stringify_error(error: str | type[Exception]) -> str:
        if not isinstance(error, str):
            return error.__name__
        return error

    if not isinstance(error_type, tuple):
        error_type = (error_type,)
    expected_errors = {stringify_error(error) for error in error_type}
    if not handler.type:
        return False
    return handler.catch(expected_errors)  # type: ignore[no-any-return]


def decorated_with_property(node: nodes.FunctionDef) -> bool:
    """Detect if the given function node is decorated with a property."""
    if not node.decorators:
        return False
    for decorator in node.decorators.nodes:
        try:
            if _is_property_decorator(decorator):
                return True
        except astroid.InferenceError:
            pass
    return False


def _is_property_kind(node: nodes.NodeNG, *kinds: str) -> bool:
    if not isinstance(node, (astroid.UnboundMethod, nodes.FunctionDef)):
        return False
    if node.decorators:
        for decorator in node.decorators.nodes:
            if isinstance(decorator, nodes.Attribute) and decorator.attrname in kinds:
                return True
    return False


def is_property_setter(node: nodes.NodeNG) -> bool:
    """Check if the given node is a property setter."""
    return _is_property_kind(node, "setter")


def is_property_deleter(node: nodes.NodeNG) -> bool:
    """Check if the given node is a property deleter."""
    return _is_property_kind(node, "deleter")


def is_property_setter_or_deleter(node: nodes.NodeNG) -> bool:
    """Check if the given node is either a property setter or a deleter."""
    return _is_property_kind(node, "setter", "deleter")


def _is_property_decorator(decorator: nodes.Name) -> bool:
    for inferred in decorator.infer():
        if isinstance(inferred, nodes.ClassDef):
            if inferred.qname() in {"builtins.property", "functools.cached_property"}:
                return True
            for ancestor in inferred.ancestors():
                if ancestor.name == "property" and ancestor.root().name == "builtins":
                    return True
        elif isinstance(inferred, nodes.FunctionDef):
            # If decorator is function, check if it has exactly one return
            # and the return is itself a function decorated with property
            returns: list[nodes.Return] = list(
                inferred._get_return_nodes_skip_functions()
            )
            if len(returns) == 1 and isinstance(
                returns[0].value, (nodes.Name, nodes.Attribute)
            ):
                inferred = safe_infer(returns[0].value)
                if (
                    inferred
                    and isinstance(inferred, astroid.objects.Property)
                    and isinstance(inferred.function, nodes.FunctionDef)
                ):
                    return decorated_with_property(inferred.function)
    return False


def decorated_with(
    func: (
        nodes.ClassDef | nodes.FunctionDef | astroid.BoundMethod | astroid.UnboundMethod
    ),
    qnames: Iterable[str],
) -> bool:
    """Determine if the `func` node has a decorator with the qualified name `qname`."""
    decorators = func.decorators.nodes if func.decorators else []
    for decorator_node in decorators:
        if isinstance(decorator_node, nodes.Call):
            # We only want to infer the function name
            decorator_node = decorator_node.func
        try:
            if any(
                i.name in qnames or i.qname() in qnames
                for i in decorator_node.infer()
                if i is not None and not isinstance(i, util.UninferableBase)
            ):
                return True
        except astroid.InferenceError:
            continue
    return False


def uninferable_final_decorators(
    node: nodes.Decorators,
) -> list[nodes.Attribute | nodes.Name | None]:
    """Return a list of uninferable `typing.final` decorators in `node`.

    This function is used to determine if the `typing.final` decorator is used
    with an unsupported Python version; the decorator cannot be inferred when
    using a Python version lower than 3.8.
    """
    decorators = []
    for decorator in getattr(node, "nodes", []):
        import_nodes: tuple[nodes.Import | nodes.ImportFrom] | None = None

        # Get the `Import` node. The decorator is of the form: @module.name
        if isinstance(decorator, nodes.Attribute):
            inferred = safe_infer(decorator.expr)
            if isinstance(inferred, nodes.Module) and inferred.qname() == "typing":
                _, import_nodes = decorator.expr.lookup(decorator.expr.name)

        # Get the `ImportFrom` node. The decorator is of the form: @name
        elif isinstance(decorator, nodes.Name):
            _, import_nodes = decorator.lookup(decorator.name)

        # The `final` decorator is expected to be found in the
        # import_nodes. Continue if we don't find any `Import` or `ImportFrom`
        # nodes for this decorator.
        if not import_nodes:
            continue
        import_node = import_nodes[0]

        if not isinstance(import_node, (astroid.Import, astroid.ImportFrom)):
            continue

        import_names = dict(import_node.names)

        # Check if the import is of the form: `from typing import final`
        is_from_import = ("final" in import_names) and import_node.modname == "typing"

        # Check if the import is of the form: `import typing`
        is_import = ("typing" in import_names) and getattr(
            decorator, "attrname", None
        ) == "final"

        if is_from_import or is_import:
            inferred = safe_infer(decorator)
            if inferred is None or isinstance(inferred, util.UninferableBase):
                decorators.append(decorator)
    return decorators


@lru_cache(maxsize=1024)
def unimplemented_abstract_methods(
    node: nodes.ClassDef, is_abstract_cb: nodes.FunctionDef | None = None
) -> dict[str, nodes.FunctionDef]:
    """Get the unimplemented abstract methods for the given *node*.

    A method can be considered abstract if the callback *is_abstract_cb*
    returns a ``True`` value. The check defaults to verifying that
    a method is decorated with abstract methods.
    It will return a dictionary of abstract method
    names and their inferred objects.
    """
    if is_abstract_cb is None:
        is_abstract_cb = partial(decorated_with, qnames=ABC_METHODS)
    visited: dict[str, nodes.FunctionDef] = {}
    try:
        mro = reversed(node.mro())
    except astroid.ResolveError:
        # Probably inconsistent hierarchy, don't try to figure this out here.
        return {}
    for ancestor in mro:
        for obj in ancestor.values():
            inferred = obj
            if isinstance(obj, nodes.AssignName):
                inferred = safe_infer(obj)
                if not inferred:
                    # Might be an abstract function,
                    # but since we don't have enough information
                    # in order to take this decision, we're taking
                    # the *safe* decision instead.
                    if obj.name in visited:
                        del visited[obj.name]
                    continue
                if not isinstance(inferred, nodes.FunctionDef):
                    if obj.name in visited:
                        del visited[obj.name]
            if isinstance(inferred, nodes.FunctionDef):
                # It's critical to use the original name,
                # since after inferring, an object can be something
                # else than expected, as in the case of the
                # following assignment.
                #
                # class A:
                #     def keys(self): pass
                #     __iter__ = keys
                abstract = is_abstract_cb(inferred)
                if abstract:
                    visited[obj.name] = inferred
                elif not abstract and obj.name in visited:
                    del visited[obj.name]
    return visited


def find_try_except_wrapper_node(
    node: nodes.NodeNG,
) -> nodes.ExceptHandler | nodes.Try | None:
    """Return the ExceptHandler or the Try node in which the node is."""
    current = node
    ignores = (nodes.ExceptHandler, nodes.Try)
    while current and not isinstance(current.parent, ignores):
        current = current.parent

    if current and isinstance(current.parent, ignores):
        return current.parent
    return None


def find_except_wrapper_node_in_scope(
    node: nodes.NodeNG,
) -> nodes.ExceptHandler | None:
    """Return the ExceptHandler in which the node is, without going out of scope."""
    for current in node.node_ancestors():
        if isinstance(current, astroid.scoped_nodes.LocalsDictNodeNG):
            # If we're inside a function/class definition, we don't want to keep checking
            # higher ancestors for `except` clauses, because if these exist, it means our
            # function/class was defined in an `except` clause, rather than the current code
            # actually running in an `except` clause.
            return None
        if isinstance(current, nodes.ExceptHandler):
            return current
    return None


def is_from_fallback_block(node: nodes.NodeNG) -> bool:
    """Check if the given node is from a fallback import block."""
    context = find_try_except_wrapper_node(node)
    if not context:
        return False

    if isinstance(context, nodes.ExceptHandler):
        other_body = context.parent.body
        handlers = context.parent.handlers
    else:
        other_body = itertools.chain.from_iterable(
            handler.body for handler in context.handlers
        )
        handlers = context.handlers

    has_fallback_imports = any(
        isinstance(import_node, (nodes.ImportFrom, nodes.Import))
        for import_node in other_body
    )
    ignores_import_error = _except_handlers_ignores_exceptions(
        handlers, (ImportError, ModuleNotFoundError)
    )
    return ignores_import_error or has_fallback_imports


def _except_handlers_ignores_exceptions(
    handlers: nodes.ExceptHandler,
    exceptions: tuple[type[ImportError], type[ModuleNotFoundError]],
) -> bool:
    func = partial(error_of_type, error_type=exceptions)
    return any(func(handler) for handler in handlers)


def get_exception_handlers(
    node: nodes.NodeNG, exception: type[Exception] | str = Exception
) -> list[nodes.ExceptHandler] | None:
    """Return the collections of handlers handling the exception in arguments.

    Args:
        node (nodes.NodeNG): A node that is potentially wrapped in a try except.
        exception (builtin.Exception or str): exception or name of the exception.

    Returns:
        list: the collection of handlers that are handling the exception or None.
    """
    context = find_try_except_wrapper_node(node)
    if isinstance(context, nodes.Try):
        return [
            handler for handler in context.handlers if error_of_type(handler, exception)
        ]
    return []


def get_contextlib_with_statements(node: nodes.NodeNG) -> Iterator[nodes.With]:
    """Get all contextlib.with statements in the ancestors of the given node."""
    for with_node in node.node_ancestors():
        if isinstance(with_node, nodes.With):
            yield with_node


def _suppresses_exception(
    call: nodes.Call, exception: type[Exception] | str = Exception
) -> bool:
    """Check if the given node suppresses the given exception."""
    if not isinstance(exception, str):
        exception = exception.__name__
    for arg in call.args:
        inferred = safe_infer(arg)
        if isinstance(inferred, nodes.ClassDef):
            if inferred.name == exception:
                return True
        elif isinstance(inferred, nodes.Tuple):
            for elt in inferred.elts:
                inferred_elt = safe_infer(elt)
                if (
                    isinstance(inferred_elt, nodes.ClassDef)
                    and inferred_elt.name == exception
                ):
                    return True
    return False


def get_contextlib_suppressors(
    node: nodes.NodeNG, exception: type[Exception] | str = Exception
) -> Iterator[nodes.With]:
    """Return the contextlib suppressors handling the exception.

    Args:
        node (nodes.NodeNG): A node that is potentially wrapped in a contextlib.suppress.
        exception (builtin.Exception): exception or name of the exception.

    Yields:
        nodes.With: A with node that is suppressing the exception.
    """
    for with_node in get_contextlib_with_statements(node):
        for item, _ in with_node.items:
            if isinstance(item, nodes.Call):
                inferred = safe_infer(item.func)
                if (
                    isinstance(inferred, nodes.ClassDef)
                    and inferred.qname() == "contextlib.suppress"
                ):
                    if _suppresses_exception(item, exception):
                        yield with_node


def is_node_inside_try_except(node: nodes.Raise) -> bool:
    """Check if the node is directly under a Try/Except statement
    (but not under an ExceptHandler!).

    Args:
        node (nodes.Raise): the node raising the exception.

    Returns:
        bool: True if the node is inside a try/except statement, False otherwise.
    """
    context = find_try_except_wrapper_node(node)
    return isinstance(context, nodes.Try)


def node_ignores_exception(
    node: nodes.NodeNG, exception: type[Exception] | str = Exception
) -> bool:
    """Check if the node is in a Try which handles the given exception.

    If the exception is not given, the function is going to look for bare
    excepts.
    """
    managing_handlers = get_exception_handlers(node, exception)
    if managing_handlers:
        return True
    return any(get_contextlib_suppressors(node, exception))


@lru_cache(maxsize=1024)
def class_is_abstract(node: nodes.ClassDef) -> bool:
    """Return true if the given class node should be considered as an abstract
    class.
    """
    # Protocol classes are considered "abstract"
    if is_protocol_class(node):
        return True

    # Only check for explicit metaclass=ABCMeta on this specific class
    meta = node.declared_metaclass()
    if meta is not None:
        if meta.name == "ABCMeta" and meta.root().name in ABC_MODULES:
            return True

    for ancestor in node.ancestors():
        if ancestor.name == "ABC" and ancestor.root().name in ABC_MODULES:
            # abc.ABC inheritance
            return True

    for method in node.methods():
        if method.parent.frame() is node:
            if method.is_abstract(pass_is_abstract=False):
                return True
    return False


def _supports_protocol_method(value: nodes.NodeNG, attr: str) -> bool:
    try:
        attributes = value.getattr(attr)
    except astroid.NotFoundError:
        return False

    first = attributes[0]

    # Return False if a constant is assigned
    if isinstance(first, nodes.AssignName):
        this_assign_parent = get_node_first_ancestor_of_type(
            first, (nodes.Assign, nodes.NamedExpr)
        )
        if this_assign_parent is None:  # pragma: no cover
            # Cannot imagine this being None, but return True to avoid false positives
            return True
        if isinstance(this_assign_parent.value, nodes.BaseContainer):
            if all(isinstance(n, nodes.Const) for n in this_assign_parent.value.elts):
                return False
        if isinstance(this_assign_parent.value, nodes.Const):
            return False
    return True


def is_comprehension(node: nodes.NodeNG) -> bool:
    comprehensions = (
        nodes.ListComp,
        nodes.SetComp,
        nodes.DictComp,
        nodes.GeneratorExp,
    )
    return isinstance(node, comprehensions)


def _supports_mapping_protocol(value: nodes.NodeNG) -> bool:
    return _supports_protocol_method(
        value, GETITEM_METHOD
    ) and _supports_protocol_method(value, KEYS_METHOD)


def _supports_membership_test_protocol(value: nodes.NodeNG) -> bool:
    return _supports_protocol_method(value, CONTAINS_METHOD)


def _supports_iteration_protocol(value: nodes.NodeNG) -> bool:
    return _supports_protocol_method(value, ITER_METHOD) or _supports_protocol_method(
        value, GETITEM_METHOD
    )


def _supports_async_iteration_protocol(value: nodes.NodeNG) -> bool:
    return _supports_protocol_method(value, AITER_METHOD)


def _supports_getitem_protocol(value: nodes.NodeNG) -> bool:
    return _supports_protocol_method(value, GETITEM_METHOD)


def _supports_setitem_protocol(value: nodes.NodeNG) -> bool:
    return _supports_protocol_method(value, SETITEM_METHOD)


def _supports_delitem_protocol(value: nodes.NodeNG) -> bool:
    return _supports_protocol_method(value, DELITEM_METHOD)


def _is_abstract_class_name(name: str) -> bool:
    lname = name.lower()
    is_mixin = lname.endswith("mixin")
    is_abstract = lname.startswith("abstract")
    is_base = lname.startswith("base") or lname.endswith("base")
    return is_mixin or is_abstract or is_base


def is_inside_abstract_class(node: nodes.NodeNG) -> bool:
    while node is not None:
        if isinstance(node, nodes.ClassDef):
            if class_is_abstract(node):
                return True
            name = getattr(node, "name", None)
            if name is not None and _is_abstract_class_name(name):
                return True
        node = node.parent
    return False


def _supports_protocol(
    value: nodes.NodeNG, protocol_callback: Callable[[nodes.NodeNG], bool]
) -> bool:
    if isinstance(value, nodes.ClassDef):
        if not has_known_bases(value):
            return True
        # classobj can only be iterable if it has an iterable metaclass
        meta = value.metaclass()
        if meta is not None:
            if protocol_callback(meta):
                return True
    if isinstance(value, astroid.BaseInstance):
        if not has_known_bases(value):
            return True
        if value.has_dynamic_getattr():
            return True
        if protocol_callback(value):
            return True

    if isinstance(value, nodes.ComprehensionScope):
        return True

    if (
        isinstance(value, astroid.bases.Proxy)
        and isinstance(value._proxied, astroid.BaseInstance)
        and has_known_bases(value._proxied)
    ):
        value = value._proxied
        return protocol_callback(value)

    return False


def is_iterable(value: nodes.NodeNG, check_async: bool = False) -> bool:
    if check_async:
        protocol_check = _supports_async_iteration_protocol
    else:
        protocol_check = _supports_iteration_protocol
    return _supports_protocol(value, protocol_check)


def is_mapping(value: nodes.NodeNG) -> bool:
    return _supports_protocol(value, _supports_mapping_protocol)


def supports_membership_test(value: nodes.NodeNG) -> bool:
    supported = _supports_protocol(value, _supports_membership_test_protocol)
    return supported or is_iterable(value)


def supports_getitem(value: nodes.NodeNG, node: nodes.NodeNG) -> bool:
    if isinstance(value, nodes.ClassDef):
        if _supports_protocol_method(value, CLASS_GETITEM_METHOD):
            return True
        if is_postponed_evaluation_enabled(node) and is_node_in_type_annotation_context(
            node
        ):
            return True
    return _supports_protocol(value, _supports_getitem_protocol)


def supports_setitem(value: nodes.NodeNG, _: nodes.NodeNG) -> bool:
    return _supports_protocol(value, _supports_setitem_protocol)


def supports_delitem(value: nodes.NodeNG, _: nodes.NodeNG) -> bool:
    return _supports_protocol(value, _supports_delitem_protocol)


def _get_python_type_of_node(node: nodes.NodeNG) -> str | None:
    pytype: Callable[[], str] | None = getattr(node, "pytype", None)
    if callable(pytype):
        return pytype()
    return None


@lru_cache(maxsize=1024)
def safe_infer(
    node: nodes.NodeNG,
    context: InferenceContext | None = None,
    *,
    compare_constants: bool = False,
) -> InferenceResult | None:
    """Return the inferred value for the given node.

    Return None if inference failed or if there is some ambiguity (more than
    one node has been inferred of different types).

    If compare_constants is True and if multiple constants are inferred,
    unequal inferred values are also considered ambiguous and return None.
    """
    inferred_types: set[str | None] = set()
    try:
        infer_gen = node.infer(context=context)
        value = next(infer_gen)
    except astroid.InferenceError:
        return None
    except Exception as e:  # pragma: no cover
        raise AstroidError from e

    if not isinstance(value, util.UninferableBase):
        inferred_types.add(_get_python_type_of_node(value))

    # pylint: disable = too-many-try-statements
    try:
        for inferred in infer_gen:
            inferred_type = _get_python_type_of_node(inferred)
            if inferred_type not in inferred_types:
                return None  # If there is ambiguity on the inferred node.
            if (
                compare_constants
                and isinstance(inferred, nodes.Const)
                and isinstance(value, nodes.Const)
                and inferred.value != value.value
            ):
                return None
            if (
                isinstance(inferred, nodes.FunctionDef)
                and isinstance(value, nodes.FunctionDef)
                and function_arguments_are_ambiguous(inferred, value)
            ):
                return None
    except astroid.InferenceError:
        return None  # There is some kind of ambiguity
    except StopIteration:
        return value
    except Exception as e:  # pragma: no cover
        raise AstroidError from e
    return value if len(inferred_types) <= 1 else None


@lru_cache(maxsize=512)
def infer_all(
    node: nodes.NodeNG, context: InferenceContext | None = None
) -> list[InferenceResult]:
    try:
        return list(node.infer(context=context))
    except astroid.InferenceError:
        return []
    except Exception as e:  # pragma: no cover
        raise AstroidError from e


def function_arguments_are_ambiguous(
    func1: nodes.FunctionDef, func2: nodes.FunctionDef
) -> bool:
    if func1.argnames() != func2.argnames():
        return True
    # Check ambiguity among function default values
    pairs_of_defaults = [
        (func1.args.defaults, func2.args.defaults),
        (func1.args.kw_defaults, func2.args.kw_defaults),
    ]
    for zippable_default in pairs_of_defaults:
        if None in zippable_default:
            continue
        if len(zippable_default[0]) != len(zippable_default[1]):
            return True
        for default1, default2 in zip(*zippable_default):
            if isinstance(default1, nodes.Const) and isinstance(default2, nodes.Const):
                if default1.value != default2.value:
                    return True
            elif isinstance(default1, nodes.Name) and isinstance(default2, nodes.Name):
                if default1.name != default2.name:
                    return True
            else:
                return True
    return False


def has_known_bases(
    klass: nodes.ClassDef, context: InferenceContext | None = None
) -> bool:
    """Return true if all base classes of a class could be inferred."""
    try:
        return klass._all_bases_known  # type: ignore[no-any-return]
    except AttributeError:
        pass
    for base in klass.bases:
        result = safe_infer(base, context=context)
        if (
            not isinstance(result, nodes.ClassDef)
            or result is klass
            or not has_known_bases(result, context=context)
        ):
            klass._all_bases_known = False
            return False
    klass._all_bases_known = True
    return True


def is_none(node: nodes.NodeNG) -> bool:
    return (
        node is None
        or (isinstance(node, nodes.Const) and node.value is None)
        or (isinstance(node, nodes.Name) and node.name == "None")
    )


def node_type(node: nodes.NodeNG) -> SuccessfulInferenceResult | None:
    """Return the inferred type for `node`.

    If there is more than one possible type, or if inferred type is Uninferable or None,
    return None
    """
    # check there is only one possible type for the assign node. Else we
    # don't handle it for now
    types: set[SuccessfulInferenceResult] = set()
    try:
        for var_type in node.infer():
            if isinstance(var_type, util.UninferableBase) or is_none(var_type):
                continue
            types.add(var_type)
            if len(types) > 1:
                return None
    except astroid.InferenceError:
        return None
    return types.pop() if types else None


def is_registered_in_singledispatch_function(node: nodes.FunctionDef) -> bool:
    """Check if the given function node is a singledispatch function."""

    singledispatch_qnames = (
        "functools.singledispatch",
        "singledispatch.singledispatch",
    )

    if not isinstance(node, nodes.FunctionDef):
        return False

    decorators = node.decorators.nodes if node.decorators else []
    for decorator in decorators:
        # func.register are function calls or register attributes
        # when the function is annotated with types
        if isinstance(decorator, nodes.Call):
            func = decorator.func
        elif isinstance(decorator, nodes.Attribute):
            func = decorator
        else:
            continue

        if not isinstance(func, nodes.Attribute) or func.attrname != "register":
            continue

        try:
            func_def = next(func.expr.infer())
        except astroid.InferenceError:
            continue

        if isinstance(func_def, nodes.FunctionDef):
            return decorated_with(func_def, singledispatch_qnames)

    return False


def find_inferred_fn_from_register(node: nodes.NodeNG) -> nodes.FunctionDef | None:
    # func.register are function calls or register attributes
    # when the function is annotated with types
    if isinstance(node, nodes.Call):
        func = node.func
    elif isinstance(node, nodes.Attribute):
        func = node
    else:
        return None

    if not isinstance(func, nodes.Attribute) or func.attrname != "register":
        return None

    func_def = safe_infer(func.expr)
    if not isinstance(func_def, nodes.FunctionDef):
        return None

    return func_def


def is_registered_in_singledispatchmethod_function(node: nodes.FunctionDef) -> bool:
    """Check if the given function node is a singledispatchmethod function."""

    singledispatchmethod_qnames = (
        "functools.singledispatchmethod",
        "singledispatch.singledispatchmethod",
    )

    decorators = node.decorators.nodes if node.decorators else []
    for decorator in decorators:
        func_def = find_inferred_fn_from_register(decorator)
        if func_def:
            return decorated_with(func_def, singledispatchmethod_qnames)

    return False


def get_node_last_lineno(node: nodes.NodeNG) -> int:
    """Get the last lineno of the given node.

    For a simple statement this will just be node.lineno,
    but for a node that has child statements (e.g. a method) this will be the lineno of the last
    child statement recursively.
    """
    # 'finalbody' is always the last clause in a try statement, if present
    if getattr(node, "finalbody", False):
        return get_node_last_lineno(node.finalbody[-1])
    # For if, while, and for statements 'orelse' is always the last clause.
    # For try statements 'orelse' is the last in the absence of a 'finalbody'
    if getattr(node, "orelse", False):
        return get_node_last_lineno(node.orelse[-1])
    # try statements have the 'handlers' last if there is no 'orelse' or 'finalbody'
    if getattr(node, "handlers", False):
        return get_node_last_lineno(node.handlers[-1])
    # All compound statements have a 'body'
    if getattr(node, "body", False):
        return get_node_last_lineno(node.body[-1])
    # Not a compound statement
    return node.lineno  # type: ignore[no-any-return]


def is_postponed_evaluation_enabled(node: nodes.NodeNG) -> bool:
    """Check if the postponed evaluation of annotations is enabled."""
    module = node.root()
    return "annotations" in module.future_imports


def is_node_in_type_annotation_context(node: nodes.NodeNG) -> bool:
    """Check if node is in type annotation context.

    Check for 'AnnAssign', function 'Arguments',
    or part of function return type annotation.
    """
    # pylint: disable=too-many-boolean-expressions
    current_node, parent_node = node, node.parent
    while True:
        if (
            isinstance(parent_node, nodes.AnnAssign)
            and parent_node.annotation == current_node
            or isinstance(parent_node, nodes.Arguments)
            and current_node
            in (
                *parent_node.annotations,
                *parent_node.posonlyargs_annotations,
                *parent_node.kwonlyargs_annotations,
                parent_node.varargannotation,
                parent_node.kwargannotation,
            )
            or isinstance(parent_node, nodes.FunctionDef)
            and parent_node.returns == current_node
        ):
            return True
        current_node, parent_node = parent_node, parent_node.parent
        if isinstance(parent_node, nodes.Module):
            return False


def is_subclass_of(child: nodes.ClassDef, parent: nodes.ClassDef) -> bool:
    """Check if first node is a subclass of second node.

    :param child: Node to check for subclass.
    :param parent: Node to check for superclass.
    :returns: True if child is derived from parent. False otherwise.
    """
    if not all(isinstance(node, nodes.ClassDef) for node in (child, parent)):
        return False

    for ancestor in child.ancestors():
        try:
            if astroid.helpers.is_subtype(ancestor, parent):
                return True
        except astroid.exceptions._NonDeducibleTypeHierarchy:
            continue
    return False


@lru_cache(maxsize=1024)
def is_overload_stub(node: nodes.NodeNG) -> bool:
    """Check if a node is a function stub decorated with typing.overload.

    :param node: Node to check.
    :returns: True if node is an overload function stub. False otherwise.
    """
    decorators = getattr(node, "decorators", None)
    return bool(decorators and decorated_with(node, ["typing.overload", "overload"]))


def is_protocol_class(cls: nodes.NodeNG) -> bool:
    """Check if the given node represents a protocol class.

    :param cls: The node to check
    :returns: True if the node is or inherits from typing.Protocol directly, false otherwise.
    """
    if not isinstance(cls, nodes.ClassDef):
        return False

    # Return if klass is protocol
    if cls.qname() in TYPING_PROTOCOLS:
        return True

    for base in cls.bases:
        try:
            for inf_base in base.infer():
                if inf_base.qname() in TYPING_PROTOCOLS:
                    return True
        except astroid.InferenceError:
            continue
    return False


def is_call_of_name(node: nodes.NodeNG, name: str) -> bool:
    """Checks if node is a function call with the given name."""
    return (
        isinstance(node, nodes.Call)
        and isinstance(node.func, nodes.Name)
        and node.func.name == name
    )


def is_test_condition(
    node: nodes.NodeNG,
    parent: nodes.NodeNG | None = None,
) -> bool:
    """Returns true if the given node is being tested for truthiness."""
    parent = parent or node.parent
    if isinstance(parent, (nodes.While, nodes.If, nodes.IfExp, nodes.Assert)):
        return node is parent.test or parent.test.parent_of(node)
    if isinstance(parent, nodes.Comprehension):
        return node in parent.ifs
    return is_call_of_name(parent, "bool") and parent.parent_of(node)


def is_classdef_type(node: nodes.ClassDef) -> bool:
    """Test if ClassDef node is Type."""
    if node.name == "type":
        return True
    return any(isinstance(b, nodes.Name) and b.name == "type" for b in node.bases)


def is_attribute_typed_annotation(
    node: nodes.ClassDef | astroid.Instance, attr_name: str
) -> bool:
    """Test if attribute is typed annotation in current node
    or any base nodes.
    """
    attribute = node.locals.get(attr_name, [None])[0]
    if (
        attribute
        and isinstance(attribute, nodes.AssignName)
        and isinstance(attribute.parent, nodes.AnnAssign)
    ):
        return True
    for base in node.bases:
        inferred = safe_infer(base)
        if (
            inferred
            and isinstance(inferred, nodes.ClassDef)
            and is_attribute_typed_annotation(inferred, attr_name)
        ):
            return True
    return False


def is_enum(node: nodes.ClassDef) -> bool:
    return node.name == "Enum" and node.root().name == "enum"  # type: ignore[no-any-return]


def is_assign_name_annotated_with(node: nodes.AssignName, typing_name: str) -> bool:
    """Test if AssignName node has `typing_name` annotation.

    Especially useful to check for `typing._SpecialForm` instances
    like: `Union`, `Optional`, `Literal`, `ClassVar`, `Final`.
    """
    if not isinstance(node.parent, nodes.AnnAssign):
        return False
    annotation = node.parent.annotation
    if isinstance(annotation, nodes.Subscript):
        annotation = annotation.value
    if (
        isinstance(annotation, nodes.Name)
        and annotation.name == typing_name
        or isinstance(annotation, nodes.Attribute)
        and annotation.attrname == typing_name
    ):
        return True
    return False


def get_iterating_dictionary_name(node: nodes.For | nodes.Comprehension) -> str | None:
    """Get the name of the dictionary which keys are being iterated over on
    a ``nodes.For`` or ``nodes.Comprehension`` node.

    If the iterating object is not either the keys method of a dictionary
    or a dictionary itself, this returns None.
    """
    # Is it a proper keys call?
    if (
        isinstance(node.iter, nodes.Call)
        and isinstance(node.iter.func, nodes.Attribute)
        and node.iter.func.attrname == "keys"
    ):
        inferred = safe_infer(node.iter.func)
        if not isinstance(inferred, astroid.BoundMethod):
            return None
        return node.iter.as_string().rpartition(".keys")[0]  # type: ignore[no-any-return]

    # Is it a dictionary?
    if isinstance(node.iter, (nodes.Name, nodes.Attribute)):
        inferred = safe_infer(node.iter)
        if not isinstance(inferred, nodes.Dict):
            return None
        return node.iter.as_string()  # type: ignore[no-any-return]

    return None


def get_subscript_const_value(node: nodes.Subscript) -> nodes.Const:
    """Returns the value 'subscript.slice' of a Subscript node.

    :param node: Subscript Node to extract value from
    :returns: Const Node containing subscript value
    :raises InferredTypeError: if the subscript node cannot be inferred as a Const
    """
    inferred = safe_infer(node.slice)
    if not isinstance(inferred, nodes.Const):
        raise InferredTypeError("Subscript.slice cannot be inferred as a nodes.Const")

    return inferred


def get_import_name(importnode: ImportNode, modname: str | None) -> str | None:
    """Get a prepared module name from the given import node.

    In the case of relative imports, this will return the
    absolute qualified module name, which might be useful
    for debugging. Otherwise, the initial module name
    is returned unchanged.

    :param importnode: node representing import statement.
    :param modname: module name from import statement.
    :returns: absolute qualified module name of the module
        used in import.
    """
    if isinstance(importnode, nodes.ImportFrom) and importnode.level:
        root = importnode.root()
        if isinstance(root, nodes.Module):
            try:
                return root.relative_to_absolute_name(  # type: ignore[no-any-return]
                    modname, level=importnode.level
                )
            except TooManyLevelsError:
                return modname
    return modname


def is_sys_guard(node: nodes.If) -> bool:
    """Return True if IF stmt is a sys.version_info guard.

    >>> import sys
    >>> if sys.version_info > (3, 8):
    >>>     from typing import Literal
    >>> else:
    >>>     from typing_extensions import Literal
    """
    if isinstance(node.test, nodes.Compare):
        value = node.test.left
        if isinstance(value, nodes.Subscript):
            value = value.value
        if (
            isinstance(value, nodes.Attribute)
            and value.as_string() == "sys.version_info"
        ):
            return True

    return False


def is_reassigned_after_current(node: nodes.NodeNG, varname: str) -> bool:
    """Check if the given variable name is reassigned in the same scope after the
    current node.
    """
    return any(
        a.name == varname and a.lineno > node.lineno
        for a in node.scope().nodes_of_class(
            (nodes.AssignName, nodes.ClassDef, nodes.FunctionDef)
        )
    )


def is_deleted_after_current(node: nodes.NodeNG, varname: str) -> bool:
    """Check if the given variable name is deleted in the same scope after the current
    node.
    """
    return any(
        getattr(target, "name", None) == varname and target.lineno > node.lineno
        for del_node in node.scope().nodes_of_class(nodes.Delete)
        for target in del_node.targets
    )


def is_function_body_ellipsis(node: nodes.FunctionDef) -> bool:
    """Checks whether a function body only consists of a single Ellipsis."""
    return (
        len(node.body) == 1
        and isinstance(node.body[0], nodes.Expr)
        and isinstance(node.body[0].value, nodes.Const)
        and node.body[0].value.value == Ellipsis
    )


def is_base_container(node: nodes.NodeNG | None) -> bool:
    return isinstance(node, nodes.BaseContainer) and not node.elts


def is_empty_dict_literal(node: nodes.NodeNG | None) -> bool:
    return isinstance(node, nodes.Dict) and not node.items


def is_empty_str_literal(node: nodes.NodeNG | None) -> bool:
    return (
        isinstance(node, nodes.Const) and isinstance(node.value, str) and not node.value
    )


def returns_bool(node: nodes.NodeNG) -> bool:
    """Returns true if a node is a nodes.Return that returns a constant boolean."""
    return (
        isinstance(node, nodes.Return)
        and isinstance(node.value, nodes.Const)
        and isinstance(node.value.value, bool)
    )


def assigned_bool(node: nodes.NodeNG) -> bool:
    """Returns true if a node is a nodes.Assign that returns a constant boolean."""
    return (
        isinstance(node, nodes.Assign)
        and isinstance(node.value, nodes.Const)
        and isinstance(node.value.value, bool)
    )


def get_node_first_ancestor_of_type(
    node: nodes.NodeNG, ancestor_type: type[_NodeT] | tuple[type[_NodeT], ...]
) -> _NodeT | None:
    """Return the first parent node that is any of the provided types (or None)."""
    for ancestor in node.node_ancestors():
        if isinstance(ancestor, ancestor_type):
            return ancestor  # type: ignore[no-any-return]
    return None


def get_node_first_ancestor_of_type_and_its_child(
    node: nodes.NodeNG, ancestor_type: type[_NodeT] | tuple[type[_NodeT], ...]
) -> tuple[None, None] | tuple[_NodeT, nodes.NodeNG]:
    """Modified version of get_node_first_ancestor_of_type to also return the
    descendant visited directly before reaching the sought ancestor.

    Useful for extracting whether a statement is guarded by a try, except, or finally
    when searching for a Try ancestor.
    """
    child = node
    for ancestor in node.node_ancestors():
        if isinstance(ancestor, ancestor_type):
            return (ancestor, child)
        child = ancestor
    return None, None


def in_type_checking_block(node: nodes.NodeNG) -> bool:
    """Check if a node is guarded by a TYPE_CHECKING guard."""
    for ancestor in node.node_ancestors():
        if not isinstance(ancestor, nodes.If):
            continue
        if isinstance(ancestor.test, nodes.Name):
            if ancestor.test.name != "TYPE_CHECKING":
                continue
            lookup_result = ancestor.test.lookup(ancestor.test.name)[1]
            if not lookup_result:
                return False
            maybe_import_from = lookup_result[0]
            if (
                isinstance(maybe_import_from, nodes.ImportFrom)
                and maybe_import_from.modname == "typing"
            ):
                return True
            inferred = safe_infer(ancestor.test)
            if isinstance(inferred, nodes.Const) and inferred.value is False:
                return True
        elif isinstance(ancestor.test, nodes.Attribute):
            if ancestor.test.attrname != "TYPE_CHECKING":
                continue
            inferred_module = safe_infer(ancestor.test.expr)
            if (
                isinstance(inferred_module, nodes.Module)
                and inferred_module.name == "typing"
            ):
                return True

    return False


def is_typing_member(node: nodes.NodeNG, names_to_check: tuple[str, ...]) -> bool:
    """Check if `node` is a member of the `typing` module and has one of the names from
    `names_to_check`.
    """
    if isinstance(node, nodes.Name):
        try:
            import_from = node.lookup(node.name)[1][0]
        except IndexError:
            return False

        if isinstance(import_from, nodes.ImportFrom):
            return (
                import_from.modname == "typing"
                and import_from.real_name(node.name) in names_to_check
            )
    elif isinstance(node, nodes.Attribute):
        inferred_module = safe_infer(node.expr)
        return (
            isinstance(inferred_module, nodes.Module)
            and inferred_module.name == "typing"
            and node.attrname in names_to_check
        )
    return False


@lru_cache
def in_for_else_branch(parent: nodes.NodeNG, stmt: nodes.Statement) -> bool:
    """Returns True if stmt is inside the else branch for a parent For stmt."""
    return isinstance(parent, nodes.For) and any(
        else_stmt.parent_of(stmt) or else_stmt == stmt for else_stmt in parent.orelse
    )


def find_assigned_names_recursive(
    target: nodes.AssignName | nodes.BaseContainer,
) -> Iterator[str]:
    """Yield the names of assignment targets, accounting for nested ones."""
    if isinstance(target, nodes.AssignName):
        if target.name is not None:
            yield target.name
    elif isinstance(target, nodes.BaseContainer):
        for elt in target.elts:
            yield from find_assigned_names_recursive(elt)


def has_starred_node_recursive(
    node: nodes.For | nodes.Comprehension | nodes.Set,
) -> Iterator[bool]:
    """Yield ``True`` if a Starred node is found recursively."""
    if isinstance(node, nodes.Starred):
        yield True
    elif isinstance(node, nodes.Set):
        for elt in node.elts:
            yield from has_starred_node_recursive(elt)
    elif isinstance(node, (nodes.For, nodes.Comprehension)):
        for elt in node.iter.elts:
            yield from has_starred_node_recursive(elt)


def is_hashable(node: nodes.NodeNG) -> bool:
    """Return whether any inferred value of `node` is hashable.

    When finding ambiguity, return True.
    """
    # pylint: disable = too-many-try-statements
    try:
        for inferred in node.infer():
            if isinstance(inferred, (nodes.ClassDef, util.UninferableBase)):
                return True
            if not hasattr(inferred, "igetattr"):
                return True
            hash_fn = next(inferred.igetattr("__hash__"))
            if hash_fn.parent is inferred:
                return True
            if getattr(hash_fn, "value", True) is not None:
                return True
        return False
    except astroid.InferenceError:
        return True


def subscript_chain_is_equal(left: nodes.Subscript, right: nodes.Subscript) -> bool:
    while isinstance(left, nodes.Subscript) and isinstance(right, nodes.Subscript):
        try:
            if (
                get_subscript_const_value(left).value
                != get_subscript_const_value(right).value
            ):
                return False

            left = left.value
            right = right.value
        except InferredTypeError:
            return False

    return left.as_string() == right.as_string()  # type: ignore[no-any-return]


def _is_target_name_in_binop_side(
    target: nodes.AssignName | nodes.AssignAttr, side: nodes.NodeNG | None
) -> bool:
    """Determine whether the target name-like node is referenced in the side node."""
    if isinstance(side, nodes.Name):
        if isinstance(target, nodes.AssignName):
            return target.name == side.name  # type: ignore[no-any-return]
        return False
    if isinstance(side, nodes.Attribute) and isinstance(target, nodes.AssignAttr):
        return target.as_string() == side.as_string()  # type: ignore[no-any-return]
    if isinstance(side, nodes.Subscript) and isinstance(target, nodes.Subscript):
        return subscript_chain_is_equal(target, side)

    return False


def is_augmented_assign(node: nodes.Assign) -> tuple[bool, str]:
    """Determine if the node is assigning itself (with modifications) to itself.

    For example: x = 1 + x
    """
    if not isinstance(node.value, nodes.BinOp):
        return False, ""

    binop = node.value
    target = node.targets[0]

    if not isinstance(target, (nodes.AssignName, nodes.AssignAttr, nodes.Subscript)):
        return False, ""

    # We don't want to catch x = "1" + x or x = "%s" % x
    if isinstance(binop.left, nodes.Const) and isinstance(
        binop.left.value, (str, bytes)
    ):
        return False, ""

    # This could probably be improved but for now we disregard all assignments from calls
    if isinstance(binop.left, nodes.Call) or isinstance(binop.right, nodes.Call):
        return False, ""

    if _is_target_name_in_binop_side(target, binop.left):
        return True, binop.op
    if (
        # Unless an operator is commutative, we should not raise (i.e. x = 3/x)
        binop.op in COMMUTATIVE_OPERATORS
        and _is_target_name_in_binop_side(target, binop.right)
    ):
        inferred_left = safe_infer(binop.left)
        if isinstance(inferred_left, nodes.Const) and isinstance(
            inferred_left.value, int
        ):
            return True, binop.op
        return False, ""
    return False, ""


def _qualified_name_parts(qualified_module_name: str) -> list[str]:
    """Split the names of the given module into subparts.

    For example,
        _qualified_name_parts('pylint.checkers.ImportsChecker')
    returns
        ['pylint', 'pylint.checkers', 'pylint.checkers.ImportsChecker']
    """
    names = qualified_module_name.split(".")
    return [".".join(names[0 : i + 1]) for i in range(len(names))]


def is_module_ignored(
    qualified_module_name: str, ignored_modules: Iterable[str]
) -> bool:
    ignored_modules = set(ignored_modules)
    for current_module in _qualified_name_parts(qualified_module_name):
        # Try to match the module name directly
        if current_module in ignored_modules:
            return True
        for ignore in ignored_modules:
            # Try to see if the ignores pattern match against the module name.
            if fnmatch.fnmatch(current_module, ignore):
                return True
    return False


def is_singleton_const(node: nodes.NodeNG) -> bool:
    return isinstance(node, nodes.Const) and any(
        node.value is value for value in SINGLETON_VALUES
    )


def is_terminating_func(node: nodes.Call) -> bool:
    """Detect call to exit(), quit(), os._exit(), or sys.exit()."""
    if (
        not isinstance(node.func, nodes.Attribute)
        and not (isinstance(node.func, nodes.Name))
        or isinstance(node.parent, nodes.Lambda)
    ):
        return False

    try:
        for inferred in node.func.infer():
            if (
                hasattr(inferred, "qname")
                and inferred.qname() in TERMINATING_FUNCS_QNAMES
            ):
                return True
    except (StopIteration, astroid.InferenceError):
        pass

    return False


def is_class_attr(name: str, klass: nodes.ClassDef) -> bool:
    try:
        klass.getattr(name)
        return True
    except astroid.NotFoundError:
        return False


def get_inverse_comparator(op: str) -> str:
    """Returns the inverse comparator given a comparator.

    E.g. when given "==", returns "!="

    :param str op: the comparator to look up.

    :returns: The inverse of the comparator in string format
    :raises KeyError: if input is not recognized as a comparator
    """
    return {
        "==": "!=",
        "!=": "==",
        "<": ">=",
        ">": "<=",
        "<=": ">",
        ">=": "<",
        "in": "not in",
        "not in": "in",
        "is": "is not",
        "is not": "is",
    }[op]


def not_condition_as_string(
    test_node: nodes.Compare | nodes.Name | nodes.UnaryOp | nodes.BoolOp | nodes.BinOp,
) -> str:
    msg = f"not {test_node.as_string()}"
    if isinstance(test_node, nodes.UnaryOp):
        msg = test_node.operand.as_string()
    elif isinstance(test_node, nodes.BoolOp):
        msg = f"not ({test_node.as_string()})"
    elif isinstance(test_node, nodes.Compare):
        lhs = test_node.left
        ops, rhs = test_node.ops[0]
        lower_priority_expressions = (
            nodes.Lambda,
            nodes.UnaryOp,
            nodes.BoolOp,
            nodes.IfExp,
            nodes.NamedExpr,
        )
        lhs = (
            f"({lhs.as_string()})"
            if isinstance(lhs, lower_priority_expressions)
            else lhs.as_string()
        )
        rhs = (
            f"({rhs.as_string()})"
            if isinstance(rhs, lower_priority_expressions)
            else rhs.as_string()
        )
        msg = f"{lhs} {get_inverse_comparator(ops)} {rhs}"
    return msg


@lru_cache(maxsize=1000)
def overridden_method(
    klass: nodes.LocalsDictNodeNG, name: str | None
) -> nodes.FunctionDef | None:
    """Get overridden method if any."""
    try:
        parent = next(klass.local_attr_ancestors(name))
    except (StopIteration, KeyError):
        return None
    try:
        meth_node = parent[name]
    except KeyError:  # pragma: no cover
        # We have found an ancestor defining <name> but it's not in the local
        # dictionary. This may happen with astroid built from living objects.
        return None
    if isinstance(meth_node, nodes.FunctionDef):
        return meth_node
    return None  # pragma: no cover


def clear_lru_caches() -> None:
    """Clear caches holding references to AST nodes."""
    caches_holding_node_references: list[_lru_cache_wrapper[Any]] = [
        class_is_abstract,
        in_for_else_branch,
        infer_all,
        is_overload_stub,
        overridden_method,
        unimplemented_abstract_methods,
        safe_infer,
    ]
    for lru in caches_holding_node_references:
        lru.cache_clear()


def is_enum_member(node: nodes.AssignName) -> bool:
    """Return `True` if `node` is an Enum member (is an item of the
    `__members__` container).
    """

    frame = node.frame()
    if (
        not isinstance(frame, nodes.ClassDef)
        or not frame.is_subtype_of("enum.Enum")
        or frame.root().qname() == "enum"
    ):
        return False

    enum_member_objects = frame.locals.get("__members__")[0].items
    return node.name in [name_obj.name for value, name_obj in enum_member_objects]

back/venvmsg/Lib/site-packages/pylint/checkers/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Variables checkers for Python code."""

from __future__ import annotations

import collections
import copy
import itertools
import math
import os
import re
from collections import defaultdict
from collections.abc import Generator, Iterable, Iterator
from enum import Enum
from functools import cached_property
from typing import TYPE_CHECKING, Any, NamedTuple

import astroid
from astroid import bases, extract_node, nodes, util
from astroid.nodes import _base_nodes
from astroid.typing import InferenceResult

from pylint.checkers import BaseChecker, utils
from pylint.checkers.utils import (
    in_type_checking_block,
    is_module_ignored,
    is_postponed_evaluation_enabled,
    is_sys_guard,
    overridden_method,
)
from pylint.constants import PY39_PLUS, TYPING_NEVER, TYPING_NORETURN
from pylint.interfaces import CONTROL_FLOW, HIGH, INFERENCE, INFERENCE_FAILURE
from pylint.typing import MessageDefinitionTuple

if TYPE_CHECKING:
    from pylint.lint import PyLinter

SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
FUTURE = "__future__"
# regexp for ignored argument name
IGNORED_ARGUMENT_NAMES = re.compile("_.*|^ignored_|^unused_")
# In Python 3.7 abc has a Python implementation which is preferred
# by astroid. Unfortunately this also messes up our explicit checks
# for `abc`
METACLASS_NAME_TRANSFORMS = {"_py_abc": "abc"}
BUILTIN_RANGE = "builtins.range"
TYPING_MODULE = "typing"
TYPING_NAMES = frozenset(
    {
        "Any",
        "Callable",
        "ClassVar",
        "Generic",
        "Optional",
        "Tuple",
        "Type",
        "TypeVar",
        "Union",
        "AbstractSet",
        "ByteString",
        "Container",
        "ContextManager",
        "Hashable",
        "ItemsView",
        "Iterable",
        "Iterator",
        "KeysView",
        "Mapping",
        "MappingView",
        "MutableMapping",
        "MutableSequence",
        "MutableSet",
        "Sequence",
        "Sized",
        "ValuesView",
        "Awaitable",
        "AsyncIterator",
        "AsyncIterable",
        "Coroutine",
        "Collection",
        "AsyncGenerator",
        "AsyncContextManager",
        "Reversible",
        "SupportsAbs",
        "SupportsBytes",
        "SupportsComplex",
        "SupportsFloat",
        "SupportsInt",
        "SupportsRound",
        "Counter",
        "Deque",
        "Dict",
        "DefaultDict",
        "List",
        "Set",
        "FrozenSet",
        "NamedTuple",
        "Generator",
        "AnyStr",
        "Text",
        "Pattern",
        "BinaryIO",
    }
)

DICT_TYPES = (
    astroid.objects.DictValues,
    astroid.objects.DictKeys,
    astroid.objects.DictItems,
    astroid.nodes.node_classes.Dict,
)

NODES_WITH_VALUE_ATTR = (
    nodes.Assign,
    nodes.AnnAssign,
    nodes.AugAssign,
    nodes.Expr,
    nodes.Return,
    nodes.Match,
)


class VariableVisitConsumerAction(Enum):
    """Reported by _check_consumer() and its sub-methods to determine the
    subsequent action to take in _undefined_and_used_before_checker().

    Continue -> continue loop to next consumer
    Return -> return and thereby break the loop
    """

    CONTINUE = 0
    RETURN = 1


def _is_from_future_import(stmt: nodes.ImportFrom, name: str) -> bool | None:
    """Check if the name is a future import from another module."""
    try:
        module = stmt.do_import_module(stmt.modname)
    except astroid.AstroidBuildingException:
        return None

    for local_node in module.locals.get(name, []):
        if isinstance(local_node, nodes.ImportFrom) and local_node.modname == FUTURE:
            return True
    return None


def _get_unpacking_extra_info(node: nodes.Assign, inferred: InferenceResult) -> str:
    """Return extra information to add to the message for unpacking-non-sequence
    and unbalanced-tuple/dict-unpacking errors.
    """
    more = ""
    if isinstance(inferred, DICT_TYPES):
        if isinstance(node, nodes.Assign):
            more = node.value.as_string()
        elif isinstance(node, nodes.For):
            more = node.iter.as_string()
        return more

    inferred_module = inferred.root().name
    if node.root().name == inferred_module:
        if node.lineno == inferred.lineno:
            more = f"'{inferred.as_string()}'"
        elif inferred.lineno:
            more = f"defined at line {inferred.lineno}"
    elif inferred.lineno:
        more = f"defined at line {inferred.lineno} of {inferred_module}"
    return more


def _detect_global_scope(
    node: nodes.Name, frame: nodes.LocalsDictNodeNG, defframe: nodes.LocalsDictNodeNG
) -> bool:
    """Detect that the given frames share a global scope.

    Two frames share a global scope when neither
    of them are hidden under a function scope, as well
    as any parent scope of them, until the root scope.
    In this case, depending from something defined later on
    will only work if guarded by a nested function definition.

    Example:
        class A:
            # B has the same global scope as `C`, leading to a NameError.
            # Return True to indicate a shared scope.
            class B(C): ...
        class C: ...

    Whereas this does not lead to a NameError:
        class A:
            def guard():
                # Return False to indicate no scope sharing.
                class B(C): ...
        class C: ...
    """
    def_scope = scope = None
    if frame and frame.parent:
        scope = frame.parent.scope()
    if defframe and defframe.parent:
        def_scope = defframe.parent.scope()
    if (
        isinstance(frame, nodes.ClassDef)
        and scope is not def_scope
        and scope is utils.get_node_first_ancestor_of_type(node, nodes.FunctionDef)
    ):
        # If the current node's scope is a class nested under a function,
        # and the def_scope is something else, then they aren't shared.
        return False
    if isinstance(frame, nodes.FunctionDef):
        # If the parent of the current node is a
        # function, then it can be under its scope (defined in); or
        # the `->` part of annotations. The same goes
        # for annotations of function arguments, they'll have
        # their parent the Arguments node.
        if frame.parent_of(defframe):
            return node.lineno < defframe.lineno  # type: ignore[no-any-return]
        if not isinstance(node.parent, (nodes.FunctionDef, nodes.Arguments)):
            return False

    break_scopes = []
    for current_scope in (scope or frame, def_scope):
        # Look for parent scopes. If there is anything different
        # than a module or a class scope, then the frames don't
        # share a global scope.
        parent_scope = current_scope
        while parent_scope:
            if not isinstance(parent_scope, (nodes.ClassDef, nodes.Module)):
                break_scopes.append(parent_scope)
                break
            if parent_scope.parent:
                parent_scope = parent_scope.parent.scope()
            else:
                break
    if len(set(break_scopes)) > 1:
        # Store different scopes than expected.
        # If the stored scopes are, in fact, the very same, then it means
        # that the two frames (frame and defframe) share the same scope,
        # and we could apply our lineno analysis over them.
        # For instance, this works when they are inside a function, the node
        # that uses a definition and the definition itself.
        return False
    # At this point, we are certain that frame and defframe share a scope
    # and the definition of the first depends on the second.
    return frame.lineno < defframe.lineno  # type: ignore[no-any-return]


def _infer_name_module(
    node: nodes.Import, name: str
) -> Generator[InferenceResult, None, None]:
    context = astroid.context.InferenceContext()
    context.lookupname = name
    return node.infer(context, asname=False)  # type: ignore[no-any-return]


def _fix_dot_imports(
    not_consumed: dict[str, list[nodes.NodeNG]]
) -> list[tuple[str, _base_nodes.ImportNode]]:
    """Try to fix imports with multiple dots, by returning a dictionary
    with the import names expanded.

    The function unflattens root imports,
    like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree'
    and 'xml.sax' respectively.
    """
    names: dict[str, _base_nodes.ImportNode] = {}
    for name, stmts in not_consumed.items():
        if any(
            isinstance(stmt, nodes.AssignName)
            and isinstance(stmt.assign_type(), nodes.AugAssign)
            for stmt in stmts
        ):
            continue
        for stmt in stmts:
            if not isinstance(stmt, (nodes.ImportFrom, nodes.Import)):
                continue
            for imports in stmt.names:
                second_name = None
                import_module_name = imports[0]
                if import_module_name == "*":
                    # In case of wildcard imports,
                    # pick the name from inside the imported module.
                    second_name = name
                else:
                    name_matches_dotted_import = False
                    if (
                        import_module_name.startswith(name)
                        and import_module_name.find(".") > -1
                    ):
                        name_matches_dotted_import = True

                    if name_matches_dotted_import or name in imports:
                        # Most likely something like 'xml.etree',
                        # which will appear in the .locals as 'xml'.
                        # Only pick the name if it wasn't consumed.
                        second_name = import_module_name
                if second_name and second_name not in names:
                    names[second_name] = stmt
    return sorted(names.items(), key=lambda a: a[1].fromlineno)


def _find_frame_imports(name: str, frame: nodes.LocalsDictNodeNG) -> bool:
    """Detect imports in the frame, with the required *name*.

    Such imports can be considered assignments if they are not globals.
    Returns True if an import for the given name was found.
    """
    if name in _flattened_scope_names(frame.nodes_of_class(nodes.Global)):
        return False

    imports = frame.nodes_of_class((nodes.Import, nodes.ImportFrom))
    for import_node in imports:
        for import_name, import_alias in import_node.names:
            # If the import uses an alias, check only that.
            # Otherwise, check only the import name.
            if import_alias:
                if import_alias == name:
                    return True
            elif import_name and import_name == name:
                return True
    return False


def _import_name_is_global(
    stmt: nodes.Global | _base_nodes.ImportNode, global_names: set[str]
) -> bool:
    for import_name, import_alias in stmt.names:
        # If the import uses an alias, check only that.
        # Otherwise, check only the import name.
        if import_alias:
            if import_alias in global_names:
                return True
        elif import_name in global_names:
            return True
    return False


def _flattened_scope_names(
    iterator: Iterator[nodes.Global | nodes.Nonlocal],
) -> set[str]:
    values = (set(stmt.names) for stmt in iterator)
    return set(itertools.chain.from_iterable(values))


def _assigned_locally(name_node: nodes.Name) -> bool:
    """Checks if name_node has corresponding assign statement in same scope."""
    name_node_scope = name_node.scope()
    assign_stmts = name_node_scope.nodes_of_class(nodes.AssignName)
    return any(a.name == name_node.name for a in assign_stmts) or _find_frame_imports(
        name_node.name, name_node_scope
    )


def _has_locals_call_after_node(stmt: nodes.NodeNG, scope: nodes.FunctionDef) -> bool:
    skip_nodes = (
        nodes.FunctionDef,
        nodes.ClassDef,
        nodes.Import,
        nodes.ImportFrom,
    )
    for call in scope.nodes_of_class(nodes.Call, skip_klass=skip_nodes):
        inferred = utils.safe_infer(call.func)
        if (
            utils.is_builtin_object(inferred)
            and getattr(inferred, "name", None) == "locals"
        ):
            if stmt.lineno < call.lineno:
                return True
    return False


MSGS: dict[str, MessageDefinitionTuple] = {
    "E0601": (
        "Using variable %r before assignment",
        "used-before-assignment",
        "Emitted when a local variable is accessed before its assignment took place. "
        "Assignments in try blocks are assumed not to have occurred when evaluating "
        "associated except/finally blocks. Assignments in except blocks are assumed "
        "not to have occurred when evaluating statements outside the block, except "
        "when the associated try block contains a return statement.",
    ),
    "E0602": (
        "Undefined variable %r",
        "undefined-variable",
        "Used when an undefined variable is accessed.",
    ),
    "E0603": (
        "Undefined variable name %r in __all__",
        "undefined-all-variable",
        "Used when an undefined variable name is referenced in __all__.",
    ),
    "E0604": (
        "Invalid object %r in __all__, must contain only strings",
        "invalid-all-object",
        "Used when an invalid (non-string) object occurs in __all__.",
    ),
    "E0605": (
        "Invalid format for __all__, must be tuple or list",
        "invalid-all-format",
        "Used when __all__ has an invalid format.",
    ),
    "E0611": (
        "No name %r in module %r",
        "no-name-in-module",
        "Used when a name cannot be found in a module.",
    ),
    "W0601": (
        "Global variable %r undefined at the module level",
        "global-variable-undefined",
        'Used when a variable is defined through the "global" statement '
        "but the variable is not defined in the module scope.",
    ),
    "W0602": (
        "Using global for %r but no assignment is done",
        "global-variable-not-assigned",
        "When a variable defined in the global scope is modified in an inner scope, "
        "the 'global' keyword is required in the inner scope only if there is an "
        "assignment operation done in the inner scope.",
    ),
    "W0603": (
        "Using the global statement",  # W0121
        "global-statement",
        'Used when you use the "global" statement to update a global '
        "variable. Pylint discourages its usage. That doesn't mean you cannot "
        "use it!",
    ),
    "W0604": (
        "Using the global statement at the module level",  # W0103
        "global-at-module-level",
        'Used when you use the "global" statement at the module level '
        "since it has no effect.",
    ),
    "W0611": (
        "Unused %s",
        "unused-import",
        "Used when an imported module or variable is not used.",
    ),
    "W0612": (
        "Unused variable %r",
        "unused-variable",
        "Used when a variable is defined but not used.",
    ),
    "W0613": (
        "Unused argument %r",
        "unused-argument",
        "Used when a function or method argument is not used.",
    ),
    "W0614": (
        "Unused import(s) %s from wildcard import of %s",
        "unused-wildcard-import",
        "Used when an imported module or variable is not used from a "
        "`'from X import *'` style import.",
    ),
    "W0621": (
        "Redefining name %r from outer scope (line %s)",
        "redefined-outer-name",
        "Used when a variable's name hides a name defined in an outer scope or except handler.",
    ),
    "W0622": (
        "Redefining built-in %r",
        "redefined-builtin",
        "Used when a variable or function override a built-in.",
    ),
    "W0631": (
        "Using possibly undefined loop variable %r",
        "undefined-loop-variable",
        "Used when a loop variable (i.e. defined by a for loop or "
        "a list comprehension or a generator expression) is used outside "
        "the loop.",
    ),
    "W0632": (
        "Possible unbalanced tuple unpacking with sequence %s: left side has %d "
        "label%s, right side has %d value%s",
        "unbalanced-tuple-unpacking",
        "Used when there is an unbalanced tuple unpacking in assignment",
        {"old_names": [("E0632", "old-unbalanced-tuple-unpacking")]},
    ),
    "E0633": (
        "Attempting to unpack a non-sequence%s",
        "unpacking-non-sequence",
        "Used when something which is not a sequence is used in an unpack assignment",
        {"old_names": [("W0633", "old-unpacking-non-sequence")]},
    ),
    "W0640": (
        "Cell variable %s defined in loop",
        "cell-var-from-loop",
        "A variable used in a closure is defined in a loop. "
        "This will result in all closures using the same value for "
        "the closed-over variable.",
    ),
    "W0641": (
        "Possibly unused variable %r",
        "possibly-unused-variable",
        "Used when a variable is defined but might not be used. "
        "The possibility comes from the fact that locals() might be used, "
        "which could consume or not the said variable",
    ),
    "W0642": (
        "Invalid assignment to %s in method",
        "self-cls-assignment",
        "Invalid assignment to self or cls in instance or class method "
        "respectively.",
    ),
    "E0643": (
        "Invalid index for iterable length",
        "potential-index-error",
        "Emitted when an index used on an iterable goes beyond the length of that "
        "iterable.",
    ),
    "W0644": (
        "Possible unbalanced dict unpacking with %s: "
        "left side has %d label%s, right side has %d value%s",
        "unbalanced-dict-unpacking",
        "Used when there is an unbalanced dict unpacking in assignment or for loop",
    ),
}


class ScopeConsumer(NamedTuple):
    """Store nodes and their consumption states."""

    to_consume: dict[str, list[nodes.NodeNG]]
    consumed: dict[str, list[nodes.NodeNG]]
    consumed_uncertain: defaultdict[str, list[nodes.NodeNG]]
    scope_type: str


class NamesConsumer:
    """A simple class to handle consumed, to consume and scope type info of node locals."""

    def __init__(self, node: nodes.NodeNG, scope_type: str) -> None:
        self._atomic = ScopeConsumer(
            copy.copy(node.locals), {}, collections.defaultdict(list), scope_type
        )
        self.node = node

    def __repr__(self) -> str:
        _to_consumes = [f"{k}->{v}" for k, v in self._atomic.to_consume.items()]
        _consumed = [f"{k}->{v}" for k, v in self._atomic.consumed.items()]
        _consumed_uncertain = [
            f"{k}->{v}" for k, v in self._atomic.consumed_uncertain.items()
        ]
        to_consumes = ", ".join(_to_consumes)
        consumed = ", ".join(_consumed)
        consumed_uncertain = ", ".join(_consumed_uncertain)
        return f"""
to_consume : {to_consumes}
consumed : {consumed}
consumed_uncertain: {consumed_uncertain}
scope_type : {self._atomic.scope_type}
"""

    def __iter__(self) -> Iterator[Any]:
        return iter(self._atomic)

    @property
    def to_consume(self) -> dict[str, list[nodes.NodeNG]]:
        return self._atomic.to_consume

    @property
    def consumed(self) -> dict[str, list[nodes.NodeNG]]:
        return self._atomic.consumed

    @property
    def consumed_uncertain(self) -> defaultdict[str, list[nodes.NodeNG]]:
        """Retrieves nodes filtered out by get_next_to_consume() that may not
        have executed.

        These include nodes such as statements in except blocks, or statements
        in try blocks (when evaluating their corresponding except and finally
        blocks). Checkers that want to treat the statements as executed
        (e.g. for unused-variable) may need to add them back.
        """
        return self._atomic.consumed_uncertain

    @property
    def scope_type(self) -> str:
        return self._atomic.scope_type

    def mark_as_consumed(self, name: str, consumed_nodes: list[nodes.NodeNG]) -> None:
        """Mark the given nodes as consumed for the name.

        If all of the nodes for the name were consumed, delete the name from
        the to_consume dictionary
        """
        unconsumed = [n for n in self.to_consume[name] if n not in set(consumed_nodes)]
        self.consumed[name] = consumed_nodes

        if unconsumed:
            self.to_consume[name] = unconsumed
        else:
            del self.to_consume[name]

    def get_next_to_consume(self, node: nodes.Name) -> list[nodes.NodeNG] | None:
        """Return a list of the nodes that define `node` from this scope.

        If it is uncertain whether a node will be consumed, such as for statements in
        except blocks, add it to self.consumed_uncertain instead of returning it.
        Return None to indicate a special case that needs to be handled by the caller.
        """
        name = node.name
        parent_node = node.parent
        found_nodes = self.to_consume.get(name)
        node_statement = node.statement()
        if (
            found_nodes
            and isinstance(parent_node, nodes.Assign)
            and parent_node == found_nodes[0].parent
        ):
            lhs = found_nodes[0].parent.targets[0]
            if (
                isinstance(lhs, nodes.AssignName) and lhs.name == name
            ):  # this name is defined in this very statement
                found_nodes = None

        if (
            found_nodes
            and isinstance(parent_node, nodes.For)
            and parent_node.iter == node
            and parent_node.target in found_nodes
        ):
            found_nodes = None

        # Before filtering, check that this node's name is not a nonlocal
        if any(
            isinstance(child, nodes.Nonlocal) and node.name in child.names
            for child in node.frame().get_children()
        ):
            return found_nodes

        # And no comprehension is under the node's frame
        if VariablesChecker._comprehension_between_frame_and_node(node):
            return found_nodes

        # Filter out assignments guarded by always false conditions
        if found_nodes:
            uncertain_nodes = self._uncertain_nodes_in_false_tests(found_nodes, node)
            self.consumed_uncertain[node.name] += uncertain_nodes
            uncertain_nodes_set = set(uncertain_nodes)
            found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]

        # Filter out assignments in ExceptHandlers that node is not contained in
        if found_nodes:
            found_nodes = [
                n
                for n in found_nodes
                if not isinstance(n.statement(), nodes.ExceptHandler)
                or n.statement().parent_of(node)
            ]

        # Filter out assignments in an Except clause that the node is not
        # contained in, assuming they may fail
        if found_nodes:
            uncertain_nodes = self._uncertain_nodes_in_except_blocks(
                found_nodes, node, node_statement
            )
            self.consumed_uncertain[node.name] += uncertain_nodes
            uncertain_nodes_set = set(uncertain_nodes)
            found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]

        # If this node is in a Finally block of a Try/Finally,
        # filter out assignments in the try portion, assuming they may fail
        if found_nodes:
            uncertain_nodes = (
                self._uncertain_nodes_in_try_blocks_when_evaluating_finally_blocks(
                    found_nodes, node_statement
                )
            )
            self.consumed_uncertain[node.name] += uncertain_nodes
            uncertain_nodes_set = set(uncertain_nodes)
            found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]

        # If this node is in an ExceptHandler,
        # filter out assignments in the try portion, assuming they may fail
        if found_nodes:
            uncertain_nodes = (
                self._uncertain_nodes_in_try_blocks_when_evaluating_except_blocks(
                    found_nodes, node_statement
                )
            )
            self.consumed_uncertain[node.name] += uncertain_nodes
            uncertain_nodes_set = set(uncertain_nodes)
            found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]

        return found_nodes

    @staticmethod
    def _inferred_to_define_name_raise_or_return(name: str, node: nodes.NodeNG) -> bool:
        """Return True if there is a path under this `if_node`
        that is inferred to define `name`, raise, or return.
        """
        # Handle try and with
        if isinstance(node, nodes.Try):
            # Allow either a path through try/else/finally OR a path through ALL except handlers
            try_except_node = node
            if node.finalbody:
                try_except_node = next(
                    (child for child in node.nodes_of_class(nodes.Try)),
                    None,
                )
            handlers = try_except_node.handlers if try_except_node else []
            return NamesConsumer._defines_name_raises_or_returns_recursive(
                name, node
            ) or all(
                NamesConsumer._defines_name_raises_or_returns_recursive(name, handler)
                for handler in handlers
            )

        if isinstance(node, (nodes.With, nodes.For, nodes.While)):
            return NamesConsumer._defines_name_raises_or_returns_recursive(name, node)

        if not isinstance(node, nodes.If):
            return False

        # Be permissive if there is a break
        if any(node.nodes_of_class(nodes.Break)):
            return True

        # Is there an assignment in this node itself, e.g. in named expression?
        if NamesConsumer._defines_name_raises_or_returns(name, node):
            return True

        test = node.test.value if isinstance(node.test, nodes.NamedExpr) else node.test
        all_inferred = utils.infer_all(test)
        only_search_if = False
        only_search_else = True

        for inferred in all_inferred:
            if not isinstance(inferred, nodes.Const):
                only_search_else = False
                continue
            val = inferred.value
            only_search_if = only_search_if or (val != NotImplemented and val)
            only_search_else = only_search_else and not val

        # Only search else branch when test condition is inferred to be false
        if all_inferred and only_search_else:
            return NamesConsumer._branch_handles_name(name, node.orelse)
        # Only search if branch when test condition is inferred to be true
        if all_inferred and only_search_if:
            return NamesConsumer._branch_handles_name(name, node.body)
        # Search both if and else branches
        return NamesConsumer._branch_handles_name(
            name, node.body
        ) or NamesConsumer._branch_handles_name(name, node.orelse)

    @staticmethod
    def _branch_handles_name(name: str, body: Iterable[nodes.NodeNG]) -> bool:
        return any(
            NamesConsumer._defines_name_raises_or_returns(name, if_body_stmt)
            or isinstance(
                if_body_stmt,
                (
                    nodes.If,
                    nodes.Try,
                    nodes.With,
                    nodes.For,
                    nodes.While,
                ),
            )
            and NamesConsumer._inferred_to_define_name_raise_or_return(
                name, if_body_stmt
            )
            for if_body_stmt in body
        )

    def _uncertain_nodes_in_false_tests(
        self, found_nodes: list[nodes.NodeNG], node: nodes.NodeNG
    ) -> list[nodes.NodeNG]:
        """Identify nodes of uncertain execution because they are defined under
        tests that evaluate false.

        Don't identify a node if there is a path that is inferred to
        define the name, raise, or return (e.g. any executed if/elif/else branch).
        """
        uncertain_nodes = []
        for other_node in found_nodes:
            if isinstance(other_node, nodes.AssignName):
                name = other_node.name
            elif isinstance(other_node, (nodes.Import, nodes.ImportFrom)):
                name = node.name
            else:
                continue

            all_if = [
                n
                for n in other_node.node_ancestors()
                if isinstance(n, nodes.If) and not n.parent_of(node)
            ]
            if not all_if:
                continue

            closest_if = all_if[0]
            if (
                isinstance(node, nodes.AssignName)
                and node.frame() is not closest_if.frame()
            ):
                continue
            if closest_if.parent_of(node):
                continue

            outer_if = all_if[-1]
            if NamesConsumer._node_guarded_by_same_test(node, outer_if):
                continue

            # Name defined in the if/else control flow
            if NamesConsumer._inferred_to_define_name_raise_or_return(name, outer_if):
                continue

            uncertain_nodes.append(other_node)

        return uncertain_nodes

    @staticmethod
    def _node_guarded_by_same_test(node: nodes.NodeNG, other_if: nodes.If) -> bool:
        """Identify if `node` is guarded by an equivalent test as `other_if`.

        Two tests are equivalent if their string representations are identical
        or if their inferred values consist only of constants and those constants
        are identical, and the if test guarding `node` is not a Name.
        """
        other_if_test_as_string = other_if.test.as_string()
        other_if_test_all_inferred = utils.infer_all(other_if.test)
        for ancestor in node.node_ancestors():
            if not isinstance(ancestor, nodes.If):
                continue
            if ancestor.test.as_string() == other_if_test_as_string:
                return True
            if isinstance(ancestor.test, nodes.Name):
                continue
            all_inferred = utils.infer_all(ancestor.test)
            if len(all_inferred) == len(other_if_test_all_inferred):
                if any(
                    not isinstance(test, nodes.Const)
                    for test in (*all_inferred, *other_if_test_all_inferred)
                ):
                    continue
                if {test.value for test in all_inferred} != {
                    test.value for test in other_if_test_all_inferred
                }:
                    continue
                return True

        return False

    @staticmethod
    def _uncertain_nodes_in_except_blocks(
        found_nodes: list[nodes.NodeNG],
        node: nodes.NodeNG,
        node_statement: nodes.Statement,
    ) -> list[nodes.NodeNG]:
        """Return any nodes in ``found_nodes`` that should be treated as uncertain
        because they are in an except block.
        """
        uncertain_nodes = []
        for other_node in found_nodes:
            other_node_statement = other_node.statement()
            # Only testing for statements in the except block of Try
            closest_except_handler = utils.get_node_first_ancestor_of_type(
                other_node_statement, nodes.ExceptHandler
            )
            if not closest_except_handler:
                continue
            # If the other node is in the same scope as this node, assume it executes
            if closest_except_handler.parent_of(node):
                continue
            closest_try_except: nodes.Try = closest_except_handler.parent
            # If the try or else blocks return, assume the except blocks execute.
            try_block_returns = any(
                isinstance(try_statement, nodes.Return)
                for try_statement in closest_try_except.body
            )
            else_block_returns = any(
                isinstance(else_statement, nodes.Return)
                for else_statement in closest_try_except.orelse
            )
            else_block_exits = any(
                isinstance(else_statement, nodes.Expr)
                and isinstance(else_statement.value, nodes.Call)
                and utils.is_terminating_func(else_statement.value)
                for else_statement in closest_try_except.orelse
            )

            if try_block_returns or else_block_returns or else_block_exits:
                # Exception: if this node is in the final block of the other_node_statement,
                # it will execute before returning. Assume the except statements are uncertain.
                if (
                    isinstance(node_statement.parent, nodes.Try)
                    and node_statement in node_statement.parent.finalbody
                    and closest_try_except.parent.parent_of(node_statement)
                ):
                    uncertain_nodes.append(other_node)
                # Or the node_statement is in the else block of the relevant Try
                elif (
                    isinstance(node_statement.parent, nodes.Try)
                    and node_statement in node_statement.parent.orelse
                    and closest_try_except.parent.parent_of(node_statement)
                ):
                    uncertain_nodes.append(other_node)
                # Assume the except blocks execute, so long as each handler
                # defines the name, raises, or returns.
                elif all(
                    NamesConsumer._defines_name_raises_or_returns_recursive(
                        node.name, handler
                    )
                    for handler in closest_try_except.handlers
                ):
                    continue

            if NamesConsumer._check_loop_finishes_via_except(node, closest_try_except):
                continue

            # Passed all tests for uncertain execution
            uncertain_nodes.append(other_node)
        return uncertain_nodes

    @staticmethod
    def _defines_name_raises_or_returns(name: str, node: nodes.NodeNG) -> bool:
        if isinstance(node, (nodes.Raise, nodes.Assert, nodes.Return)):
            return True
        if (
            isinstance(node, nodes.AnnAssign)
            and node.value
            and isinstance(node.target, nodes.AssignName)
            and node.target.name == name
        ):
            return True
        if isinstance(node, nodes.Assign):
            for target in node.targets:
                for elt in utils.get_all_elements(target):
                    if isinstance(elt, nodes.Starred):
                        elt = elt.value
                    if isinstance(elt, nodes.AssignName) and elt.name == name:
                        return True
        if isinstance(node, nodes.If):
            if any(
                child_named_expr.target.name == name
                for child_named_expr in node.nodes_of_class(nodes.NamedExpr)
            ):
                return True
        if isinstance(node, (nodes.Import, nodes.ImportFrom)) and any(
            (node_name[1] and node_name[1] == name) or (node_name[0] == name)
            for node_name in node.names
        ):
            return True
        if isinstance(node, nodes.With) and any(
            isinstance(item[1], nodes.AssignName) and item[1].name == name
            for item in node.items
        ):
            return True
        if isinstance(node, (nodes.ClassDef, nodes.FunctionDef)) and node.name == name:
            return True
        if (
            isinstance(node, nodes.ExceptHandler)
            and node.name
            and node.name.name == name
        ):
            return True
        return False

    @staticmethod
    def _defines_name_raises_or_returns_recursive(
        name: str, node: nodes.NodeNG
    ) -> bool:
        """Return True if some child of `node` defines the name `name`,
        raises, or returns.
        """
        for stmt in node.get_children():
            if NamesConsumer._defines_name_raises_or_returns(name, stmt):
                return True
            if isinstance(stmt, (nodes.If, nodes.With)):
                if any(
                    NamesConsumer._defines_name_raises_or_returns(name, nested_stmt)
                    for nested_stmt in stmt.get_children()
                ):
                    return True
            if (
                isinstance(stmt, nodes.Try)
                and not stmt.finalbody
                and NamesConsumer._defines_name_raises_or_returns_recursive(name, stmt)
            ):
                return True
        return False

    @staticmethod
    def _check_loop_finishes_via_except(
        node: nodes.NodeNG, other_node_try_except: nodes.Try
    ) -> bool:
        """Check for a specific control flow scenario.

        Described in https://github.com/pylint-dev/pylint/issues/5683.

        A scenario where the only non-break exit from a loop consists of the very
        except handler we are examining, such that code in the `else` branch of
        the loop can depend on it being assigned.

        Example:

        for _ in range(3):
            try:
                do_something()
            except:
                name = 1  <-- only non-break exit from loop
            else:
                break
        else:
            print(name)
        """
        if not other_node_try_except.orelse:
            return False
        closest_loop: None | (
            nodes.For | nodes.While
        ) = utils.get_node_first_ancestor_of_type(node, (nodes.For, nodes.While))
        if closest_loop is None:
            return False
        if not any(
            else_statement is node or else_statement.parent_of(node)
            for else_statement in closest_loop.orelse
        ):
            # `node` not guarded by `else`
            return False
        for inner_else_statement in other_node_try_except.orelse:
            if isinstance(inner_else_statement, nodes.Break):
                break_stmt = inner_else_statement
                break
        else:
            # No break statement
            return False

        def _try_in_loop_body(
            other_node_try_except: nodes.Try, loop: nodes.For | nodes.While
        ) -> bool:
            """Return True if `other_node_try_except` is a descendant of `loop`."""
            return any(
                loop_body_statement is other_node_try_except
                or loop_body_statement.parent_of(other_node_try_except)
                for loop_body_statement in loop.body
            )

        if not _try_in_loop_body(other_node_try_except, closest_loop):
            for ancestor in closest_loop.node_ancestors():
                if isinstance(ancestor, (nodes.For, nodes.While)):
                    if _try_in_loop_body(other_node_try_except, ancestor):
                        break
            else:
                # `other_node_try_except` didn't have a shared ancestor loop
                return False

        for loop_stmt in closest_loop.body:
            if NamesConsumer._recursive_search_for_continue_before_break(
                loop_stmt, break_stmt
            ):
                break
        else:
            # No continue found, so we arrived at our special case!
            return True
        return False

    @staticmethod
    def _recursive_search_for_continue_before_break(
        stmt: nodes.Statement, break_stmt: nodes.Break
    ) -> bool:
        """Return True if any Continue node can be found in descendants of `stmt`
        before encountering `break_stmt`, ignoring any nested loops.
        """
        if stmt is break_stmt:
            return False
        if isinstance(stmt, nodes.Continue):
            return True
        for child in stmt.get_children():
            if isinstance(stmt, (nodes.For, nodes.While)):
                continue
            if NamesConsumer._recursive_search_for_continue_before_break(
                child, break_stmt
            ):
                return True
        return False

    @staticmethod
    def _uncertain_nodes_in_try_blocks_when_evaluating_except_blocks(
        found_nodes: list[nodes.NodeNG], node_statement: nodes.Statement
    ) -> list[nodes.NodeNG]:
        """Return any nodes in ``found_nodes`` that should be treated as uncertain.

        Nodes are uncertain when they are in a try block and the ``node_statement``
        being evaluated is in one of its except handlers.
        """
        uncertain_nodes: list[nodes.NodeNG] = []
        closest_except_handler = utils.get_node_first_ancestor_of_type(
            node_statement, nodes.ExceptHandler
        )
        if closest_except_handler is None:
            return uncertain_nodes
        for other_node in found_nodes:
            other_node_statement = other_node.statement()
            # If the other statement is the except handler guarding `node`, it executes
            if other_node_statement is closest_except_handler:
                continue
            # Ensure other_node is in a try block
            (
                other_node_try_ancestor,
                other_node_try_ancestor_visited_child,
            ) = utils.get_node_first_ancestor_of_type_and_its_child(
                other_node_statement, nodes.Try
            )
            if other_node_try_ancestor is None:
                continue
            if (
                other_node_try_ancestor_visited_child
                not in other_node_try_ancestor.body
            ):
                continue
            # Make sure nesting is correct -- there should be at least one
            # except handler that is a sibling attached to the try ancestor,
            # or is an ancestor of the try ancestor.
            if not any(
                closest_except_handler in other_node_try_ancestor.handlers
                or other_node_try_ancestor_except_handler
                in closest_except_handler.node_ancestors()
                for other_node_try_ancestor_except_handler in other_node_try_ancestor.handlers
            ):
                continue
            # Passed all tests for uncertain execution
            uncertain_nodes.append(other_node)
        return uncertain_nodes

    @staticmethod
    def _uncertain_nodes_in_try_blocks_when_evaluating_finally_blocks(
        found_nodes: list[nodes.NodeNG], node_statement: nodes.Statement
    ) -> list[nodes.NodeNG]:
        uncertain_nodes: list[nodes.NodeNG] = []
        (
            closest_try_finally_ancestor,
            child_of_closest_try_finally_ancestor,
        ) = utils.get_node_first_ancestor_of_type_and_its_child(
            node_statement, nodes.Try
        )
        if closest_try_finally_ancestor is None:
            return uncertain_nodes
        if (
            child_of_closest_try_finally_ancestor
            not in closest_try_finally_ancestor.finalbody
        ):
            return uncertain_nodes
        for other_node in found_nodes:
            other_node_statement = other_node.statement()
            (
                other_node_try_finally_ancestor,
                child_of_other_node_try_finally_ancestor,
            ) = utils.get_node_first_ancestor_of_type_and_its_child(
                other_node_statement, nodes.Try
            )
            if other_node_try_finally_ancestor is None:
                continue
            # other_node needs to descend from the try of a try/finally.
            if (
                child_of_other_node_try_finally_ancestor
                not in other_node_try_finally_ancestor.body
            ):
                continue
            # If the two try/finally ancestors are not the same, then
            # node_statement's closest try/finally ancestor needs to be in
            # the final body of other_node's try/finally ancestor, or
            # descend from one of the statements in that final body.
            if (
                other_node_try_finally_ancestor is not closest_try_finally_ancestor
                and not any(
                    other_node_final_statement is closest_try_finally_ancestor
                    or other_node_final_statement.parent_of(
                        closest_try_finally_ancestor
                    )
                    for other_node_final_statement in other_node_try_finally_ancestor.finalbody
                )
            ):
                continue
            # Passed all tests for uncertain execution
            uncertain_nodes.append(other_node)
        return uncertain_nodes


# pylint: disable=too-many-public-methods
class VariablesChecker(BaseChecker):
    """BaseChecker for variables.

    Checks for
    * unused variables / imports
    * undefined variables
    * redefinition of variable from builtins or from an outer scope or except handler
    * use of variable before assignment
    * __all__ consistency
    * self/cls assignment
    """

    name = "variables"
    msgs = MSGS
    options = (
        (
            "init-import",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Tells whether we should check for unused import in "
                "__init__ files.",
            },
        ),
        (
            "dummy-variables-rgx",
            {
                "default": "_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_",
                "type": "regexp",
                "metavar": "<regexp>",
                "help": "A regular expression matching the name of dummy "
                "variables (i.e. expected to not be used).",
            },
        ),
        (
            "additional-builtins",
            {
                "default": (),
                "type": "csv",
                "metavar": "<comma separated list>",
                "help": "List of additional names supposed to be defined in "
                "builtins. Remember that you should avoid defining new builtins "
                "when possible.",
            },
        ),
        (
            "callbacks",
            {
                "default": ("cb_", "_cb"),
                "type": "csv",
                "metavar": "<callbacks>",
                "help": "List of strings which can identify a callback "
                "function by name. A callback name must start or "
                "end with one of those strings.",
            },
        ),
        (
            "redefining-builtins-modules",
            {
                "default": (
                    "six.moves",
                    "past.builtins",
                    "future.builtins",
                    "builtins",
                    "io",
                ),
                "type": "csv",
                "metavar": "<comma separated list>",
                "help": "List of qualified module names which can have objects "
                "that can redefine builtins.",
            },
        ),
        (
            "ignored-argument-names",
            {
                "default": IGNORED_ARGUMENT_NAMES,
                "type": "regexp",
                "metavar": "<regexp>",
                "help": "Argument names that match this expression will be ignored.",
            },
        ),
        (
            "allow-global-unused-variables",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Tells whether unused global variables should be treated as a violation.",
            },
        ),
        (
            "allowed-redefined-builtins",
            {
                "default": (),
                "type": "csv",
                "metavar": "<comma separated list>",
                "help": "List of names allowed to shadow builtins",
            },
        ),
    )

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._to_consume: list[NamesConsumer] = []
        self._type_annotation_names: list[str] = []
        self._except_handler_names_queue: list[
            tuple[nodes.ExceptHandler, nodes.AssignName]
        ] = []
        """This is a queue, last in first out."""
        self._evaluated_type_checking_scopes: dict[
            str, list[nodes.LocalsDictNodeNG]
        ] = {}
        self._postponed_evaluation_enabled = False

    @utils.only_required_for_messages(
        "unbalanced-dict-unpacking",
    )
    def visit_for(self, node: nodes.For) -> None:
        if not isinstance(node.target, nodes.Tuple):
            return

        targets = node.target.elts

        inferred = utils.safe_infer(node.iter)
        if not isinstance(inferred, DICT_TYPES):
            return

        values = self._nodes_to_unpack(inferred)
        if not values:
            # no dict items returned
            return

        if isinstance(inferred, astroid.objects.DictItems):
            # dict.items() is a bit special because values will be a tuple
            # So as long as there are always 2 targets and values each are
            # a tuple with two items, this will unpack correctly.
            # Example: `for key, val in {1: 2, 3: 4}.items()`
            if len(targets) == 2 and all(len(x.elts) == 2 for x in values):
                return

            # Starred nodes indicate ambiguous unpacking
            # if `dict.items()` is used so we won't flag them.
            if any(isinstance(target, nodes.Starred) for target in targets):
                return

        if isinstance(inferred, nodes.Dict):
            if isinstance(node.iter, nodes.Name):
                # If this a case of 'dict-items-missing-iter', we don't want to
                # report it as an 'unbalanced-dict-unpacking' as well
                # TODO (performance), merging both checks would streamline this
                if len(targets) == 2:
                    return

        else:
            is_starred_targets = any(
                isinstance(target, nodes.Starred) for target in targets
            )
            for value in values:
                value_length = self._get_value_length(value)
                is_valid_star_unpack = is_starred_targets and value_length >= len(
                    targets
                )
                if len(targets) != value_length and not is_valid_star_unpack:
                    details = _get_unpacking_extra_info(node, inferred)
                    self._report_unbalanced_unpacking(
                        node, inferred, targets, value_length, details
                    )
                    break

    def leave_for(self, node: nodes.For) -> None:
        self._store_type_annotation_names(node)

    def visit_module(self, node: nodes.Module) -> None:
        """Visit module : update consumption analysis variable
        checks globals doesn't overrides builtins.
        """
        self._to_consume = [NamesConsumer(node, "module")]
        self._postponed_evaluation_enabled = is_postponed_evaluation_enabled(node)

        for name, stmts in node.locals.items():
            if utils.is_builtin(name):
                if self._should_ignore_redefined_builtin(stmts[0]) or name == "__doc__":
                    continue
                self.add_message("redefined-builtin", args=name, node=stmts[0])

    @utils.only_required_for_messages(
        "unused-import",
        "unused-wildcard-import",
        "redefined-builtin",
        "undefined-all-variable",
        "invalid-all-object",
        "invalid-all-format",
        "unused-variable",
        "undefined-variable",
    )
    def leave_module(self, node: nodes.Module) -> None:
        """Leave module: check globals."""
        assert len(self._to_consume) == 1

        self._check_metaclasses(node)
        not_consumed = self._to_consume.pop().to_consume
        # attempt to check for __all__ if defined
        if "__all__" in node.locals:
            self._check_all(node, not_consumed)

        # check for unused globals
        self._check_globals(not_consumed)

        # don't check unused imports in __init__ files
        if not self.linter.config.init_import and node.package:
            return

        self._check_imports(not_consumed)
        self._type_annotation_names = []

    def visit_classdef(self, node: nodes.ClassDef) -> None:
        """Visit class: update consumption analysis variable."""
        self._to_consume.append(NamesConsumer(node, "class"))

    def leave_classdef(self, node: nodes.ClassDef) -> None:
        """Leave class: update consumption analysis variable."""
        # Check for hidden ancestor names
        # e.g. "six" in: Class X(six.with_metaclass(ABCMeta, object)):
        for name_node in node.nodes_of_class(nodes.Name):
            if (
                isinstance(name_node.parent, nodes.Call)
                and isinstance(name_node.parent.func, nodes.Attribute)
                and isinstance(name_node.parent.func.expr, nodes.Name)
            ):
                hidden_name_node = name_node.parent.func.expr
                for consumer in self._to_consume:
                    if hidden_name_node.name in consumer.to_consume:
                        consumer.mark_as_consumed(
                            hidden_name_node.name,
                            consumer.to_consume[hidden_name_node.name],
                        )
                        break
        self._to_consume.pop()

    def visit_lambda(self, node: nodes.Lambda) -> None:
        """Visit lambda: update consumption analysis variable."""
        self._to_consume.append(NamesConsumer(node, "lambda"))

    def leave_lambda(self, _: nodes.Lambda) -> None:
        """Leave lambda: update consumption analysis variable."""
        # do not check for not used locals here
        self._to_consume.pop()

    def visit_generatorexp(self, node: nodes.GeneratorExp) -> None:
        """Visit genexpr: update consumption analysis variable."""
        self._to_consume.append(NamesConsumer(node, "comprehension"))

    def leave_generatorexp(self, _: nodes.GeneratorExp) -> None:
        """Leave genexpr: update consumption analysis variable."""
        # do not check for not used locals here
        self._to_consume.pop()

    def visit_dictcomp(self, node: nodes.DictComp) -> None:
        """Visit dictcomp: update consumption analysis variable."""
        self._to_consume.append(NamesConsumer(node, "comprehension"))

    def leave_dictcomp(self, _: nodes.DictComp) -> None:
        """Leave dictcomp: update consumption analysis variable."""
        # do not check for not used locals here
        self._to_consume.pop()

    def visit_setcomp(self, node: nodes.SetComp) -> None:
        """Visit setcomp: update consumption analysis variable."""
        self._to_consume.append(NamesConsumer(node, "comprehension"))

    def leave_setcomp(self, _: nodes.SetComp) -> None:
        """Leave setcomp: update consumption analysis variable."""
        # do not check for not used locals here
        self._to_consume.pop()

    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        """Visit function: update consumption analysis variable and check locals."""
        self._to_consume.append(NamesConsumer(node, "function"))
        if not (
            self.linter.is_message_enabled("redefined-outer-name")
            or self.linter.is_message_enabled("redefined-builtin")
        ):
            return
        globs = node.root().globals
        for name, stmt in node.items():
            if name in globs and not isinstance(stmt, nodes.Global):
                definition = globs[name][0]
                if (
                    isinstance(definition, nodes.ImportFrom)
                    and definition.modname == FUTURE
                ):
                    # It is a __future__ directive, not a symbol.
                    continue

                # Do not take in account redefined names for the purpose
                # of type checking.:
                if any(
                    in_type_checking_block(definition) for definition in globs[name]
                ):
                    continue

                line = definition.fromlineno
                if not self._is_name_ignored(stmt, name):
                    self.add_message(
                        "redefined-outer-name", args=(name, line), node=stmt
                    )

            elif (
                utils.is_builtin(name)
                and not self._allowed_redefined_builtin(name)
                and not self._should_ignore_redefined_builtin(stmt)
            ):
                # do not print Redefining builtin for additional builtins
                self.add_message("redefined-builtin", args=name, node=stmt)

    def leave_functiondef(self, node: nodes.FunctionDef) -> None:
        """Leave function: check function's locals are consumed."""
        self._check_metaclasses(node)

        if node.type_comment_returns:
            self._store_type_annotation_node(node.type_comment_returns)
        if node.type_comment_args:
            for argument_annotation in node.type_comment_args:
                self._store_type_annotation_node(argument_annotation)

        not_consumed = self._to_consume.pop().to_consume
        if not (
            self.linter.is_message_enabled("unused-variable")
            or self.linter.is_message_enabled("possibly-unused-variable")
            or self.linter.is_message_enabled("unused-argument")
        ):
            return

        # Don't check arguments of function which are only raising an exception.
        if utils.is_error(node):
            return

        # Don't check arguments of abstract methods or within an interface.
        is_method = node.is_method()
        if is_method and node.is_abstract():
            return

        global_names = _flattened_scope_names(node.nodes_of_class(nodes.Global))
        nonlocal_names = _flattened_scope_names(node.nodes_of_class(nodes.Nonlocal))
        comprehension_target_names: set[str] = set()

        for comprehension_scope in node.nodes_of_class(nodes.ComprehensionScope):
            for generator in comprehension_scope.generators:
                for name in utils.find_assigned_names_recursive(generator.target):
                    comprehension_target_names.add(name)

        for name, stmts in not_consumed.items():
            self._check_is_unused(
                name,
                node,
                stmts[0],
                global_names,
                nonlocal_names,
                comprehension_target_names,
            )

    visit_asyncfunctiondef = visit_functiondef
    leave_asyncfunctiondef = leave_functiondef

    @utils.only_required_for_messages(
        "global-variable-undefined",
        "global-variable-not-assigned",
        "global-statement",
        "global-at-module-level",
        "redefined-builtin",
    )
    def visit_global(self, node: nodes.Global) -> None:
        """Check names imported exists in the global scope."""
        frame = node.frame()
        if isinstance(frame, nodes.Module):
            self.add_message("global-at-module-level", node=node, confidence=HIGH)
            return

        module = frame.root()
        default_message = True
        locals_ = node.scope().locals
        for name in node.names:
            try:
                assign_nodes = module.getattr(name)
            except astroid.NotFoundError:
                # unassigned global, skip
                assign_nodes = []

            not_defined_locally_by_import = not any(
                isinstance(local, (nodes.Import, nodes.ImportFrom))
                for local in locals_.get(name, ())
            )
            if (
                not utils.is_reassigned_after_current(node, name)
                and not utils.is_deleted_after_current(node, name)
                and not_defined_locally_by_import
            ):
                self.add_message(
                    "global-variable-not-assigned",
                    args=name,
                    node=node,
                    confidence=HIGH,
                )
                default_message = False
                continue

            for anode in assign_nodes:
                if (
                    isinstance(anode, nodes.AssignName)
                    and anode.name in module.special_attributes
                ):
                    self.add_message("redefined-builtin", args=name, node=node)
                    break
                if anode.frame() is module:
                    # module level assignment
                    break
                if (
                    isinstance(anode, (nodes.ClassDef, nodes.FunctionDef))
                    and anode.parent is module
                ):
                    # module level function assignment
                    break
            else:
                if not_defined_locally_by_import:
                    # global undefined at the module scope
                    self.add_message(
                        "global-variable-undefined",
                        args=name,
                        node=node,
                        confidence=HIGH,
                    )
                    default_message = False

        if default_message:
            self.add_message("global-statement", node=node, confidence=HIGH)

    def visit_assignname(self, node: nodes.AssignName) -> None:
        if isinstance(node.assign_type(), nodes.AugAssign):
            self.visit_name(node)

    def visit_delname(self, node: nodes.DelName) -> None:
        self.visit_name(node)

    def visit_name(self, node: nodes.Name | nodes.AssignName | nodes.DelName) -> None:
        """Don't add the 'utils.only_required_for_messages' decorator here!

        It's important that all 'Name' nodes are visited, otherwise the
        'NamesConsumers' won't be correct.
        """
        stmt = node.statement()
        if stmt.fromlineno is None:
            # name node from an astroid built from live code, skip
            assert not stmt.root().file.endswith(".py")
            return

        self._undefined_and_used_before_checker(node, stmt)
        self._loopvar_name(node)

    @utils.only_required_for_messages("redefined-outer-name")
    def visit_excepthandler(self, node: nodes.ExceptHandler) -> None:
        if not node.name or not isinstance(node.name, nodes.AssignName):
            return

        for outer_except, outer_except_assign_name in self._except_handler_names_queue:
            if node.name.name == outer_except_assign_name.name:
                self.add_message(
                    "redefined-outer-name",
                    args=(outer_except_assign_name.name, outer_except.fromlineno),
                    node=node,
                )
                break

        self._except_handler_names_queue.append((node, node.name))

    @utils.only_required_for_messages("redefined-outer-name")
    def leave_excepthandler(self, node: nodes.ExceptHandler) -> None:
        if not node.name or not isinstance(node.name, nodes.AssignName):
            return
        self._except_handler_names_queue.pop()

    def _undefined_and_used_before_checker(
        self, node: nodes.Name, stmt: nodes.NodeNG
    ) -> None:
        frame = stmt.scope()
        start_index = len(self._to_consume) - 1

        # iterates through parent scopes, from the inner to the outer
        base_scope_type = self._to_consume[start_index].scope_type

        for i in range(start_index, -1, -1):
            current_consumer = self._to_consume[i]

            # Certain nodes shouldn't be checked as they get checked another time
            if self._should_node_be_skipped(node, current_consumer, i == start_index):
                continue

            action, nodes_to_consume = self._check_consumer(
                node, stmt, frame, current_consumer, base_scope_type
            )
            if nodes_to_consume:
                # Any nodes added to consumed_uncertain by get_next_to_consume()
                # should be added back so that they are marked as used.
                # They will have already had a chance to emit used-before-assignment.
                # We check here instead of before every single return in _check_consumer()
                nodes_to_consume += current_consumer.consumed_uncertain[node.name]
                current_consumer.mark_as_consumed(node.name, nodes_to_consume)
            if action is VariableVisitConsumerAction.CONTINUE:
                continue
            if action is VariableVisitConsumerAction.RETURN:
                return

        # we have not found the name, if it isn't a builtin, that's an
        # undefined name !
        if not (
            node.name in nodes.Module.scope_attrs
            or utils.is_builtin(node.name)
            or node.name in self.linter.config.additional_builtins
            or (
                node.name == "__class__"
                and any(
                    i.is_method()
                    for i in node.node_ancestors()
                    if isinstance(i, nodes.FunctionDef)
                )
            )
        ) and not utils.node_ignores_exception(node, NameError):
            self.add_message("undefined-variable", args=node.name, node=node)

    def _should_node_be_skipped(
        self, node: nodes.Name, consumer: NamesConsumer, is_start_index: bool
    ) -> bool:
        """Tests a consumer and node for various conditions in which the node shouldn't
        be checked for the undefined-variable and used-before-assignment checks.
        """
        if consumer.scope_type == "class":
            # The list of base classes in the class definition is not part
            # of the class body.
            # If the current scope is a class scope but it's not the inner
            # scope, ignore it. This prevents to access this scope instead of
            # the globals one in function members when there are some common
            # names.
            if utils.is_ancestor_name(consumer.node, node) or (
                not is_start_index and self._ignore_class_scope(node)
            ):
                return True

            # Ignore inner class scope for keywords in class definition
            if isinstance(node.parent, nodes.Keyword) and isinstance(
                node.parent.parent, nodes.ClassDef
            ):
                return True

        elif consumer.scope_type == "function" and self._defined_in_function_definition(
            node, consumer.node
        ):
            # If the name node is used as a function default argument's value or as
            # a decorator, then start from the parent frame of the function instead
            # of the function frame - and thus open an inner class scope
            return True

        elif consumer.scope_type == "lambda" and utils.is_default_argument(
            node, consumer.node
        ):
            return True

        return False

    # pylint: disable = too-many-return-statements, too-many-branches
    def _check_consumer(
        self,
        node: nodes.Name,
        stmt: nodes.NodeNG,
        frame: nodes.LocalsDictNodeNG,
        current_consumer: NamesConsumer,
        base_scope_type: str,
    ) -> tuple[VariableVisitConsumerAction, list[nodes.NodeNG] | None]:
        """Checks a consumer for conditions that should trigger messages."""
        # If the name has already been consumed, only check it's not a loop
        # variable used outside the loop.
        if node.name in current_consumer.consumed:
            # Avoid the case where there are homonyms inside function scope and
            # comprehension current scope (avoid bug #1731)
            if utils.is_func_decorator(current_consumer.node) or not isinstance(
                node, nodes.ComprehensionScope
            ):
                self._check_late_binding_closure(node)
                return (VariableVisitConsumerAction.RETURN, None)

        found_nodes = current_consumer.get_next_to_consume(node)
        if found_nodes is None:
            return (VariableVisitConsumerAction.CONTINUE, None)
        if not found_nodes:
            self._report_unfound_name_definition(node, current_consumer)
            # Mark for consumption any nodes added to consumed_uncertain by
            # get_next_to_consume() because they might not have executed.
            nodes_to_consume = current_consumer.consumed_uncertain[node.name]
            nodes_to_consume = self._filter_type_checking_import_from_consumption(
                node, nodes_to_consume
            )
            return (
                VariableVisitConsumerAction.RETURN,
                nodes_to_consume,
            )

        self._check_late_binding_closure(node)

        defnode = utils.assign_parent(found_nodes[0])
        defstmt = defnode.statement()
        defframe = defstmt.frame()

        # The class reuses itself in the class scope.
        is_recursive_klass: bool = (
            frame is defframe
            and defframe.parent_of(node)
            and isinstance(defframe, nodes.ClassDef)
            and node.name == defframe.name
        )

        if (
            is_recursive_klass
            and utils.get_node_first_ancestor_of_type(node, nodes.Lambda)
            and (
                not utils.is_default_argument(node)
                or node.scope().parent.scope() is not defframe
            )
        ):
            # Self-referential class references are fine in lambda's --
            # As long as they are not part of the default argument directly
            # under the scope of the parent self-referring class.
            # Example of valid default argument:
            # class MyName3:
            #     myattr = 1
            #     mylambda3 = lambda: lambda a=MyName3: a
            # Example of invalid default argument:
            # class MyName4:
            #     myattr = 1
            #     mylambda4 = lambda a=MyName4: lambda: a

            # If the above conditional is True,
            # there is no possibility of undefined-variable
            # Also do not consume class name
            # (since consuming blocks subsequent checks)
            # -- quit
            return (VariableVisitConsumerAction.RETURN, None)

        (
            maybe_before_assign,
            annotation_return,
            use_outer_definition,
        ) = self._is_variable_violation(
            node,
            defnode,
            stmt,
            defstmt,
            frame,
            defframe,
            base_scope_type,
            is_recursive_klass,
        )

        if use_outer_definition:
            return (VariableVisitConsumerAction.CONTINUE, None)

        if (
            maybe_before_assign
            and not utils.is_defined_before(node)
            and not astroid.are_exclusive(stmt, defstmt, ("NameError",))
        ):
            # Used and defined in the same place, e.g `x += 1` and `del x`
            defined_by_stmt = defstmt is stmt and isinstance(
                node, (nodes.DelName, nodes.AssignName)
            )
            if (
                is_recursive_klass
                or defined_by_stmt
                or annotation_return
                or isinstance(defstmt, nodes.Delete)
            ):
                if not utils.node_ignores_exception(node, NameError):
                    # Handle postponed evaluation of annotations
                    if not (
                        self._postponed_evaluation_enabled
                        and isinstance(
                            stmt,
                            (
                                nodes.AnnAssign,
                                nodes.FunctionDef,
                                nodes.Arguments,
                            ),
                        )
                        and node.name in node.root().locals
                    ):
                        if defined_by_stmt:
                            return (VariableVisitConsumerAction.CONTINUE, [node])
                        return (VariableVisitConsumerAction.CONTINUE, None)

            elif base_scope_type != "lambda":
                # E0601 may *not* occurs in lambda scope.

                # Skip postponed evaluation of annotations
                # and unevaluated annotations inside a function body
                if not (
                    self._postponed_evaluation_enabled
                    and isinstance(stmt, (nodes.AnnAssign, nodes.FunctionDef))
                ) and not (
                    isinstance(stmt, nodes.AnnAssign)
                    and utils.get_node_first_ancestor_of_type(stmt, nodes.FunctionDef)
                ):
                    self.add_message(
                        "used-before-assignment",
                        args=node.name,
                        node=node,
                        confidence=HIGH,
                    )
                    return (VariableVisitConsumerAction.RETURN, found_nodes)

            elif base_scope_type == "lambda":
                # E0601 can occur in class-level scope in lambdas, as in
                # the following example:
                #   class A:
                #      x = lambda attr: f + attr
                #      f = 42
                # We check lineno because doing the following is fine:
                #   class A:
                #      x = 42
                #      y = lambda attr: x + attr
                if (
                    isinstance(frame, nodes.ClassDef)
                    and node.name in frame.locals
                    and stmt.fromlineno <= defstmt.fromlineno
                ):
                    self.add_message(
                        "used-before-assignment",
                        args=node.name,
                        node=node,
                        confidence=HIGH,
                    )

        elif not self._is_builtin(node.name) and self._is_only_type_assignment(
            node, defstmt
        ):
            if node.scope().locals.get(node.name):
                self.add_message(
                    "used-before-assignment", args=node.name, node=node, confidence=HIGH
                )
            else:
                self.add_message(
                    "undefined-variable", args=node.name, node=node, confidence=HIGH
                )
            return (VariableVisitConsumerAction.RETURN, found_nodes)

        elif isinstance(defstmt, nodes.ClassDef):
            return self._is_first_level_self_reference(node, defstmt, found_nodes)

        elif isinstance(defnode, nodes.NamedExpr):
            if isinstance(defnode.parent, nodes.IfExp):
                if self._is_never_evaluated(defnode, defnode.parent):
                    self.add_message(
                        "undefined-variable",
                        args=node.name,
                        node=node,
                        confidence=INFERENCE,
                    )
                    return (VariableVisitConsumerAction.RETURN, found_nodes)

        return (VariableVisitConsumerAction.RETURN, found_nodes)

    def _report_unfound_name_definition(
        self, node: nodes.NodeNG, current_consumer: NamesConsumer
    ) -> None:
        """Reports used-before-assignment when all name definition nodes
        get filtered out by NamesConsumer.
        """
        if (
            self._postponed_evaluation_enabled
            and utils.is_node_in_type_annotation_context(node)
        ):
            return
        if self._is_builtin(node.name):
            return
        if self._is_variable_annotation_in_function(node):
            return
        if (
            node.name in self._evaluated_type_checking_scopes
            and node.scope() in self._evaluated_type_checking_scopes[node.name]
        ):
            return

        confidence = (
            CONTROL_FLOW if node.name in current_consumer.consumed_uncertain else HIGH
        )
        self.add_message(
            "used-before-assignment",
            args=node.name,
            node=node,
            confidence=confidence,
        )

    def _filter_type_checking_import_from_consumption(
        self, node: nodes.NodeNG, nodes_to_consume: list[nodes.NodeNG]
    ) -> list[nodes.NodeNG]:
        """Do not consume type-checking import node as used-before-assignment
        may invoke in different scopes.
        """
        type_checking_import = next(
            (
                n
                for n in nodes_to_consume
                if isinstance(n, (nodes.Import, nodes.ImportFrom))
                and in_type_checking_block(n)
            ),
            None,
        )
        # If used-before-assignment reported for usage of type checking import
        # keep track of its scope
        if type_checking_import and not self._is_variable_annotation_in_function(node):
            self._evaluated_type_checking_scopes.setdefault(node.name, []).append(
                node.scope()
            )
        nodes_to_consume = [n for n in nodes_to_consume if n != type_checking_import]
        return nodes_to_consume

    @utils.only_required_for_messages("no-name-in-module")
    def visit_import(self, node: nodes.Import) -> None:
        """Check modules attribute accesses."""
        if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node):
            # No need to verify this, since ImportError is already
            # handled by the client code.
            return
        # Don't verify import if part of guarded import block
        if in_type_checking_block(node):
            return
        if isinstance(node.parent, nodes.If) and is_sys_guard(node.parent):
            return

        for name, _ in node.names:
            parts = name.split(".")
            try:
                module = next(_infer_name_module(node, parts[0]))
            except astroid.ResolveError:
                continue
            if not isinstance(module, nodes.Module):
                continue
            self._check_module_attrs(node, module, parts[1:])

    @utils.only_required_for_messages("no-name-in-module")
    def visit_importfrom(self, node: nodes.ImportFrom) -> None:
        """Check modules attribute accesses."""
        if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node):
            # No need to verify this, since ImportError is already
            # handled by the client code.
            return
        # Don't verify import if part of guarded import block
        # I.e. `sys.version_info` or `typing.TYPE_CHECKING`
        if in_type_checking_block(node):
            return
        if isinstance(node.parent, nodes.If) and is_sys_guard(node.parent):
            return

        name_parts = node.modname.split(".")
        try:
            module = node.do_import_module(name_parts[0])
        except astroid.AstroidBuildingException:
            return
        module = self._check_module_attrs(node, module, name_parts[1:])
        if not module:
            return
        for name, _ in node.names:
            if name == "*":
                continue
            self._check_module_attrs(node, module, name.split("."))

    @utils.only_required_for_messages(
        "unbalanced-tuple-unpacking",
        "unpacking-non-sequence",
        "self-cls-assignment",
        "unbalanced_dict_unpacking",
    )
    def visit_assign(self, node: nodes.Assign) -> None:
        """Check unbalanced tuple unpacking for assignments and unpacking
        non-sequences as well as in case self/cls get assigned.
        """
        self._check_self_cls_assign(node)
        if not isinstance(node.targets[0], (nodes.Tuple, nodes.List)):
            return

        targets = node.targets[0].itered()

        # Check if we have starred nodes.
        if any(isinstance(target, nodes.Starred) for target in targets):
            return

        try:
            inferred = utils.safe_infer(node.value)
            if inferred is not None:
                self._check_unpacking(inferred, node, targets)
        except astroid.InferenceError:
            return

    # listcomp have now also their scope
    def visit_listcomp(self, node: nodes.ListComp) -> None:
        """Visit listcomp: update consumption analysis variable."""
        self._to_consume.append(NamesConsumer(node, "comprehension"))

    def leave_listcomp(self, _: nodes.ListComp) -> None:
        """Leave listcomp: update consumption analysis variable."""
        # do not check for not used locals here
        self._to_consume.pop()

    def leave_assign(self, node: nodes.Assign) -> None:
        self._store_type_annotation_names(node)

    def leave_with(self, node: nodes.With) -> None:
        self._store_type_annotation_names(node)

    def visit_arguments(self, node: nodes.Arguments) -> None:
        for annotation in node.type_comment_args:
            self._store_type_annotation_node(annotation)

    # Relying on other checker's options, which might not have been initialized yet.
    @cached_property
    def _analyse_fallback_blocks(self) -> bool:
        return bool(self.linter.config.analyse_fallback_blocks)

    @cached_property
    def _ignored_modules(self) -> Iterable[str]:
        return self.linter.config.ignored_modules  # type: ignore[no-any-return]

    @cached_property
    def _allow_global_unused_variables(self) -> bool:
        return bool(self.linter.config.allow_global_unused_variables)

    @staticmethod
    def _defined_in_function_definition(
        node: nodes.NodeNG, frame: nodes.NodeNG
    ) -> bool:
        in_annotation_or_default_or_decorator = False
        if isinstance(frame, nodes.FunctionDef) and node.statement() is frame:
            in_annotation_or_default_or_decorator = (
                (
                    node in frame.args.annotations
                    or node in frame.args.posonlyargs_annotations
                    or node in frame.args.kwonlyargs_annotations
                    or node is frame.args.varargannotation
                    or node is frame.args.kwargannotation
                )
                or frame.args.parent_of(node)
                or (frame.decorators and frame.decorators.parent_of(node))
                or (
                    frame.returns
                    and (node is frame.returns or frame.returns.parent_of(node))
                )
            )
        return in_annotation_or_default_or_decorator

    @staticmethod
    def _in_lambda_or_comprehension_body(
        node: nodes.NodeNG, frame: nodes.NodeNG
    ) -> bool:
        """Return True if node within a lambda/comprehension body (or similar) and thus
        should not have access to class attributes in frame.
        """
        child = node
        parent = node.parent
        while parent is not None:
            if parent is frame:
                return False
            if isinstance(parent, nodes.Lambda) and child is not parent.args:
                # Body of lambda should not have access to class attributes.
                return True
            if isinstance(parent, nodes.Comprehension) and child is not parent.iter:
                # Only iter of list/set/dict/generator comprehension should have access.
                return True
            if isinstance(parent, nodes.ComprehensionScope) and not (
                parent.generators and child is parent.generators[0]
            ):
                # Body of list/set/dict/generator comprehension should not have access to class attributes.
                # Furthermore, only the first generator (if multiple) in comprehension should have access.
                return True
            child = parent
            parent = parent.parent
        return False

    @staticmethod
    def _is_variable_violation(
        node: nodes.Name,
        defnode: nodes.NodeNG,
        stmt: nodes.Statement,
        defstmt: nodes.Statement,
        frame: nodes.LocalsDictNodeNG,  # scope of statement of node
        defframe: nodes.LocalsDictNodeNG,
        base_scope_type: str,
        is_recursive_klass: bool,
    ) -> tuple[bool, bool, bool]:
        maybe_before_assign = True
        annotation_return = False
        use_outer_definition = False
        if frame is not defframe:
            maybe_before_assign = _detect_global_scope(node, frame, defframe)
        elif defframe.parent is None:
            # we are at the module level, check the name is not
            # defined in builtins
            if (
                node.name in defframe.scope_attrs
                or astroid.builtin_lookup(node.name)[1]
            ):
                maybe_before_assign = False
        else:
            # we are in a local scope, check the name is not
            # defined in global or builtin scope
            # skip this lookup if name is assigned later in function scope/lambda
            # Note: the node.frame() is not the same as the `frame` argument which is
            # equivalent to frame.statement().scope()
            forbid_lookup = (
                isinstance(frame, nodes.FunctionDef)
                or isinstance(node.frame(), nodes.Lambda)
            ) and _assigned_locally(node)
            if not forbid_lookup and defframe.root().lookup(node.name)[1]:
                maybe_before_assign = False
                use_outer_definition = stmt == defstmt and not isinstance(
                    defnode, nodes.Comprehension
                )
            # check if we have a nonlocal
            elif node.name in defframe.locals:
                maybe_before_assign = not any(
                    isinstance(child, nodes.Nonlocal) and node.name in child.names
                    for child in defframe.get_children()
                )

        if (
            base_scope_type == "lambda"
            and isinstance(frame, nodes.ClassDef)
            and node.name in frame.locals
        ):
            # This rule verifies that if the definition node of the
            # checked name is an Arguments node and if the name
            # is used a default value in the arguments defaults
            # and the actual definition of the variable label
            # is happening before the Arguments definition.
            #
            # bar = None
            # foo = lambda bar=bar: bar
            #
            # In this case, maybe_before_assign should be False, otherwise
            # it should be True.
            maybe_before_assign = not (
                isinstance(defnode, nodes.Arguments)
                and node in defnode.defaults
                and frame.locals[node.name][0].fromlineno < defstmt.fromlineno
            )
        elif isinstance(defframe, nodes.ClassDef) and isinstance(
            frame, nodes.FunctionDef
        ):
            # Special rule for function return annotations,
            # using a name defined earlier in the class containing the function.
            if node is frame.returns and defframe.parent_of(frame.returns):
                annotation_return = True
                if (
                    frame.returns.name in defframe.locals
                    and defframe.locals[node.name][0].lineno < frame.lineno
                ):
                    # Detect class assignments with a name defined earlier in the
                    # class. In this case, no warning should be raised.
                    maybe_before_assign = False
                else:
                    maybe_before_assign = True
            if isinstance(node.parent, nodes.Arguments):
                maybe_before_assign = stmt.fromlineno <= defstmt.fromlineno
        elif is_recursive_klass:
            maybe_before_assign = True
        else:
            maybe_before_assign = (
                maybe_before_assign and stmt.fromlineno <= defstmt.fromlineno
            )
            if maybe_before_assign and stmt.fromlineno == defstmt.fromlineno:
                if (
                    isinstance(defframe, nodes.FunctionDef)
                    and frame is defframe
                    and defframe.parent_of(node)
                    and stmt is not defstmt
                ):
                    # Single statement function, with the statement on the
                    # same line as the function definition
                    maybe_before_assign = False
                elif (
                    isinstance(defstmt, NODES_WITH_VALUE_ATTR)
                    and VariablesChecker._maybe_used_and_assigned_at_once(defstmt)
                    and frame is defframe
                    and defframe.parent_of(node)
                    and stmt is defstmt
                ):
                    # Single statement if, with assignment expression on same
                    # line as assignment
                    # x = b if (b := True) else False
                    maybe_before_assign = False
                elif (
                    isinstance(  # pylint: disable=too-many-boolean-expressions
                        defnode, nodes.NamedExpr
                    )
                    and frame is defframe
                    and defframe.parent_of(stmt)
                    and stmt is defstmt
                    and (
                        (
                            defnode.lineno == node.lineno
                            and defnode.col_offset < node.col_offset
                        )
                        or (defnode.lineno < node.lineno)
                        or (
                            # Issue in the `ast` module until py39
                            # Nodes in a multiline string have the same lineno
                            # Could be false-positive without check
                            not PY39_PLUS
                            and defnode.lineno == node.lineno
                            and isinstance(
                                defstmt,
                                (
                                    nodes.Assign,
                                    nodes.AnnAssign,
                                    nodes.AugAssign,
                                    nodes.Return,
                                ),
                            )
                            and isinstance(defstmt.value, nodes.JoinedStr)
                        )
                    )
                ):
                    # Relation of a name to the same name in a named expression
                    # Could be used before assignment if self-referencing:
                    # (b := b)
                    # Otherwise, safe if used after assignment:
                    # (b := 2) and b
                    maybe_before_assign = defnode.value is node or any(
                        anc is defnode.value for anc in node.node_ancestors()
                    )

        return maybe_before_assign, annotation_return, use_outer_definition

    @staticmethod
    def _maybe_used_and_assigned_at_once(defstmt: nodes.Statement) -> bool:
        """Check if `defstmt` has the potential to use and assign a name in the
        same statement.
        """
        if isinstance(defstmt, nodes.Match):
            return any(case.guard for case in defstmt.cases)
        if isinstance(defstmt, nodes.IfExp):
            return True
        if isinstance(defstmt.value, nodes.BaseContainer):
            return any(
                VariablesChecker._maybe_used_and_assigned_at_once(elt)
                for elt in defstmt.value.elts
                if isinstance(elt, (*NODES_WITH_VALUE_ATTR, nodes.IfExp, nodes.Match))
            )
        value = defstmt.value
        if isinstance(value, nodes.IfExp):
            return True
        if isinstance(value, nodes.Lambda) and isinstance(value.body, nodes.IfExp):
            return True
        if isinstance(value, nodes.Dict) and any(
            isinstance(item[0], nodes.IfExp) or isinstance(item[1], nodes.IfExp)
            for item in value.items
        ):
            return True
        if not isinstance(value, nodes.Call):
            return False
        return any(
            any(isinstance(kwarg.value, nodes.IfExp) for kwarg in call.keywords)
            or any(isinstance(arg, nodes.IfExp) for arg in call.args)
            or (
                isinstance(call.func, nodes.Attribute)
                and isinstance(call.func.expr, nodes.IfExp)
            )
            for call in value.nodes_of_class(klass=nodes.Call)
        )

    def _is_builtin(self, name: str) -> bool:
        return name in self.linter.config.additional_builtins or utils.is_builtin(name)

    @staticmethod
    def _is_only_type_assignment(node: nodes.Name, defstmt: nodes.Statement) -> bool:
        """Check if variable only gets assigned a type and never a value."""
        if not isinstance(defstmt, nodes.AnnAssign) or defstmt.value:
            return False

        defstmt_frame = defstmt.frame()
        node_frame = node.frame()

        parent = node
        while parent is not defstmt_frame.parent:
            parent_scope = parent.scope()

            # Find out if any nonlocals receive values in nested functions
            for inner_func in parent_scope.nodes_of_class(nodes.FunctionDef):
                if inner_func is parent_scope:
                    continue
                if any(
                    node.name in nl.names
                    for nl in inner_func.nodes_of_class(nodes.Nonlocal)
                ) and any(
                    node.name == an.name
                    for an in inner_func.nodes_of_class(nodes.AssignName)
                ):
                    return False

            local_refs = parent_scope.locals.get(node.name, [])
            for ref_node in local_refs:
                # If local ref is in the same frame as our node, but on a later lineno
                # we don't actually care about this local ref.
                # Local refs are ordered, so we break.
                #     print(var)
                #     var = 1  # <- irrelevant
                if defstmt_frame == node_frame and ref_node.lineno > node.lineno:
                    break

                # If the parent of the local reference is anything but an AnnAssign
                # Or if the AnnAssign adds a value the variable will now have a value
                #     var = 1  # OR
                #     var: int = 1
                if (
                    not isinstance(ref_node.parent, nodes.AnnAssign)
                    or ref_node.parent.value
                ) and not (
                    # EXCEPTION: will not have a value if a self-referencing named expression
                    # var: int
                    # if (var := var * var)  <-- "var" still undefined
                    isinstance(ref_node.parent, nodes.NamedExpr)
                    and any(
                        anc is ref_node.parent.value for anc in node.node_ancestors()
                    )
                ):
                    return False
            parent = parent_scope.parent
        return True

    @staticmethod
    def _is_first_level_self_reference(
        node: nodes.Name, defstmt: nodes.ClassDef, found_nodes: list[nodes.NodeNG]
    ) -> tuple[VariableVisitConsumerAction, list[nodes.NodeNG] | None]:
        """Check if a first level method's annotation or default values
        refers to its own class, and return a consumer action.
        """
        if node.frame().parent == defstmt and node.statement() == node.frame():
            # Check if used as type annotation
            # Break if postponed evaluation is enabled
            if utils.is_node_in_type_annotation_context(node):
                if not utils.is_postponed_evaluation_enabled(node):
                    return (VariableVisitConsumerAction.CONTINUE, None)
                return (VariableVisitConsumerAction.RETURN, None)
            # Check if used as default value by calling the class
            if isinstance(node.parent, nodes.Call) and isinstance(
                node.parent.parent, nodes.Arguments
            ):
                return (VariableVisitConsumerAction.CONTINUE, None)
        return (VariableVisitConsumerAction.RETURN, found_nodes)

    @staticmethod
    def _is_never_evaluated(
        defnode: nodes.NamedExpr, defnode_parent: nodes.IfExp
    ) -> bool:
        """Check if a NamedExpr is inside a side of if ... else that never
        gets evaluated.
        """
        inferred_test = utils.safe_infer(defnode_parent.test)
        if isinstance(inferred_test, nodes.Const):
            if inferred_test.value is True and defnode == defnode_parent.orelse:
                return True
            if inferred_test.value is False and defnode == defnode_parent.body:
                return True
        return False

    @staticmethod
    def _is_variable_annotation_in_function(node: nodes.NodeNG) -> bool:
        is_annotation = utils.get_node_first_ancestor_of_type(node, nodes.AnnAssign)
        return (
            is_annotation
            and utils.get_node_first_ancestor_of_type(  # type: ignore[return-value]
                is_annotation, nodes.FunctionDef
            )
        )

    def _ignore_class_scope(self, node: nodes.NodeNG) -> bool:
        """Return True if the node is in a local class scope, as an assignment.

        Detect if we are in a local class scope, as an assignment.
        For example, the following is fair game.

        class A:
           b = 1
           c = lambda b=b: b * b

        class B:
           tp = 1
           def func(self, arg: tp):
               ...
        class C:
           tp = 2
           def func(self, arg=tp):
               ...
        class C:
           class Tp:
               pass
           class D(Tp):
               ...
        """
        name = node.name
        frame = node.statement().scope()
        in_annotation_or_default_or_decorator = self._defined_in_function_definition(
            node, frame
        )
        in_ancestor_list = utils.is_ancestor_name(frame, node)
        if in_annotation_or_default_or_decorator or in_ancestor_list:
            frame_locals = frame.parent.scope().locals
        else:
            frame_locals = frame.locals
        return not (
            (isinstance(frame, nodes.ClassDef) or in_annotation_or_default_or_decorator)
            and not self._in_lambda_or_comprehension_body(node, frame)
            and name in frame_locals
        )

    # pylint: disable = too-many-branches
    def _loopvar_name(self, node: astroid.Name) -> None:
        # filter variables according to node's scope
        astmts = [s for s in node.lookup(node.name)[1] if hasattr(s, "assign_type")]
        # If this variable usage exists inside a function definition
        # that exists in the same loop,
        # the usage is safe because the function will not be defined either if
        # the variable is not defined.
        scope = node.scope()
        if isinstance(scope, (nodes.Lambda, nodes.FunctionDef)) and any(
            asmt.scope().parent_of(scope) for asmt in astmts
        ):
            return
        # Filter variables according to their respective scope. Test parent
        # and statement to avoid #74747. This is not a total fix, which would
        # introduce a mechanism similar to special attribute lookup in
        # modules. Also, in order to get correct inference in this case, the
        # scope lookup rules would need to be changed to return the initial
        # assignment (which does not exist in code per se) as well as any later
        # modifications.
        if (
            not astmts  # pylint: disable=too-many-boolean-expressions
            or (
                astmts[0].parent == astmts[0].root()
                and astmts[0].parent.parent_of(node)
            )
            or (
                astmts[0].is_statement
                or not isinstance(astmts[0].parent, nodes.Module)
                and astmts[0].statement().parent_of(node)
            )
        ):
            _astmts = []
        else:
            _astmts = astmts[:1]
        for i, stmt in enumerate(astmts[1:]):
            if astmts[i].statement().parent_of(stmt) and not utils.in_for_else_branch(
                astmts[i].statement(), stmt
            ):
                continue
            _astmts.append(stmt)
        astmts = _astmts
        if len(astmts) != 1:
            return

        assign = astmts[0].assign_type()
        if not (
            isinstance(assign, (nodes.For, nodes.Comprehension, nodes.GeneratorExp))
            and assign.statement() is not node.statement()
        ):
            return

        if not isinstance(assign, nodes.For):
            self.add_message("undefined-loop-variable", args=node.name, node=node)
            return
        for else_stmt in assign.orelse:
            if isinstance(
                else_stmt, (nodes.Return, nodes.Raise, nodes.Break, nodes.Continue)
            ):
                return
            # TODO: 3.0: Consider using RefactoringChecker._is_function_def_never_returning
            if isinstance(else_stmt, nodes.Expr) and isinstance(
                else_stmt.value, nodes.Call
            ):
                inferred_func = utils.safe_infer(else_stmt.value.func)
                if (
                    isinstance(inferred_func, nodes.FunctionDef)
                    and inferred_func.returns
                ):
                    inferred_return = utils.safe_infer(inferred_func.returns)
                    if isinstance(
                        inferred_return, nodes.FunctionDef
                    ) and inferred_return.qname() in {
                        *TYPING_NORETURN,
                        *TYPING_NEVER,
                        "typing._SpecialForm",
                    }:
                        return
                    # typing_extensions.NoReturn returns a _SpecialForm
                    if (
                        isinstance(inferred_return, bases.Instance)
                        and inferred_return.qname() == "typing._SpecialForm"
                    ):
                        return

        maybe_walrus = utils.get_node_first_ancestor_of_type(node, nodes.NamedExpr)
        if maybe_walrus:
            maybe_comprehension = utils.get_node_first_ancestor_of_type(
                maybe_walrus, nodes.Comprehension
            )
            if maybe_comprehension:
                comprehension_scope = utils.get_node_first_ancestor_of_type(
                    maybe_comprehension, nodes.ComprehensionScope
                )
                if comprehension_scope is None:
                    # Should not be possible.
                    pass
                elif (
                    comprehension_scope.parent.scope() is scope
                    and node.name in comprehension_scope.locals
                ):
                    return

        # For functions we can do more by inferring the length of the itered object
        try:
            inferred = next(assign.iter.infer())
            # Prefer the target of enumerate() rather than the enumerate object itself
            if (
                isinstance(inferred, astroid.Instance)
                and inferred.qname() == "builtins.enumerate"
            ):
                likely_call = assign.iter
                if isinstance(assign.iter, nodes.IfExp):
                    likely_call = assign.iter.body
                if isinstance(likely_call, nodes.Call):
                    inferred = next(likely_call.args[0].infer())
        except astroid.InferenceError:
            self.add_message("undefined-loop-variable", args=node.name, node=node)
        else:
            if (
                isinstance(inferred, astroid.Instance)
                and inferred.qname() == BUILTIN_RANGE
            ):
                # Consider range() objects safe, even if they might not yield any results.
                return

            # Consider sequences.
            sequences = (
                nodes.List,
                nodes.Tuple,
                nodes.Dict,
                nodes.Set,
                astroid.objects.FrozenSet,
            )
            if not isinstance(inferred, sequences):
                self.add_message("undefined-loop-variable", args=node.name, node=node)
                return

            elements = getattr(inferred, "elts", getattr(inferred, "items", []))
            if not elements:
                self.add_message("undefined-loop-variable", args=node.name, node=node)

    # pylint: disable = too-many-branches
    def _check_is_unused(
        self,
        name: str,
        node: nodes.FunctionDef,
        stmt: nodes.NodeNG,
        global_names: set[str],
        nonlocal_names: Iterable[str],
        comprehension_target_names: Iterable[str],
    ) -> None:
        # Ignore some special names specified by user configuration.
        if self._is_name_ignored(stmt, name):
            return
        # Ignore names that were added dynamically to the Function scope
        if (
            isinstance(node, nodes.FunctionDef)
            and name == "__class__"
            and len(node.locals["__class__"]) == 1
            and isinstance(node.locals["__class__"][0], nodes.ClassDef)
        ):
            return

        # Ignore names imported by the global statement.
        if isinstance(stmt, (nodes.Global, nodes.Import, nodes.ImportFrom)):
            # Detect imports, assigned to global statements.
            if global_names and _import_name_is_global(stmt, global_names):
                return

        # Ignore names in comprehension targets
        if name in comprehension_target_names:
            return

        # Ignore names in string literal type annotation.
        if name in self._type_annotation_names:
            return

        argnames = node.argnames()
        # Care about functions with unknown argument (builtins)
        if name in argnames:
            if node.name == "__new__":
                is_init_def = False
                # Look for the `__init__` method in all the methods of the same class.
                for n in node.parent.get_children():
                    is_init_def = hasattr(n, "name") and (n.name == "__init__")
                    if is_init_def:
                        break
                # Ignore unused arguments check for `__new__` if `__init__` is defined.
                if is_init_def:
                    return
            self._check_unused_arguments(name, node, stmt, argnames, nonlocal_names)
        else:
            if stmt.parent and isinstance(
                stmt.parent, (nodes.Assign, nodes.AnnAssign, nodes.Tuple, nodes.For)
            ):
                if name in nonlocal_names:
                    return

            qname = asname = None
            if isinstance(stmt, (nodes.Import, nodes.ImportFrom)):
                # Need the complete name, which we don't have in .locals.
                if len(stmt.names) > 1:
                    import_names = next(
                        (names for names in stmt.names if name in names), None
                    )
                else:
                    import_names = stmt.names[0]
                if import_names:
                    qname, asname = import_names
                    name = asname or qname

            if _has_locals_call_after_node(stmt, node.scope()):
                message_name = "possibly-unused-variable"
            else:
                if isinstance(stmt, nodes.Import):
                    if asname is not None:
                        msg = f"{qname} imported as {asname}"
                    else:
                        msg = f"import {name}"
                    self.add_message("unused-import", args=msg, node=stmt)
                    return
                if isinstance(stmt, nodes.ImportFrom):
                    if asname is not None:
                        msg = f"{qname} imported from {stmt.modname} as {asname}"
                    else:
                        msg = f"{name} imported from {stmt.modname}"
                    self.add_message("unused-import", args=msg, node=stmt)
                    return
                message_name = "unused-variable"

            if isinstance(stmt, nodes.FunctionDef) and stmt.decorators:
                return

            # Don't check function stubs created only for type information
            if utils.is_overload_stub(node):
                return

            # Special case for exception variable
            if isinstance(stmt.parent, nodes.ExceptHandler) and any(
                n.name == name for n in stmt.parent.nodes_of_class(nodes.Name)
            ):
                return

            self.add_message(message_name, args=name, node=stmt)

    def _is_name_ignored(
        self, stmt: nodes.NodeNG, name: str
    ) -> re.Pattern[str] | re.Match[str] | None:
        authorized_rgx = self.linter.config.dummy_variables_rgx
        if (
            isinstance(stmt, nodes.AssignName)
            and isinstance(stmt.parent, nodes.Arguments)
            or isinstance(stmt, nodes.Arguments)
        ):
            regex: re.Pattern[str] = self.linter.config.ignored_argument_names
        else:
            regex = authorized_rgx
        # See https://stackoverflow.com/a/47007761/2519059 to
        # understand what this function return. Please do NOT use
        # this elsewhere, this is confusing for no benefit
        return regex and regex.match(name)

    def _check_unused_arguments(
        self,
        name: str,
        node: nodes.FunctionDef,
        stmt: nodes.NodeNG,
        argnames: list[str],
        nonlocal_names: Iterable[str],
    ) -> None:
        is_method = node.is_method()
        klass = node.parent.frame()
        if is_method and isinstance(klass, nodes.ClassDef):
            confidence = (
                INFERENCE if utils.has_known_bases(klass) else INFERENCE_FAILURE
            )
        else:
            confidence = HIGH

        if is_method:
            # Don't warn for the first argument of a (non static) method
            if node.type != "staticmethod" and name == argnames[0]:
                return
            # Don't warn for argument of an overridden method
            overridden = overridden_method(klass, node.name)
            if overridden is not None and name in overridden.argnames():
                return
            if node.name in utils.PYMETHODS and node.name not in (
                "__init__",
                "__new__",
            ):
                return
        # Don't check callback arguments
        if any(
            node.name.startswith(cb) or node.name.endswith(cb)
            for cb in self.linter.config.callbacks
        ):
            return
        # Don't check arguments of singledispatch.register function.
        if utils.is_registered_in_singledispatch_function(node):
            return

        # Don't check function stubs created only for type information
        if utils.is_overload_stub(node):
            return

        # Don't check protocol classes
        if utils.is_protocol_class(klass):
            return

        if name in nonlocal_names:
            return

        self.add_message("unused-argument", args=name, node=stmt, confidence=confidence)

    def _check_late_binding_closure(self, node: nodes.Name) -> None:
        """Check whether node is a cell var that is assigned within a containing loop.

        Special cases where we don't care about the error:
        1. When the node's function is immediately called, e.g. (lambda: i)()
        2. When the node's function is returned from within the loop, e.g. return lambda: i
        """
        if not self.linter.is_message_enabled("cell-var-from-loop"):
            return

        node_scope = node.frame()

        # If node appears in a default argument expression,
        # look at the next enclosing frame instead
        if utils.is_default_argument(node, node_scope):
            node_scope = node_scope.parent.frame()

        # Check if node is a cell var
        if (
            not isinstance(node_scope, (nodes.Lambda, nodes.FunctionDef))
            or node.name in node_scope.locals
        ):
            return

        assign_scope, stmts = node.lookup(node.name)
        if not stmts or not assign_scope.parent_of(node_scope):
            return

        if utils.is_comprehension(assign_scope):
            self.add_message("cell-var-from-loop", node=node, args=node.name)
        else:
            # Look for an enclosing For loop.
            # Currently, we only consider the first assignment
            assignment_node = stmts[0]

            maybe_for = assignment_node
            while maybe_for and not isinstance(maybe_for, nodes.For):
                if maybe_for is assign_scope:
                    break
                maybe_for = maybe_for.parent
            else:
                if (
                    maybe_for
                    and maybe_for.parent_of(node_scope)
                    and not utils.is_being_called(node_scope)
                    and node_scope.parent
                    and not isinstance(node_scope.statement(), nodes.Return)
                ):
                    self.add_message("cell-var-from-loop", node=node, args=node.name)

    def _should_ignore_redefined_builtin(self, stmt: nodes.NodeNG) -> bool:
        if not isinstance(stmt, nodes.ImportFrom):
            return False
        return stmt.modname in self.linter.config.redefining_builtins_modules

    def _allowed_redefined_builtin(self, name: str) -> bool:
        return name in self.linter.config.allowed_redefined_builtins

    @staticmethod
    def _comprehension_between_frame_and_node(node: nodes.Name) -> bool:
        """Return True if a ComprehensionScope intervenes between `node` and its
        frame.
        """
        closest_comprehension_scope = utils.get_node_first_ancestor_of_type(
            node, nodes.ComprehensionScope
        )
        return closest_comprehension_scope is not None and node.frame().parent_of(
            closest_comprehension_scope
        )

    def _store_type_annotation_node(self, type_annotation: nodes.NodeNG) -> None:
        """Given a type annotation, store all the name nodes it refers to."""
        if isinstance(type_annotation, nodes.Name):
            self._type_annotation_names.append(type_annotation.name)
            return

        if isinstance(type_annotation, nodes.Attribute):
            self._store_type_annotation_node(type_annotation.expr)
            return

        if not isinstance(type_annotation, nodes.Subscript):
            return

        if (
            isinstance(type_annotation.value, nodes.Attribute)
            and isinstance(type_annotation.value.expr, nodes.Name)
            and type_annotation.value.expr.name == TYPING_MODULE
        ):
            self._type_annotation_names.append(TYPING_MODULE)
            return

        self._type_annotation_names.extend(
            annotation.name for annotation in type_annotation.nodes_of_class(nodes.Name)
        )

    def _store_type_annotation_names(
        self, node: nodes.For | nodes.Assign | nodes.With
    ) -> None:
        type_annotation = node.type_annotation
        if not type_annotation:
            return
        self._store_type_annotation_node(node.type_annotation)

    def _check_self_cls_assign(self, node: nodes.Assign) -> None:
        """Check that self/cls don't get assigned."""
        assign_names: set[str | None] = set()
        for target in node.targets:
            if isinstance(target, nodes.AssignName):
                assign_names.add(target.name)
            elif isinstance(target, nodes.Tuple):
                assign_names.update(
                    elt.name for elt in target.elts if isinstance(elt, nodes.AssignName)
                )
        scope = node.scope()
        nonlocals_with_same_name = node.scope().parent and any(
            child for child in scope.body if isinstance(child, nodes.Nonlocal)
        )
        if nonlocals_with_same_name:
            scope = node.scope().parent.scope()

        if not (
            isinstance(scope, nodes.FunctionDef)
            and scope.is_method()
            and "builtins.staticmethod" not in scope.decoratornames()
        ):
            return
        argument_names = scope.argnames()
        if not argument_names:
            return
        self_cls_name = argument_names[0]
        if self_cls_name in assign_names:
            self.add_message("self-cls-assignment", node=node, args=(self_cls_name,))

    def _check_unpacking(
        self, inferred: InferenceResult, node: nodes.Assign, targets: list[nodes.NodeNG]
    ) -> None:
        """Check for unbalanced tuple unpacking
        and unpacking non sequences.
        """
        if utils.is_inside_abstract_class(node):
            return
        if utils.is_comprehension(node):
            return
        if isinstance(inferred, util.UninferableBase):
            return
        if (
            isinstance(inferred.parent, nodes.Arguments)
            and isinstance(node.value, nodes.Name)
            and node.value.name == inferred.parent.vararg
        ):
            # Variable-length argument, we can't determine the length.
            return

        # Attempt to check unpacking is properly balanced
        values = self._nodes_to_unpack(inferred)
        details = _get_unpacking_extra_info(node, inferred)

        if values is not None:
            if len(targets) != len(values):
                self._report_unbalanced_unpacking(
                    node, inferred, targets, len(values), details
                )
        # attempt to check unpacking may be possible (i.e. RHS is iterable)
        elif not utils.is_iterable(inferred):
            self._report_unpacking_non_sequence(node, details)

    @staticmethod
    def _get_value_length(value_node: nodes.NodeNG) -> int:
        value_subnodes = VariablesChecker._nodes_to_unpack(value_node)
        if value_subnodes is not None:
            return len(value_subnodes)
        if isinstance(value_node, nodes.Const) and isinstance(
            value_node.value, (str, bytes)
        ):
            return len(value_node.value)
        if isinstance(value_node, nodes.Subscript):
            step = value_node.slice.step or 1
            splice_range = value_node.slice.upper.value - value_node.slice.lower.value
            splice_length = int(math.ceil(splice_range / step))
            return splice_length
        return 1

    @staticmethod
    def _nodes_to_unpack(node: nodes.NodeNG) -> list[nodes.NodeNG] | None:
        """Return the list of values of the `Assign` node."""
        if isinstance(node, (nodes.Tuple, nodes.List, nodes.Set, *DICT_TYPES)):
            return node.itered()  # type: ignore[no-any-return]
        if isinstance(node, astroid.Instance) and any(
            ancestor.qname() == "typing.NamedTuple" for ancestor in node.ancestors()
        ):
            return [i for i in node.values() if isinstance(i, nodes.AssignName)]
        return None

    def _report_unbalanced_unpacking(
        self,
        node: nodes.NodeNG,
        inferred: InferenceResult,
        targets: list[nodes.NodeNG],
        values_count: int,
        details: str,
    ) -> None:
        args = (
            details,
            len(targets),
            "" if len(targets) == 1 else "s",
            values_count,
            "" if values_count == 1 else "s",
        )

        symbol = (
            "unbalanced-dict-unpacking"
            if isinstance(inferred, DICT_TYPES)
            else "unbalanced-tuple-unpacking"
        )
        self.add_message(symbol, node=node, args=args, confidence=INFERENCE)

    def _report_unpacking_non_sequence(self, node: nodes.NodeNG, details: str) -> None:
        if details and not details.startswith(" "):
            details = f" {details}"
        self.add_message("unpacking-non-sequence", node=node, args=details)

    def _check_module_attrs(
        self,
        node: _base_nodes.ImportNode,
        module: nodes.Module,
        module_names: list[str],
    ) -> nodes.Module | None:
        """Check that module_names (list of string) are accessible through the
        given module, if the latest access name corresponds to a module, return it.
        """
        while module_names:
            name = module_names.pop(0)
            if name == "__dict__":
                module = None
                break
            try:
                module = next(module.getattr(name)[0].infer())
                if not isinstance(module, nodes.Module):
                    return None
            except astroid.NotFoundError:
                # Unable to import `name` from `module`. Since `name` may itself be a
                # module, we first check if it matches the ignored modules.
                if is_module_ignored(f"{module.qname()}.{name}", self._ignored_modules):
                    return None
                self.add_message(
                    "no-name-in-module", args=(name, module.name), node=node
                )
                return None
            except astroid.InferenceError:
                return None
        if module_names:
            modname = module.name if module else "__dict__"
            self.add_message(
                "no-name-in-module", node=node, args=(".".join(module_names), modname)
            )
            return None
        if isinstance(module, nodes.Module):
            return module
        return None

    def _check_all(
        self, node: nodes.Module, not_consumed: dict[str, list[nodes.NodeNG]]
    ) -> None:
        try:
            assigned = next(node.igetattr("__all__"))
        except astroid.InferenceError:
            return
        if isinstance(assigned, util.UninferableBase):
            return
        if assigned.pytype() not in {"builtins.list", "builtins.tuple"}:
            line, col = assigned.tolineno, assigned.col_offset
            self.add_message("invalid-all-format", line=line, col_offset=col, node=node)
            return
        for elt in getattr(assigned, "elts", ()):
            try:
                elt_name = next(elt.infer())
            except astroid.InferenceError:
                continue
            if isinstance(elt_name, util.UninferableBase):
                continue
            if not elt_name.parent:
                continue

            if not isinstance(elt_name, nodes.Const) or not isinstance(
                elt_name.value, str
            ):
                self.add_message("invalid-all-object", args=elt.as_string(), node=elt)
                continue

            elt_name = elt_name.value
            # If elt is in not_consumed, remove it from not_consumed
            if elt_name in not_consumed:
                del not_consumed[elt_name]
                continue

            if elt_name not in node.locals:
                if not node.package:
                    self.add_message(
                        "undefined-all-variable", args=(elt_name,), node=elt
                    )
                else:
                    basename = os.path.splitext(node.file)[0]
                    if os.path.basename(basename) == "__init__":
                        name = node.name + "." + elt_name
                        try:
                            astroid.modutils.file_from_modpath(name.split("."))
                        except ImportError:
                            self.add_message(
                                "undefined-all-variable", args=(elt_name,), node=elt
                            )
                        except SyntaxError:
                            # don't yield a syntax-error warning,
                            # because it will be later yielded
                            # when the file will be checked
                            pass

    def _check_globals(self, not_consumed: dict[str, nodes.NodeNG]) -> None:
        if self._allow_global_unused_variables:
            return
        for name, node_lst in not_consumed.items():
            for node in node_lst:
                if in_type_checking_block(node):
                    continue
                self.add_message("unused-variable", args=(name,), node=node)

    # pylint: disable = too-many-branches
    def _check_imports(self, not_consumed: dict[str, list[nodes.NodeNG]]) -> None:
        local_names = _fix_dot_imports(not_consumed)
        checked = set()
        unused_wildcard_imports: defaultdict[
            tuple[str, nodes.ImportFrom], list[str]
        ] = collections.defaultdict(list)
        for name, stmt in local_names:
            for imports in stmt.names:
                real_name = imported_name = imports[0]
                if imported_name == "*":
                    real_name = name
                as_name = imports[1]
                if real_name in checked:
                    continue
                if name not in (real_name, as_name):
                    continue
                checked.add(real_name)

                is_type_annotation_import = (
                    imported_name in self._type_annotation_names
                    or as_name in self._type_annotation_names
                )

                is_dummy_import = (
                    as_name
                    and self.linter.config.dummy_variables_rgx
                    and self.linter.config.dummy_variables_rgx.match(as_name)
                )

                if isinstance(stmt, nodes.Import) or (
                    isinstance(stmt, nodes.ImportFrom) and not stmt.modname
                ):
                    if isinstance(stmt, nodes.ImportFrom) and SPECIAL_OBJ.search(
                        imported_name
                    ):
                        # Filter special objects (__doc__, __all__) etc.,
                        # because they can be imported for exporting.
                        continue

                    if is_type_annotation_import or is_dummy_import:
                        # Most likely a typing import if it wasn't used so far.
                        # Also filter dummy variables.
                        continue

                    if as_name is None:
                        msg = f"import {imported_name}"
                    else:
                        msg = f"{imported_name} imported as {as_name}"
                    if not in_type_checking_block(stmt):
                        self.add_message("unused-import", args=msg, node=stmt)
                elif isinstance(stmt, nodes.ImportFrom) and stmt.modname != FUTURE:
                    if SPECIAL_OBJ.search(imported_name):
                        # Filter special objects (__doc__, __all__) etc.,
                        # because they can be imported for exporting.
                        continue

                    if _is_from_future_import(stmt, name):
                        # Check if the name is in fact loaded from a
                        # __future__ import in another module.
                        continue

                    if is_type_annotation_import or is_dummy_import:
                        # Most likely a typing import if it wasn't used so far.
                        # Also filter dummy variables.
                        continue

                    if imported_name == "*":
                        unused_wildcard_imports[(stmt.modname, stmt)].append(name)
                    else:
                        if as_name is None:
                            msg = f"{imported_name} imported from {stmt.modname}"
                        else:
                            msg = f"{imported_name} imported from {stmt.modname} as {as_name}"
                        if not in_type_checking_block(stmt):
                            self.add_message("unused-import", args=msg, node=stmt)

        # Construct string for unused-wildcard-import message
        for module, unused_list in unused_wildcard_imports.items():
            if len(unused_list) == 1:
                arg_string = unused_list[0]
            else:
                arg_string = (
                    f"{', '.join(i for i in unused_list[:-1])} and {unused_list[-1]}"
                )
            self.add_message(
                "unused-wildcard-import", args=(arg_string, module[0]), node=module[1]
            )
        del self._to_consume

    def _check_metaclasses(self, node: nodes.Module | nodes.FunctionDef) -> None:
        """Update consumption analysis for metaclasses."""
        consumed: list[tuple[dict[str, list[nodes.NodeNG]], str]] = []

        for child_node in node.get_children():
            if isinstance(child_node, nodes.ClassDef):
                consumed.extend(self._check_classdef_metaclasses(child_node, node))

        # Pop the consumed items, in order to avoid having
        # unused-import and unused-variable false positives
        for scope_locals, name in consumed:
            scope_locals.pop(name, None)

    def _check_classdef_metaclasses(
        self, klass: nodes.ClassDef, parent_node: nodes.Module | nodes.FunctionDef
    ) -> list[tuple[dict[str, list[nodes.NodeNG]], str]]:
        if not klass._metaclass:
            # Skip if this class doesn't use explicitly a metaclass, but inherits it from ancestors
            return []

        consumed: list[tuple[dict[str, list[nodes.NodeNG]], str]] = []
        metaclass = klass.metaclass()
        name = ""
        if isinstance(klass._metaclass, nodes.Name):
            name = klass._metaclass.name
        elif isinstance(klass._metaclass, nodes.Attribute) and klass._metaclass.expr:
            attr = klass._metaclass.expr
            while not isinstance(attr, nodes.Name):
                attr = attr.expr
            name = attr.name
        elif isinstance(klass._metaclass, nodes.Call) and isinstance(
            klass._metaclass.func, nodes.Name
        ):
            name = klass._metaclass.func.name
        elif metaclass:
            name = metaclass.root().name

        found = False
        name = METACLASS_NAME_TRANSFORMS.get(name, name)
        if name:
            # check enclosing scopes starting from most local
            for scope_locals, _, _, _ in self._to_consume[::-1]:
                found_nodes = scope_locals.get(name, [])
                for found_node in found_nodes:
                    if found_node.lineno <= klass.lineno:
                        consumed.append((scope_locals, name))
                        found = True
                        break
            # Check parent scope
            nodes_in_parent_scope = parent_node.locals.get(name, [])
            for found_node_parent in nodes_in_parent_scope:
                if found_node_parent.lineno <= klass.lineno:
                    found = True
                    break
        if (
            not found
            and not metaclass
            and not (
                name in nodes.Module.scope_attrs
                or utils.is_builtin(name)
                or name in self.linter.config.additional_builtins
            )
        ):
            self.add_message("undefined-variable", node=klass, args=(name,))

        return consumed

    def visit_subscript(self, node: nodes.Subscript) -> None:
        inferred_slice = utils.safe_infer(node.slice)

        self._check_potential_index_error(node, inferred_slice)

    def _check_potential_index_error(
        self, node: nodes.Subscript, inferred_slice: nodes.NodeNG | None
    ) -> None:
        """Check for the potential-index-error message."""
        # Currently we only check simple slices of a single integer
        if not isinstance(inferred_slice, nodes.Const) or not isinstance(
            inferred_slice.value, int
        ):
            return

        # If the node.value is a Tuple or List without inference it is defined in place
        if isinstance(node.value, (nodes.Tuple, nodes.List)):
            # Add 1 because iterables are 0-indexed
            if len(node.value.elts) < inferred_slice.value + 1:
                self.add_message(
                    "potential-index-error", node=node, confidence=INFERENCE
                )
            return

    @utils.only_required_for_messages(
        "unused-import",
        "unused-variable",
    )
    def visit_const(self, node: nodes.Const) -> None:
        """Take note of names that appear inside string literal type annotations
        unless the string is a parameter to `typing.Literal` or `typing.Annotation`.
        """
        if node.pytype() != "builtins.str":
            return
        if not utils.is_node_in_type_annotation_context(node):
            return

        # Check if parent's or grandparent's first child is typing.Literal
        parent = node.parent
        if isinstance(parent, nodes.Tuple):
            parent = parent.parent
        if isinstance(parent, nodes.Subscript):
            origin = next(parent.get_children(), None)
            if origin is not None and utils.is_typing_member(
                origin, ("Annotated", "Literal")
            ):
                return

        try:
            annotation = extract_node(node.value)
            self._store_type_annotation_node(annotation)
        except ValueError:
            # e.g. node.value is white space
            pass
        except astroid.AstroidSyntaxError:
            # e.g. "?" or ":" in typing.Literal["?", ":"]
            pass


def register(linter: PyLinter) -> None:
    linter.register_checker(VariablesChecker(linter))

back/venvmsg/Lib/site-packages/pylint/checkers/variables.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

__all__ = ["find_default_config_files"]

from pylint.config.find_default_config_files import find_default_config_files

back/venvmsg/Lib/site-packages/pylint/config/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Everything related to the 'pylint-config' command.

Everything in this module is private.
"""

from pylint.config._pylint_config.main import _handle_pylint_config_commands
from pylint.config._pylint_config.setup import _register_generate_config_options

__all__ = ("_handle_pylint_config_commands", "_register_generate_config_options")

back/venvmsg/Lib/site-packages/pylint/config/_pylint_config/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Everything related to the 'pylint-config generate' command."""


from __future__ import annotations

from io import StringIO
from typing import TYPE_CHECKING

from pylint.config._pylint_config import utils
from pylint.config._pylint_config.help_message import get_subparser_help

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


def generate_interactive_config(linter: PyLinter) -> None:
    print("Starting interactive pylint configuration generation")

    format_type = utils.get_and_validate_format()
    minimal = format_type == "toml" and utils.get_minimal_setting()
    to_file, output_file_name = utils.get_and_validate_output_file()

    if format_type == "toml":
        config_string = linter._generate_config_file(minimal=minimal)
    else:
        output_stream = StringIO()
        linter._generate_config(stream=output_stream, skipsections=("Commands",))
        config_string = output_stream.getvalue()

    if to_file:
        with open(output_file_name, "w", encoding="utf-8") as f:
            print(config_string, file=f)
        print(f"Wrote configuration file to {output_file_name.resolve()}")
    else:
        print(config_string)


def handle_generate_command(linter: PyLinter) -> int:
    """Handle 'pylint-config generate'."""
    # Interactively generate a pylint configuration
    if linter.config.interactive:
        generate_interactive_config(linter)
        return 0
    print(get_subparser_help(linter, "generate"))
    return 32

back/venvmsg/Lib/site-packages/pylint/config/_pylint_config/generate_command.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Everything related to the 'pylint-config -h' command and subcommands."""


from __future__ import annotations

import argparse
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


def get_subparser_help(linter: PyLinter, command: str) -> str:
    """Get the help message for one of the subcommands."""
    # Make sure subparsers are initialized properly
    assert linter._arg_parser._subparsers
    subparser_action = linter._arg_parser._subparsers._group_actions[0]
    assert isinstance(subparser_action, argparse._SubParsersAction)

    for name, subparser in subparser_action.choices.items():
        assert isinstance(subparser, argparse.ArgumentParser)
        if name == command:
            # Remove last character which is an extra new line
            return subparser.format_help()[:-1]
    return ""  # pragma: no cover


def get_help(parser: argparse.ArgumentParser) -> str:
    """Get the help message for the main 'pylint-config' command.

    Taken from argparse.ArgumentParser.format_help.
    """
    formatter = parser._get_formatter()

    # usage
    formatter.add_usage(
        parser.usage, parser._actions, parser._mutually_exclusive_groups
    )

    # description
    formatter.add_text(parser.description)

    # positionals, optionals and user-defined groups
    for action_group in parser._action_groups:
        if action_group.title == "Subcommands":
            formatter.start_section(action_group.title)
            formatter.add_text(action_group.description)
            formatter.add_arguments(action_group._group_actions)
            formatter.end_section()

    # epilog
    formatter.add_text(parser.epilog)

    # determine help from format above
    return formatter.format_help()

back/venvmsg/Lib/site-packages/pylint/config/_pylint_config/help_message.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Everything related to the 'pylint-config' command."""


from __future__ import annotations

from typing import TYPE_CHECKING

from pylint.config._pylint_config.generate_command import handle_generate_command
from pylint.config._pylint_config.help_message import get_help

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


def _handle_pylint_config_commands(linter: PyLinter) -> int:
    """Handle whichever command is passed to 'pylint-config'."""
    if linter.config.config_subcommand == "generate":
        return handle_generate_command(linter)

    print(get_help(linter._arg_parser))
    return 32

back/venvmsg/Lib/site-packages/pylint/config/_pylint_config/main.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Everything related to the setup of the 'pylint-config' command."""


from __future__ import annotations

import argparse
from collections.abc import Sequence
from typing import Any

from pylint.config._pylint_config.help_message import get_help
from pylint.config.callback_actions import _AccessParserAction


class _HelpAction(_AccessParserAction):
    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--help",
    ) -> None:
        get_help(self.parser)


def _register_generate_config_options(parser: argparse.ArgumentParser) -> None:
    """Registers the necessary arguments on the parser."""
    parser.prog = "pylint-config"
    # Overwrite the help command
    parser.add_argument(
        "-h",
        "--help",
        action=_HelpAction,
        default=argparse.SUPPRESS,
        help="show this help message and exit",
        parser=parser,
    )

    # We use subparsers to create various subcommands under 'pylint-config'
    subparsers = parser.add_subparsers(dest="config_subcommand", title="Subcommands")

    # Add the generate command
    generate_parser = subparsers.add_parser(
        "generate", help="Generate a pylint configuration"
    )
    generate_parser.add_argument("--interactive", action="store_true")

back/venvmsg/Lib/site-packages/pylint/config/_pylint_config/setup.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Utils for the 'pylint-config' command."""

from __future__ import annotations

import sys
from collections.abc import Callable
from pathlib import Path
from typing import Literal, TypeVar

if sys.version_info >= (3, 10):
    from typing import ParamSpec
else:
    from typing_extensions import ParamSpec

_P = ParamSpec("_P")
_ReturnValueT = TypeVar("_ReturnValueT", bool, str)

SUPPORTED_FORMATS = {"t", "toml", "i", "ini"}
YES_NO_ANSWERS = {"y", "yes", "n", "no"}


class InvalidUserInput(Exception):
    """Raised whenever a user input is invalid."""

    def __init__(self, valid_input: str, input_value: str, *args: object) -> None:
        self.valid = valid_input
        self.input = input_value
        super().__init__(*args)


def should_retry_after_invalid_input(
    func: Callable[_P, _ReturnValueT]
) -> Callable[_P, _ReturnValueT]:
    """Decorator that handles InvalidUserInput exceptions and retries."""

    def inner_function(*args: _P.args, **kwargs: _P.kwargs) -> _ReturnValueT:
        called_once = False
        while True:
            try:
                return func(*args, **kwargs)
            except InvalidUserInput as exc:
                if called_once and exc.input == "exit()":
                    print("Stopping 'pylint-config'.")
                    sys.exit()
                print(f"Answer should be one of {exc.valid}.")
                print("Type 'exit()' if you want to exit the program.")
                called_once = True

    return inner_function


@should_retry_after_invalid_input
def get_and_validate_format() -> Literal["toml", "ini"]:
    """Make sure that the output format is either .toml or .ini."""
    # pylint: disable-next=bad-builtin
    format_type = input(
        "Please choose the format of configuration, (T)oml or (I)ni (.cfg): "
    ).lower()

    if format_type not in SUPPORTED_FORMATS:
        raise InvalidUserInput(", ".join(sorted(SUPPORTED_FORMATS)), format_type)

    if format_type.startswith("t"):
        return "toml"
    return "ini"


@should_retry_after_invalid_input
def validate_yes_no(question: str, default: Literal["yes", "no"] | None) -> bool:
    """Validate that a yes or no answer is correct."""
    question = f"{question} (y)es or (n)o "
    if default:
        question += f" (default={default}) "
    # pylint: disable-next=bad-builtin
    answer = input(question).lower()

    if not answer and default:
        answer = default

    if answer not in YES_NO_ANSWERS:
        raise InvalidUserInput(", ".join(sorted(YES_NO_ANSWERS)), answer)

    return answer.startswith("y")


def get_minimal_setting() -> bool:
    """Ask the user if they want to use the minimal setting."""
    return validate_yes_no(
        "Do you want a minimal configuration without comments or default values?", "no"
    )


def get_and_validate_output_file() -> tuple[bool, Path]:
    """Make sure that the output file is correct."""
    to_file = validate_yes_no("Do you want to write the output to a file?", "no")

    if not to_file:
        return False, Path()

    # pylint: disable-next=bad-builtin
    file_name = Path(input("What should the file be called: "))
    if file_name.exists():
        overwrite = validate_yes_no(
            f"{file_name} already exists. Are you sure you want to overwrite?", "no"
        )

        if not overwrite:
            return False, file_name
        return True, file_name

    return True, file_name

back/venvmsg/Lib/site-packages/pylint/config/_pylint_config/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Definition of an Argument class and transformers for various argument types.

An Argument instance represents a pylint option to be handled by an argparse.ArgumentParser
"""

from __future__ import annotations

import argparse
import os
import pathlib
import re
from collections.abc import Callable
from glob import glob
from typing import Any, Literal, Pattern, Sequence, Tuple, Union

from pylint import interfaces
from pylint import utils as pylint_utils
from pylint.config.callback_actions import _CallbackAction
from pylint.config.deprecation_actions import _NewNamesAction, _OldNamesAction

_ArgumentTypes = Union[
    str,
    int,
    float,
    bool,
    Pattern[str],
    Sequence[str],
    Sequence[Pattern[str]],
    Tuple[int, ...],
]
"""List of possible argument types."""


def _confidence_transformer(value: str) -> Sequence[str]:
    """Transforms a comma separated string of confidence values."""
    if not value:
        return interfaces.CONFIDENCE_LEVEL_NAMES
    values = pylint_utils._check_csv(value)
    for confidence in values:
        if confidence not in interfaces.CONFIDENCE_LEVEL_NAMES:
            raise argparse.ArgumentTypeError(
                f"{value} should be in {*interfaces.CONFIDENCE_LEVEL_NAMES,}"
            )
    return values


def _csv_transformer(value: str) -> Sequence[str]:
    """Transforms a comma separated string."""
    return pylint_utils._check_csv(value)


YES_VALUES = {"y", "yes", "true"}
NO_VALUES = {"n", "no", "false"}


def _yn_transformer(value: str) -> bool:
    """Transforms a yes/no or stringified bool into a bool."""
    value = value.lower()
    if value in YES_VALUES:
        return True
    if value in NO_VALUES:
        return False
    raise argparse.ArgumentTypeError(
        None, f"Invalid yn value '{value}', should be in {*YES_VALUES, *NO_VALUES}"
    )


def _non_empty_string_transformer(value: str) -> str:
    """Check that a string is not empty and remove quotes."""
    if not value:
        raise argparse.ArgumentTypeError("Option cannot be an empty string.")
    return pylint_utils._unquote(value)


def _path_transformer(value: str) -> str:
    """Expand user and variables in a path."""
    return os.path.expandvars(os.path.expanduser(value))


def _glob_paths_csv_transformer(value: str) -> Sequence[str]:
    """Transforms a comma separated list of paths while expanding user and
    variables and glob patterns.
    """
    paths: list[str] = []
    for path in _csv_transformer(value):
        paths.extend(glob(_path_transformer(path), recursive=True))
    return paths


def _py_version_transformer(value: str) -> tuple[int, ...]:
    """Transforms a version string into a version tuple."""
    try:
        version = tuple(int(val) for val in value.replace(",", ".").split("."))
    except ValueError:
        raise argparse.ArgumentTypeError(
            f"{value} has an invalid format, should be a version string. E.g., '3.8'"
        ) from None
    return version


def _regex_transformer(value: str) -> Pattern[str]:
    """Return `re.compile(value)`."""
    try:
        return re.compile(value)
    except re.error as e:
        msg = f"Error in provided regular expression: {value} beginning at index {e.pos}: {e.msg}"
        raise argparse.ArgumentTypeError(msg) from e


def _regexp_csv_transfomer(value: str) -> Sequence[Pattern[str]]:
    """Transforms a comma separated list of regular expressions."""
    patterns: list[Pattern[str]] = []
    for pattern in pylint_utils._check_regexp_csv(value):
        patterns.append(_regex_transformer(pattern))
    return patterns


def _regexp_paths_csv_transfomer(value: str) -> Sequence[Pattern[str]]:
    """Transforms a comma separated list of regular expressions paths."""
    patterns: list[Pattern[str]] = []
    for pattern in _csv_transformer(value):
        patterns.append(
            re.compile(
                str(pathlib.PureWindowsPath(pattern)).replace("\\", "\\\\")
                + "|"
                + pathlib.PureWindowsPath(pattern).as_posix()
            )
        )
    return patterns


_TYPE_TRANSFORMERS: dict[str, Callable[[str], _ArgumentTypes]] = {
    "choice": str,
    "csv": _csv_transformer,
    "float": float,
    "int": int,
    "confidence": _confidence_transformer,
    "non_empty_string": _non_empty_string_transformer,
    "path": _path_transformer,
    "glob_paths_csv": _glob_paths_csv_transformer,
    "py_version": _py_version_transformer,
    "regexp": _regex_transformer,
    "regexp_csv": _regexp_csv_transfomer,
    "regexp_paths_csv": _regexp_paths_csv_transfomer,
    "string": pylint_utils._unquote,
    "yn": _yn_transformer,
}
"""Type transformers for all argument types.

A transformer should accept a string and return one of the supported
Argument types. It will only be called when parsing 1) command-line,
2) configuration files and 3) a string default value.
Non-string default values are assumed to be of the correct type.
"""


class _Argument:
    """Class representing an argument to be parsed by an argparse.ArgumentsParser.

    This is based on the parameters passed to argparse.ArgumentsParser.add_message.
    See:
    https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
    """

    def __init__(
        self,
        *,
        flags: list[str],
        arg_help: str,
        hide_help: bool,
        section: str | None,
    ) -> None:
        self.flags = flags
        """The name of the argument."""

        self.hide_help = hide_help
        """Whether to hide this argument in the help message."""

        # argparse uses % formatting on help strings, so a % needs to be escaped
        self.help = arg_help.replace("%", "%%")
        """The description of the argument."""

        if hide_help:
            self.help = argparse.SUPPRESS

        self.section = section
        """The section to add this argument to."""


class _BaseStoreArgument(_Argument):
    """Base class for store arguments to be parsed by an argparse.ArgumentsParser.

    This is based on the parameters passed to argparse.ArgumentsParser.add_message.
    See:
    https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
    """

    def __init__(
        self,
        *,
        flags: list[str],
        action: str,
        default: _ArgumentTypes,
        arg_help: str,
        hide_help: bool,
        section: str | None,
    ) -> None:
        super().__init__(
            flags=flags, arg_help=arg_help, hide_help=hide_help, section=section
        )

        self.action = action
        """The action to perform with the argument."""

        self.default = default
        """The default value of the argument."""


class _StoreArgument(_BaseStoreArgument):
    """Class representing a store argument to be parsed by an argparse.ArgumentsParser.

    This is based on the parameters passed to argparse.ArgumentsParser.add_message.
    See:
    https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
    """

    # pylint: disable-next=too-many-arguments
    def __init__(
        self,
        *,
        flags: list[str],
        action: str,
        default: _ArgumentTypes,
        arg_type: str,
        choices: list[str] | None,
        arg_help: str,
        metavar: str,
        hide_help: bool,
        section: str | None,
    ) -> None:
        super().__init__(
            flags=flags,
            action=action,
            default=default,
            arg_help=arg_help,
            hide_help=hide_help,
            section=section,
        )

        self.type = _TYPE_TRANSFORMERS[arg_type]
        """A transformer function that returns a transformed type of the argument."""

        self.choices = choices
        """A list of possible choices for the argument.

        None if there are no restrictions.
        """

        self.metavar = metavar
        """The metavar of the argument.

        See:
        https://docs.python.org/3/library/argparse.html#metavar
        """


class _StoreTrueArgument(_BaseStoreArgument):
    """Class representing a 'store_true' argument to be parsed by an
    argparse.ArgumentsParser.

    This is based on the parameters passed to argparse.ArgumentsParser.add_message.
    See:
    https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
    """

    # pylint: disable-next=useless-parent-delegation # We narrow down the type of action
    def __init__(
        self,
        *,
        flags: list[str],
        action: Literal["store_true"],
        default: _ArgumentTypes,
        arg_help: str,
        hide_help: bool,
        section: str | None,
    ) -> None:
        super().__init__(
            flags=flags,
            action=action,
            default=default,
            arg_help=arg_help,
            hide_help=hide_help,
            section=section,
        )


class _DeprecationArgument(_Argument):
    """Store arguments while also handling deprecation warnings for old and new names.

    This is based on the parameters passed to argparse.ArgumentsParser.add_message.
    See:
    https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
    """

    # pylint: disable-next=too-many-arguments
    def __init__(
        self,
        *,
        flags: list[str],
        action: type[argparse.Action],
        default: _ArgumentTypes,
        arg_type: str,
        choices: list[str] | None,
        arg_help: str,
        metavar: str,
        hide_help: bool,
        section: str | None,
    ) -> None:
        super().__init__(
            flags=flags, arg_help=arg_help, hide_help=hide_help, section=section
        )

        self.action = action
        """The action to perform with the argument."""

        self.default = default
        """The default value of the argument."""

        self.type = _TYPE_TRANSFORMERS[arg_type]
        """A transformer function that returns a transformed type of the argument."""

        self.choices = choices
        """A list of possible choices for the argument.

        None if there are no restrictions.
        """

        self.metavar = metavar
        """The metavar of the argument.

        See:
        https://docs.python.org/3/library/argparse.html#metavar
        """


class _ExtendArgument(_DeprecationArgument):
    """Class for extend arguments to be parsed by an argparse.ArgumentsParser.

    This is based on the parameters passed to argparse.ArgumentsParser.add_message.
    See:
    https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
    """

    # pylint: disable-next=too-many-arguments
    def __init__(
        self,
        *,
        flags: list[str],
        action: Literal["extend"],
        default: _ArgumentTypes,
        arg_type: str,
        metavar: str,
        arg_help: str,
        hide_help: bool,
        section: str | None,
        choices: list[str] | None,
        dest: str | None,
    ) -> None:
        action_class = argparse._ExtendAction

        self.dest = dest
        """The destination of the argument."""

        super().__init__(
            flags=flags,
            action=action_class,
            default=default,
            arg_type=arg_type,
            choices=choices,
            arg_help=arg_help,
            metavar=metavar,
            hide_help=hide_help,
            section=section,
        )


class _StoreOldNamesArgument(_DeprecationArgument):
    """Store arguments while also handling old names.

    This is based on the parameters passed to argparse.ArgumentsParser.add_message.
    See:
    https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
    """

    # pylint: disable-next=too-many-arguments
    def __init__(
        self,
        *,
        flags: list[str],
        default: _ArgumentTypes,
        arg_type: str,
        choices: list[str] | None,
        arg_help: str,
        metavar: str,
        hide_help: bool,
        kwargs: dict[str, Any],
        section: str | None,
    ) -> None:
        super().__init__(
            flags=flags,
            action=_OldNamesAction,
            default=default,
            arg_type=arg_type,
            choices=choices,
            arg_help=arg_help,
            metavar=metavar,
            hide_help=hide_help,
            section=section,
        )

        self.kwargs = kwargs
        """Any additional arguments passed to the action."""


class _StoreNewNamesArgument(_DeprecationArgument):
    """Store arguments while also emitting deprecation warnings.

    This is based on the parameters passed to argparse.ArgumentsParser.add_message.
    See:
    https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
    """

    # pylint: disable-next=too-many-arguments
    def __init__(
        self,
        *,
        flags: list[str],
        default: _ArgumentTypes,
        arg_type: str,
        choices: list[str] | None,
        arg_help: str,
        metavar: str,
        hide_help: bool,
        kwargs: dict[str, Any],
        section: str | None,
    ) -> None:
        super().__init__(
            flags=flags,
            action=_NewNamesAction,
            default=default,
            arg_type=arg_type,
            choices=choices,
            arg_help=arg_help,
            metavar=metavar,
            hide_help=hide_help,
            section=section,
        )

        self.kwargs = kwargs
        """Any additional arguments passed to the action."""


class _CallableArgument(_Argument):
    """Class representing an callable argument to be parsed by an
    argparse.ArgumentsParser.

    This is based on the parameters passed to argparse.ArgumentsParser.add_message.
    See:
    https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument
    """

    def __init__(
        self,
        *,
        flags: list[str],
        action: type[_CallbackAction],
        arg_help: str,
        kwargs: dict[str, Any],
        hide_help: bool,
        section: str | None,
        metavar: str,
    ) -> None:
        super().__init__(
            flags=flags, arg_help=arg_help, hide_help=hide_help, section=section
        )

        self.action = action
        """The action to perform with the argument."""

        self.kwargs = kwargs
        """Any additional arguments passed to the action."""

        self.metavar = metavar
        """The metavar of the argument.

        See:
        https://docs.python.org/3/library/argparse.html#metavar
        """

back/venvmsg/Lib/site-packages/pylint/config/argument.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Arguments manager class used to handle command-line arguments and options."""

from __future__ import annotations

import argparse
import re
import sys
import textwrap
import warnings
from collections.abc import Sequence
from typing import TYPE_CHECKING, Any, TextIO

import tomlkit

from pylint import utils
from pylint.config.argument import (
    _Argument,
    _CallableArgument,
    _ExtendArgument,
    _StoreArgument,
    _StoreNewNamesArgument,
    _StoreOldNamesArgument,
    _StoreTrueArgument,
)
from pylint.config.exceptions import (
    UnrecognizedArgumentAction,
    _UnrecognizedOptionError,
)
from pylint.config.help_formatter import _HelpFormatter
from pylint.config.utils import _convert_option_to_argument, _parse_rich_type_value
from pylint.constants import MAIN_CHECKER_NAME
from pylint.typing import DirectoryNamespaceDict, OptionDict

if sys.version_info >= (3, 11):
    import tomllib
else:
    import tomli as tomllib


if TYPE_CHECKING:
    from pylint.config.arguments_provider import _ArgumentsProvider


class _ArgumentsManager:
    """Arguments manager class used to handle command-line arguments and options."""

    def __init__(
        self, prog: str, usage: str | None = None, description: str | None = None
    ) -> None:
        self._config = argparse.Namespace()
        """Namespace for all options."""

        self._base_config = self._config
        """Fall back Namespace object created during initialization.

        This is necessary for the per-directory configuration support. Whenever we
        fail to match a file with a directory we fall back to the Namespace object
        created during initialization.
        """

        self._arg_parser = argparse.ArgumentParser(
            prog=prog,
            usage=usage or "%(prog)s [options]",
            description=description,
            formatter_class=_HelpFormatter,
            # Needed to let 'pylint-config' overwrite the -h command
            conflict_handler="resolve",
        )
        """The command line argument parser."""

        self._argument_groups_dict: dict[str, argparse._ArgumentGroup] = {}
        """Dictionary of all the argument groups."""

        self._option_dicts: dict[str, OptionDict] = {}
        """All option dictionaries that have been registered."""

        self._directory_namespaces: DirectoryNamespaceDict = {}
        """Mapping of directories and their respective namespace objects."""

    @property
    def config(self) -> argparse.Namespace:
        """Namespace for all options."""
        return self._config

    @config.setter
    def config(self, value: argparse.Namespace) -> None:
        self._config = value

    def _register_options_provider(self, provider: _ArgumentsProvider) -> None:
        """Register an options provider and load its defaults."""
        for opt, optdict in provider.options:
            self._option_dicts[opt] = optdict
            argument = _convert_option_to_argument(opt, optdict)
            section = argument.section or provider.name.capitalize()

            section_desc = provider.option_groups_descs.get(section, None)

            # We exclude main since its docstring comes from PyLinter
            if provider.name != MAIN_CHECKER_NAME and provider.__doc__:
                section_desc = provider.__doc__.split("\n\n")[0]

            self._add_arguments_to_parser(section, section_desc, argument)

        self._load_default_argument_values()

    def _add_arguments_to_parser(
        self, section: str, section_desc: str | None, argument: _Argument
    ) -> None:
        """Add an argument to the correct argument section/group."""
        try:
            section_group = self._argument_groups_dict[section]
        except KeyError:
            if section_desc:
                section_group = self._arg_parser.add_argument_group(
                    section, section_desc
                )
            else:
                section_group = self._arg_parser.add_argument_group(title=section)
            self._argument_groups_dict[section] = section_group
        self._add_parser_option(section_group, argument)

    @staticmethod
    def _add_parser_option(
        section_group: argparse._ArgumentGroup, argument: _Argument
    ) -> None:
        """Add an argument."""
        if isinstance(argument, _StoreArgument):
            section_group.add_argument(
                *argument.flags,
                action=argument.action,
                default=argument.default,
                type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
                help=argument.help,
                metavar=argument.metavar,
                choices=argument.choices,
            )
        elif isinstance(argument, _StoreOldNamesArgument):
            section_group.add_argument(
                *argument.flags,
                **argument.kwargs,
                action=argument.action,
                default=argument.default,
                type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
                help=argument.help,
                metavar=argument.metavar,
                choices=argument.choices,
            )
            # We add the old name as hidden option to make it's default value gets loaded when
            # argparse initializes all options from the checker
            assert argument.kwargs["old_names"]
            for old_name in argument.kwargs["old_names"]:
                section_group.add_argument(
                    f"--{old_name}",
                    action="store",
                    default=argument.default,
                    type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
                    help=argparse.SUPPRESS,
                    metavar=argument.metavar,
                    choices=argument.choices,
                )
        elif isinstance(argument, _StoreNewNamesArgument):
            section_group.add_argument(
                *argument.flags,
                **argument.kwargs,
                action=argument.action,
                default=argument.default,
                type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
                help=argument.help,
                metavar=argument.metavar,
                choices=argument.choices,
            )
        elif isinstance(argument, _StoreTrueArgument):
            section_group.add_argument(
                *argument.flags,
                action=argument.action,
                default=argument.default,
                help=argument.help,
            )
        elif isinstance(argument, _CallableArgument):
            section_group.add_argument(
                *argument.flags,
                **argument.kwargs,
                action=argument.action,
                help=argument.help,
                metavar=argument.metavar,
            )
        elif isinstance(argument, _ExtendArgument):
            section_group.add_argument(
                *argument.flags,
                action=argument.action,
                default=argument.default,
                type=argument.type,  # type: ignore[arg-type] # incorrect typing in typeshed
                help=argument.help,
                metavar=argument.metavar,
                choices=argument.choices,
                dest=argument.dest,
            )
        else:
            raise UnrecognizedArgumentAction

    def _load_default_argument_values(self) -> None:
        """Loads the default values of all registered options."""
        self.config = self._arg_parser.parse_args([], self.config)

    def _parse_configuration_file(self, arguments: list[str]) -> None:
        """Parse the arguments found in a configuration file into the namespace."""
        try:
            self.config, parsed_args = self._arg_parser.parse_known_args(
                arguments, self.config
            )
        except SystemExit:
            sys.exit(32)
        unrecognized_options: list[str] = []
        for opt in parsed_args:
            if opt.startswith("--"):
                unrecognized_options.append(opt[2:])
        if unrecognized_options:
            raise _UnrecognizedOptionError(options=unrecognized_options)

    def _parse_command_line_configuration(
        self, arguments: Sequence[str] | None = None
    ) -> list[str]:
        """Parse the arguments found on the command line into the namespace."""
        arguments = sys.argv[1:] if arguments is None else arguments

        self.config, parsed_args = self._arg_parser.parse_known_args(
            arguments, self.config
        )

        return parsed_args

    def _generate_config(
        self, stream: TextIO | None = None, skipsections: tuple[str, ...] = ()
    ) -> None:
        """Write a configuration file according to the current configuration
        into the given stream or stdout.
        """
        options_by_section = {}
        sections = []
        for group in sorted(
            self._arg_parser._action_groups,
            key=lambda x: (x.title != "Main", x.title),
        ):
            group_name = group.title
            assert group_name
            if group_name in skipsections:
                continue

            options = []
            option_actions = [
                i
                for i in group._group_actions
                if not isinstance(i, argparse._SubParsersAction)
            ]
            for opt in sorted(option_actions, key=lambda x: x.option_strings[0][2:]):
                if "--help" in opt.option_strings:
                    continue

                optname = opt.option_strings[0][2:]

                try:
                    optdict = self._option_dicts[optname]
                except KeyError:
                    continue

                options.append(
                    (
                        optname,
                        optdict,
                        getattr(self.config, optname.replace("-", "_")),
                    )
                )

                options = [
                    (n, d, v) for (n, d, v) in options if not d.get("deprecated")
                ]

            if options:
                sections.append(group_name)
                options_by_section[group_name] = options
        stream = stream or sys.stdout
        printed = False
        for section in sections:
            if printed:
                print("\n", file=stream)
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", category=DeprecationWarning)
                utils.format_section(
                    stream, section.upper(), sorted(options_by_section[section])
                )
            printed = True

    def help(self) -> str:
        """Return the usage string based on the available options."""
        return self._arg_parser.format_help()

    def _generate_config_file(self, *, minimal: bool = False) -> str:
        """Write a configuration file according to the current configuration into
        stdout.
        """
        toml_doc = tomlkit.document()
        tool_table = tomlkit.table(is_super_table=True)
        toml_doc.add(tomlkit.key("tool"), tool_table)

        pylint_tool_table = tomlkit.table(is_super_table=True)
        tool_table.add(tomlkit.key("pylint"), pylint_tool_table)

        for group in sorted(
            self._arg_parser._action_groups,
            key=lambda x: (x.title != "Main", x.title),
        ):
            # Skip the options section with the --help option
            if group.title in {"options", "optional arguments", "Commands"}:
                continue

            # Skip sections without options such as "positional arguments"
            if not group._group_actions:
                continue

            group_table = tomlkit.table()
            option_actions = [
                i
                for i in group._group_actions
                if not isinstance(i, argparse._SubParsersAction)
            ]
            for action in sorted(option_actions, key=lambda x: x.option_strings[0][2:]):
                optname = action.option_strings[0][2:]

                # We skip old name options that don't have their own optdict
                try:
                    optdict = self._option_dicts[optname]
                except KeyError:
                    continue

                if optdict.get("hide_from_config_file"):
                    continue

                # Add help comment
                if not minimal:
                    help_msg = optdict.get("help", "")
                    assert isinstance(help_msg, str)
                    help_text = textwrap.wrap(help_msg, width=79)
                    for line in help_text:
                        group_table.add(tomlkit.comment(line))

                # Get current value of option
                value = getattr(self.config, optname.replace("-", "_"))

                # Create a comment if the option has no value
                if not value:
                    if not minimal:
                        group_table.add(tomlkit.comment(f"{optname} ="))
                        group_table.add(tomlkit.nl())
                    continue

                # Skip deprecated options
                if "kwargs" in optdict:
                    assert isinstance(optdict["kwargs"], dict)
                    if "new_names" in optdict["kwargs"]:
                        continue

                # Tomlkit doesn't support regular expressions
                if isinstance(value, re.Pattern):
                    value = value.pattern
                elif isinstance(value, (list, tuple)) and isinstance(
                    value[0], re.Pattern
                ):
                    value = [i.pattern for i in value]

                # Handle tuples that should be strings
                if optdict.get("type") == "py_version":
                    value = ".".join(str(i) for i in value)

                # Check if it is default value if we are in minimal mode
                if minimal and value == optdict.get("default"):
                    continue

                # Add to table
                group_table.add(optname, value)
                group_table.add(tomlkit.nl())

            assert group.title
            if group_table:
                pylint_tool_table.add(group.title.lower(), group_table)

        toml_string = tomlkit.dumps(toml_doc)

        # Make sure the string we produce is valid toml and can be parsed
        tomllib.loads(toml_string)

        return str(toml_string)

    def set_option(self, optname: str, value: Any) -> None:
        """Set an option on the namespace object."""
        self.config = self._arg_parser.parse_known_args(
            [f"--{optname.replace('_', '-')}", _parse_rich_type_value(value)],
            self.config,
        )[0]

back/venvmsg/Lib/site-packages/pylint/config/arguments_manager.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Arguments provider class used to expose options."""

from __future__ import annotations

from collections.abc import Iterator
from typing import Any

from pylint.config.arguments_manager import _ArgumentsManager
from pylint.typing import OptionDict, Options


class _ArgumentsProvider:
    """Base class for classes that provide arguments."""

    name: str
    """Name of the provider."""

    options: Options = ()
    """Options provided by this provider."""

    option_groups_descs: dict[str, str] = {}
    """Option groups of this provider and their descriptions."""

    def __init__(self, arguments_manager: _ArgumentsManager) -> None:
        self._arguments_manager = arguments_manager
        """The manager that will parse and register any options provided."""

        self._arguments_manager._register_options_provider(self)

    def _option_value(self, opt: str) -> Any:
        """Get the current value for the given option."""
        return getattr(self._arguments_manager.config, opt.replace("-", "_"), None)

    def _options_by_section(
        self,
    ) -> Iterator[
        tuple[str, list[tuple[str, OptionDict, Any]]]
        | tuple[None, dict[str, list[tuple[str, OptionDict, Any]]]]
    ]:
        """Return an iterator on options grouped by section.

        (section, [list of (optname, optdict, optvalue)])
        """
        sections: dict[str, list[tuple[str, OptionDict, Any]]] = {}
        for optname, optdict in self.options:
            sections.setdefault(optdict.get("group"), []).append(  # type: ignore[arg-type]
                (optname, optdict, self._option_value(optname))
            )
        if None in sections:
            yield None, sections.pop(None)  # type: ignore[call-overload]
        for section, options in sorted(sections.items()):
            yield section.upper(), options

    def _options_and_values(
        self, options: Options | None = None
    ) -> Iterator[tuple[str, OptionDict, Any]]:
        """DEPRECATED."""
        if options is None:
            options = self.options
        for optname, optdict in options:
            yield optname, optdict, self._option_value(optname)

back/venvmsg/Lib/site-packages/pylint/config/arguments_provider.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

# pylint: disable=too-many-arguments, redefined-builtin, duplicate-code

"""Callback actions for various options."""

from __future__ import annotations

import abc
import argparse
import sys
from collections.abc import Callable, Sequence
from pathlib import Path
from typing import TYPE_CHECKING, Any

from pylint import exceptions, extensions, interfaces, utils

if TYPE_CHECKING:
    from pylint.config.help_formatter import _HelpFormatter
    from pylint.lint import PyLinter
    from pylint.lint.run import Run


class _CallbackAction(argparse.Action):
    """Custom callback action."""

    @abc.abstractmethod
    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = None,
    ) -> None:
        raise NotImplementedError  # pragma: no cover


class _DoNothingAction(_CallbackAction):
    """Action that just passes.

    This action is used to allow pre-processing of certain options
    without erroring when they are then processed again by argparse.
    """

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = None,
    ) -> None:
        return None


class _AccessRunObjectAction(_CallbackAction):
    """Action that has access to the Run object."""

    def __init__(
        self,
        option_strings: Sequence[str],
        dest: str,
        nargs: None = None,
        const: None = None,
        default: None = None,
        type: None = None,
        choices: None = None,
        required: bool = False,
        help: str = "",
        metavar: str = "",
        **kwargs: Run,
    ) -> None:
        self.run = kwargs["Run"]

        super().__init__(
            option_strings,
            dest,
            0,
            const,
            default,
            type,
            choices,
            required,
            help,
            metavar,
        )

    @abc.abstractmethod
    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = None,
    ) -> None:
        raise NotImplementedError  # pragma: no cover


class _MessageHelpAction(_CallbackAction):
    """Display the help message of a message."""

    def __init__(
        self,
        option_strings: Sequence[str],
        dest: str,
        nargs: None = None,
        const: None = None,
        default: None = None,
        type: None = None,
        choices: None = None,
        required: bool = False,
        help: str = "",
        metavar: str = "",
        **kwargs: Run,
    ) -> None:
        self.run = kwargs["Run"]
        super().__init__(
            option_strings,
            dest,
            "+",
            const,
            default,
            type,
            choices,
            required,
            help,
            metavar,
        )

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[str] | None,
        option_string: str | None = "--help-msg",
    ) -> None:
        assert isinstance(values, (list, tuple))
        values_to_print: list[str] = []
        for msg in values:
            assert isinstance(msg, str)
            values_to_print += utils._check_csv(msg)
        self.run.linter.msgs_store.help_message(values_to_print)
        sys.exit(0)


class _ListMessagesAction(_AccessRunObjectAction):
    """Display all available messages."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--list-enabled",
    ) -> None:
        self.run.linter.msgs_store.list_messages()
        sys.exit(0)


class _ListMessagesEnabledAction(_AccessRunObjectAction):
    """Display all enabled messages."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--list-msgs-enabled",
    ) -> None:
        self.run.linter.list_messages_enabled()
        sys.exit(0)


class _ListCheckGroupsAction(_AccessRunObjectAction):
    """Display all the check groups that pylint knows about."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--list-groups",
    ) -> None:
        for check in self.run.linter.get_checker_names():
            print(check)
        sys.exit(0)


class _ListConfidenceLevelsAction(_AccessRunObjectAction):
    """Display all the confidence levels that pylint knows about."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--list-conf-levels",
    ) -> None:
        for level in interfaces.CONFIDENCE_LEVELS:
            print(f"%-18s: {level}")
        sys.exit(0)


class _ListExtensionsAction(_AccessRunObjectAction):
    """Display all extensions under pylint.extensions."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--list-extensions",
    ) -> None:
        for filename in Path(extensions.__file__).parent.iterdir():
            if filename.suffix == ".py" and not filename.stem.startswith("_"):
                extension_name, _, _ = filename.stem.partition(".")
                print(f"pylint.extensions.{extension_name}")
        sys.exit(0)


class _FullDocumentationAction(_AccessRunObjectAction):
    """Display the full documentation."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--full-documentation",
    ) -> None:
        utils.print_full_documentation(self.run.linter)
        sys.exit(0)


class _GenerateRCFileAction(_AccessRunObjectAction):
    """Generate a pylintrc file."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--generate-rcfile",
    ) -> None:
        # TODO: 3.x: Deprecate this after the auto-upgrade functionality of
        # pylint-config is sufficient.
        self.run.linter._generate_config(skipsections=("Commands",))
        sys.exit(0)


class _GenerateConfigFileAction(_AccessRunObjectAction):
    """Generate a .toml format configuration file."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--generate-toml-config",
    ) -> None:
        print(self.run.linter._generate_config_file())
        sys.exit(0)


class _ErrorsOnlyModeAction(_AccessRunObjectAction):
    """Turn on errors-only mode.

    Error mode:
        * disable all but error messages
        * disable the 'miscellaneous' checker which can be safely deactivated in
          debug
        * disable reports
        * do not save execution information
    """

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--errors-only",
    ) -> None:
        self.run.linter._error_mode = True


class _LongHelpAction(_AccessRunObjectAction):
    """Display the long help message."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--long-help",
    ) -> None:
        formatter: _HelpFormatter = self.run.linter._arg_parser._get_formatter()  # type: ignore[assignment]

        # Add extra info as epilog to the help message
        self.run.linter._arg_parser.epilog = formatter.get_long_description()
        print(self.run.linter.help())

        sys.exit(0)


class _AccessLinterObjectAction(_CallbackAction):
    """Action that has access to the Linter object."""

    def __init__(
        self,
        option_strings: Sequence[str],
        dest: str,
        nargs: None = None,
        const: None = None,
        default: None = None,
        type: None = None,
        choices: None = None,
        required: bool = False,
        help: str = "",
        metavar: str = "",
        **kwargs: PyLinter,
    ) -> None:
        self.linter = kwargs["linter"]

        super().__init__(
            option_strings,
            dest,
            1,
            const,
            default,
            type,
            choices,
            required,
            help,
            metavar,
        )

    @abc.abstractmethod
    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = None,
    ) -> None:
        raise NotImplementedError  # pragma: no cover


class _XableAction(_AccessLinterObjectAction):
    """Callback action for enabling or disabling a message."""

    def _call(
        self,
        xabling_function: Callable[[str], None],
        values: str | Sequence[Any] | None,
        option_string: str | None,
    ) -> None:
        assert isinstance(values, (tuple, list))
        for msgid in utils._check_csv(values[0]):
            try:
                xabling_function(msgid)
            except (
                exceptions.DeletedMessageError,
                exceptions.MessageBecameExtensionError,
            ) as e:
                self.linter._stashed_messages[
                    (self.linter.current_name, "useless-option-value")
                ].append((option_string, str(e)))
            except exceptions.UnknownMessageError:
                self.linter._stashed_messages[
                    (self.linter.current_name, "unknown-option-value")
                ].append((option_string, msgid))

    @abc.abstractmethod
    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--disable",
    ) -> None:
        raise NotImplementedError  # pragma: no cover


class _DisableAction(_XableAction):
    """Callback action for disabling a message."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--disable",
    ) -> None:
        self._call(self.linter.disable, values, option_string)


class _EnableAction(_XableAction):
    """Callback action for enabling a message."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--enable",
    ) -> None:
        self._call(self.linter.enable, values, option_string)


class _OutputFormatAction(_AccessLinterObjectAction):
    """Callback action for setting the output format."""

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = "--enable",
    ) -> None:
        assert isinstance(values, (tuple, list))
        assert isinstance(
            values[0], str
        ), "'output-format' should be a comma separated string of reporters"
        self.linter._load_reporters(values[0])


class _AccessParserAction(_CallbackAction):
    """Action that has access to the ArgumentParser object."""

    def __init__(
        self,
        option_strings: Sequence[str],
        dest: str,
        nargs: None = None,
        const: None = None,
        default: None = None,
        type: None = None,
        choices: None = None,
        required: bool = False,
        help: str = "",
        metavar: str = "",
        **kwargs: argparse.ArgumentParser,
    ) -> None:
        self.parser = kwargs["parser"]

        super().__init__(
            option_strings,
            dest,
            0,
            const,
            default,
            type,
            choices,
            required,
            help,
            metavar,
        )

    @abc.abstractmethod
    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = None,
    ) -> None:
        raise NotImplementedError  # pragma: no cover

back/venvmsg/Lib/site-packages/pylint/config/callback_actions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Configuration file parser class."""

from __future__ import annotations

import configparser
import os
import sys
from pathlib import Path
from typing import TYPE_CHECKING, Dict, List, Tuple

from pylint.config.utils import _parse_rich_type_value

if sys.version_info >= (3, 11):
    import tomllib
else:
    import tomli as tomllib

if TYPE_CHECKING:
    from pylint.lint import PyLinter

PylintConfigFileData = Tuple[Dict[str, str], List[str]]


class _RawConfParser:
    """Class to parse various formats of configuration files."""

    @staticmethod
    def parse_ini_file(file_path: Path) -> PylintConfigFileData:
        """Parse and handle errors of an ini configuration file.

        Raises ``configparser.Error``.
        """
        parser = configparser.ConfigParser(inline_comment_prefixes=("#", ";"))
        # Use this encoding in order to strip the BOM marker, if any.
        with open(file_path, encoding="utf_8_sig") as fp:
            parser.read_file(fp)

        config_content: dict[str, str] = {}
        options: list[str] = []
        ini_file_with_sections = _RawConfParser._ini_file_with_sections(file_path)
        for section in parser.sections():
            if ini_file_with_sections and not section.startswith("pylint"):
                continue
            for option, value in parser[section].items():
                config_content[option] = value
                options += [f"--{option}", value]
        return config_content, options

    @staticmethod
    def _ini_file_with_sections(file_path: Path) -> bool:
        """Return whether the file uses sections."""
        if "setup.cfg" in file_path.parts:
            return True
        if "tox.ini" in file_path.parts:
            return True
        return False

    @staticmethod
    def parse_toml_file(file_path: Path) -> PylintConfigFileData:
        """Parse and handle errors of a toml configuration file.

        Raises ``tomllib.TOMLDecodeError``.
        """
        with open(file_path, mode="rb") as fp:
            content = tomllib.load(fp)
        try:
            sections_values = content["tool"]["pylint"]
        except KeyError:
            return {}, []

        config_content: dict[str, str] = {}
        options: list[str] = []
        for opt, values in sections_values.items():
            if isinstance(values, dict):
                for config, value in values.items():
                    value = _parse_rich_type_value(value)
                    config_content[config] = value
                    options += [f"--{config}", value]
            else:
                values = _parse_rich_type_value(values)
                config_content[opt] = values
                options += [f"--{opt}", values]
        return config_content, options

    @staticmethod
    def parse_config_file(
        file_path: Path | None, verbose: bool
    ) -> PylintConfigFileData:
        """Parse a config file and return str-str pairs.

        Raises ``tomllib.TOMLDecodeError``, ``configparser.Error``.
        """
        if file_path is None:
            if verbose:
                print(
                    "No config file found, using default configuration", file=sys.stderr
                )
            return {}, []

        file_path = Path(os.path.expandvars(file_path)).expanduser()
        if not file_path.exists():
            raise OSError(f"The config file {file_path} doesn't exist!")

        if verbose:
            print(f"Using config file {file_path}", file=sys.stderr)

        if file_path.suffix == ".toml":
            return _RawConfParser.parse_toml_file(file_path)
        return _RawConfParser.parse_ini_file(file_path)


class _ConfigurationFileParser:
    """Class to parse various formats of configuration files."""

    def __init__(self, verbose: bool, linter: PyLinter) -> None:
        self.verbose_mode = verbose
        self.linter = linter

    def parse_config_file(self, file_path: Path | None) -> PylintConfigFileData:
        """Parse a config file and return str-str pairs."""
        try:
            return _RawConfParser.parse_config_file(file_path, self.verbose_mode)
        except (configparser.Error, tomllib.TOMLDecodeError) as e:
            self.linter.add_message("config-parse-error", line=0, args=str(e))
            return {}, []

back/venvmsg/Lib/site-packages/pylint/config/config_file_parser.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import sys
import warnings
from glob import glob
from itertools import chain
from pathlib import Path
from typing import TYPE_CHECKING

from pylint import reporters
from pylint.config.config_file_parser import _ConfigurationFileParser
from pylint.config.exceptions import (
    ArgumentPreprocessingError,
    _UnrecognizedOptionError,
)
from pylint.utils import utils

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def _config_initialization(
    linter: PyLinter,
    args_list: list[str],
    reporter: reporters.BaseReporter | reporters.MultiReporter | None = None,
    config_file: None | str | Path = None,
    verbose_mode: bool = False,
) -> list[str]:
    """Parse all available options, read config files and command line arguments and
    set options accordingly.
    """
    config_file = Path(config_file) if config_file else None

    # Set the current module to the configuration file
    # to allow raising messages on the configuration file.
    linter.set_current_module(str(config_file) if config_file else "")

    # Read the configuration file
    config_file_parser = _ConfigurationFileParser(verbose_mode, linter)
    try:
        config_data, config_args = config_file_parser.parse_config_file(
            file_path=config_file
        )
    except OSError as ex:
        print(ex, file=sys.stderr)
        sys.exit(32)

    # Order --enable=all or --disable=all to come first.
    config_args = _order_all_first(config_args, joined=False)

    # Run init hook, if present, before loading plugins
    if "init-hook" in config_data:
        exec(utils._unquote(config_data["init-hook"]))  # pylint: disable=exec-used

    # Load plugins if specified in the config file
    if "load-plugins" in config_data:
        linter.load_plugin_modules(utils._splitstrip(config_data["load-plugins"]))

    unrecognized_options_message = None
    # First we parse any options from a configuration file
    try:
        linter._parse_configuration_file(config_args)
    except _UnrecognizedOptionError as exc:
        unrecognized_options_message = ", ".join(exc.options)

    # Then, if a custom reporter is provided as argument, it may be overridden
    # by file parameters, so we re-set it here. We do this before command line
    # parsing, so it's still overridable by command line options
    if reporter:
        linter.set_reporter(reporter)

    # Set the current module to the command line
    # to allow raising messages on it
    linter.set_current_module("Command line")

    # Now we parse any options from the command line, so they can override
    # the configuration file
    args_list = _order_all_first(args_list, joined=True)
    parsed_args_list = linter._parse_command_line_configuration(args_list)

    # Remove the positional arguments separator from the list of arguments if it exists
    try:
        parsed_args_list.remove("--")
    except ValueError:
        pass

    # Check if there are any options that we do not recognize
    unrecognized_options: list[str] = []
    for opt in parsed_args_list:
        if opt.startswith("--"):
            unrecognized_options.append(opt[2:])
        elif opt.startswith("-"):
            unrecognized_options.append(opt[1:])
    if unrecognized_options:
        msg = ", ".join(unrecognized_options)
        try:
            linter._arg_parser.error(f"Unrecognized option found: {msg}")
        except SystemExit:
            sys.exit(32)

    # Now that config file and command line options have been loaded
    # with all disables, it is safe to emit messages
    if unrecognized_options_message is not None:
        linter.set_current_module(str(config_file) if config_file else "")
        linter.add_message(
            "unrecognized-option", args=unrecognized_options_message, line=0
        )

    # TODO 3.1: Change this to emit unknown-option-value
    for exc_name in linter.config.overgeneral_exceptions:
        if "." not in exc_name:
            warnings.warn_explicit(
                f"'{exc_name}' is not a proper value for the 'overgeneral-exceptions' option. "
                f"Use fully qualified name (maybe 'builtins.{exc_name}' ?) instead. "
                "This will cease to be checked at runtime in 3.1.0.",
                category=UserWarning,
                filename="pylint: Command line or configuration file",
                lineno=1,
                module="pylint",
            )

    linter._emit_stashed_messages()

    # Set the current module to configuration as we don't know where
    # the --load-plugins key is coming from
    linter.set_current_module("Command line or configuration file")

    # We have loaded configuration from config file and command line. Now, we can
    # load plugin specific configuration.
    linter.load_plugin_configuration()

    # Now that plugins are loaded, get list of all fail_on messages, and
    # enable them
    linter.enable_fail_on_messages()

    linter._parse_error_mode()

    # Link the base Namespace object on the current directory
    linter._directory_namespaces[Path(".").resolve()] = (linter.config, {})

    # parsed_args_list should now only be a list of inputs to lint.
    # All other options have been removed from the list.
    return list(
        chain.from_iterable(
            # NOTE: 'or [arg]' is needed in the case the input file or directory does
            # not exist and 'glob(arg)' cannot find anything. Without this we would
            # not be able to output the fatal import error for this module later on,
            # as it would get silently ignored.
            glob(arg, recursive=True) or [arg]
            for arg in parsed_args_list
        )
    )


def _order_all_first(config_args: list[str], *, joined: bool) -> list[str]:
    """Reorder config_args such that --enable=all or --disable=all comes first.

    Raise if both are given.

    If joined is True, expect args in the form '--enable=all,for-any-all'.
    If joined is False, expect args in the form '--enable', 'all,for-any-all'.
    """
    indexes_to_prepend = []
    all_action = ""

    for i, arg in enumerate(config_args):
        if joined and (arg.startswith("--enable=") or arg.startswith("--disable=")):
            value = arg.split("=")[1]
        elif arg in {"--enable", "--disable"}:
            value = config_args[i + 1]
        else:
            continue

        if "all" not in (msg.strip() for msg in value.split(",")):
            continue

        arg = arg.split("=")[0]
        if all_action and (arg != all_action):
            raise ArgumentPreprocessingError(
                "--enable=all and --disable=all are incompatible."
            )
        all_action = arg

        indexes_to_prepend.append(i)
        if not joined:
            indexes_to_prepend.append(i + 1)

    returned_args = []
    for i in indexes_to_prepend:
        returned_args.append(config_args[i])

    for i, arg in enumerate(config_args):
        if i in indexes_to_prepend:
            continue
        returned_args.append(arg)

    return returned_args

back/venvmsg/Lib/site-packages/pylint/config/config_initialization.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

# pylint: disable=too-many-arguments, redefined-builtin

"""Deprecated option actions."""

from __future__ import annotations

import argparse
import warnings
from collections.abc import Sequence
from typing import Any


class _OldNamesAction(argparse._StoreAction):
    """Store action that also sets the value to old names."""

    def __init__(
        self,
        option_strings: Sequence[str],
        dest: str,
        nargs: None = None,
        const: None = None,
        default: None = None,
        type: None = None,
        choices: None = None,
        required: bool = False,
        help: str = "",
        metavar: str = "",
        old_names: list[str] | None = None,
    ) -> None:
        assert old_names
        self.old_names = old_names
        super().__init__(
            option_strings,
            dest,
            1,
            const,
            default,
            type,
            choices,
            required,
            help,
            metavar,
        )

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = None,
    ) -> None:
        assert isinstance(values, list)
        setattr(namespace, self.dest, values[0])
        for old_name in self.old_names:
            setattr(namespace, old_name, values[0])


class _NewNamesAction(argparse._StoreAction):
    """Store action that also emits a deprecation warning about a new name."""

    def __init__(
        self,
        option_strings: Sequence[str],
        dest: str,
        nargs: None = None,
        const: None = None,
        default: None = None,
        type: None = None,
        choices: None = None,
        required: bool = False,
        help: str = "",
        metavar: str = "",
        new_names: list[str] | None = None,
    ) -> None:
        assert new_names
        self.new_names = new_names
        super().__init__(
            option_strings,
            dest,
            1,
            const,
            default,
            type,
            choices,
            required,
            help,
            metavar,
        )

    def __call__(
        self,
        parser: argparse.ArgumentParser,
        namespace: argparse.Namespace,
        values: str | Sequence[Any] | None,
        option_string: str | None = None,
    ) -> None:
        assert isinstance(values, list)
        setattr(namespace, self.dest, values[0])
        warnings.warn(
            f"{self.option_strings[0]} has been deprecated. Please look into "
            f"using any of the following options: {', '.join(self.new_names)}.",
            DeprecationWarning,
            stacklevel=2,
        )

back/venvmsg/Lib/site-packages/pylint/config/deprecation_actions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations


class UnrecognizedArgumentAction(Exception):
    """Raised if an ArgumentManager instance tries to add an argument for which the
    action is not recognized.
    """


class _UnrecognizedOptionError(Exception):
    """Raised if an ArgumentManager instance tries to parse an option that is
    unknown.
    """

    def __init__(self, options: list[str], *args: object) -> None:
        self.options = options
        super().__init__(*args)


class ArgumentPreprocessingError(Exception):
    """Raised if an error occurs during argument pre-processing."""

back/venvmsg/Lib/site-packages/pylint/config/exceptions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import configparser
import os
import sys
from collections.abc import Iterator
from pathlib import Path

if sys.version_info >= (3, 11):
    import tomllib
else:
    import tomli as tomllib

RC_NAMES = (Path("pylintrc"), Path(".pylintrc"))
PYPROJECT_NAME = Path("pyproject.toml")
CONFIG_NAMES = (*RC_NAMES, PYPROJECT_NAME, Path("setup.cfg"))


def _find_pyproject() -> Path:
    """Search for file pyproject.toml in the parent directories recursively.

    It resolves symlinks, so if there is any symlink up in the tree, it does not respect them
    """
    current_dir = Path.cwd().resolve()
    is_root = False
    while not is_root:
        if (current_dir / PYPROJECT_NAME).is_file():
            return current_dir / PYPROJECT_NAME
        is_root = (
            current_dir == current_dir.parent
            or (current_dir / ".git").is_dir()
            or (current_dir / ".hg").is_dir()
        )
        current_dir = current_dir.parent

    return current_dir


def _toml_has_config(path: Path | str) -> bool:
    with open(path, mode="rb") as toml_handle:
        try:
            content = tomllib.load(toml_handle)
        except tomllib.TOMLDecodeError as error:
            print(f"Failed to load '{path}': {error}")
            return False
    return "pylint" in content.get("tool", [])


def _cfg_has_config(path: Path | str) -> bool:
    parser = configparser.ConfigParser()
    try:
        parser.read(path, encoding="utf-8")
    except configparser.Error:
        return False
    return any(section.startswith("pylint.") for section in parser.sections())


def _yield_default_files() -> Iterator[Path]:
    """Iterate over the default config file names and see if they exist."""
    for config_name in CONFIG_NAMES:
        try:
            if config_name.is_file():
                if config_name.suffix == ".toml" and not _toml_has_config(config_name):
                    continue
                if config_name.suffix == ".cfg" and not _cfg_has_config(config_name):
                    continue

                yield config_name.resolve()
        except OSError:
            pass


def _find_project_config() -> Iterator[Path]:
    """Traverse up the directory tree to find a config file.

    Stop if no '__init__' is found and thus we are no longer in a package.
    """
    if Path("__init__.py").is_file():
        curdir = Path(os.getcwd()).resolve()
        while (curdir / "__init__.py").is_file():
            curdir = curdir.parent
            for rc_name in RC_NAMES:
                rc_path = curdir / rc_name
                if rc_path.is_file():
                    yield rc_path.resolve()


def _find_config_in_home_or_environment() -> Iterator[Path]:
    """Find a config file in the specified environment var or the home directory."""
    if "PYLINTRC" in os.environ and Path(os.environ["PYLINTRC"]).exists():
        if Path(os.environ["PYLINTRC"]).is_file():
            yield Path(os.environ["PYLINTRC"]).resolve()
    else:
        try:
            user_home = Path.home()
        except RuntimeError:
            # If the home directory does not exist a RuntimeError will be raised
            user_home = None

        if user_home is not None and str(user_home) not in ("~", "/root"):
            home_rc = user_home / ".pylintrc"
            if home_rc.is_file():
                yield home_rc.resolve()

            home_rc = user_home / ".config" / "pylintrc"
            if home_rc.is_file():
                yield home_rc.resolve()


def find_default_config_files() -> Iterator[Path]:
    """Find all possible config files."""
    yield from _yield_default_files()

    try:
        yield from _find_project_config()
    except OSError:
        pass

    try:
        parent_pyproject = _find_pyproject()
        if parent_pyproject.is_file() and _toml_has_config(parent_pyproject):
            yield parent_pyproject.resolve()
    except OSError:
        pass

    try:
        yield from _find_config_in_home_or_environment()
    except OSError:
        pass

    try:
        if os.path.isfile("/etc/pylintrc"):
            yield Path("/etc/pylintrc").resolve()
    except OSError:
        pass

back/venvmsg/Lib/site-packages/pylint/config/find_default_config_files.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import argparse

from pylint.config.callback_actions import _CallbackAction
from pylint.constants import DEFAULT_PYLINT_HOME


class _HelpFormatter(argparse.RawDescriptionHelpFormatter):
    """Formatter for the help message emitted by argparse."""

    def _get_help_string(self, action: argparse.Action) -> str | None:
        """Copied from argparse.ArgumentDefaultsHelpFormatter."""
        assert action.help
        help_string = action.help

        # CallbackActions don't have a default
        if isinstance(action, _CallbackAction):
            return help_string

        if "%(default)" not in help_string:
            if action.default is not argparse.SUPPRESS:
                defaulting_nargs = [argparse.OPTIONAL, argparse.ZERO_OR_MORE]
                if action.option_strings or action.nargs in defaulting_nargs:
                    help_string += " (default: %(default)s)"
        return help_string

    @staticmethod
    def get_long_description() -> str:
        return f"""
Environment variables:
    The following environment variables are used:
        * PYLINTHOME    Path to the directory where persistent data for the run will
                        be stored. If not found, it defaults to '{DEFAULT_PYLINT_HOME}'.
        * PYLINTRC      Path to the configuration file. See the documentation for the method used
                        to search for configuration file.

Output:
    Using the default text output, the message format is :

        MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE

    There are 5 kind of message types :
        * (I) info,         for informational messages
        * (C) convention,   for programming standard violation
        * (R) refactor,     for bad code smell
        * (W) warning,      for python specific problems
        * (E) error,        for probable bugs in the code
        * (F) fatal,        if an error occurred which prevented pylint from doing further processing.

Output status code:
    Pylint should leave with following bitwise status codes:
        * 0 if everything went fine
        * 1 if a fatal message was issued
        * 2 if an error message was issued
        * 4 if a warning message was issued
        * 8 if a refactor message was issued
        * 16 if a convention message was issued
        * 32 on usage error
"""

back/venvmsg/Lib/site-packages/pylint/config/help_formatter.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Utils for arguments/options parsing and handling."""

from __future__ import annotations

import re
from collections.abc import Callable, Sequence
from pathlib import Path
from typing import TYPE_CHECKING, Any

from pylint import extensions, utils
from pylint.config.argument import (
    _CallableArgument,
    _ExtendArgument,
    _StoreArgument,
    _StoreNewNamesArgument,
    _StoreOldNamesArgument,
    _StoreTrueArgument,
)
from pylint.config.callback_actions import _CallbackAction
from pylint.config.exceptions import ArgumentPreprocessingError

if TYPE_CHECKING:
    from pylint.lint.run import Run


def _convert_option_to_argument(
    opt: str, optdict: dict[str, Any]
) -> (
    _StoreArgument
    | _StoreTrueArgument
    | _CallableArgument
    | _StoreOldNamesArgument
    | _StoreNewNamesArgument
    | _ExtendArgument
):
    """Convert an optdict to an Argument class instance."""
    # Get the long and short flags
    flags = [f"--{opt}"]
    if "short" in optdict:
        flags += [f"-{optdict['short']}"]

    # Get the action type
    action = optdict.get("action", "store")

    if action == "store_true":
        return _StoreTrueArgument(
            flags=flags,
            action=action,
            default=optdict.get("default", True),
            arg_help=optdict.get("help", ""),
            hide_help=optdict.get("hide", False),
            section=optdict.get("group", None),
        )
    if not isinstance(action, str) and issubclass(action, _CallbackAction):
        return _CallableArgument(
            flags=flags,
            action=action,
            arg_help=optdict.get("help", ""),
            kwargs=optdict.get("kwargs", {}),
            hide_help=optdict.get("hide", False),
            section=optdict.get("group", None),
            metavar=optdict.get("metavar", None),
        )

    default = optdict["default"]

    if action == "extend":
        return _ExtendArgument(
            flags=flags,
            action=action,
            default=[] if default is None else default,
            arg_type=optdict["type"],
            choices=optdict.get("choices", None),
            arg_help=optdict.get("help", ""),
            metavar=optdict.get("metavar", ""),
            hide_help=optdict.get("hide", False),
            section=optdict.get("group", None),
            dest=optdict.get("dest", None),
        )
    if "kwargs" in optdict:
        if "old_names" in optdict["kwargs"]:
            return _StoreOldNamesArgument(
                flags=flags,
                default=default,
                arg_type=optdict["type"],
                choices=optdict.get("choices", None),
                arg_help=optdict.get("help", ""),
                metavar=optdict.get("metavar", ""),
                hide_help=optdict.get("hide", False),
                kwargs=optdict.get("kwargs", {}),
                section=optdict.get("group", None),
            )
        if "new_names" in optdict["kwargs"]:
            return _StoreNewNamesArgument(
                flags=flags,
                default=default,
                arg_type=optdict["type"],
                choices=optdict.get("choices", None),
                arg_help=optdict.get("help", ""),
                metavar=optdict.get("metavar", ""),
                hide_help=optdict.get("hide", False),
                kwargs=optdict.get("kwargs", {}),
                section=optdict.get("group", None),
            )
    if "dest" in optdict:
        return _StoreOldNamesArgument(
            flags=flags,
            default=default,
            arg_type=optdict["type"],
            choices=optdict.get("choices", None),
            arg_help=optdict.get("help", ""),
            metavar=optdict.get("metavar", ""),
            hide_help=optdict.get("hide", False),
            kwargs={"old_names": [optdict["dest"]]},
            section=optdict.get("group", None),
        )
    return _StoreArgument(
        flags=flags,
        action=action,
        default=default,
        arg_type=optdict["type"],
        choices=optdict.get("choices", None),
        arg_help=optdict.get("help", ""),
        metavar=optdict.get("metavar", ""),
        hide_help=optdict.get("hide", False),
        section=optdict.get("group", None),
    )


def _parse_rich_type_value(value: Any) -> str:
    """Parse rich (toml) types into strings."""
    if isinstance(value, (list, tuple)):
        return ",".join(_parse_rich_type_value(i) for i in value)
    if isinstance(value, re.Pattern):
        return str(value.pattern)
    if isinstance(value, dict):
        return ",".join(f"{k}:{v}" for k, v in value.items())
    return str(value)


# pylint: disable-next=unused-argument
def _init_hook(run: Run, value: str | None) -> None:
    """Execute arbitrary code from the init_hook.

    This can be used to set the 'sys.path' for example.
    """
    assert value is not None
    exec(value)  # pylint: disable=exec-used


def _set_rcfile(run: Run, value: str | None) -> None:
    """Set the rcfile."""
    assert value is not None
    run._rcfile = value


def _set_output(run: Run, value: str | None) -> None:
    """Set the output."""
    assert value is not None
    run._output = value


def _add_plugins(run: Run, value: str | None) -> None:
    """Add plugins to the list of loadable plugins."""
    assert value is not None
    run._plugins.extend(utils._splitstrip(value))


def _set_verbose_mode(run: Run, value: str | None) -> None:
    assert value is None
    run.verbose = True


def _enable_all_extensions(run: Run, value: str | None) -> None:
    """Enable all extensions."""
    assert value is None
    for filename in Path(extensions.__file__).parent.iterdir():
        if filename.suffix == ".py" and not filename.stem.startswith("_"):
            extension_name = f"pylint.extensions.{filename.stem}"
            if extension_name not in run._plugins:
                run._plugins.append(extension_name)


PREPROCESSABLE_OPTIONS: dict[
    str, tuple[bool, Callable[[Run, str | None], None], int]
] = {  # pylint: disable=consider-using-namedtuple-or-dataclass
    # pylint: disable=useless-suppression, wrong-spelling-in-comment
    # Argparse by default allows abbreviations. It behaves differently
    # if you turn this off, so we also turn it on. We mimic this
    # by allowing some abbreviations or incorrect spelling here.
    # The integer at the end of the tuple indicates how many letters
    # should match, include the '-'. 0 indicates a full match.
    #
    # Clashes with --init-(import)
    "--init-hook": (True, _init_hook, 8),
    # Clashes with --r(ecursive)
    "--rcfile": (True, _set_rcfile, 4),
    # Clashes with --output(-format)
    "--output": (True, _set_output, 0),
    # Clashes with --lo(ng-help)
    "--load-plugins": (True, _add_plugins, 5),
    # Clashes with --v(ariable-rgx)
    "--verbose": (False, _set_verbose_mode, 4),
    "-v": (False, _set_verbose_mode, 2),
    # Clashes with --enable
    "--enable-all-extensions": (False, _enable_all_extensions, 9),
}
# pylint: enable=wrong-spelling-in-comment


def _preprocess_options(run: Run, args: Sequence[str]) -> list[str]:
    """Pre-process options before full config parsing has started."""
    processed_args: list[str] = []

    i = 0
    while i < len(args):
        argument = args[i]
        if not argument.startswith("-"):
            processed_args.append(argument)
            i += 1
            continue

        try:
            option, value = argument.split("=", 1)
        except ValueError:
            option, value = argument, None

        matched_option = None
        for option_name, data in PREPROCESSABLE_OPTIONS.items():
            to_match = data[2]
            if to_match == 0:
                if option == option_name:
                    matched_option = option_name
            elif option.startswith(option_name[:to_match]):
                matched_option = option_name

        if matched_option is None:
            processed_args.append(argument)
            i += 1
            continue

        takearg, cb, _ = PREPROCESSABLE_OPTIONS[matched_option]

        if takearg and value is None:
            i += 1
            if i >= len(args) or args[i].startswith("-"):
                raise ArgumentPreprocessingError(f"Option {option} expects a value")
            value = args[i]
        elif not takearg and value is not None:
            raise ArgumentPreprocessingError(f"Option {option} doesn't expect a value")

        cb(run, value)
        i += 1

    return processed_args

back/venvmsg/Lib/site-packages/pylint/config/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import os
import platform
import sys

import astroid
import platformdirs

from pylint.__pkginfo__ import __version__
from pylint.typing import MessageTypesFullName

PY38_PLUS = sys.version_info[:2] >= (3, 8)
PY39_PLUS = sys.version_info[:2] >= (3, 9)
PY310_PLUS = sys.version_info[:2] >= (3, 10)
PY312_PLUS = sys.version_info[:2] >= (3, 12)

IS_PYPY = platform.python_implementation() == "PyPy"

PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll")

MSG_STATE_CONFIDENCE = 2
_MSG_ORDER = "EWRCIF"
MSG_STATE_SCOPE_CONFIG = 0
MSG_STATE_SCOPE_MODULE = 1

# The line/node distinction does not apply to fatal errors and reports.
_SCOPE_EXEMPT = "FR"

MSG_TYPES: dict[str, MessageTypesFullName] = {
    "I": "info",
    "C": "convention",
    "R": "refactor",
    "W": "warning",
    "E": "error",
    "F": "fatal",
}
MSG_TYPES_LONG: dict[str, str] = {v: k for k, v in MSG_TYPES.items()}

MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1}

# You probably don't want to change the MAIN_CHECKER_NAME
# This would affect rcfile generation and retro-compatibility
# on all project using [MAIN] in their rcfile.
MAIN_CHECKER_NAME = "main"

DEFAULT_PYLINT_HOME = platformdirs.user_cache_dir("pylint")

DEFAULT_IGNORE_LIST = ("CVS",)


class WarningScope:
    LINE = "line-based-msg"
    NODE = "node-based-msg"


full_version = f"""pylint {__version__}
astroid {astroid.__version__}
Python {sys.version}"""

HUMAN_READABLE_TYPES = {
    "file": "file",
    "module": "module",
    "const": "constant",
    "class": "class",
    "function": "function",
    "method": "method",
    "attr": "attribute",
    "argument": "argument",
    "variable": "variable",
    "class_attribute": "class attribute",
    "class_const": "class constant",
    "inlinevar": "inline iteration",
    "typevar": "type variable",
    "typealias": "type alias",
}

# ignore some messages when emitting useless-suppression:
# - cyclic-import: can show false positives due to incomplete context
# - deprecated-{module, argument, class, method, decorator}:
#   can cause false positives for multi-interpreter projects
#   when linting with an interpreter on a lower python version
INCOMPATIBLE_WITH_USELESS_SUPPRESSION = frozenset(
    [
        "R0401",  # cyclic-import
        "W0402",  # deprecated-module
        "W1505",  # deprecated-method
        "W1511",  # deprecated-argument
        "W1512",  # deprecated-class
        "W1513",  # deprecated-decorator
        "R0801",  # duplicate-code
    ]
)


def _get_pylint_home() -> str:
    """Return the pylint home."""
    if "PYLINTHOME" in os.environ:
        return os.environ["PYLINTHOME"]
    return DEFAULT_PYLINT_HOME


PYLINT_HOME = _get_pylint_home()

TYPING_NORETURN = frozenset(
    (
        "typing.NoReturn",
        "typing_extensions.NoReturn",
    )
)
TYPING_NEVER = frozenset(
    (
        "typing.Never",
        "typing_extensions.Never",
    )
)

DUNDER_METHODS: dict[tuple[int, int], dict[str, str]] = {
    (0, 0): {
        "__init__": "Instantiate class directly",
        "__del__": "Use del keyword",
        "__repr__": "Use repr built-in function",
        "__str__": "Use str built-in function",
        "__bytes__": "Use bytes built-in function",
        "__format__": "Use format built-in function, format string method, or f-string",
        "__lt__": "Use < operator",
        "__le__": "Use <= operator",
        "__eq__": "Use == operator",
        "__ne__": "Use != operator",
        "__gt__": "Use > operator",
        "__ge__": "Use >= operator",
        "__hash__": "Use hash built-in function",
        "__bool__": "Use bool built-in function",
        "__getattr__": "Access attribute directly or use getattr built-in function",
        "__getattribute__": "Access attribute directly or use getattr built-in function",
        "__setattr__": "Set attribute directly or use setattr built-in function",
        "__delattr__": "Use del keyword",
        "__dir__": "Use dir built-in function",
        "__get__": "Use get method",
        "__set__": "Use set method",
        "__delete__": "Use del keyword",
        "__instancecheck__": "Use isinstance built-in function",
        "__subclasscheck__": "Use issubclass built-in function",
        "__call__": "Invoke instance directly",
        "__len__": "Use len built-in function",
        "__length_hint__": "Use length_hint method",
        "__getitem__": "Access item via subscript",
        "__setitem__": "Set item via subscript",
        "__delitem__": "Use del keyword",
        "__iter__": "Use iter built-in function",
        "__next__": "Use next built-in function",
        "__reversed__": "Use reversed built-in function",
        "__contains__": "Use in keyword",
        "__add__": "Use + operator",
        "__sub__": "Use - operator",
        "__mul__": "Use * operator",
        "__matmul__": "Use @ operator",
        "__truediv__": "Use / operator",
        "__floordiv__": "Use // operator",
        "__mod__": "Use % operator",
        "__divmod__": "Use divmod built-in function",
        "__pow__": "Use ** operator or pow built-in function",
        "__lshift__": "Use << operator",
        "__rshift__": "Use >> operator",
        "__and__": "Use & operator",
        "__xor__": "Use ^ operator",
        "__or__": "Use | operator",
        "__radd__": "Use + operator",
        "__rsub__": "Use - operator",
        "__rmul__": "Use * operator",
        "__rmatmul__": "Use @ operator",
        "__rtruediv__": "Use / operator",
        "__rfloordiv__": "Use // operator",
        "__rmod__": "Use % operator",
        "__rdivmod__": "Use divmod built-in function",
        "__rpow__": "Use ** operator or pow built-in function",
        "__rlshift__": "Use << operator",
        "__rrshift__": "Use >> operator",
        "__rand__": "Use & operator",
        "__rxor__": "Use ^ operator",
        "__ror__": "Use | operator",
        "__iadd__": "Use += operator",
        "__isub__": "Use -= operator",
        "__imul__": "Use *= operator",
        "__imatmul__": "Use @= operator",
        "__itruediv__": "Use /= operator",
        "__ifloordiv__": "Use //= operator",
        "__imod__": "Use %= operator",
        "__ipow__": "Use **= operator",
        "__ilshift__": "Use <<= operator",
        "__irshift__": "Use >>= operator",
        "__iand__": "Use &= operator",
        "__ixor__": "Use ^= operator",
        "__ior__": "Use |= operator",
        "__neg__": "Multiply by -1 instead",
        "__pos__": "Multiply by +1 instead",
        "__abs__": "Use abs built-in function",
        "__invert__": "Use ~ operator",
        "__complex__": "Use complex built-in function",
        "__int__": "Use int built-in function",
        "__float__": "Use float built-in function",
        "__round__": "Use round built-in function",
        "__trunc__": "Use math.trunc function",
        "__floor__": "Use math.floor function",
        "__ceil__": "Use math.ceil function",
        "__enter__": "Invoke context manager directly",
        "__aenter__": "Invoke context manager directly",
        "__copy__": "Use copy.copy function",
        "__deepcopy__": "Use copy.deepcopy function",
        "__fspath__": "Use os.fspath function instead",
    },
    (3, 10): {
        "__aiter__": "Use aiter built-in function",
        "__anext__": "Use anext built-in function",
    },
}

EXTRA_DUNDER_METHODS = [
    "__new__",
    "__subclasses__",
    "__init_subclass__",
    "__set_name__",
    "__class_getitem__",
    "__missing__",
    "__exit__",
    "__await__",
    "__aexit__",
    "__getnewargs_ex__",
    "__getnewargs__",
    "__getstate__",
    "__index__",
    "__setstate__",
    "__reduce__",
    "__reduce_ex__",
    "__post_init__",  # part of `dataclasses` module
]

DUNDER_PROPERTIES = [
    "__class__",
    "__dict__",
    "__doc__",
    "__format__",
    "__module__",
    "__sizeof__",
    "__subclasshook__",
    "__weakref__",
]

# C2801 rule exceptions as their corresponding function/method/operator
# is not valid python syntax in a lambda definition
UNNECESSARY_DUNDER_CALL_LAMBDA_EXCEPTIONS = [
    "__init__",
    "__del__",
    "__delattr__",
    "__set__",
    "__delete__",
    "__setitem__",
    "__delitem__",
    "__iadd__",
    "__isub__",
    "__imul__",
    "__imatmul__",
    "__itruediv__",
    "__ifloordiv__",
    "__imod__",
    "__ipow__",
    "__ilshift__",
    "__irshift__",
    "__iand__",
    "__ixor__",
    "__ior__",
]

back/venvmsg/Lib/site-packages/pylint/constants.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Exception classes raised by various operations within pylint."""


class InvalidMessageError(Exception):
    """Raised when a message creation, registration or addition is rejected."""


class UnknownMessageError(Exception):
    """Raised when an unregistered message id is encountered."""


class DeletedMessageError(UnknownMessageError):
    """Raised when a message id or symbol that was deleted from pylint is
    encountered.
    """

    def __init__(self, msgid_or_symbol: str, removal_explanation: str):
        super().__init__(
            f"'{msgid_or_symbol}' was removed from pylint, see {removal_explanation}."
        )


class MessageBecameExtensionError(UnknownMessageError):
    """Raised when a message id or symbol that was moved to an optional
    extension is encountered.
    """

    def __init__(self, msgid_or_symbol: str, moved_explanation: str):
        super().__init__(
            f"'{msgid_or_symbol}' was moved to an optional extension, see {moved_explanation}."
        )


class EmptyReportError(Exception):
    """Raised when a report is empty and so should not be displayed."""


class InvalidReporterError(Exception):
    """Raised when selected reporter is invalid (e.g. not found)."""


class InvalidArgsError(ValueError):
    """Raised when passed arguments are invalid, e.g., have the wrong length."""


class NoLineSuppliedError(Exception):
    """Raised when trying to disable a message on a next line without supplying a line
    number.
    """

back/venvmsg/Lib/site-packages/pylint/exceptions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from pylint.utils import register_plugins

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def initialize(linter: PyLinter) -> None:
    """Initialize linter with checkers in the extensions' directory."""
    register_plugins(linter, __path__[0])


__all__ = ["initialize"]

back/venvmsg/Lib/site-packages/pylint/extensions/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Utility methods for docstring checking."""

from __future__ import annotations

import itertools
import re
from collections.abc import Iterable

import astroid
from astroid import nodes
from astroid.util import UninferableBase

from pylint.checkers import utils


def space_indentation(s: str) -> int:
    """The number of leading spaces in a string.

    :param str s: input string

    :rtype: int
    :return: number of leading spaces
    """
    return len(s) - len(s.lstrip(" "))


def get_setters_property_name(node: nodes.FunctionDef) -> str | None:
    """Get the name of the property that the given node is a setter for.

    :param node: The node to get the property name for.
    :type node: str

    :rtype: str or None
    :returns: The name of the property that the node is a setter for,
        or None if one could not be found.
    """
    decorators = node.decorators.nodes if node.decorators else []
    for decorator in decorators:
        if (
            isinstance(decorator, nodes.Attribute)
            and decorator.attrname == "setter"
            and isinstance(decorator.expr, nodes.Name)
        ):
            return decorator.expr.name  # type: ignore[no-any-return]
    return None


def get_setters_property(node: nodes.FunctionDef) -> nodes.FunctionDef | None:
    """Get the property node for the given setter node.

    :param node: The node to get the property for.
    :type node: nodes.FunctionDef

    :rtype: nodes.FunctionDef or None
    :returns: The node relating to the property of the given setter node,
        or None if one could not be found.
    """
    property_ = None

    property_name = get_setters_property_name(node)
    class_node = utils.node_frame_class(node)
    if property_name and class_node:
        class_attrs: list[nodes.FunctionDef] = class_node.getattr(node.name)
        for attr in class_attrs:
            if utils.decorated_with_property(attr):
                property_ = attr
                break

    return property_


def returns_something(return_node: nodes.Return) -> bool:
    """Check if a return node returns a value other than None.

    :param return_node: The return node to check.
    :type return_node: astroid.Return

    :rtype: bool
    :return: True if the return node returns a value other than None,
        False otherwise.
    """
    returns = return_node.value

    if returns is None:
        return False

    return not (isinstance(returns, nodes.Const) and returns.value is None)


def _get_raise_target(node: nodes.NodeNG) -> nodes.NodeNG | UninferableBase | None:
    if isinstance(node.exc, nodes.Call):
        func = node.exc.func
        if isinstance(func, (nodes.Name, nodes.Attribute)):
            return utils.safe_infer(func)
    return None


def _split_multiple_exc_types(target: str) -> list[str]:
    delimiters = r"(\s*,(?:\s*or\s)?\s*|\s+or\s+)"
    return re.split(delimiters, target)


def possible_exc_types(node: nodes.NodeNG) -> set[nodes.ClassDef]:
    """Gets all the possible raised exception types for the given raise node.

    .. note::

        Caught exception types are ignored.

    :param node: The raise node to find exception types for.

    :returns: A list of exception types possibly raised by :param:`node`.
    """
    exceptions = []
    if isinstance(node.exc, nodes.Name):
        inferred = utils.safe_infer(node.exc)
        if inferred:
            exceptions = [inferred]
    elif node.exc is None:
        handler = node.parent
        while handler and not isinstance(handler, nodes.ExceptHandler):
            handler = handler.parent

        if handler and handler.type:
            try:
                for exception in astroid.unpack_infer(handler.type):
                    if not isinstance(exception, UninferableBase):
                        exceptions.append(exception)
            except astroid.InferenceError:
                pass
    else:
        target = _get_raise_target(node)
        if isinstance(target, nodes.ClassDef):
            exceptions = [target]
        elif isinstance(target, nodes.FunctionDef):
            for ret in target.nodes_of_class(nodes.Return):
                if ret.value is None:
                    continue
                if ret.frame() != target:
                    # return from inner function - ignore it
                    continue

                val = utils.safe_infer(ret.value)
                if val and utils.inherit_from_std_ex(val):
                    if isinstance(val, nodes.ClassDef):
                        exceptions.append(val)
                    elif isinstance(val, astroid.Instance):
                        exceptions.append(val.getattr("__class__")[0])

    try:
        return {
            exc
            for exc in exceptions
            if not utils.node_ignores_exception(node, exc.name)
        }
    except astroid.InferenceError:
        return set()


def _is_ellipsis(node: nodes.NodeNG) -> bool:
    return isinstance(node, nodes.Const) and node.value == Ellipsis


def _merge_annotations(
    annotations: Iterable[nodes.NodeNG], comment_annotations: Iterable[nodes.NodeNG]
) -> Iterable[nodes.NodeNG | None]:
    for ann, comment_ann in itertools.zip_longest(annotations, comment_annotations):
        if ann and not _is_ellipsis(ann):
            yield ann
        elif comment_ann and not _is_ellipsis(comment_ann):
            yield comment_ann
        else:
            yield None


def _annotations_list(args_node: nodes.Arguments) -> list[nodes.NodeNG]:
    """Get a merged list of annotations.

    The annotations can come from:

    * Real type annotations.
    * A type comment on the function.
    * A type common on the individual argument.

    :param args_node: The node to get the annotations for.
    :returns: The annotations.
    """
    plain_annotations = args_node.annotations or ()
    func_comment_annotations = args_node.parent.type_comment_args or ()
    comment_annotations = args_node.type_comment_posonlyargs
    comment_annotations += args_node.type_comment_args or []
    comment_annotations += args_node.type_comment_kwonlyargs
    return list(
        _merge_annotations(
            plain_annotations,
            _merge_annotations(func_comment_annotations, comment_annotations),
        )
    )


def args_with_annotation(args_node: nodes.Arguments) -> set[str]:
    result = set()
    annotations = _annotations_list(args_node)
    annotation_offset = 0

    if args_node.posonlyargs:
        posonlyargs_annotations = args_node.posonlyargs_annotations
        if not any(args_node.posonlyargs_annotations):
            num_args = len(args_node.posonlyargs)
            posonlyargs_annotations = annotations[
                annotation_offset : annotation_offset + num_args
            ]
            annotation_offset += num_args

        for arg, annotation in zip(args_node.posonlyargs, posonlyargs_annotations):
            if annotation:
                result.add(arg.name)

    if args_node.args:
        num_args = len(args_node.args)
        for arg, annotation in zip(
            args_node.args,
            annotations[annotation_offset : annotation_offset + num_args],
        ):
            if annotation:
                result.add(arg.name)

        annotation_offset += num_args

    if args_node.vararg:
        if args_node.varargannotation:
            result.add(args_node.vararg)
        elif len(annotations) > annotation_offset and annotations[annotation_offset]:
            result.add(args_node.vararg)
            annotation_offset += 1

    if args_node.kwonlyargs:
        kwonlyargs_annotations = args_node.kwonlyargs_annotations
        if not any(args_node.kwonlyargs_annotations):
            num_args = len(args_node.kwonlyargs)
            kwonlyargs_annotations = annotations[
                annotation_offset : annotation_offset + num_args
            ]
            annotation_offset += num_args

        for arg, annotation in zip(args_node.kwonlyargs, kwonlyargs_annotations):
            if annotation:
                result.add(arg.name)

    if args_node.kwarg:
        if args_node.kwargannotation:
            result.add(args_node.kwarg)
        elif len(annotations) > annotation_offset and annotations[annotation_offset]:
            result.add(args_node.kwarg)
            annotation_offset += 1

    return result


def docstringify(
    docstring: nodes.Const | None, default_type: str = "default"
) -> Docstring:
    best_match = (0, DOCSTRING_TYPES.get(default_type, Docstring)(docstring))
    for docstring_type in (
        SphinxDocstring,
        EpytextDocstring,
        GoogleDocstring,
        NumpyDocstring,
    ):
        instance = docstring_type(docstring)
        matching_sections = instance.matching_sections()
        if matching_sections > best_match[0]:
            best_match = (matching_sections, instance)

    return best_match[1]


class Docstring:
    re_for_parameters_see = re.compile(
        r"""
        For\s+the\s+(other)?\s*parameters\s*,\s+see
        """,
        re.X | re.S,
    )

    supports_yields: bool = False
    """True if the docstring supports a "yield" section.

    False if the docstring uses the returns section to document generators.
    """

    # These methods are designed to be overridden
    def __init__(self, doc: nodes.Const | None) -> None:
        docstring: str = doc.value if doc else ""
        self.doc = docstring.expandtabs()

    def __repr__(self) -> str:
        return f"<{self.__class__.__name__}:'''{self.doc}'''>"

    def matching_sections(self) -> int:
        """Returns the number of matching docstring sections."""
        return 0

    def exceptions(self) -> set[str]:
        return set()

    def has_params(self) -> bool:
        return False

    def has_returns(self) -> bool:
        return False

    def has_rtype(self) -> bool:
        return False

    def has_property_returns(self) -> bool:
        return False

    def has_property_type(self) -> bool:
        return False

    def has_yields(self) -> bool:
        return False

    def has_yields_type(self) -> bool:
        return False

    def match_param_docs(self) -> tuple[set[str], set[str]]:
        return set(), set()

    def params_documented_elsewhere(self) -> bool:
        return self.re_for_parameters_see.search(self.doc) is not None


class SphinxDocstring(Docstring):
    re_type = r"""
        [~!.]?               # Optional link style prefix
        \w(?:\w|\.[^\.])*    # Valid python name
        """

    re_simple_container_type = rf"""
        {re_type}                     # a container type
        [\(\[] [^\n\s]+ [\)\]]        # with the contents of the container
    """

    re_multiple_simple_type = rf"""
        (?:{re_simple_container_type}|{re_type})
        (?:(?:\s+(?:of|or)\s+|\s*,\s*|\s+\|\s+)(?:{re_simple_container_type}|{re_type}))*
    """

    re_xref = rf"""
        (?::\w+:)?                    # optional tag
        `{re_type}`                   # what to reference
        """

    re_param_raw = rf"""
        :                       # initial colon
        (?:                     # Sphinx keywords
        param|parameter|
        arg|argument|
        key|keyword
        )
        \s+                     # whitespace

        (?:                     # optional type declaration
        ({re_type}|{re_simple_container_type})
        \s+
        )?

        ((\\\*{{0,2}}\w+)|(\w+))  # Parameter name with potential asterisks
        \s*                       # whitespace
        :                         # final colon
        """
    re_param_in_docstring = re.compile(re_param_raw, re.X | re.S)

    re_type_raw = rf"""
        :type                           # Sphinx keyword
        \s+                             # whitespace
        ({re_multiple_simple_type})     # Parameter name
        \s*                             # whitespace
        :                               # final colon
        """
    re_type_in_docstring = re.compile(re_type_raw, re.X | re.S)

    re_property_type_raw = rf"""
        :type:                      # Sphinx keyword
        \s+                         # whitespace
        {re_multiple_simple_type}   # type declaration
        """
    re_property_type_in_docstring = re.compile(re_property_type_raw, re.X | re.S)

    re_raise_raw = rf"""
        :                               # initial colon
        (?:                             # Sphinx keyword
        raises?|
        except|exception
        )
        \s+                             # whitespace
        ({re_multiple_simple_type})     # exception type
        \s*                             # whitespace
        :                               # final colon
        """
    re_raise_in_docstring = re.compile(re_raise_raw, re.X | re.S)

    re_rtype_in_docstring = re.compile(r":rtype:")

    re_returns_in_docstring = re.compile(r":returns?:")

    supports_yields = False

    def matching_sections(self) -> int:
        """Returns the number of matching docstring sections."""
        return sum(
            bool(i)
            for i in (
                self.re_param_in_docstring.search(self.doc),
                self.re_raise_in_docstring.search(self.doc),
                self.re_rtype_in_docstring.search(self.doc),
                self.re_returns_in_docstring.search(self.doc),
                self.re_property_type_in_docstring.search(self.doc),
            )
        )

    def exceptions(self) -> set[str]:
        types: set[str] = set()

        for match in re.finditer(self.re_raise_in_docstring, self.doc):
            raise_type = match.group(1)
            types.update(_split_multiple_exc_types(raise_type))

        return types

    def has_params(self) -> bool:
        if not self.doc:
            return False

        return self.re_param_in_docstring.search(self.doc) is not None

    def has_returns(self) -> bool:
        if not self.doc:
            return False

        return bool(self.re_returns_in_docstring.search(self.doc))

    def has_rtype(self) -> bool:
        if not self.doc:
            return False

        return bool(self.re_rtype_in_docstring.search(self.doc))

    def has_property_returns(self) -> bool:
        if not self.doc:
            return False

        # The summary line is the return doc,
        # so the first line must not be a known directive.
        return not self.doc.lstrip().startswith(":")

    def has_property_type(self) -> bool:
        if not self.doc:
            return False

        return bool(self.re_property_type_in_docstring.search(self.doc))

    def match_param_docs(self) -> tuple[set[str], set[str]]:
        params_with_doc = set()
        params_with_type = set()

        for match in re.finditer(self.re_param_in_docstring, self.doc):
            name = match.group(2)
            # Remove escape characters necessary for asterisks
            name = name.replace("\\", "")
            params_with_doc.add(name)
            param_type = match.group(1)
            if param_type is not None:
                params_with_type.add(name)

        params_with_type.update(re.findall(self.re_type_in_docstring, self.doc))
        return params_with_doc, params_with_type


class EpytextDocstring(SphinxDocstring):
    """Epytext is similar to Sphinx.

    See the docs:
        http://epydoc.sourceforge.net/epytext.html
        http://epydoc.sourceforge.net/fields.html#fields

    It's used in PyCharm:
        https://www.jetbrains.com/help/pycharm/2016.1/creating-documentation-comments.html#d848203e314
        https://www.jetbrains.com/help/pycharm/2016.1/using-docstrings-to-specify-types.html
    """

    re_param_in_docstring = re.compile(
        SphinxDocstring.re_param_raw.replace(":", "@", 1), re.X | re.S
    )

    re_type_in_docstring = re.compile(
        SphinxDocstring.re_type_raw.replace(":", "@", 1), re.X | re.S
    )

    re_property_type_in_docstring = re.compile(
        SphinxDocstring.re_property_type_raw.replace(":", "@", 1), re.X | re.S
    )

    re_raise_in_docstring = re.compile(
        SphinxDocstring.re_raise_raw.replace(":", "@", 1), re.X | re.S
    )

    re_rtype_in_docstring = re.compile(
        r"""
        @                       # initial "at" symbol
        (?:                     # Epytext keyword
        rtype|returntype
        )
        :                       # final colon
        """,
        re.X | re.S,
    )

    re_returns_in_docstring = re.compile(r"@returns?:")

    def has_property_returns(self) -> bool:
        if not self.doc:
            return False

        # If this is a property docstring, the summary is the return doc.
        if self.has_property_type():
            # The summary line is the return doc,
            # so the first line must not be a known directive.
            return not self.doc.lstrip().startswith("@")

        return False


class GoogleDocstring(Docstring):
    re_type = SphinxDocstring.re_type

    re_xref = SphinxDocstring.re_xref

    re_container_type = rf"""
        (?:{re_type}|{re_xref})       # a container type
        [\(\[] [^\n]+ [\)\]]          # with the contents of the container
    """

    re_multiple_type = rf"""
        (?:{re_container_type}|{re_type}|{re_xref})
        (?:(?:\s+(?:of|or)\s+|\s*,\s*|\s+\|\s+)(?:{re_container_type}|{re_type}|{re_xref}))*
    """

    _re_section_template = r"""
        ^([ ]*)   {0} \s*:   \s*$     # Google parameter header
        (  .* )                       # section
        """

    re_param_section = re.compile(
        _re_section_template.format(r"(?:Args|Arguments|Parameters)"),
        re.X | re.S | re.M,
    )

    re_keyword_param_section = re.compile(
        _re_section_template.format(r"Keyword\s(?:Args|Arguments|Parameters)"),
        re.X | re.S | re.M,
    )

    re_param_line = re.compile(
        rf"""
        \s*  ((?:\\?\*{{0,2}})?[\w\\]+) # identifier potentially with asterisks or escaped `\`
        \s*  ( [(]
            {re_multiple_type}
            (?:,\s+optional)?
            [)] )? \s* :                # optional type declaration
        \s*  (.*)                       # beginning of optional description
    """,
        re.X | re.S | re.M,
    )

    re_raise_section = re.compile(
        _re_section_template.format(r"Raises"), re.X | re.S | re.M
    )

    re_raise_line = re.compile(
        rf"""
        \s*  ({re_multiple_type}) \s* :  # identifier
        \s*  (.*)                        # beginning of optional description
    """,
        re.X | re.S | re.M,
    )

    re_returns_section = re.compile(
        _re_section_template.format(r"Returns?"), re.X | re.S | re.M
    )

    re_returns_line = re.compile(
        rf"""
        \s* ({re_multiple_type}:)?        # identifier
        \s* (.*)                          # beginning of description
    """,
        re.X | re.S | re.M,
    )

    re_property_returns_line = re.compile(
        rf"""
        ^{re_multiple_type}:           # identifier
        \s* (.*)                       # Summary line / description
    """,
        re.X | re.S | re.M,
    )

    re_yields_section = re.compile(
        _re_section_template.format(r"Yields?"), re.X | re.S | re.M
    )

    re_yields_line = re_returns_line

    supports_yields = True

    def matching_sections(self) -> int:
        """Returns the number of matching docstring sections."""
        return sum(
            bool(i)
            for i in (
                self.re_param_section.search(self.doc),
                self.re_raise_section.search(self.doc),
                self.re_returns_section.search(self.doc),
                self.re_yields_section.search(self.doc),
                self.re_property_returns_line.search(self._first_line()),
            )
        )

    def has_params(self) -> bool:
        if not self.doc:
            return False

        return self.re_param_section.search(self.doc) is not None

    def has_returns(self) -> bool:
        if not self.doc:
            return False

        entries = self._parse_section(self.re_returns_section)
        for entry in entries:
            match = self.re_returns_line.match(entry)
            if not match:
                continue

            return_desc = match.group(2)
            if return_desc:
                return True

        return False

    def has_rtype(self) -> bool:
        if not self.doc:
            return False

        entries = self._parse_section(self.re_returns_section)
        for entry in entries:
            match = self.re_returns_line.match(entry)
            if not match:
                continue

            return_type = match.group(1)
            if return_type:
                return True

        return False

    def has_property_returns(self) -> bool:
        # The summary line is the return doc,
        # so the first line must not be a known directive.
        first_line = self._first_line()
        return not bool(
            self.re_param_section.search(first_line)
            or self.re_raise_section.search(first_line)
            or self.re_returns_section.search(first_line)
            or self.re_yields_section.search(first_line)
        )

    def has_property_type(self) -> bool:
        if not self.doc:
            return False

        return bool(self.re_property_returns_line.match(self._first_line()))

    def has_yields(self) -> bool:
        if not self.doc:
            return False

        entries = self._parse_section(self.re_yields_section)
        for entry in entries:
            match = self.re_yields_line.match(entry)
            if not match:
                continue

            yield_desc = match.group(2)
            if yield_desc:
                return True

        return False

    def has_yields_type(self) -> bool:
        if not self.doc:
            return False

        entries = self._parse_section(self.re_yields_section)
        for entry in entries:
            match = self.re_yields_line.match(entry)
            if not match:
                continue

            yield_type = match.group(1)
            if yield_type:
                return True

        return False

    def exceptions(self) -> set[str]:
        types: set[str] = set()

        entries = self._parse_section(self.re_raise_section)
        for entry in entries:
            match = self.re_raise_line.match(entry)
            if not match:
                continue

            exc_type = match.group(1)
            exc_desc = match.group(2)
            if exc_desc:
                types.update(_split_multiple_exc_types(exc_type))

        return types

    def match_param_docs(self) -> tuple[set[str], set[str]]:
        params_with_doc: set[str] = set()
        params_with_type: set[str] = set()

        entries = self._parse_section(self.re_param_section)
        entries.extend(self._parse_section(self.re_keyword_param_section))
        for entry in entries:
            match = self.re_param_line.match(entry)
            if not match:
                continue

            param_name = match.group(1)
            # Remove escape characters necessary for asterisks
            param_name = param_name.replace("\\", "")

            param_type = match.group(2)
            param_desc = match.group(3)

            if param_type:
                params_with_type.add(param_name)

            if param_desc:
                params_with_doc.add(param_name)

        return params_with_doc, params_with_type

    def _first_line(self) -> str:
        return self.doc.lstrip().split("\n", 1)[0]

    @staticmethod
    def min_section_indent(section_match: re.Match[str]) -> int:
        return len(section_match.group(1)) + 1

    @staticmethod
    def _is_section_header(_: str) -> bool:
        # Google parsing does not need to detect section headers,
        # because it works off of indentation level only
        return False

    def _parse_section(self, section_re: re.Pattern[str]) -> list[str]:
        section_match = section_re.search(self.doc)
        if section_match is None:
            return []

        min_indentation = self.min_section_indent(section_match)

        entries: list[str] = []
        entry: list[str] = []
        is_first = True
        for line in section_match.group(2).splitlines():
            if not line.strip():
                continue
            indentation = space_indentation(line)
            if indentation < min_indentation:
                break

            # The first line after the header defines the minimum
            # indentation.
            if is_first:
                min_indentation = indentation
                is_first = False

            if indentation == min_indentation:
                if self._is_section_header(line):
                    break
                # Lines with minimum indentation must contain the beginning
                # of a new parameter documentation.
                if entry:
                    entries.append("\n".join(entry))
                    entry = []

            entry.append(line)

        if entry:
            entries.append("\n".join(entry))

        return entries


class NumpyDocstring(GoogleDocstring):
    _re_section_template = r"""
        ^([ ]*)   {0}   \s*?$          # Numpy parameters header
        \s*     [-=]+   \s*?$          # underline
        (  .* )                        # section
    """

    re_param_section = re.compile(
        _re_section_template.format(r"(?:Args|Arguments|Parameters)"),
        re.X | re.S | re.M,
    )

    re_default_value = r"""((['"]\w+\s*['"])|(\d+)|(True)|(False)|(None))"""

    re_param_line = re.compile(
        rf"""
        \s*  (?P<param_name>\*{{0,2}}\w+)(\s?(:|\n)) # identifier with potential asterisks
        \s*
        (?P<param_type>
         (
          ({GoogleDocstring.re_multiple_type})      # default type declaration
          (,\s+optional)?                           # optional 'optional' indication
         )?
         (
          {{({re_default_value},?\s*)+}}            # set of default values
         )?
         (?:$|\n)
        )?
        (
         \s* (?P<param_desc>.*)                     # optional description
        )?
    """,
        re.X | re.S,
    )

    re_raise_section = re.compile(
        _re_section_template.format(r"Raises"), re.X | re.S | re.M
    )

    re_raise_line = re.compile(
        rf"""
        \s* ({GoogleDocstring.re_type})$   # type declaration
        \s* (.*)                           # optional description
    """,
        re.X | re.S | re.M,
    )

    re_returns_section = re.compile(
        _re_section_template.format(r"Returns?"), re.X | re.S | re.M
    )

    re_returns_line = re.compile(
        rf"""
        \s* (?:\w+\s+:\s+)? # optional name
        ({GoogleDocstring.re_multiple_type})$   # type declaration
        \s* (.*)                                # optional description
    """,
        re.X | re.S | re.M,
    )

    re_yields_section = re.compile(
        _re_section_template.format(r"Yields?"), re.X | re.S | re.M
    )

    re_yields_line = re_returns_line

    supports_yields = True

    def match_param_docs(self) -> tuple[set[str], set[str]]:
        """Matches parameter documentation section to parameter documentation rules."""
        params_with_doc = set()
        params_with_type = set()

        entries = self._parse_section(self.re_param_section)
        entries.extend(self._parse_section(self.re_keyword_param_section))
        for entry in entries:
            match = self.re_param_line.match(entry)
            if not match:
                continue

            # check if parameter has description only
            re_only_desc = re.match(r"\s*(\*{0,2}\w+)\s*:?\n\s*\w*$", entry)
            if re_only_desc:
                param_name = match.group("param_name")
                param_desc = match.group("param_type")
                param_type = None
            else:
                param_name = match.group("param_name")
                param_type = match.group("param_type")
                param_desc = match.group("param_desc")
                # The re_param_line pattern needs to match multi-line which removes the ability
                # to match a single line description like 'arg : a number type.'
                # We are not trying to determine whether 'a number type' is correct typing
                # but we do accept it as typing as it is in the place where typing
                # should be
                if param_type is None and re.match(r"\s*(\*{0,2}\w+)\s*:.+$", entry):
                    param_type = param_desc
                # If the description is "" but we have a type description
                # we consider the description to be the type
                if not param_desc and param_type:
                    param_desc = param_type

            if param_type:
                params_with_type.add(param_name)

            if param_desc:
                params_with_doc.add(param_name)

        return params_with_doc, params_with_type

    @staticmethod
    def min_section_indent(section_match: re.Match[str]) -> int:
        return len(section_match.group(1))

    @staticmethod
    def _is_section_header(line: str) -> bool:
        return bool(re.match(r"\s*-+$", line))


DOCSTRING_TYPES = {
    "sphinx": SphinxDocstring,
    "epytext": EpytextDocstring,
    "google": GoogleDocstring,
    "numpy": NumpyDocstring,
    "default": Docstring,
}
"""A map of the name of the docstring type to its class.

:type: dict(str, type)
"""

back/venvmsg/Lib/site-packages/pylint/extensions/_check_docs_utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checker for deprecated builtins."""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import only_required_for_messages

if TYPE_CHECKING:
    from pylint.lint import PyLinter

BAD_FUNCTIONS = ["map", "filter"]
# Some hints regarding the use of bad builtins.
LIST_COMP_MSG = "Using a list comprehension can be clearer."
BUILTIN_HINTS = {"map": LIST_COMP_MSG, "filter": LIST_COMP_MSG}


class BadBuiltinChecker(BaseChecker):
    name = "deprecated_builtins"
    msgs = {
        "W0141": (
            "Used builtin function %s",
            "bad-builtin",
            "Used when a disallowed builtin function is used (see the "
            "bad-function option). Usual disallowed functions are the ones "
            "like map, or filter , where Python offers now some cleaner "
            "alternative like list comprehension.",
        )
    }

    options = (
        (
            "bad-functions",
            {
                "default": BAD_FUNCTIONS,
                "type": "csv",
                "metavar": "<builtin function names>",
                "help": "List of builtins function names that should not be "
                "used, separated by a comma",
            },
        ),
    )

    @only_required_for_messages("bad-builtin")
    def visit_call(self, node: nodes.Call) -> None:
        if isinstance(node.func, nodes.Name):
            name = node.func.name
            # ignore the name if it's not a builtin (i.e. not defined in the
            # locals nor globals scope)
            if not (name in node.frame() or name in node.root()):
                if name in self.linter.config.bad_functions:
                    hint = BUILTIN_HINTS.get(name)
                    args = f"{name!r}. {hint}" if hint else repr(name)
                    self.add_message("bad-builtin", node=node, args=args)


def register(linter: PyLinter) -> None:
    linter.register_checker(BadBuiltinChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/bad_builtin.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Looks for try/except statements with too much code in the try clause."""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint import checkers

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class BroadTryClauseChecker(checkers.BaseChecker):
    """Checks for try clauses with too many lines.

    According to PEP 8, ``try`` clauses shall contain the absolute minimum
    amount of code. This checker enforces a maximum number of statements within
    ``try`` clauses.
    """

    # configuration section name
    name = "broad_try_clause"
    msgs = {
        "W0717": (
            "%s",
            "too-many-try-statements",
            "Try clause contains too many statements.",
        )
    }

    options = (
        (
            "max-try-statements",
            {
                "default": 1,
                "type": "int",
                "metavar": "<int>",
                "help": "Maximum number of statements allowed in a try clause",
            },
        ),
    )

    def _count_statements(
        self, node: nodes.For | nodes.If | nodes.Try | nodes.While | nodes.With
    ) -> int:
        statement_count = len(node.body)

        for body_node in node.body:
            if isinstance(body_node, (nodes.For, nodes.If, nodes.While, nodes.With)):
                statement_count += self._count_statements(body_node)

        return statement_count

    def visit_try(self, node: nodes.Try) -> None:
        try_clause_statements = self._count_statements(node)
        if try_clause_statements > self.linter.config.max_try_statements:
            msg = (
                f"try clause contains {try_clause_statements} statements, expected at"
                f" most {self.linter.config.max_try_statements}"
            )
            self.add_message(
                "too-many-try-statements", node.lineno, node=node, args=msg
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(BroadTryClauseChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/broad_try_clause.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import tokenize
from tokenize import TokenInfo
from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseTokenChecker
from pylint.checkers.utils import only_required_for_messages
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class ElseifUsedChecker(BaseTokenChecker):
    """Checks for use of "else if" when an "elif" could be used."""

    name = "else_if_used"
    msgs = {
        "R5501": (
            'Consider using "elif" instead of "else" then "if" to remove one indentation level',
            "else-if-used",
            "Used when an else statement is immediately followed by "
            "an if statement and does not contain statements that "
            "would be unrelated to it.",
        )
    }

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._init()

    def _init(self) -> None:
        self._elifs: dict[tokenize._Position, str] = {}

    def process_tokens(self, tokens: list[TokenInfo]) -> None:
        """Process tokens and look for 'if' or 'elif'."""
        self._elifs = {
            begin: token for _, token, begin, _, _ in tokens if token in {"elif", "if"}
        }

    def leave_module(self, _: nodes.Module) -> None:
        self._init()

    @only_required_for_messages("else-if-used")
    def visit_if(self, node: nodes.If) -> None:
        """Current if node must directly follow an 'else'."""
        if (
            isinstance(node.parent, nodes.If)
            and node.parent.orelse == [node]
            and (node.lineno, node.col_offset) in self._elifs
            and self._elifs[(node.lineno, node.col_offset)] == "if"
        ):
            self.add_message("else-if-used", node=node, confidence=HIGH)


def register(linter: PyLinter) -> None:
    linter.register_checker(ElseifUsedChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/check_elif.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import sys
from typing import TYPE_CHECKING, Tuple, Type, cast

from astroid import nodes

from pylint.checkers import BaseChecker, utils
from pylint.checkers.utils import only_required_for_messages, safe_infer
from pylint.interfaces import INFERENCE

if TYPE_CHECKING:
    from pylint.lint import PyLinter

if sys.version_info >= (3, 10):
    from typing import TypeGuard
else:
    from typing_extensions import TypeGuard


class CodeStyleChecker(BaseChecker):
    """Checkers that can improve code consistency.

    As such they don't necessarily provide a performance benefit and
    are often times opinionated.

    Before adding another checker here, consider this:
    1. Does the checker provide a clear benefit,
       i.e. detect a common issue or improve performance
       => it should probably be part of the core checker classes
    2. Is it something that would improve code consistency,
       maybe because it's slightly better with regard to performance
       and therefore preferred => this is the right place
    3. Everything else should go into another extension
    """

    name = "code_style"
    msgs = {
        "R6101": (
            "Consider using namedtuple or dataclass for dictionary values",
            "consider-using-namedtuple-or-dataclass",
            "Emitted when dictionary values can be replaced by namedtuples or dataclass instances.",
        ),
        "R6102": (
            "Consider using an in-place tuple instead of list",
            "consider-using-tuple",
            "Only for style consistency! "
            "Emitted where an in-place defined ``list`` can be replaced by a ``tuple``. "
            "Due to optimizations by CPython, there is no performance benefit from it.",
        ),
        "R6103": (
            "Use '%s' instead",
            "consider-using-assignment-expr",
            "Emitted when an if assignment is directly followed by an if statement and "
            "both can be combined by using an assignment expression ``:=``. "
            "Requires Python 3.8 and ``py-version >= 3.8``.",
        ),
        "R6104": (
            "Use '%s' to do an augmented assign directly",
            "consider-using-augmented-assign",
            "Emitted when an assignment is referring to the object that it is assigning "
            "to. This can be changed to be an augmented assign.\n"
            "Disabled by default!",
            {
                "default_enabled": False,
            },
        ),
        "R6105": (
            "Prefer 'typing.NamedTuple' over 'collections.namedtuple'",
            "prefer-typing-namedtuple",
            "'typing.NamedTuple' uses the well-known 'class' keyword "
            "with type-hints for readability (it's also faster as it avoids "
            "an internal exec call).\n"
            "Disabled by default!",
            {
                "default_enabled": False,
            },
        ),
    }
    options = (
        (
            "max-line-length-suggestions",
            {
                "type": "int",
                "default": 0,
                "metavar": "<int>",
                "help": (
                    "Max line length for which to sill emit suggestions. "
                    "Used to prevent optional suggestions which would get split "
                    "by a code formatter (e.g., black). "
                    "Will default to the setting for ``max-line-length``."
                ),
            },
        ),
    )

    def open(self) -> None:
        py_version = self.linter.config.py_version
        self._py36_plus = py_version >= (3, 6)
        self._py38_plus = py_version >= (3, 8)
        self._max_length: int = (
            self.linter.config.max_line_length_suggestions
            or self.linter.config.max_line_length
        )

    @only_required_for_messages("prefer-typing-namedtuple")
    def visit_call(self, node: nodes.Call) -> None:
        if self._py36_plus:
            called = safe_infer(node.func)
            if called and called.qname() == "collections.namedtuple":
                self.add_message(
                    "prefer-typing-namedtuple", node=node, confidence=INFERENCE
                )

    @only_required_for_messages("consider-using-namedtuple-or-dataclass")
    def visit_dict(self, node: nodes.Dict) -> None:
        self._check_dict_consider_namedtuple_dataclass(node)

    @only_required_for_messages("consider-using-tuple")
    def visit_for(self, node: nodes.For) -> None:
        if isinstance(node.iter, nodes.List):
            self.add_message("consider-using-tuple", node=node.iter)

    @only_required_for_messages("consider-using-tuple")
    def visit_comprehension(self, node: nodes.Comprehension) -> None:
        if isinstance(node.iter, nodes.List):
            self.add_message("consider-using-tuple", node=node.iter)

    @only_required_for_messages("consider-using-assignment-expr")
    def visit_if(self, node: nodes.If) -> None:
        if self._py38_plus:
            self._check_consider_using_assignment_expr(node)

    def _check_dict_consider_namedtuple_dataclass(self, node: nodes.Dict) -> None:
        """Check if dictionary values can be replaced by Namedtuple or Dataclass."""
        if not (
            isinstance(node.parent, (nodes.Assign, nodes.AnnAssign))
            and isinstance(node.parent.parent, nodes.Module)
            or isinstance(node.parent, nodes.AnnAssign)
            and isinstance(node.parent.target, nodes.AssignName)
            and utils.is_assign_name_annotated_with(node.parent.target, "Final")
        ):
            # If dict is not part of an 'Assign' or 'AnnAssign' node in
            # a module context OR 'AnnAssign' with 'Final' annotation, skip check.
            return

        # All dict_values are itself dict nodes
        if len(node.items) > 1 and all(
            isinstance(dict_value, nodes.Dict) for _, dict_value in node.items
        ):
            KeyTupleT = Tuple[Type[nodes.NodeNG], str]

            # Makes sure all keys are 'Const' string nodes
            keys_checked: set[KeyTupleT] = set()
            for _, dict_value in node.items:
                dict_value = cast(nodes.Dict, dict_value)
                for key, _ in dict_value.items:
                    key_tuple = (type(key), key.as_string())
                    if key_tuple in keys_checked:
                        continue
                    inferred = safe_infer(key)
                    if not (
                        isinstance(inferred, nodes.Const)
                        and inferred.pytype() == "builtins.str"
                    ):
                        return
                    keys_checked.add(key_tuple)

            # Makes sure all subdicts have at least 1 common key
            key_tuples: list[tuple[KeyTupleT, ...]] = []
            for _, dict_value in node.items:
                dict_value = cast(nodes.Dict, dict_value)
                key_tuples.append(
                    tuple((type(key), key.as_string()) for key, _ in dict_value.items)
                )
            keys_intersection: set[KeyTupleT] = set(key_tuples[0])
            for sub_key_tuples in key_tuples[1:]:
                keys_intersection.intersection_update(sub_key_tuples)
            if not keys_intersection:
                return

            self.add_message("consider-using-namedtuple-or-dataclass", node=node)
            return

        # All dict_values are itself either list or tuple nodes
        if len(node.items) > 1 and all(
            isinstance(dict_value, (nodes.List, nodes.Tuple))
            for _, dict_value in node.items
        ):
            # Make sure all sublists have the same length > 0
            list_length = len(node.items[0][1].elts)
            if list_length == 0:
                return
            for _, dict_value in node.items[1:]:
                if len(dict_value.elts) != list_length:
                    return

            # Make sure at least one list entry isn't a dict
            for _, dict_value in node.items:
                if all(isinstance(entry, nodes.Dict) for entry in dict_value.elts):
                    return

            self.add_message("consider-using-namedtuple-or-dataclass", node=node)
            return

    def _check_consider_using_assignment_expr(self, node: nodes.If) -> None:
        """Check if an assignment expression (walrus operator) can be used.

        For example if an assignment is directly followed by an if statement:
        >>> x = 2
        >>> if x:
        >>>     ...

        Can be replaced by:
        >>> if (x := 2):
        >>>     ...

        Note: Assignment expressions were added in Python 3.8
        """
        # Check if `node.test` contains a `Name` node
        node_name: nodes.Name | None = None
        if isinstance(node.test, nodes.Name):
            node_name = node.test
        elif (
            isinstance(node.test, nodes.UnaryOp)
            and node.test.op == "not"
            and isinstance(node.test.operand, nodes.Name)
        ):
            node_name = node.test.operand
        elif (
            isinstance(node.test, nodes.Compare)
            and isinstance(node.test.left, nodes.Name)
            and len(node.test.ops) == 1
        ):
            node_name = node.test.left
        else:
            return

        # Make sure the previous node is an assignment to the same name
        # used in `node.test`. Furthermore, ignore if assignment spans multiple lines.
        prev_sibling = node.previous_sibling()
        if CodeStyleChecker._check_prev_sibling_to_if_stmt(
            prev_sibling, node_name.name
        ):
            # Check if match statement would be a better fit.
            # I.e. multiple ifs that test the same name.
            if CodeStyleChecker._check_ignore_assignment_expr_suggestion(
                node, node_name.name
            ):
                return

            # Build suggestion string. Check length of suggestion
            # does not exceed max-line-length-suggestions
            test_str = node.test.as_string().replace(
                node_name.name,
                f"({node_name.name} := {prev_sibling.value.as_string()})",
                1,
            )
            suggestion = f"if {test_str}:"
            if (
                node.col_offset is not None
                and len(suggestion) + node.col_offset > self._max_length
                or len(suggestion) > self._max_length
            ):
                return

            self.add_message(
                "consider-using-assignment-expr",
                node=node_name,
                args=(suggestion,),
            )

    @staticmethod
    def _check_prev_sibling_to_if_stmt(
        prev_sibling: nodes.NodeNG | None, name: str | None
    ) -> TypeGuard[nodes.Assign | nodes.AnnAssign]:
        """Check if previous sibling is an assignment with the same name.

        Ignore statements which span multiple lines.
        """
        if prev_sibling is None or prev_sibling.tolineno - prev_sibling.fromlineno != 0:
            return False

        if (
            isinstance(prev_sibling, nodes.Assign)
            and len(prev_sibling.targets) == 1
            and isinstance(prev_sibling.targets[0], nodes.AssignName)
            and prev_sibling.targets[0].name == name
        ):
            return True
        if (
            isinstance(prev_sibling, nodes.AnnAssign)
            and isinstance(prev_sibling.target, nodes.AssignName)
            and prev_sibling.target.name == name
        ):
            return True
        return False

    @staticmethod
    def _check_ignore_assignment_expr_suggestion(
        node: nodes.If, name: str | None
    ) -> bool:
        """Return True if suggestion for assignment expr should be ignored.

        E.g., in cases where a match statement would be a better fit
        (multiple conditions).
        """
        if isinstance(node.test, nodes.Compare):
            next_if_node: nodes.If | None = None
            next_sibling = node.next_sibling()
            if len(node.orelse) == 1 and isinstance(node.orelse[0], nodes.If):
                # elif block
                next_if_node = node.orelse[0]
            elif isinstance(next_sibling, nodes.If):
                # separate if block
                next_if_node = next_sibling

            if (  # pylint: disable=too-many-boolean-expressions
                next_if_node is not None
                and (
                    isinstance(next_if_node.test, nodes.Compare)
                    and isinstance(next_if_node.test.left, nodes.Name)
                    and next_if_node.test.left.name == name
                    or isinstance(next_if_node.test, nodes.Name)
                    and next_if_node.test.name == name
                )
            ):
                return True
        return False

    @only_required_for_messages("consider-using-augmented-assign")
    def visit_assign(self, node: nodes.Assign) -> None:
        is_aug, op = utils.is_augmented_assign(node)
        if is_aug:
            self.add_message(
                "consider-using-augmented-assign",
                args=f"{op}=",
                node=node,
                line=node.lineno,
                col_offset=node.col_offset,
                confidence=INFERENCE,
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(CodeStyleChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/code_style.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checks for yoda comparisons (variable before constant)
See https://en.wikipedia.org/wiki/Yoda_conditions.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker, utils

if TYPE_CHECKING:
    from pylint.lint import PyLinter

REVERSED_COMPS = {"<": ">", "<=": ">=", ">": "<", ">=": "<="}
COMPARISON_OPERATORS = frozenset(("==", "!=", "<", ">", "<=", ">="))


class MisplacedComparisonConstantChecker(BaseChecker):
    """Checks the placement of constants in comparisons."""

    # configuration section name
    name = "comparison-placement"
    msgs = {
        "C2201": (
            "Comparison should be %s",
            "misplaced-comparison-constant",
            "Used when the constant is placed on the left side "
            "of a comparison. It is usually clearer in intent to "
            "place it in the right hand side of the comparison.",
            {"old_names": [("C0122", "old-misplaced-comparison-constant")]},
        )
    }

    options = ()

    def _check_misplaced_constant(
        self,
        node: nodes.Compare,
        left: nodes.NodeNG,
        right: nodes.NodeNG,
        operator: str,
    ) -> None:
        if isinstance(right, nodes.Const):
            return
        operator = REVERSED_COMPS.get(operator, operator)
        suggestion = f"{right.as_string()} {operator} {left.value!r}"
        self.add_message("misplaced-comparison-constant", node=node, args=(suggestion,))

    @utils.only_required_for_messages("misplaced-comparison-constant")
    def visit_compare(self, node: nodes.Compare) -> None:
        # NOTE: this checker only works with binary comparisons like 'x == 42'
        # but not 'x == y == 42'
        if len(node.ops) != 1:
            return

        left = node.left
        operator, right = node.ops[0]
        if operator in COMPARISON_OPERATORS and isinstance(left, nodes.Const):
            self._check_misplaced_constant(node, left, right, operator)


def register(linter: PyLinter) -> None:
    linter.register_checker(MisplacedComparisonConstantChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/comparison_placement.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import only_required_for_messages

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class ConfusingConsecutiveElifChecker(BaseChecker):
    """Checks if "elif" is used right after an indented block that finishes with "if" or
    "elif" itself.
    """

    name = "confusing_elif"
    msgs = {
        "R5601": (
            "Consecutive elif with differing indentation level, consider creating a function to separate the inner"
            " elif",
            "confusing-consecutive-elif",
            "Used when an elif statement follows right after an indented block which itself ends with if or elif. "
            "It may not be ovious if the elif statement was willingly or mistakenly unindented. "
            "Extracting the indented if statement into a separate function might avoid confusion and prevent "
            "errors.",
        )
    }

    @only_required_for_messages("confusing-consecutive-elif")
    def visit_if(self, node: nodes.If) -> None:
        body_ends_with_if = isinstance(
            node.body[-1], nodes.If
        ) and self._has_no_else_clause(node.body[-1])
        if node.has_elif_block() and body_ends_with_if:
            self.add_message("confusing-consecutive-elif", node=node.orelse[0])

    @staticmethod
    def _has_no_else_clause(node: nodes.If) -> bool:
        orelse = node.orelse
        while orelse and isinstance(orelse[0], nodes.If):
            orelse = orelse[0].orelse
        if not orelse or isinstance(orelse[0], nodes.If):
            return True
        return False


def register(linter: PyLinter) -> None:
    linter.register_checker(ConfusingConsecutiveElifChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/confusing_elif.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Looks for try/except statements with too much code in the try clause."""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint import checkers
from pylint.checkers import utils
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class ConsiderRefactorIntoWhileConditionChecker(checkers.BaseChecker):
    """Checks for instances where while loops are implemented with a constant condition
    which.

    always evaluates to truthy and the first statement(s) is/are if statements which, when
    evaluated.

    to True, breaks out of the loop.

    The if statement(s) can be refactored into the while loop.
    """

    name = "consider_refactoring_into_while"
    msgs = {
        "R3501": (
            "Consider using 'while %s' instead of 'while %s:' an 'if', and a 'break'",
            "consider-refactoring-into-while-condition",
            "Emitted when `while True:` loop is used and the first statement is a break condition. "
            "The ``if / break`` construct can be removed if the check is inverted and moved to "
            "the ``while`` statement.",
        ),
    }

    @utils.only_required_for_messages("consider-refactoring-into-while-condition")
    def visit_while(self, node: nodes.While) -> None:
        self._check_breaking_after_while_true(node)

    def _check_breaking_after_while_true(self, node: nodes.While) -> None:
        """Check that any loop with an ``if`` clause has a break statement."""
        if not isinstance(node.test, nodes.Const) or not node.test.bool_value():
            return
        pri_candidates: list[nodes.If] = []
        for n in node.body:
            if not isinstance(n, nodes.If):
                break
            pri_candidates.append(n)
        candidates = []
        tainted = False
        for c in pri_candidates:
            if tainted or not isinstance(c.body[0], nodes.Break):
                break
            candidates.append(c)
            orelse = c.orelse
            while orelse:
                orelse_node = orelse[0]
                if not isinstance(orelse_node, nodes.If):
                    tainted = True
                else:
                    candidates.append(orelse_node)
                if not isinstance(orelse_node, nodes.If):
                    break
                orelse = orelse_node.orelse

        candidates = [n for n in candidates if isinstance(n.body[0], nodes.Break)]
        msg = " and ".join(
            [f"({utils.not_condition_as_string(c.test)})" for c in candidates]
        )
        if len(candidates) == 1:
            msg = utils.not_condition_as_string(candidates[0].test)
        if not msg:
            return

        self.add_message(
            "consider-refactoring-into-while-condition",
            node=node,
            line=node.lineno,
            args=(msg, node.test.as_string()),
            confidence=HIGH,
        )


def register(linter: PyLinter) -> None:
    linter.register_checker(ConsiderRefactorIntoWhileConditionChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/consider_refactoring_into_while_condition.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Check for if / assign blocks that can be rewritten with if-expressions."""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class ConsiderTernaryExpressionChecker(BaseChecker):
    name = "consider_ternary_expression"
    msgs = {
        "W0160": (
            "Consider rewriting as a ternary expression",
            "consider-ternary-expression",
            "Multiple assign statements spread across if/else blocks can be "
            "rewritten with a single assignment and ternary expression",
        )
    }

    def visit_if(self, node: nodes.If) -> None:
        if isinstance(node.parent, nodes.If):
            return

        if len(node.body) != 1 or len(node.orelse) != 1:
            return

        bst = node.body[0]
        ost = node.orelse[0]

        if not isinstance(bst, nodes.Assign) or not isinstance(ost, nodes.Assign):
            return

        for bname, oname in zip(bst.targets, ost.targets):
            if not isinstance(bname, nodes.AssignName) or not isinstance(
                oname, nodes.AssignName
            ):
                return

            if bname.name != oname.name:
                return

        self.add_message("consider-ternary-expression", node=node)


def register(linter: PyLinter) -> None:
    linter.register_checker(ConsiderTernaryExpressionChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/consider_ternary_expression.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Check for use of dictionary mutation after initialization."""
from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import only_required_for_messages
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


class DictInitMutateChecker(BaseChecker):
    name = "dict-init-mutate"
    msgs = {
        "C3401": (
            "Declare all known key/values when initializing the dictionary.",
            "dict-init-mutate",
            "Dictionaries can be initialized with a single statement "
            "using dictionary literal syntax.",
        )
    }

    @only_required_for_messages("dict-init-mutate")
    def visit_assign(self, node: nodes.Assign) -> None:
        """
        Detect dictionary mutation immediately after initialization.

        At this time, detecting nested mutation is not supported.
        """
        if not isinstance(node.value, nodes.Dict):
            return

        dict_name = node.targets[0]
        if len(node.targets) != 1 or not isinstance(dict_name, nodes.AssignName):
            return

        first_sibling = node.next_sibling()
        if (
            not first_sibling
            or not isinstance(first_sibling, nodes.Assign)
            or len(first_sibling.targets) != 1
        ):
            return

        sibling_target = first_sibling.targets[0]
        if not isinstance(sibling_target, nodes.Subscript):
            return

        sibling_name = sibling_target.value
        if not isinstance(sibling_name, nodes.Name):
            return

        if sibling_name.name == dict_name.name:
            self.add_message("dict-init-mutate", node=node, confidence=HIGH)


def register(linter: PyLinter) -> None:
    linter.register_checker(DictInitMutateChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/dict_init_mutate.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Pylint plugin for checking in Sphinx, Google, or Numpy style docstrings."""

from __future__ import annotations

import re
from typing import TYPE_CHECKING

import astroid
from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers import utils as checker_utils
from pylint.extensions import _check_docs_utils as utils
from pylint.extensions._check_docs_utils import Docstring
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class DocstringParameterChecker(BaseChecker):
    """Checker for Sphinx, Google, or Numpy style docstrings.

    * Check that all function, method and constructor parameters are mentioned
      in the params and types part of the docstring.  Constructor parameters
      can be documented in either the class docstring or ``__init__`` docstring,
      but not both.
    * Check that there are no naming inconsistencies between the signature and
      the documentation, i.e. also report documented parameters that are missing
      in the signature. This is important to find cases where parameters are
      renamed only in the code, not in the documentation.
    * Check that all explicitly raised exceptions in a function are documented
      in the function docstring. Caught exceptions are ignored.

    Activate this checker by adding the line::

        load-plugins=pylint.extensions.docparams

    to the ``MAIN`` section of your ``.pylintrc``.
    """

    name = "parameter_documentation"
    msgs = {
        "W9005": (
            '"%s" has constructor parameters documented in class and __init__',
            "multiple-constructor-doc",
            "Please remove parameter declarations in the class or constructor.",
        ),
        "W9006": (
            '"%s" not documented as being raised',
            "missing-raises-doc",
            "Please document exceptions for all raised exception types.",
        ),
        "W9008": (
            "Redundant returns documentation",
            "redundant-returns-doc",
            "Please remove the return/rtype documentation from this method.",
        ),
        "W9010": (
            "Redundant yields documentation",
            "redundant-yields-doc",
            "Please remove the yields documentation from this method.",
        ),
        "W9011": (
            "Missing return documentation",
            "missing-return-doc",
            "Please add documentation about what this method returns.",
            {"old_names": [("W9007", "old-missing-returns-doc")]},
        ),
        "W9012": (
            "Missing return type documentation",
            "missing-return-type-doc",
            "Please document the type returned by this method.",
            # we can't use the same old_name for two different warnings
            # {'old_names': [('W9007', 'missing-returns-doc')]},
        ),
        "W9013": (
            "Missing yield documentation",
            "missing-yield-doc",
            "Please add documentation about what this generator yields.",
            {"old_names": [("W9009", "old-missing-yields-doc")]},
        ),
        "W9014": (
            "Missing yield type documentation",
            "missing-yield-type-doc",
            "Please document the type yielded by this method.",
            # we can't use the same old_name for two different warnings
            # {'old_names': [('W9009', 'missing-yields-doc')]},
        ),
        "W9015": (
            '"%s" missing in parameter documentation',
            "missing-param-doc",
            "Please add parameter declarations for all parameters.",
            {"old_names": [("W9003", "old-missing-param-doc")]},
        ),
        "W9016": (
            '"%s" missing in parameter type documentation',
            "missing-type-doc",
            "Please add parameter type declarations for all parameters.",
            {"old_names": [("W9004", "old-missing-type-doc")]},
        ),
        "W9017": (
            '"%s" differing in parameter documentation',
            "differing-param-doc",
            "Please check parameter names in declarations.",
        ),
        "W9018": (
            '"%s" differing in parameter type documentation',
            "differing-type-doc",
            "Please check parameter names in type declarations.",
        ),
        "W9019": (
            '"%s" useless ignored parameter documentation',
            "useless-param-doc",
            "Please remove the ignored parameter documentation.",
        ),
        "W9020": (
            '"%s" useless ignored parameter type documentation',
            "useless-type-doc",
            "Please remove the ignored parameter type documentation.",
        ),
        "W9021": (
            'Missing any documentation in "%s"',
            "missing-any-param-doc",
            "Please add parameter and/or type documentation.",
        ),
    }

    options = (
        (
            "accept-no-param-doc",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Whether to accept totally missing parameter "
                "documentation in the docstring of a function that has "
                "parameters.",
            },
        ),
        (
            "accept-no-raise-doc",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Whether to accept totally missing raises "
                "documentation in the docstring of a function that "
                "raises an exception.",
            },
        ),
        (
            "accept-no-return-doc",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Whether to accept totally missing return "
                "documentation in the docstring of a function that "
                "returns a statement.",
            },
        ),
        (
            "accept-no-yields-doc",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Whether to accept totally missing yields "
                "documentation in the docstring of a generator.",
            },
        ),
        (
            "default-docstring-type",
            {
                "type": "choice",
                "default": "default",
                "metavar": "<docstring type>",
                "choices": list(utils.DOCSTRING_TYPES),
                "help": "If the docstring type cannot be guessed "
                "the specified docstring type will be used.",
            },
        ),
    )

    constructor_names = {"__init__", "__new__"}
    not_needed_param_in_docstring = {"self", "cls"}

    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        """Called for function and method definitions (def).

        :param node: Node for a function or method definition in the AST
        :type node: :class:`astroid.scoped_nodes.Function`
        """
        node_doc = utils.docstringify(
            node.doc_node, self.linter.config.default_docstring_type
        )

        # skip functions that match the 'no-docstring-rgx' config option
        no_docstring_rgx = self.linter.config.no_docstring_rgx
        if no_docstring_rgx and re.match(no_docstring_rgx, node.name):
            return

        # skip functions smaller than 'docstring-min-length'
        lines = checker_utils.get_node_last_lineno(node) - node.lineno
        max_lines = self.linter.config.docstring_min_length
        if max_lines > -1 and lines < max_lines:
            return

        self.check_functiondef_params(node, node_doc)
        self.check_functiondef_returns(node, node_doc)
        self.check_functiondef_yields(node, node_doc)

    visit_asyncfunctiondef = visit_functiondef

    def check_functiondef_params(
        self, node: nodes.FunctionDef, node_doc: Docstring
    ) -> None:
        node_allow_no_param = None
        if node.name in self.constructor_names:
            class_node = checker_utils.node_frame_class(node)
            if class_node is not None:
                class_doc = utils.docstringify(
                    class_node.doc_node, self.linter.config.default_docstring_type
                )
                self.check_single_constructor_params(class_doc, node_doc, class_node)

                # __init__ or class docstrings can have no parameters documented
                # as long as the other documents them.
                node_allow_no_param = (
                    class_doc.has_params()
                    or class_doc.params_documented_elsewhere()
                    or None
                )
                class_allow_no_param = (
                    node_doc.has_params()
                    or node_doc.params_documented_elsewhere()
                    or None
                )

                self.check_arguments_in_docstring(
                    class_doc, node.args, class_node, class_allow_no_param
                )

        self.check_arguments_in_docstring(
            node_doc, node.args, node, node_allow_no_param
        )

    def check_functiondef_returns(
        self, node: nodes.FunctionDef, node_doc: Docstring
    ) -> None:
        if (not node_doc.supports_yields and node.is_generator()) or node.is_abstract():
            return

        return_nodes = node.nodes_of_class(astroid.Return)
        if (node_doc.has_returns() or node_doc.has_rtype()) and not any(
            utils.returns_something(ret_node) for ret_node in return_nodes
        ):
            self.add_message("redundant-returns-doc", node=node, confidence=HIGH)

    def check_functiondef_yields(
        self, node: nodes.FunctionDef, node_doc: Docstring
    ) -> None:
        if not node_doc.supports_yields or node.is_abstract():
            return

        if (
            node_doc.has_yields() or node_doc.has_yields_type()
        ) and not node.is_generator():
            self.add_message("redundant-yields-doc", node=node)

    def visit_raise(self, node: nodes.Raise) -> None:
        func_node = node.frame()
        if not isinstance(func_node, astroid.FunctionDef):
            return

        # skip functions that match the 'no-docstring-rgx' config option
        no_docstring_rgx = self.linter.config.no_docstring_rgx
        if no_docstring_rgx and re.match(no_docstring_rgx, func_node.name):
            return

        expected_excs = utils.possible_exc_types(node)

        if not expected_excs:
            return

        if not func_node.doc_node:
            # If this is a property setter,
            # the property should have the docstring instead.
            property_ = utils.get_setters_property(func_node)
            if property_:
                func_node = property_

        doc = utils.docstringify(
            func_node.doc_node, self.linter.config.default_docstring_type
        )

        if self.linter.config.accept_no_raise_doc and not doc.exceptions():
            return

        if not doc.matching_sections():
            if doc.doc:
                missing = {exc.name for exc in expected_excs}
                self._add_raise_message(missing, func_node)
            return

        found_excs_full_names = doc.exceptions()

        # Extract just the class name, e.g. "error" from "re.error"
        found_excs_class_names = {exc.split(".")[-1] for exc in found_excs_full_names}

        missing_excs = set()
        for expected in expected_excs:
            for found_exc in found_excs_class_names:
                if found_exc == expected.name:
                    break
                if any(found_exc == ancestor.name for ancestor in expected.ancestors()):
                    break
            else:
                missing_excs.add(expected.name)

        self._add_raise_message(missing_excs, func_node)

    def visit_return(self, node: nodes.Return) -> None:
        if not utils.returns_something(node):
            return

        if self.linter.config.accept_no_return_doc:
            return

        func_node: astroid.FunctionDef = node.frame()

        # skip functions that match the 'no-docstring-rgx' config option
        no_docstring_rgx = self.linter.config.no_docstring_rgx
        if no_docstring_rgx and re.match(no_docstring_rgx, func_node.name):
            return

        doc = utils.docstringify(
            func_node.doc_node, self.linter.config.default_docstring_type
        )

        is_property = checker_utils.decorated_with_property(func_node)

        if not (doc.has_returns() or (doc.has_property_returns() and is_property)):
            self.add_message("missing-return-doc", node=func_node, confidence=HIGH)

        if func_node.returns or func_node.type_comment_returns:
            return

        if not (doc.has_rtype() or (doc.has_property_type() and is_property)):
            self.add_message("missing-return-type-doc", node=func_node, confidence=HIGH)

    def visit_yield(self, node: nodes.Yield | nodes.YieldFrom) -> None:
        if self.linter.config.accept_no_yields_doc:
            return

        func_node: astroid.FunctionDef = node.frame()

        # skip functions that match the 'no-docstring-rgx' config option
        no_docstring_rgx = self.linter.config.no_docstring_rgx
        if no_docstring_rgx and re.match(no_docstring_rgx, func_node.name):
            return

        doc = utils.docstringify(
            func_node.doc_node, self.linter.config.default_docstring_type
        )

        if doc.supports_yields:
            doc_has_yields = doc.has_yields()
            doc_has_yields_type = doc.has_yields_type()
        else:
            doc_has_yields = doc.has_returns()
            doc_has_yields_type = doc.has_rtype()

        if not doc_has_yields:
            self.add_message("missing-yield-doc", node=func_node, confidence=HIGH)

        if not (
            doc_has_yields_type or func_node.returns or func_node.type_comment_returns
        ):
            self.add_message("missing-yield-type-doc", node=func_node, confidence=HIGH)

    visit_yieldfrom = visit_yield

    def _compare_missing_args(
        self,
        found_argument_names: set[str],
        message_id: str,
        not_needed_names: set[str],
        expected_argument_names: set[str],
        warning_node: nodes.NodeNG,
    ) -> None:
        """Compare the found argument names with the expected ones and
        generate a message if there are arguments missing.

        :param found_argument_names: argument names found in the docstring

        :param message_id: pylint message id

        :param not_needed_names: names that may be omitted

        :param expected_argument_names: Expected argument names

        :param warning_node: The node to be analyzed
        """
        potential_missing_argument_names = (
            expected_argument_names - found_argument_names
        ) - not_needed_names

        # Handle variadic and keyword args without asterisks
        missing_argument_names = set()
        for name in potential_missing_argument_names:
            if name.replace("*", "") in found_argument_names:
                continue
            missing_argument_names.add(name)

        if missing_argument_names:
            self.add_message(
                message_id,
                args=(", ".join(sorted(missing_argument_names)),),
                node=warning_node,
                confidence=HIGH,
            )

    def _compare_different_args(
        self,
        found_argument_names: set[str],
        message_id: str,
        not_needed_names: set[str],
        expected_argument_names: set[str],
        warning_node: nodes.NodeNG,
    ) -> None:
        """Compare the found argument names with the expected ones and
        generate a message if there are extra arguments found.

        :param found_argument_names: argument names found in the docstring

        :param message_id: pylint message id

        :param not_needed_names: names that may be omitted

        :param expected_argument_names: Expected argument names

        :param warning_node: The node to be analyzed
        """
        # Handle variadic and keyword args without asterisks
        modified_expected_argument_names: set[str] = set()
        for name in expected_argument_names:
            if name.replace("*", "") in found_argument_names:
                modified_expected_argument_names.add(name.replace("*", ""))
            else:
                modified_expected_argument_names.add(name)

        differing_argument_names = (
            (modified_expected_argument_names ^ found_argument_names)
            - not_needed_names
            - expected_argument_names
        )

        if differing_argument_names:
            self.add_message(
                message_id,
                args=(", ".join(sorted(differing_argument_names)),),
                node=warning_node,
                confidence=HIGH,
            )

    def _compare_ignored_args(  # pylint: disable=useless-param-doc
        self,
        found_argument_names: set[str],
        message_id: str,
        ignored_argument_names: set[str],
        warning_node: nodes.NodeNG,
    ) -> None:
        """Compare the found argument names with the ignored ones and
        generate a message if there are ignored arguments found.

        :param found_argument_names: argument names found in the docstring
        :param message_id: pylint message id
        :param ignored_argument_names: Expected argument names
        :param warning_node: The node to be analyzed
        """
        existing_ignored_argument_names = ignored_argument_names & found_argument_names

        if existing_ignored_argument_names:
            self.add_message(
                message_id,
                args=(", ".join(sorted(existing_ignored_argument_names)),),
                node=warning_node,
                confidence=HIGH,
            )

    def check_arguments_in_docstring(
        self,
        doc: Docstring,
        arguments_node: astroid.Arguments,
        warning_node: astroid.NodeNG,
        accept_no_param_doc: bool | None = None,
    ) -> None:
        """Check that all parameters are consistent with the parameters mentioned
        in the parameter documentation (e.g. the Sphinx tags 'param' and 'type').

        * Undocumented parameters except 'self' are noticed.
        * Undocumented parameter types except for 'self' and the ``*<args>``
          and ``**<kwargs>`` parameters are noticed.
        * Parameters mentioned in the parameter documentation that don't or no
          longer exist in the function parameter list are noticed.
        * If the text "For the parameters, see" or "For the other parameters,
          see" (ignoring additional white-space) is mentioned in the docstring,
          missing parameter documentation is tolerated.
        * If there's no Sphinx style, Google style or NumPy style parameter
          documentation at all, i.e. ``:param`` is never mentioned etc., the
          checker assumes that the parameters are documented in another format
          and the absence is tolerated.

        :param doc: Docstring for the function, method or class.
        :type doc: :class:`Docstring`

        :param arguments_node: Arguments node for the function, method or
            class constructor.
        :type arguments_node: :class:`astroid.scoped_nodes.Arguments`

        :param warning_node: The node to assign the warnings to
        :type warning_node: :class:`astroid.scoped_nodes.Node`

        :param accept_no_param_doc: Whether to allow no parameters to be
            documented. If None then this value is read from the configuration.
        :type accept_no_param_doc: bool or None
        """
        # Tolerate missing param or type declarations if there is a link to
        # another method carrying the same name.
        if not doc.doc:
            return

        if accept_no_param_doc is None:
            accept_no_param_doc = self.linter.config.accept_no_param_doc
        tolerate_missing_params = doc.params_documented_elsewhere()

        # Collect the function arguments.
        expected_argument_names = {arg.name for arg in arguments_node.args}
        expected_argument_names.update(
            a.name for a in arguments_node.posonlyargs + arguments_node.kwonlyargs
        )
        not_needed_type_in_docstring = self.not_needed_param_in_docstring.copy()

        expected_but_ignored_argument_names = set()
        ignored_argument_names = self.linter.config.ignored_argument_names
        if ignored_argument_names:
            expected_but_ignored_argument_names = {
                arg
                for arg in expected_argument_names
                if ignored_argument_names.match(arg)
            }

        if arguments_node.vararg is not None:
            expected_argument_names.add(f"*{arguments_node.vararg}")
            not_needed_type_in_docstring.add(f"*{arguments_node.vararg}")
        if arguments_node.kwarg is not None:
            expected_argument_names.add(f"**{arguments_node.kwarg}")
            not_needed_type_in_docstring.add(f"**{arguments_node.kwarg}")
        params_with_doc, params_with_type = doc.match_param_docs()
        # Tolerate no parameter documentation at all.
        if not params_with_doc and not params_with_type and accept_no_param_doc:
            tolerate_missing_params = True

        # This is before the update of params_with_type because this must check only
        # the type documented in a docstring, not the one using pep484
        # See #4117 and #4593
        self._compare_ignored_args(
            params_with_type,
            "useless-type-doc",
            expected_but_ignored_argument_names,
            warning_node,
        )
        params_with_type |= utils.args_with_annotation(arguments_node)

        if not tolerate_missing_params:
            missing_param_doc = (expected_argument_names - params_with_doc) - (
                self.not_needed_param_in_docstring | expected_but_ignored_argument_names
            )
            missing_type_doc = (expected_argument_names - params_with_type) - (
                not_needed_type_in_docstring | expected_but_ignored_argument_names
            )
            if (
                missing_param_doc == expected_argument_names == missing_type_doc
                and len(expected_argument_names) != 0
            ):
                self.add_message(
                    "missing-any-param-doc",
                    args=(warning_node.name,),
                    node=warning_node,
                    confidence=HIGH,
                )
            else:
                self._compare_missing_args(
                    params_with_doc,
                    "missing-param-doc",
                    self.not_needed_param_in_docstring
                    | expected_but_ignored_argument_names,
                    expected_argument_names,
                    warning_node,
                )
                self._compare_missing_args(
                    params_with_type,
                    "missing-type-doc",
                    not_needed_type_in_docstring | expected_but_ignored_argument_names,
                    expected_argument_names,
                    warning_node,
                )

        self._compare_different_args(
            params_with_doc,
            "differing-param-doc",
            self.not_needed_param_in_docstring,
            expected_argument_names,
            warning_node,
        )
        self._compare_different_args(
            params_with_type,
            "differing-type-doc",
            not_needed_type_in_docstring,
            expected_argument_names,
            warning_node,
        )
        self._compare_ignored_args(
            params_with_doc,
            "useless-param-doc",
            expected_but_ignored_argument_names,
            warning_node,
        )

    def check_single_constructor_params(
        self, class_doc: Docstring, init_doc: Docstring, class_node: nodes.ClassDef
    ) -> None:
        if class_doc.has_params() and init_doc.has_params():
            self.add_message(
                "multiple-constructor-doc",
                args=(class_node.name,),
                node=class_node,
                confidence=HIGH,
            )

    def _add_raise_message(
        self, missing_exceptions: set[str], node: nodes.FunctionDef
    ) -> None:
        """Adds a message on :param:`node` for the missing exception type.

        :param missing_exceptions: A list of missing exception types.
        :param node: The node show the message on.
        """
        if node.is_abstract():
            try:
                missing_exceptions.remove("NotImplementedError")
            except KeyError:
                pass
        if missing_exceptions:
            self.add_message(
                "missing-raises-doc",
                args=(", ".join(sorted(missing_exceptions)),),
                node=node,
                confidence=HIGH,
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(DocstringParameterChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/docparams.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import linecache
from typing import TYPE_CHECKING

from astroid import nodes

from pylint import checkers
from pylint.checkers.utils import only_required_for_messages
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class DocStringStyleChecker(checkers.BaseChecker):
    """Checks format of docstrings based on PEP 0257."""

    name = "docstyle"

    msgs = {
        "C0198": (
            'Bad docstring quotes in %s, expected """, given %s',
            "bad-docstring-quotes",
            "Used when a docstring does not have triple double quotes.",
        ),
        "C0199": (
            "First line empty in %s docstring",
            "docstring-first-line-empty",
            "Used when a blank line is found at the beginning of a docstring.",
        ),
    }

    @only_required_for_messages("docstring-first-line-empty", "bad-docstring-quotes")
    def visit_module(self, node: nodes.Module) -> None:
        self._check_docstring("module", node)

    def visit_classdef(self, node: nodes.ClassDef) -> None:
        self._check_docstring("class", node)

    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        ftype = "method" if node.is_method() else "function"
        self._check_docstring(ftype, node)

    visit_asyncfunctiondef = visit_functiondef

    def _check_docstring(
        self, node_type: str, node: nodes.Module | nodes.ClassDef | nodes.FunctionDef
    ) -> None:
        docstring = node.doc_node.value if node.doc_node else None
        if docstring and docstring[0] == "\n":
            self.add_message(
                "docstring-first-line-empty",
                node=node,
                args=(node_type,),
                confidence=HIGH,
            )

        # Use "linecache", instead of node.as_string(), because the latter
        # looses the original form of the docstrings.

        if docstring:
            lineno = node.fromlineno + 1
            line = linecache.getline(node.root().file, lineno).lstrip()
            if line and line.find('"""') == 0:
                return
            if line and "'''" in line:
                quotes = "'''"
            elif line and line[0] == '"':
                quotes = '"'
            elif line and line[0] == "'":
                quotes = "'"
            else:
                quotes = ""
            if quotes:
                self.add_message(
                    "bad-docstring-quotes",
                    node=node,
                    args=(node_type, quotes),
                    confidence=HIGH,
                )


def register(linter: PyLinter) -> None:
    linter.register_checker(DocStringStyleChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/docstyle.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.constants import DUNDER_METHODS, DUNDER_PROPERTIES, EXTRA_DUNDER_METHODS
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class DunderChecker(BaseChecker):
    """Checks related to dunder methods."""

    name = "dunder"
    priority = -1
    msgs = {
        "W3201": (
            "Bad or misspelled dunder method name %s.",
            "bad-dunder-name",
            "Used when a dunder method is misspelled or defined with a name "
            "not within the predefined list of dunder names.",
        ),
    }
    options = (
        (
            "good-dunder-names",
            {
                "default": [],
                "type": "csv",
                "metavar": "<comma-separated names>",
                "help": "Good dunder names which should always be accepted.",
            },
        ),
    )

    def open(self) -> None:
        self._dunder_methods = (
            EXTRA_DUNDER_METHODS
            + DUNDER_PROPERTIES
            + self.linter.config.good_dunder_names
        )
        for since_vers, dunder_methods in DUNDER_METHODS.items():
            if since_vers <= self.linter.config.py_version:
                self._dunder_methods.extend(list(dunder_methods.keys()))

    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        """Check if known dunder method is misspelled or dunder name is not one
        of the pre-defined names.
        """
        # ignore module-level functions
        if not node.is_method():
            return

        # Detect something that could be a bad dunder method
        if (
            node.name.startswith("_")
            and node.name.endswith("_")
            and node.name not in self._dunder_methods
        ):
            self.add_message(
                "bad-dunder-name",
                node=node,
                args=(node.name),
                confidence=HIGH,
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(DunderChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/dunder.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseRawFileChecker

if TYPE_CHECKING:
    from pylint.lint import PyLinter


def is_line_commented(line: bytes) -> bool:
    """Checks if a `# symbol that is not part of a string was found in line."""

    comment_idx = line.find(b"#")
    if comment_idx == -1:
        return False
    if comment_part_of_string(line, comment_idx):
        return is_line_commented(line[:comment_idx] + line[comment_idx + 1 :])
    return True


def comment_part_of_string(line: bytes, comment_idx: int) -> bool:
    """Checks if the symbol at comment_idx is part of a string."""

    if (
        line[:comment_idx].count(b"'") % 2 == 1
        and line[comment_idx:].count(b"'") % 2 == 1
    ) or (
        line[:comment_idx].count(b'"') % 2 == 1
        and line[comment_idx:].count(b'"') % 2 == 1
    ):
        return True
    return False


class CommentChecker(BaseRawFileChecker):
    name = "empty-comment"
    msgs = {
        "R2044": (
            "Line with empty comment",
            "empty-comment",
            (
                "Used when a # symbol appears on a line not followed by an actual comment"
            ),
        )
    }
    options = ()

    def process_module(self, node: nodes.Module) -> None:
        with node.stream() as stream:
            for line_num, line in enumerate(stream):
                line = line.rstrip()
                if line.endswith(b"#"):
                    if not is_line_commented(line[:-1]):
                        self.add_message("empty-comment", line=line_num + 1)


def register(linter: PyLinter) -> None:
    linter.register_checker(CommentChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/empty_comment.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""This is the remnant of the python3 checker.

It was removed because the transition from python 2 to python3 is
behind us, but some checks are still useful in python3 after all.
See https://github.com/pylint-dev/pylint/issues/5025
"""

from astroid import nodes

from pylint import checkers, interfaces
from pylint.checkers import utils
from pylint.lint import PyLinter


class EqWithoutHash(checkers.BaseChecker):
    name = "eq-without-hash"

    msgs = {
        "W1641": (
            "Implementing __eq__ without also implementing __hash__",
            "eq-without-hash",
            "Used when a class implements __eq__ but not __hash__. Objects get "
            "None as their default __hash__ implementation if they also implement __eq__.",
        ),
    }

    @utils.only_required_for_messages("eq-without-hash")
    def visit_classdef(self, node: nodes.ClassDef) -> None:
        locals_and_methods = set(node.locals).union(x.name for x in node.mymethods())
        if "__eq__" in locals_and_methods and "__hash__" not in locals_and_methods:
            self.add_message("eq-without-hash", node=node, confidence=interfaces.HIGH)


def register(linter: PyLinter) -> None:
    linter.register_checker(EqWithoutHash(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/eq_without_hash.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Check for use of for loops that only check for a condition."""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import (
    assigned_bool,
    only_required_for_messages,
    returns_bool,
)
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


class ConsiderUsingAnyOrAllChecker(BaseChecker):
    name = "consider-using-any-or-all"
    msgs = {
        "C0501": (
            "`for` loop could be `%s`",
            "consider-using-any-or-all",
            "A for loop that checks for a condition and return a bool can be replaced with any or all.",
        )
    }

    @only_required_for_messages("consider-using-any-or-all")
    def visit_for(self, node: nodes.For) -> None:
        if len(node.body) != 1:  # Only If node with no Else
            return
        if not isinstance(node.body[0], nodes.If):
            return

        if_children = list(node.body[0].get_children())
        if any(isinstance(child, nodes.If) for child in if_children):
            # an if node within the if-children indicates an elif clause,
            # suggesting complex logic.
            return

        node_after_loop = node.next_sibling()

        if self._assigned_reassigned_returned(node, if_children, node_after_loop):
            final_return_bool = node_after_loop.value.name
            suggested_string = self._build_suggested_string(node, final_return_bool)
            self.add_message(
                "consider-using-any-or-all",
                node=node,
                args=suggested_string,
                confidence=HIGH,
            )
            return

        if self._if_statement_returns_bool(if_children, node_after_loop):
            final_return_bool = node_after_loop.value.value
            suggested_string = self._build_suggested_string(node, final_return_bool)
            self.add_message(
                "consider-using-any-or-all",
                node=node,
                args=suggested_string,
                confidence=HIGH,
            )
            return

    @staticmethod
    def _if_statement_returns_bool(
        if_children: list[nodes.NodeNG], node_after_loop: nodes.NodeNG
    ) -> bool:
        """Detect for-loop, if-statement, return pattern:

        Ex:
            def any_uneven(items):
                for item in items:
                    if not item % 2 == 0:
                        return True
                return False
        """
        if not len(if_children) == 2:
            # The If node has only a comparison and return
            return False
        if not returns_bool(if_children[1]):
            return False

        # Check for terminating boolean return right after the loop
        return returns_bool(node_after_loop)

    @staticmethod
    def _assigned_reassigned_returned(
        node: nodes.For, if_children: list[nodes.NodeNG], node_after_loop: nodes.NodeNG
    ) -> bool:
        """Detect boolean-assign, for-loop, re-assign, return pattern:

        Ex:
            def check_lines(lines, max_chars):
                long_line = False
                for line in lines:
                    if len(line) > max_chars:
                        long_line = True
                    # no elif / else statement
                return long_line
        """
        node_before_loop = node.previous_sibling()

        if not assigned_bool(node_before_loop):
            # node before loop isn't assigning to boolean
            return False

        assign_children = [x for x in if_children if isinstance(x, nodes.Assign)]
        if not assign_children:
            # if-nodes inside loop aren't assignments
            return False

        # We only care for the first assign node of the if-children. Otherwise it breaks the pattern.
        first_target = assign_children[0].targets[0]
        target_before_loop = node_before_loop.targets[0]

        if not (
            isinstance(first_target, nodes.AssignName)
            and isinstance(target_before_loop, nodes.AssignName)
        ):
            return False

        node_before_loop_name = node_before_loop.targets[0].name
        return (
            first_target.name == node_before_loop_name
            and isinstance(node_after_loop, nodes.Return)
            and isinstance(node_after_loop.value, nodes.Name)
            and node_after_loop.value.name == node_before_loop_name
        )

    @staticmethod
    def _build_suggested_string(node: nodes.For, final_return_bool: bool) -> str:
        """When a nodes.For node can be rewritten as an any/all statement, return a
        suggestion for that statement.

        'final_return_bool' is the boolean literal returned after the for loop if all
        conditions fail.
        """
        loop_var = node.target.as_string()
        loop_iter = node.iter.as_string()
        test_node = next(node.body[0].get_children())

        if isinstance(test_node, nodes.UnaryOp) and test_node.op == "not":
            # The condition is negated. Advance the node to the operand and modify the suggestion
            test_node = test_node.operand
            suggested_function = "all" if final_return_bool else "not all"
        else:
            suggested_function = "not any" if final_return_bool else "any"

        test = test_node.as_string()
        return f"{suggested_function}({test} for {loop_var} in {loop_iter})"


def register(linter: PyLinter) -> None:
    linter.register_checker(ConsiderUsingAnyOrAllChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/for_any_all.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Checks for magic values instead of literals."""

from __future__ import annotations

from re import match as regex_match
from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker, utils
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class MagicValueChecker(BaseChecker):
    """Checks for constants in comparisons."""

    name = "magic-value"
    msgs = {
        "R2004": (
            "Consider using a named constant or an enum instead of '%s'.",
            "magic-value-comparison",
            "Using named constants instead of magic values helps improve readability and maintainability of your"
            " code, try to avoid them in comparisons.",
        )
    }

    options = (
        (
            "valid-magic-values",
            {
                "default": (0, -1, 1, "", "__main__"),
                "type": "csv",
                "metavar": "<argument names>",
                "help": "List of valid magic values that `magic-value-compare` will not detect. "
                "Supports integers, floats, negative numbers, for empty string enter ``''``,"
                " for backslash values just use one backslash e.g \\n.",
            },
        ),
    )

    def __init__(self, linter: PyLinter) -> None:
        """Initialize checker instance."""
        super().__init__(linter=linter)
        self.valid_magic_vals: tuple[float | str, ...] = ()

    def open(self) -> None:
        # Extra manipulation is needed in case of using external configuration like an rcfile
        if self._magic_vals_ext_configured():
            self.valid_magic_vals = tuple(
                self._parse_rcfile_magic_numbers(value)
                for value in self.linter.config.valid_magic_values
            )
        else:
            self.valid_magic_vals = self.linter.config.valid_magic_values

    def _magic_vals_ext_configured(self) -> bool:
        return not isinstance(self.linter.config.valid_magic_values, tuple)

    def _check_constants_comparison(self, node: nodes.Compare) -> None:
        """
        Magic values in any side of the comparison should be avoided,
        Detects comparisons that `comparison-of-constants` core checker cannot detect.
        """
        const_operands = []
        LEFT_OPERAND = 0
        RIGHT_OPERAND = 1

        left_operand = node.left
        const_operands.append(isinstance(left_operand, nodes.Const))

        right_operand = node.ops[0][1]
        const_operands.append(isinstance(right_operand, nodes.Const))

        if all(const_operands):
            # `comparison-of-constants` avoided
            return

        operand_value = None
        if const_operands[LEFT_OPERAND] and self._is_magic_value(left_operand):
            operand_value = left_operand.value
        elif const_operands[RIGHT_OPERAND] and self._is_magic_value(right_operand):
            operand_value = right_operand.value
        if operand_value is not None:
            self.add_message(
                "magic-value-comparison",
                node=node,
                args=(operand_value),
                confidence=HIGH,
            )

    def _is_magic_value(self, node: nodes.Const) -> bool:
        return (not utils.is_singleton_const(node)) and (
            node.value not in (self.valid_magic_vals)
        )

    @staticmethod
    def _parse_rcfile_magic_numbers(parsed_val: str) -> float | str:
        parsed_val = parsed_val.encode().decode("unicode_escape")

        if parsed_val.startswith("'") and parsed_val.endswith("'"):
            return parsed_val[1:-1]

        is_number = regex_match(r"[-+]?\d+(\.0*)?$", parsed_val)
        return float(parsed_val) if is_number else parsed_val

    @utils.only_required_for_messages("magic-comparison")
    def visit_compare(self, node: nodes.Compare) -> None:
        self._check_constants_comparison(node)


def register(linter: PyLinter) -> None:
    linter.register_checker(MagicValueChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/magic_value.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Module to add McCabe checker class for pylint."""

from __future__ import annotations

from collections.abc import Sequence
from typing import TYPE_CHECKING, Any, TypeVar, Union

from astroid import nodes
from mccabe import PathGraph as Mccabe_PathGraph
from mccabe import PathGraphingAstVisitor as Mccabe_PathGraphingAstVisitor

from pylint import checkers
from pylint.checkers.utils import only_required_for_messages
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint import PyLinter

_StatementNodes = Union[
    nodes.Assert,
    nodes.Assign,
    nodes.AugAssign,
    nodes.Delete,
    nodes.Raise,
    nodes.Yield,
    nodes.Import,
    nodes.Call,
    nodes.Subscript,
    nodes.Pass,
    nodes.Continue,
    nodes.Break,
    nodes.Global,
    nodes.Return,
    nodes.Expr,
    nodes.Await,
]

_SubGraphNodes = Union[nodes.If, nodes.Try, nodes.For, nodes.While]
_AppendableNodeT = TypeVar(
    "_AppendableNodeT", bound=Union[_StatementNodes, nodes.While, nodes.FunctionDef]
)


class PathGraph(Mccabe_PathGraph):  # type: ignore[misc]
    def __init__(self, node: _SubGraphNodes | nodes.FunctionDef):
        super().__init__(name="", entity="", lineno=1)
        self.root = node


class PathGraphingAstVisitor(Mccabe_PathGraphingAstVisitor):  # type: ignore[misc]
    def __init__(self) -> None:
        super().__init__()
        self._bottom_counter = 0
        self.graph: PathGraph | None = None

    def default(self, node: nodes.NodeNG, *args: Any) -> None:
        for child in node.get_children():
            self.dispatch(child, *args)

    def dispatch(self, node: nodes.NodeNG, *args: Any) -> Any:
        self.node = node
        klass = node.__class__
        meth = self._cache.get(klass)
        if meth is None:
            class_name = klass.__name__
            meth = getattr(self.visitor, "visit" + class_name, self.default)
            self._cache[klass] = meth
        return meth(node, *args)

    def visitFunctionDef(self, node: nodes.FunctionDef) -> None:
        if self.graph is not None:
            # closure
            pathnode = self._append_node(node)
            self.tail = pathnode
            self.dispatch_list(node.body)
            bottom = f"{self._bottom_counter}"
            self._bottom_counter += 1
            self.graph.connect(self.tail, bottom)
            self.graph.connect(node, bottom)
            self.tail = bottom
        else:
            self.graph = PathGraph(node)
            self.tail = node
            self.dispatch_list(node.body)
            self.graphs[f"{self.classname}{node.name}"] = self.graph
            self.reset()

    visitAsyncFunctionDef = visitFunctionDef

    def visitSimpleStatement(self, node: _StatementNodes) -> None:
        self._append_node(node)

    visitAssert = (
        visitAssign
    ) = (
        visitAugAssign
    ) = (
        visitDelete
    ) = (
        visitRaise
    ) = (
        visitYield
    ) = (
        visitImport
    ) = (
        visitCall
    ) = (
        visitSubscript
    ) = (
        visitPass
    ) = (
        visitContinue
    ) = (
        visitBreak
    ) = visitGlobal = visitReturn = visitExpr = visitAwait = visitSimpleStatement

    def visitWith(self, node: nodes.With) -> None:
        self._append_node(node)
        self.dispatch_list(node.body)

    visitAsyncWith = visitWith

    def _append_node(self, node: _AppendableNodeT) -> _AppendableNodeT | None:
        if not self.tail or not self.graph:
            return None
        self.graph.connect(self.tail, node)
        self.tail = node
        return node

    def _subgraph(
        self,
        node: _SubGraphNodes,
        name: str,
        extra_blocks: Sequence[nodes.ExceptHandler] = (),
    ) -> None:
        """Create the subgraphs representing any `if` and `for` statements."""
        if self.graph is None:
            # global loop
            self.graph = PathGraph(node)
            self._subgraph_parse(node, node, extra_blocks)
            self.graphs[f"{self.classname}{name}"] = self.graph
            self.reset()
        else:
            self._append_node(node)
            self._subgraph_parse(node, node, extra_blocks)

    def _subgraph_parse(
        self,
        node: _SubGraphNodes,
        pathnode: _SubGraphNodes,
        extra_blocks: Sequence[nodes.ExceptHandler],
    ) -> None:
        """Parse the body and any `else` block of `if` and `for` statements."""
        loose_ends = []
        self.tail = node
        self.dispatch_list(node.body)
        loose_ends.append(self.tail)
        for extra in extra_blocks:
            self.tail = node
            self.dispatch_list(extra.body)
            loose_ends.append(self.tail)
        if node.orelse:
            self.tail = node
            self.dispatch_list(node.orelse)
            loose_ends.append(self.tail)
        else:
            loose_ends.append(node)
        if node and self.graph:
            bottom = f"{self._bottom_counter}"
            self._bottom_counter += 1
            for end in loose_ends:
                self.graph.connect(end, bottom)
            self.tail = bottom


class McCabeMethodChecker(checkers.BaseChecker):
    """Checks McCabe complexity cyclomatic threshold in methods and functions
    to validate a too complex code.
    """

    name = "design"

    msgs = {
        "R1260": (
            "%s is too complex. The McCabe rating is %d",
            "too-complex",
            "Used when a method or function is too complex based on "
            "McCabe Complexity Cyclomatic",
        )
    }
    options = (
        (
            "max-complexity",
            {
                "default": 10,
                "type": "int",
                "metavar": "<int>",
                "help": "McCabe complexity cyclomatic threshold",
            },
        ),
    )

    @only_required_for_messages("too-complex")
    def visit_module(self, node: nodes.Module) -> None:
        """Visit an astroid.Module node to check too complex rating and
        add message if is greater than max_complexity stored from options.
        """
        visitor = PathGraphingAstVisitor()
        for child in node.body:
            visitor.preorder(child, visitor)
        for graph in visitor.graphs.values():
            complexity = graph.complexity()
            node = graph.root
            if hasattr(node, "name"):
                node_name = f"'{node.name}'"
            else:
                node_name = f"This '{node.__class__.__name__.lower()}'"
            if complexity <= self.linter.config.max_complexity:
                continue
            self.add_message(
                "too-complex", node=node, confidence=HIGH, args=(node_name, complexity)
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(McCabeMethodChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/mccabe.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import (
    PYMETHODS,
    decorated_with_property,
    is_overload_stub,
    is_protocol_class,
    overrides_a_method,
)
from pylint.interfaces import INFERENCE

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


class NoSelfUseChecker(BaseChecker):
    name = "no_self_use"
    msgs = {
        "R6301": (
            "Method could be a function",
            "no-self-use",
            "Used when a method doesn't use its bound instance, and so could "
            "be written as a function.",
            {"old_names": [("R0201", "old-no-self-use")]},
        ),
    }

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._first_attrs: list[str | None] = []
        self._meth_could_be_func: bool | None = None

    def visit_name(self, node: nodes.Name) -> None:
        """Check if the name handle an access to a class member
        if so, register it.
        """
        if self._first_attrs and (
            node.name == self._first_attrs[-1] or not self._first_attrs[-1]
        ):
            self._meth_could_be_func = False

    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        if not node.is_method():
            return
        self._meth_could_be_func = True
        self._check_first_arg_for_type(node)

    visit_asyncfunctiondef = visit_functiondef

    def _check_first_arg_for_type(self, node: nodes.FunctionDef) -> None:
        """Check the name of first argument."""
        # pylint: disable=duplicate-code
        if node.args.posonlyargs:
            first_arg = node.args.posonlyargs[0].name
        elif node.args.args:
            first_arg = node.argnames()[0]
        else:
            first_arg = None
        self._first_attrs.append(first_arg)
        # static method
        if node.type == "staticmethod":
            self._first_attrs[-1] = None

    def leave_functiondef(self, node: nodes.FunctionDef) -> None:
        """On method node, check if this method couldn't be a function.

        ignore class, static and abstract methods, initializer,
        methods overridden from a parent class.
        """
        if node.is_method():
            first = self._first_attrs.pop()
            if first is None:
                return
            class_node = node.parent.frame()
            if (
                self._meth_could_be_func
                and node.type == "method"
                and node.name not in PYMETHODS
                and not (
                    node.is_abstract()
                    or overrides_a_method(class_node, node.name)
                    or decorated_with_property(node)
                    or _has_bare_super_call(node)
                    or is_protocol_class(class_node)
                    or is_overload_stub(node)
                )
            ):
                self.add_message("no-self-use", node=node, confidence=INFERENCE)

    leave_asyncfunctiondef = leave_functiondef


def _has_bare_super_call(fundef_node: nodes.FunctionDef) -> bool:
    for call in fundef_node.nodes_of_class(nodes.Call):
        func = call.func
        if isinstance(func, nodes.Name) and func.name == "super" and not call.args:
            return True
    return False


def register(linter: PyLinter) -> None:
    linter.register_checker(NoSelfUseChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/no_self_use.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Looks for overlapping exceptions."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

import astroid
from astroid import nodes, util

from pylint import checkers
from pylint.checkers import utils
from pylint.checkers.exceptions import _annotated_unpack_infer

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class OverlappingExceptionsChecker(checkers.BaseChecker):
    """Checks for two or more exceptions in the same exception handler
    clause that are identical or parts of the same inheritance hierarchy.

    (i.e. overlapping).
    """

    name = "overlap-except"
    msgs = {
        "W0714": (
            "Overlapping exceptions (%s)",
            "overlapping-except",
            "Used when exceptions in handler overlap or are identical",
        )
    }
    options = ()

    @utils.only_required_for_messages("overlapping-except")
    def visit_try(self, node: nodes.Try) -> None:
        """Check for empty except."""
        for handler in node.handlers:
            if handler.type is None:
                continue
            if isinstance(handler.type, astroid.BoolOp):
                continue
            try:
                excs = list(_annotated_unpack_infer(handler.type))
            except astroid.InferenceError:
                continue

            handled_in_clause: list[tuple[Any, Any]] = []
            for part, exc in excs:
                if isinstance(exc, util.UninferableBase):
                    continue
                if isinstance(exc, astroid.Instance) and utils.inherit_from_std_ex(exc):
                    exc = exc._proxied

                if not isinstance(exc, astroid.ClassDef):
                    continue

                exc_ancestors = [
                    anc for anc in exc.ancestors() if isinstance(anc, astroid.ClassDef)
                ]

                for prev_part, prev_exc in handled_in_clause:
                    prev_exc_ancestors = [
                        anc
                        for anc in prev_exc.ancestors()
                        if isinstance(anc, astroid.ClassDef)
                    ]
                    if exc == prev_exc:
                        self.add_message(
                            "overlapping-except",
                            node=handler.type,
                            args=f"{prev_part.as_string()} and {part.as_string()} are the same",
                        )
                    elif prev_exc in exc_ancestors or exc in prev_exc_ancestors:
                        ancestor = part if exc in prev_exc_ancestors else prev_part
                        descendant = part if prev_exc in exc_ancestors else prev_part
                        self.add_message(
                            "overlapping-except",
                            node=handler.type,
                            args=f"{ancestor.as_string()} is an ancestor class of {descendant.as_string()}",
                        )
                handled_in_clause += [(part, exc)]


def register(linter: PyLinter) -> None:
    linter.register_checker(OverlappingExceptionsChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/overlapping_exceptions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Check for imports on private external modules and names."""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker, utils
from pylint.interfaces import HIGH

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


class PrivateImportChecker(BaseChecker):
    name = "import-private-name"
    msgs = {
        "C2701": (
            "Imported private %s (%s)",
            "import-private-name",
            "Used when a private module or object prefixed with _ is imported. "
            "PEP8 guidance on Naming Conventions states that public attributes with "
            "leading underscores should be considered private.",
        ),
    }

    def __init__(self, linter: PyLinter) -> None:
        BaseChecker.__init__(self, linter)

        # A mapping of private names used as a type annotation to whether it is an acceptable import
        self.all_used_type_annotations: dict[str, bool] = {}
        self.populated_annotations = False

    @utils.only_required_for_messages("import-private-name")
    def visit_import(self, node: nodes.Import) -> None:
        if utils.in_type_checking_block(node):
            return
        names = [name[0] for name in node.names]
        private_names = self._get_private_imports(names)
        private_names = self._get_type_annotation_names(node, private_names)
        if private_names:
            imported_identifier = "modules" if len(private_names) > 1 else "module"
            private_name_string = ", ".join(private_names)
            self.add_message(
                "import-private-name",
                node=node,
                args=(imported_identifier, private_name_string),
                confidence=HIGH,
            )

    @utils.only_required_for_messages("import-private-name")
    def visit_importfrom(self, node: nodes.ImportFrom) -> None:
        if utils.in_type_checking_block(node):
            return
        # Only check imported names if the module is external
        if self.same_root_dir(node, node.modname):
            return

        names = [n[0] for n in node.names]

        # Check the imported objects first. If they are all valid type annotations,
        # the package can be private
        private_names = self._get_type_annotation_names(node, names)
        if not private_names:
            return

        # There are invalid imported objects, so check the name of the package
        private_module_imports = self._get_private_imports([node.modname])
        private_module_imports = self._get_type_annotation_names(
            node, private_module_imports
        )
        if private_module_imports:
            self.add_message(
                "import-private-name",
                node=node,
                args=("module", private_module_imports[0]),
                confidence=HIGH,
            )
            return  # Do not emit messages on the objects if the package is private

        private_names = self._get_private_imports(private_names)

        if private_names:
            imported_identifier = "objects" if len(private_names) > 1 else "object"
            private_name_string = ", ".join(private_names)
            self.add_message(
                "import-private-name",
                node=node,
                args=(imported_identifier, private_name_string),
                confidence=HIGH,
            )

    def _get_private_imports(self, names: list[str]) -> list[str]:
        """Returns the private names from input names by a simple string check."""
        return [name for name in names if self._name_is_private(name)]

    @staticmethod
    def _name_is_private(name: str) -> bool:
        """Returns true if the name exists, starts with `_`, and if len(name) > 4
        it is not a dunder, i.e. it does not begin and end with two underscores.
        """
        return (
            bool(name)
            and name[0] == "_"
            and (len(name) <= 4 or name[1] != "_" or name[-2:] != "__")
        )

    def _get_type_annotation_names(
        self, node: nodes.Import | nodes.ImportFrom, names: list[str]
    ) -> list[str]:
        """Removes from names any names that are used as type annotations with no other
        illegal usages.
        """
        if names and not self.populated_annotations:
            self._populate_type_annotations(node.root(), self.all_used_type_annotations)
            self.populated_annotations = True

        return [
            n
            for n in names
            if n not in self.all_used_type_annotations
            or (
                n in self.all_used_type_annotations
                and not self.all_used_type_annotations[n]
            )
        ]

    def _populate_type_annotations(
        self, node: nodes.LocalsDictNodeNG, all_used_type_annotations: dict[str, bool]
    ) -> None:
        """Adds to `all_used_type_annotations` all names ever used as a type annotation
        in the node's (nested) scopes and whether they are only used as annotation.
        """
        for name in node.locals:
            # If we find a private type annotation, make sure we do not mask illegal usages
            private_name = None
            # All the assignments using this variable that we might have to check for
            # illegal usages later
            name_assignments = []
            for usage_node in node.locals[name]:
                if isinstance(usage_node, nodes.AssignName) and isinstance(
                    usage_node.parent, (nodes.AnnAssign, nodes.Assign)
                ):
                    assign_parent = usage_node.parent
                    if isinstance(assign_parent, nodes.AnnAssign):
                        name_assignments.append(assign_parent)
                        private_name = self._populate_type_annotations_annotation(
                            usage_node.parent.annotation, all_used_type_annotations
                        )
                    elif isinstance(assign_parent, nodes.Assign):
                        name_assignments.append(assign_parent)

                if isinstance(usage_node, nodes.FunctionDef):
                    self._populate_type_annotations_function(
                        usage_node, all_used_type_annotations
                    )
                if isinstance(usage_node, nodes.LocalsDictNodeNG):
                    self._populate_type_annotations(
                        usage_node, all_used_type_annotations
                    )
            if private_name is not None:
                # Found a new private annotation, make sure we are not accessing it elsewhere
                all_used_type_annotations[
                    private_name
                ] = self._assignments_call_private_name(name_assignments, private_name)

    def _populate_type_annotations_function(
        self, node: nodes.FunctionDef, all_used_type_annotations: dict[str, bool]
    ) -> None:
        """Adds all names used as type annotation in the arguments and return type of
        the function node into the dict `all_used_type_annotations`.
        """
        if node.args and node.args.annotations:
            for annotation in node.args.annotations:
                self._populate_type_annotations_annotation(
                    annotation, all_used_type_annotations
                )
        if node.returns:
            self._populate_type_annotations_annotation(
                node.returns, all_used_type_annotations
            )

    def _populate_type_annotations_annotation(
        self,
        node: nodes.Attribute | nodes.Subscript | nodes.Name | None,
        all_used_type_annotations: dict[str, bool],
    ) -> str | None:
        """Handles the possibility of an annotation either being a Name, i.e. just type,
        or a Subscript e.g. `Optional[type]` or an Attribute, e.g. `pylint.lint.linter`.
        """
        if isinstance(node, nodes.Name) and node.name not in all_used_type_annotations:
            all_used_type_annotations[node.name] = True
            return node.name  # type: ignore[no-any-return]
        if isinstance(node, nodes.Subscript):  # e.g. Optional[List[str]]
            # slice is the next nested type
            self._populate_type_annotations_annotation(
                node.slice, all_used_type_annotations
            )
            # value is the current type name: could be a Name or Attribute
            return self._populate_type_annotations_annotation(
                node.value, all_used_type_annotations
            )
        if isinstance(node, nodes.Attribute):
            # An attribute is a type like `pylint.lint.pylinter`. node.expr is the next level
            # up, could be another attribute
            return self._populate_type_annotations_annotation(
                node.expr, all_used_type_annotations
            )
        return None

    @staticmethod
    def _assignments_call_private_name(
        assignments: list[nodes.AnnAssign | nodes.Assign], private_name: str
    ) -> bool:
        """Returns True if no assignments involve accessing `private_name`."""
        if all(not assignment.value for assignment in assignments):
            # Variable annotated but unassigned is not allowed because there may be
            # possible illegal access elsewhere
            return False
        for assignment in assignments:
            current_attribute = None
            if isinstance(assignment.value, nodes.Call):
                current_attribute = assignment.value.func
            elif isinstance(assignment.value, nodes.Attribute):
                current_attribute = assignment.value
            elif isinstance(assignment.value, nodes.Name):
                current_attribute = assignment.value.name
            if not current_attribute:
                continue
            while isinstance(current_attribute, (nodes.Attribute, nodes.Call)):
                if isinstance(current_attribute, nodes.Call):
                    current_attribute = current_attribute.func
                if not isinstance(current_attribute, nodes.Name):
                    current_attribute = current_attribute.expr
            if (
                isinstance(current_attribute, nodes.Name)
                and current_attribute.name == private_name
            ):
                return False
        return True

    @staticmethod
    def same_root_dir(
        node: nodes.Import | nodes.ImportFrom, import_mod_name: str
    ) -> bool:
        """Does the node's file's path contain the base name of `import_mod_name`?"""
        if not import_mod_name:  # from . import ...
            return True
        if node.level:  # from .foo import ..., from ..bar import ...
            return True

        base_import_package = import_mod_name.split(".")[0]

        return base_import_package in Path(node.root().file).parent.parts


def register(linter: PyLinter) -> None:
    linter.register_checker(PrivateImportChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/private_import.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Optional checker to warn when loop variables are overwritten in the loop's body."""

from __future__ import annotations

from astroid import nodes

from pylint import checkers
from pylint.checkers import utils
from pylint.interfaces import HIGH
from pylint.lint import PyLinter


class RedefinedLoopNameChecker(checkers.BaseChecker):
    name = "redefined-loop-name"

    msgs = {
        "W2901": (
            "Redefining %r from loop (line %s)",
            "redefined-loop-name",
            "Used when a loop variable is overwritten in the loop body.",
        ),
    }

    def __init__(self, linter: PyLinter) -> None:
        super().__init__(linter)
        self._loop_variables: list[
            tuple[nodes.For, list[str], nodes.LocalsDictNodeNG]
        ] = []

    @utils.only_required_for_messages("redefined-loop-name")
    def visit_assignname(self, node: nodes.AssignName) -> None:
        assign_type = node.assign_type()
        if not isinstance(assign_type, (nodes.Assign, nodes.AugAssign)):
            return
        node_scope = node.scope()
        for outer_for, outer_variables, outer_for_scope in self._loop_variables:
            if node_scope is not outer_for_scope:
                continue
            if node.name in outer_variables and not utils.in_for_else_branch(
                outer_for, node
            ):
                self.add_message(
                    "redefined-loop-name",
                    args=(node.name, outer_for.fromlineno),
                    node=node,
                    confidence=HIGH,
                )
                break

    @utils.only_required_for_messages("redefined-loop-name")
    def visit_for(self, node: nodes.For) -> None:
        assigned_to = [a.name for a in node.target.nodes_of_class(nodes.AssignName)]
        # Only check variables that are used
        assigned_to = [
            var
            for var in assigned_to
            if not self.linter.config.dummy_variables_rgx.match(var)
        ]

        node_scope = node.scope()
        for variable in assigned_to:
            for outer_for, outer_variables, outer_for_scope in self._loop_variables:
                if node_scope is not outer_for_scope:
                    continue
                if variable in outer_variables and not utils.in_for_else_branch(
                    outer_for, node
                ):
                    self.add_message(
                        "redefined-loop-name",
                        args=(variable, outer_for.fromlineno),
                        node=node,
                        confidence=HIGH,
                    )
                    break

        self._loop_variables.append((node, assigned_to, node.scope()))

    @utils.only_required_for_messages("redefined-loop-name")
    def leave_for(self, node: nodes.For) -> None:  # pylint: disable=unused-argument
        self._loop_variables.pop()


def register(linter: PyLinter) -> None:
    linter.register_checker(RedefinedLoopNameChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/redefined_loop_name.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import is_none, node_type, only_required_for_messages

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class MultipleTypesChecker(BaseChecker):
    """Checks for variable type redefinition (NoneType excepted).

    At a function, method, class or module scope

    This rule could be improved:

    - Currently, if an attribute is set to different types in 2 methods of a
      same class, it won't be detected (see functional test)
    - One could improve the support for inference on assignment with tuples,
      ifexpr, etc. Also, it would be great to have support for inference on
      str.split()
    """

    name = "multiple_types"
    msgs = {
        "R0204": (
            "Redefinition of %s type from %s to %s",
            "redefined-variable-type",
            "Used when the type of a variable changes inside a "
            "method or a function.",
        )
    }

    def visit_classdef(self, _: nodes.ClassDef) -> None:
        self._assigns.append({})

    @only_required_for_messages("redefined-variable-type")
    def leave_classdef(self, _: nodes.ClassDef) -> None:
        self._check_and_add_messages()

    visit_functiondef = visit_asyncfunctiondef = visit_classdef
    leave_functiondef = leave_asyncfunctiondef = leave_module = leave_classdef

    def visit_module(self, _: nodes.Module) -> None:
        self._assigns: list[dict[str, list[tuple[nodes.Assign, str]]]] = [{}]

    def _check_and_add_messages(self) -> None:
        assigns = self._assigns.pop()
        for name, args in assigns.items():
            if len(args) <= 1:
                continue
            orig_node, orig_type = args[0]
            # Check if there is a type in the following nodes that would be
            # different from orig_type.
            for redef_node, redef_type in args[1:]:
                if redef_type == orig_type:
                    continue
                # if a variable is defined to several types in an if node,
                # this is not actually redefining.
                orig_parent = orig_node.parent
                redef_parent = redef_node.parent
                if isinstance(orig_parent, nodes.If):
                    if orig_parent == redef_parent:
                        if (
                            redef_node in orig_parent.orelse
                            and orig_node not in orig_parent.orelse
                        ):
                            orig_node, orig_type = redef_node, redef_type
                            continue
                    elif isinstance(
                        redef_parent, nodes.If
                    ) and redef_parent in orig_parent.nodes_of_class(nodes.If):
                        orig_node, orig_type = redef_node, redef_type
                        continue
                orig_type = orig_type.replace("builtins.", "")
                redef_type = redef_type.replace("builtins.", "")
                self.add_message(
                    "redefined-variable-type",
                    node=redef_node,
                    args=(name, orig_type, redef_type),
                )
                break

    def visit_assign(self, node: nodes.Assign) -> None:
        # we don't handle multiple assignment nor slice assignment
        target = node.targets[0]
        if isinstance(target, (nodes.Tuple, nodes.Subscript)):
            return
        # ignore NoneType
        if is_none(node):
            return
        _type = node_type(node.value)
        if _type:
            self._assigns[-1].setdefault(target.as_string(), []).append(
                (node, _type.pytype())
            )


def register(linter: PyLinter) -> None:
    linter.register_checker(MultipleTypesChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/redefined_variable_type.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import only_required_for_messages

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class SetMembershipChecker(BaseChecker):
    name = "set_membership"
    msgs = {
        "R6201": (
            "Consider using set for membership test",
            "use-set-for-membership",
            "Membership tests are more efficient when performed on "
            "a lookup optimized datatype like ``sets``.",
        ),
    }

    def __init__(self, linter: PyLinter) -> None:
        """Initialize checker instance."""
        super().__init__(linter=linter)

    @only_required_for_messages("use-set-for-membership")
    def visit_compare(self, node: nodes.Compare) -> None:
        for op, comparator in node.ops:
            if op == "in":
                self._check_in_comparison(comparator)

    def _check_in_comparison(self, comparator: nodes.NodeNG) -> None:
        """Checks for membership comparisons with in-place container objects."""
        if not isinstance(comparator, nodes.BaseContainer) or isinstance(
            comparator, nodes.Set
        ):
            return

        # Heuristic - We need to be sure all items in set are hashable
        if all(isinstance(item, nodes.Const) for item in comparator.elts):
            self.add_message("use-set-for-membership", node=comparator)


def register(linter: PyLinter) -> None:
    linter.register_checker(SetMembershipChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/set_membership.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING, NamedTuple

import astroid.bases
from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import (
    in_type_checking_block,
    is_node_in_type_annotation_context,
    is_postponed_evaluation_enabled,
    only_required_for_messages,
    safe_infer,
)
from pylint.constants import TYPING_NORETURN
from pylint.interfaces import HIGH, INFERENCE

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class TypingAlias(NamedTuple):
    name: str
    name_collision: bool


DEPRECATED_TYPING_ALIASES: dict[str, TypingAlias] = {
    "typing.Tuple": TypingAlias("tuple", False),
    "typing.List": TypingAlias("list", False),
    "typing.Dict": TypingAlias("dict", False),
    "typing.Set": TypingAlias("set", False),
    "typing.FrozenSet": TypingAlias("frozenset", False),
    "typing.Type": TypingAlias("type", False),
    "typing.Deque": TypingAlias("collections.deque", True),
    "typing.DefaultDict": TypingAlias("collections.defaultdict", True),
    "typing.OrderedDict": TypingAlias("collections.OrderedDict", True),
    "typing.Counter": TypingAlias("collections.Counter", True),
    "typing.ChainMap": TypingAlias("collections.ChainMap", True),
    "typing.Awaitable": TypingAlias("collections.abc.Awaitable", True),
    "typing.Coroutine": TypingAlias("collections.abc.Coroutine", True),
    "typing.AsyncIterable": TypingAlias("collections.abc.AsyncIterable", True),
    "typing.AsyncIterator": TypingAlias("collections.abc.AsyncIterator", True),
    "typing.AsyncGenerator": TypingAlias("collections.abc.AsyncGenerator", True),
    "typing.Iterable": TypingAlias("collections.abc.Iterable", True),
    "typing.Iterator": TypingAlias("collections.abc.Iterator", True),
    "typing.Generator": TypingAlias("collections.abc.Generator", True),
    "typing.Reversible": TypingAlias("collections.abc.Reversible", True),
    "typing.Container": TypingAlias("collections.abc.Container", True),
    "typing.Collection": TypingAlias("collections.abc.Collection", True),
    "typing.Callable": TypingAlias("collections.abc.Callable", True),
    "typing.AbstractSet": TypingAlias("collections.abc.Set", False),
    "typing.MutableSet": TypingAlias("collections.abc.MutableSet", True),
    "typing.Mapping": TypingAlias("collections.abc.Mapping", True),
    "typing.MutableMapping": TypingAlias("collections.abc.MutableMapping", True),
    "typing.Sequence": TypingAlias("collections.abc.Sequence", True),
    "typing.MutableSequence": TypingAlias("collections.abc.MutableSequence", True),
    "typing.ByteString": TypingAlias("collections.abc.ByteString", True),
    "typing.MappingView": TypingAlias("collections.abc.MappingView", True),
    "typing.KeysView": TypingAlias("collections.abc.KeysView", True),
    "typing.ItemsView": TypingAlias("collections.abc.ItemsView", True),
    "typing.ValuesView": TypingAlias("collections.abc.ValuesView", True),
    "typing.ContextManager": TypingAlias("contextlib.AbstractContextManager", False),
    "typing.AsyncContextManager": TypingAlias(
        "contextlib.AbstractAsyncContextManager", False
    ),
    "typing.Pattern": TypingAlias("re.Pattern", True),
    "typing.Match": TypingAlias("re.Match", True),
    "typing.Hashable": TypingAlias("collections.abc.Hashable", True),
    "typing.Sized": TypingAlias("collections.abc.Sized", True),
}

ALIAS_NAMES = frozenset(key.split(".")[1] for key in DEPRECATED_TYPING_ALIASES)
UNION_NAMES = ("Optional", "Union")


class DeprecatedTypingAliasMsg(NamedTuple):
    node: nodes.Name | nodes.Attribute
    qname: str
    alias: str
    parent_subscript: bool = False


class TypingChecker(BaseChecker):
    """Find issue specifically related to type annotations."""

    name = "typing"
    msgs = {
        "W6001": (
            "'%s' is deprecated, use '%s' instead",
            "deprecated-typing-alias",
            "Emitted when a deprecated typing alias is used.",
        ),
        "R6002": (
            "'%s' will be deprecated with PY39, consider using '%s' instead%s",
            "consider-using-alias",
            "Only emitted if 'runtime-typing=no' and a deprecated "
            "typing alias is used in a type annotation context in "
            "Python 3.7 or 3.8.",
        ),
        "R6003": (
            "Consider using alternative Union syntax instead of '%s'%s",
            "consider-alternative-union-syntax",
            "Emitted when 'typing.Union' or 'typing.Optional' is used "
            "instead of the alternative Union syntax 'int | None'.",
        ),
        "E6004": (
            "'NoReturn' inside compound types is broken in 3.7.0 / 3.7.1",
            "broken-noreturn",
            "``typing.NoReturn`` inside compound types is broken in "
            "Python 3.7.0 and 3.7.1. If not dependent on runtime introspection, "
            "use string annotation instead. E.g. "
            "``Callable[..., 'NoReturn']``. https://bugs.python.org/issue34921",
        ),
        "E6005": (
            "'collections.abc.Callable' inside Optional and Union is broken in "
            "3.9.0 / 3.9.1 (use 'typing.Callable' instead)",
            "broken-collections-callable",
            "``collections.abc.Callable`` inside Optional and Union is broken in "
            "Python 3.9.0 and 3.9.1. Use ``typing.Callable`` for these cases instead. "
            "https://bugs.python.org/issue42965",
        ),
        "R6006": (
            "Type `%s` is used more than once in union type annotation. Remove redundant typehints.",
            "redundant-typehint-argument",
            "Duplicated type arguments will be skipped by `mypy` tool, therefore should be "
            "removed to avoid confusion.",
        ),
    }
    options = (
        (
            "runtime-typing",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": (
                    "Set to ``no`` if the app / library does **NOT** need to "
                    "support runtime introspection of type annotations. "
                    "If you use type annotations **exclusively** for type checking "
                    "of an application, you're probably fine. For libraries, "
                    "evaluate if some users want to access the type hints "
                    "at runtime first, e.g., through ``typing.get_type_hints``. "
                    "Applies to Python versions 3.7 - 3.9"
                ),
            },
        ),
    )

    _should_check_typing_alias: bool
    """The use of type aliases (PEP 585) requires Python 3.9
    or Python 3.7+ with postponed evaluation.
    """

    _should_check_alternative_union_syntax: bool
    """The use of alternative union syntax (PEP 604) requires Python 3.10
    or Python 3.7+ with postponed evaluation.
    """

    def __init__(self, linter: PyLinter) -> None:
        """Initialize checker instance."""
        super().__init__(linter=linter)
        self._found_broken_callable_location: bool = False
        self._alias_name_collisions: set[str] = set()
        self._deprecated_typing_alias_msgs: list[DeprecatedTypingAliasMsg] = []
        self._consider_using_alias_msgs: list[DeprecatedTypingAliasMsg] = []

    def open(self) -> None:
        py_version = self.linter.config.py_version
        self._py37_plus = py_version >= (3, 7)
        self._py39_plus = py_version >= (3, 9)
        self._py310_plus = py_version >= (3, 10)

        self._should_check_typing_alias = self._py39_plus or (
            self._py37_plus and self.linter.config.runtime_typing is False
        )
        self._should_check_alternative_union_syntax = self._py310_plus or (
            self._py37_plus and self.linter.config.runtime_typing is False
        )

        self._should_check_noreturn = py_version < (3, 7, 2)
        self._should_check_callable = py_version < (3, 9, 2)

    def _msg_postponed_eval_hint(self, node: nodes.NodeNG) -> str:
        """Message hint if postponed evaluation isn't enabled."""
        if self._py310_plus or "annotations" in node.root().future_imports:
            return ""
        return ". Add 'from __future__ import annotations' as well"

    @only_required_for_messages(
        "deprecated-typing-alias",
        "consider-using-alias",
        "consider-alternative-union-syntax",
        "broken-noreturn",
        "broken-collections-callable",
    )
    def visit_name(self, node: nodes.Name) -> None:
        if self._should_check_typing_alias and node.name in ALIAS_NAMES:
            self._check_for_typing_alias(node)
        if self._should_check_alternative_union_syntax and node.name in UNION_NAMES:
            self._check_for_alternative_union_syntax(node, node.name)
        if self._should_check_noreturn and node.name == "NoReturn":
            self._check_broken_noreturn(node)
        if self._should_check_callable and node.name == "Callable":
            self._check_broken_callable(node)

    @only_required_for_messages(
        "deprecated-typing-alias",
        "consider-using-alias",
        "consider-alternative-union-syntax",
        "broken-noreturn",
        "broken-collections-callable",
    )
    def visit_attribute(self, node: nodes.Attribute) -> None:
        if self._should_check_typing_alias and node.attrname in ALIAS_NAMES:
            self._check_for_typing_alias(node)
        if self._should_check_alternative_union_syntax and node.attrname in UNION_NAMES:
            self._check_for_alternative_union_syntax(node, node.attrname)
        if self._should_check_noreturn and node.attrname == "NoReturn":
            self._check_broken_noreturn(node)
        if self._should_check_callable and node.attrname == "Callable":
            self._check_broken_callable(node)

    @only_required_for_messages("redundant-typehint-argument")
    def visit_annassign(self, node: nodes.AnnAssign) -> None:
        annotation = node.annotation
        if self._is_deprecated_union_annotation(annotation, "Optional"):
            if self._is_optional_none_annotation(annotation):
                self.add_message(
                    "redundant-typehint-argument",
                    node=annotation,
                    args="None",
                    confidence=HIGH,
                )
            return
        if self._is_deprecated_union_annotation(annotation, "Union") and isinstance(
            annotation.slice, nodes.Tuple
        ):
            types = annotation.slice.elts
        elif self._is_binop_union_annotation(annotation):
            types = self._parse_binops_typehints(annotation)
        else:
            return

        self._check_union_types(types, node)

    @staticmethod
    def _is_deprecated_union_annotation(
        annotation: nodes.NodeNG, union_name: str
    ) -> bool:
        return (
            isinstance(annotation, nodes.Subscript)
            and isinstance(annotation.value, nodes.Name)
            and annotation.value.name == union_name
        )

    def _is_binop_union_annotation(self, annotation: nodes.NodeNG) -> bool:
        return self._should_check_alternative_union_syntax and isinstance(
            annotation, nodes.BinOp
        )

    @staticmethod
    def _is_optional_none_annotation(annotation: nodes.Subscript) -> bool:
        return (
            isinstance(annotation.slice, nodes.Const) and annotation.slice.value is None
        )

    def _parse_binops_typehints(
        self, binop_node: nodes.BinOp, typehints_list: list[nodes.NodeNG] | None = None
    ) -> list[nodes.NodeNG]:
        typehints_list = typehints_list or []
        if isinstance(binop_node.left, nodes.BinOp):
            typehints_list.extend(
                self._parse_binops_typehints(binop_node.left, typehints_list)
            )
        else:
            typehints_list.append(binop_node.left)
        typehints_list.append(binop_node.right)
        return typehints_list

    def _check_union_types(
        self, types: list[nodes.NodeNG], annotation: nodes.NodeNG
    ) -> None:
        types_set = set()
        for typehint in types:
            typehint_str = typehint.as_string()
            if typehint_str in types_set:
                self.add_message(
                    "redundant-typehint-argument",
                    node=annotation,
                    args=(typehint_str),
                    confidence=HIGH,
                )
            else:
                types_set.add(typehint_str)

    def _check_for_alternative_union_syntax(
        self,
        node: nodes.Name | nodes.Attribute,
        name: str,
    ) -> None:
        """Check if alternative union syntax could be used.

        Requires
        - Python 3.10
        - OR: Python 3.7+ with postponed evaluation in
              a type annotation context
        """
        inferred = safe_infer(node)
        if not (
            isinstance(inferred, nodes.FunctionDef)
            and inferred.qname() in {"typing.Optional", "typing.Union"}
            or isinstance(inferred, astroid.bases.Instance)
            and inferred.qname() == "typing._SpecialForm"
        ):
            return
        if not (self._py310_plus or is_node_in_type_annotation_context(node)):
            return
        self.add_message(
            "consider-alternative-union-syntax",
            node=node,
            args=(name, self._msg_postponed_eval_hint(node)),
            confidence=INFERENCE,
        )

    def _check_for_typing_alias(
        self,
        node: nodes.Name | nodes.Attribute,
    ) -> None:
        """Check if typing alias is deprecated or could be replaced.

        Requires
        - Python 3.9
        - OR: Python 3.7+ with postponed evaluation in
              a type annotation context

        For Python 3.7+: Only emit message if change doesn't create
            any name collisions, only ever used in a type annotation
            context, and can safely be replaced.
        """
        inferred = safe_infer(node)
        if not isinstance(inferred, nodes.ClassDef):
            return
        alias = DEPRECATED_TYPING_ALIASES.get(inferred.qname(), None)
        if alias is None:
            return

        if self._py39_plus:
            if inferred.qname() == "typing.Callable" and self._broken_callable_location(
                node
            ):
                self._found_broken_callable_location = True
            self._deprecated_typing_alias_msgs.append(
                DeprecatedTypingAliasMsg(
                    node,
                    inferred.qname(),
                    alias.name,
                )
            )
            return

        # For PY37+, check for type annotation context first
        if not is_node_in_type_annotation_context(node) and isinstance(
            node.parent, nodes.Subscript
        ):
            if alias.name_collision is True:
                self._alias_name_collisions.add(inferred.qname())
            return
        self._consider_using_alias_msgs.append(
            DeprecatedTypingAliasMsg(
                node,
                inferred.qname(),
                alias.name,
                isinstance(node.parent, nodes.Subscript),
            )
        )

    @only_required_for_messages("consider-using-alias", "deprecated-typing-alias")
    def leave_module(self, node: nodes.Module) -> None:
        """After parsing of module is complete, add messages for
        'consider-using-alias' check.

        Make sure results are safe to recommend / collision free.
        """
        if self._py39_plus:
            for msg in self._deprecated_typing_alias_msgs:
                if (
                    self._found_broken_callable_location
                    and msg.qname == "typing.Callable"
                ):
                    continue
                self.add_message(
                    "deprecated-typing-alias",
                    node=msg.node,
                    args=(msg.qname, msg.alias),
                    confidence=INFERENCE,
                )

        elif self._py37_plus:
            msg_future_import = self._msg_postponed_eval_hint(node)
            for msg in self._consider_using_alias_msgs:
                if msg.qname in self._alias_name_collisions:
                    continue
                self.add_message(
                    "consider-using-alias",
                    node=msg.node,
                    args=(
                        msg.qname,
                        msg.alias,
                        msg_future_import if msg.parent_subscript else "",
                    ),
                    confidence=INFERENCE,
                )

        # Clear all module cache variables
        self._found_broken_callable_location = False
        self._deprecated_typing_alias_msgs.clear()
        self._alias_name_collisions.clear()
        self._consider_using_alias_msgs.clear()

    def _check_broken_noreturn(self, node: nodes.Name | nodes.Attribute) -> None:
        """Check for 'NoReturn' inside compound types."""
        if not isinstance(node.parent, nodes.BaseContainer):
            # NoReturn not part of a Union or Callable type
            return

        if (
            in_type_checking_block(node)
            or is_postponed_evaluation_enabled(node)
            and is_node_in_type_annotation_context(node)
        ):
            return

        for inferred in node.infer():
            # To deal with typing_extensions, don't use safe_infer
            if (
                isinstance(inferred, (nodes.FunctionDef, nodes.ClassDef))
                and inferred.qname() in TYPING_NORETURN
                # In Python 3.7 - 3.8, NoReturn is alias of '_SpecialForm'
                or isinstance(inferred, astroid.bases.BaseInstance)
                and isinstance(inferred._proxied, nodes.ClassDef)
                and inferred._proxied.qname() == "typing._SpecialForm"
            ):
                self.add_message("broken-noreturn", node=node, confidence=INFERENCE)
                break

    def _check_broken_callable(self, node: nodes.Name | nodes.Attribute) -> None:
        """Check for 'collections.abc.Callable' inside Optional and Union."""
        inferred = safe_infer(node)
        if not (
            isinstance(inferred, nodes.ClassDef)
            and inferred.qname() == "_collections_abc.Callable"
            and self._broken_callable_location(node)
        ):
            return

        self.add_message("broken-collections-callable", node=node, confidence=INFERENCE)

    def _broken_callable_location(self, node: nodes.Name | nodes.Attribute) -> bool:
        """Check if node would be a broken location for collections.abc.Callable."""
        if (
            in_type_checking_block(node)
            or is_postponed_evaluation_enabled(node)
            and is_node_in_type_annotation_context(node)
        ):
            return False

        # Check first Callable arg is a list of arguments -> Callable[[int], None]
        if not (
            isinstance(node.parent, nodes.Subscript)
            and isinstance(node.parent.slice, nodes.Tuple)
            and len(node.parent.slice.elts) == 2
            and isinstance(node.parent.slice.elts[0], nodes.List)
        ):
            return False

        # Check nested inside Optional or Union
        parent_subscript = node.parent.parent
        if isinstance(parent_subscript, nodes.BaseContainer):
            parent_subscript = parent_subscript.parent
        if not (
            isinstance(parent_subscript, nodes.Subscript)
            and isinstance(parent_subscript.value, (nodes.Name, nodes.Attribute))
        ):
            return False

        inferred_parent = safe_infer(parent_subscript.value)
        if not (
            isinstance(inferred_parent, nodes.FunctionDef)
            and inferred_parent.qname() in {"typing.Optional", "typing.Union"}
            or isinstance(inferred_parent, astroid.bases.Instance)
            and inferred_parent.qname() == "typing._SpecialForm"
        ):
            return False

        return True


def register(linter: PyLinter) -> None:
    linter.register_checker(TypingChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/typing.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Check for use of while loops."""

from __future__ import annotations

from typing import TYPE_CHECKING

from astroid import nodes

from pylint.checkers import BaseChecker
from pylint.checkers.utils import only_required_for_messages

if TYPE_CHECKING:
    from pylint.lint import PyLinter


class WhileChecker(BaseChecker):
    name = "while_used"
    msgs = {
        "W0149": (
            "Used `while` loop",
            "while-used",
            "Unbounded `while` loops can often be rewritten as bounded `for` loops. "
            "Exceptions can be made for cases such as event loops, listeners, etc.",
        )
    }

    @only_required_for_messages("while-used")
    def visit_while(self, node: nodes.While) -> None:
        self.add_message("while-used", node=node)


def register(linter: PyLinter) -> None:
    linter.register_checker(WhileChecker(linter))

back/venvmsg/Lib/site-packages/pylint/extensions/while_used.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Graph manipulation utilities.

(dot generation adapted from pypy/translator/tool/make_dot.py)
"""

from __future__ import annotations

import codecs
import os
import shutil
import subprocess
import tempfile
from collections.abc import Sequence
from typing import Any


def target_info_from_filename(filename: str) -> tuple[str, str, str]:
    """Transforms /some/path/foo.png into ('/some/path', 'foo.png', 'png')."""
    basename = os.path.basename(filename)
    storedir = os.path.dirname(os.path.abspath(filename))
    target = os.path.splitext(filename)[-1][1:]
    return storedir, basename, target


class DotBackend:
    """Dot File back-end."""

    def __init__(
        self,
        graphname: str,
        rankdir: str | None = None,
        size: Any = None,
        ratio: Any = None,
        charset: str = "utf-8",
        renderer: str = "dot",
        additional_param: dict[str, Any] | None = None,
    ) -> None:
        if additional_param is None:
            additional_param = {}
        self.graphname = graphname
        self.renderer = renderer
        self.lines: list[str] = []
        self._source: str | None = None
        self.emit(f"digraph {normalize_node_id(graphname)} {{")
        if rankdir:
            self.emit(f"rankdir={rankdir}")
        if ratio:
            self.emit(f"ratio={ratio}")
        if size:
            self.emit(f'size="{size}"')
        if charset:
            assert charset.lower() in {
                "utf-8",
                "iso-8859-1",
                "latin1",
            }, f"unsupported charset {charset}"
            self.emit(f'charset="{charset}"')
        for param in additional_param.items():
            self.emit("=".join(param))

    def get_source(self) -> str:
        """Returns self._source."""
        if self._source is None:
            self.emit("}\n")
            self._source = "\n".join(self.lines)
            del self.lines
        return self._source

    source = property(get_source)

    def generate(
        self, outputfile: str | None = None, mapfile: str | None = None
    ) -> str:
        """Generates a graph file.

        :param str outputfile: filename and path [defaults to graphname.png]
        :param str mapfile: filename and path

        :rtype: str
        :return: a path to the generated file
        :raises RuntimeError: if the executable for rendering was not found
        """
        # pylint: disable=duplicate-code
        graphviz_extensions = ("dot", "gv")
        name = self.graphname
        if outputfile is None:
            target = "png"
            pdot, dot_sourcepath = tempfile.mkstemp(".gv", name)
            ppng, outputfile = tempfile.mkstemp(".png", name)
            os.close(pdot)
            os.close(ppng)
        else:
            _, _, target = target_info_from_filename(outputfile)
            if not target:
                target = "png"
                outputfile = outputfile + "." + target
            if target not in graphviz_extensions:
                pdot, dot_sourcepath = tempfile.mkstemp(".gv", name)
                os.close(pdot)
            else:
                dot_sourcepath = outputfile
        with codecs.open(dot_sourcepath, "w", encoding="utf8") as file:
            file.write(self.source)
        if target not in graphviz_extensions:
            if shutil.which(self.renderer) is None:
                raise RuntimeError(
                    f"Cannot generate `{outputfile}` because '{self.renderer}' "
                    "executable not found. Install graphviz, or specify a `.gv` "
                    "outputfile to produce the DOT source code."
                )
            if mapfile:
                subprocess.run(
                    [
                        self.renderer,
                        "-Tcmapx",
                        "-o",
                        mapfile,
                        "-T",
                        target,
                        dot_sourcepath,
                        "-o",
                        outputfile,
                    ],
                    check=True,
                )
            else:
                subprocess.run(
                    [self.renderer, "-T", target, dot_sourcepath, "-o", outputfile],
                    check=True,
                )
            os.unlink(dot_sourcepath)
        return outputfile

    def emit(self, line: str) -> None:
        """Adds <line> to final output."""
        self.lines.append(line)

    def emit_edge(self, name1: str, name2: str, **props: Any) -> None:
        """Emit an edge from <name1> to <name2>.

        For edge properties: see https://www.graphviz.org/doc/info/attrs.html
        """
        attrs = [f'{prop}="{value}"' for prop, value in props.items()]
        n_from, n_to = normalize_node_id(name1), normalize_node_id(name2)
        self.emit(f"{n_from} -> {n_to} [{', '.join(sorted(attrs))}];")

    def emit_node(self, name: str, **props: Any) -> None:
        """Emit a node with given properties.

        For node properties: see https://www.graphviz.org/doc/info/attrs.html
        """
        attrs = [f'{prop}="{value}"' for prop, value in props.items()]
        self.emit(f"{normalize_node_id(name)} [{', '.join(sorted(attrs))}];")


def normalize_node_id(nid: str) -> str:
    """Returns a suitable DOT node id for `nid`."""
    return f'"{nid}"'


def get_cycles(
    graph_dict: dict[str, set[str]], vertices: list[str] | None = None
) -> Sequence[list[str]]:
    """Return a list of detected cycles based on an ordered graph (i.e. keys are
    vertices and values are lists of destination vertices representing edges).
    """
    if not graph_dict:
        return ()
    result: list[list[str]] = []
    if vertices is None:
        vertices = list(graph_dict.keys())
    for vertice in vertices:
        _get_cycles(graph_dict, [], set(), result, vertice)
    return result


def _get_cycles(
    graph_dict: dict[str, set[str]],
    path: list[str],
    visited: set[str],
    result: list[list[str]],
    vertice: str,
) -> None:
    """Recursive function doing the real work for get_cycles."""
    if vertice in path:
        cycle = [vertice]
        for node in path[::-1]:
            if node == vertice:
                break
            cycle.insert(0, node)
        # make a canonical representation
        start_from = min(cycle)
        index = cycle.index(start_from)
        cycle = cycle[index:] + cycle[0:index]
        # append it to result if not already in
        if cycle not in result:
            result.append(cycle)
        return
    path.append(vertice)
    try:
        for node in graph_dict[vertice]:
            # don't check already visited nodes again
            if node not in visited:
                _get_cycles(graph_dict, path, visited, result, node)
                visited.add(node)
    except KeyError:
        pass
    path.pop()

back/venvmsg/Lib/site-packages/pylint/graph.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import NamedTuple

__all__ = (
    "HIGH",
    "CONTROL_FLOW",
    "INFERENCE",
    "INFERENCE_FAILURE",
    "UNDEFINED",
    "CONFIDENCE_LEVELS",
    "CONFIDENCE_LEVEL_NAMES",
)


class Confidence(NamedTuple):
    name: str
    description: str


# Warning Certainties
HIGH = Confidence("HIGH", "Warning that is not based on inference result.")
CONTROL_FLOW = Confidence(
    "CONTROL_FLOW", "Warning based on assumptions about control flow."
)
INFERENCE = Confidence("INFERENCE", "Warning based on inference result.")
INFERENCE_FAILURE = Confidence(
    "INFERENCE_FAILURE", "Warning based on inference with failures."
)
UNDEFINED = Confidence("UNDEFINED", "Warning without any associated confidence level.")

CONFIDENCE_LEVELS = [HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, UNDEFINED]
CONFIDENCE_LEVEL_NAMES = [i.name for i in CONFIDENCE_LEVELS]
CONFIDENCE_MAP = {i.name: i for i in CONFIDENCE_LEVELS}

back/venvmsg/Lib/site-packages/pylint/interfaces.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Pylint [options] modules_or_packages.

  Check that module(s) satisfy a coding standard (and more !).

    pylint --help

  Display this help message and exit.

    pylint --help-msg <msg-id>[,<msg-id>]

  Display help messages about given message identifiers and exit.
"""
import sys

from pylint.config.exceptions import ArgumentPreprocessingError
from pylint.lint.caching import load_results, save_results
from pylint.lint.expand_modules import discover_package_path
from pylint.lint.parallel import check_parallel
from pylint.lint.pylinter import PyLinter
from pylint.lint.report_functions import (
    report_messages_by_module_stats,
    report_messages_stats,
    report_total_messages_stats,
)
from pylint.lint.run import Run
from pylint.lint.utils import _augment_sys_path, augmented_sys_path

__all__ = [
    "check_parallel",
    "PyLinter",
    "report_messages_by_module_stats",
    "report_messages_stats",
    "report_total_messages_stats",
    "Run",
    "ArgumentPreprocessingError",
    "_augment_sys_path",
    "augmented_sys_path",
    "discover_package_path",
    "save_results",
    "load_results",
]

if __name__ == "__main__":
    Run(sys.argv[1:])

back/venvmsg/Lib/site-packages/pylint/lint/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Functions that creates the basic options for the Run and PyLinter classes."""

from __future__ import annotations

import re
import sys
from typing import TYPE_CHECKING

from pylint import constants, interfaces
from pylint.config.callback_actions import (
    _DisableAction,
    _DoNothingAction,
    _EnableAction,
    _ErrorsOnlyModeAction,
    _FullDocumentationAction,
    _GenerateConfigFileAction,
    _GenerateRCFileAction,
    _ListCheckGroupsAction,
    _ListConfidenceLevelsAction,
    _ListExtensionsAction,
    _ListMessagesAction,
    _ListMessagesEnabledAction,
    _LongHelpAction,
    _MessageHelpAction,
    _OutputFormatAction,
)
from pylint.typing import Options

if TYPE_CHECKING:
    from pylint.lint import PyLinter, Run


def _make_linter_options(linter: PyLinter) -> Options:
    """Return the options used in a PyLinter class."""
    return (
        (
            "ignore",
            {
                "type": "csv",
                "metavar": "<file>[,<file>...]",
                "dest": "black_list",
                "kwargs": {"old_names": ["black_list"]},
                "default": constants.DEFAULT_IGNORE_LIST,
                "help": "Files or directories to be skipped. "
                "They should be base names, not paths.",
            },
        ),
        (
            "ignore-patterns",
            {
                "type": "regexp_csv",
                "metavar": "<pattern>[,<pattern>...]",
                "dest": "black_list_re",
                "default": (re.compile(r"^\.#"),),
                "help": "Files or directories matching the regular expression patterns are"
                " skipped. The regex matches against base names, not paths. The default value "
                "ignores Emacs file locks",
            },
        ),
        (
            "ignore-paths",
            {
                "type": "regexp_paths_csv",
                "metavar": "<pattern>[,<pattern>...]",
                "default": [],
                "help": "Add files or directories matching the regular expressions patterns to the "
                "ignore-list. The regex matches against paths and can be in "
                "Posix or Windows format. Because '\\\\' represents the directory delimiter "
                "on Windows systems, it can't be used as an escape character.",
            },
        ),
        (
            "persistent",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Pickle collected data for later comparisons.",
            },
        ),
        (
            "load-plugins",
            {
                "type": "csv",
                "metavar": "<modules>",
                "default": (),
                "help": "List of plugins (as comma separated values of "
                "python module names) to load, usually to register "
                "additional checkers.",
            },
        ),
        (
            "output-format",
            {
                "default": "text",
                "action": _OutputFormatAction,
                "callback": lambda x: x,
                "metavar": "<format>",
                "short": "f",
                "group": "Reports",
                "help": "Set the output format. Available formats are: text, "
                "parseable, colorized, json2 (improved json format), json "
                "(old json format) and msvs (visual studio). "
                "You can also give a reporter class, e.g. mypackage.mymodule."
                "MyReporterClass.",
                "kwargs": {"linter": linter},
            },
        ),
        (
            "reports",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "short": "r",
                "group": "Reports",
                "help": "Tells whether to display a full report or only the "
                "messages.",
            },
        ),
        (
            "evaluation",
            {
                "type": "string",
                "metavar": "<python_expression>",
                "group": "Reports",
                "default": "max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + "
                "convention) / statement) * 10))",
                "help": "Python expression which should return a score less "
                "than or equal to 10. You have access to the variables 'fatal', "
                "'error', 'warning', 'refactor', 'convention', and 'info' which "
                "contain the number of messages in each category, as well as "
                "'statement' which is the total number of statements "
                "analyzed. This score is used by the global "
                "evaluation report (RP0004).",
            },
        ),
        (
            "score",
            {
                "default": True,
                "type": "yn",
                "metavar": "<y or n>",
                "short": "s",
                "group": "Reports",
                "help": "Activate the evaluation score.",
            },
        ),
        (
            "fail-under",
            {
                "default": 10,
                "type": "float",
                "metavar": "<score>",
                "help": "Specify a score threshold under which the program will exit with error.",
            },
        ),
        (
            "fail-on",
            {
                "default": "",
                "type": "csv",
                "metavar": "<msg ids>",
                "help": "Return non-zero exit code if any of these messages/categories are detected,"
                " even if score is above --fail-under value. Syntax same as enable."
                " Messages specified are enabled, while categories only check already-enabled messages.",
            },
        ),
        (
            "confidence",
            {
                "type": "confidence",
                "metavar": "<levels>",
                "default": interfaces.CONFIDENCE_LEVEL_NAMES,
                "group": "Messages control",
                "help": "Only show warnings with the listed confidence levels."
                f" Leave empty to show all. Valid levels: {', '.join(interfaces.CONFIDENCE_LEVEL_NAMES)}.",
            },
        ),
        (
            "enable",
            {
                "action": _EnableAction,
                "callback": lambda x1, x2, x3, x4: x1,
                "default": (),
                "metavar": "<msg ids>",
                "short": "e",
                "group": "Messages control",
                "help": "Enable the message, report, category or checker with the "
                "given id(s). You can either give multiple identifier "
                "separated by comma (,) or put this option multiple time "
                "(only on the command line, not in the configuration file "
                "where it should appear only once). "
                'See also the "--disable" option for examples.',
                "kwargs": {"linter": linter},
            },
        ),
        (
            "disable",
            {
                "action": _DisableAction,
                "callback": lambda x1, x2, x3, x4: x1,
                "metavar": "<msg ids>",
                "default": (),
                "short": "d",
                "group": "Messages control",
                "help": "Disable the message, report, category or checker "
                "with the given id(s). You can either give multiple identifiers "
                "separated by comma (,) or put this option multiple times "
                "(only on the command line, not in the configuration file "
                "where it should appear only once). "
                'You can also use "--disable=all" to disable everything first '
                "and then re-enable specific checks. For example, if you want "
                "to run only the similarities checker, you can use "
                '"--disable=all --enable=similarities". '
                "If you want to run only the classes checker, but have no "
                "Warning level messages displayed, use "
                '"--disable=all --enable=classes --disable=W".',
                "kwargs": {"linter": linter},
            },
        ),
        (
            "msg-template",
            {
                "type": "string",
                "default": "",
                "metavar": "<template>",
                "group": "Reports",
                "help": (
                    "Template used to display messages. "
                    "This is a python new-style format string "
                    "used to format the message information. "
                    "See doc for all details."
                ),
            },
        ),
        (
            "jobs",
            {
                "type": "int",
                "metavar": "<n-processes>",
                "short": "j",
                "default": 1,
                "help": "Use multiple processes to speed up Pylint. Specifying 0 will "
                "auto-detect the number of processors available to use, and will cap "
                "the count on Windows to avoid hangs.",
            },
        ),
        (
            "unsafe-load-any-extension",
            {
                "type": "yn",
                "metavar": "<y or n>",
                "default": False,
                "hide": True,
                "help": (
                    "Allow loading of arbitrary C extensions. Extensions"
                    " are imported into the active Python interpreter and"
                    " may run arbitrary code."
                ),
            },
        ),
        (
            "limit-inference-results",
            {
                "type": "int",
                "metavar": "<number-of-results>",
                "default": 100,
                "help": (
                    "Control the amount of potential inferred values when inferring "
                    "a single object. This can help the performance when dealing with "
                    "large functions or complex, nested conditions."
                ),
            },
        ),
        (
            "extension-pkg-allow-list",
            {
                "type": "csv",
                "metavar": "<pkg[,pkg]>",
                "default": [],
                "help": (
                    "A comma-separated list of package or module names"
                    " from where C extensions may be loaded. Extensions are"
                    " loading into the active Python interpreter and may run"
                    " arbitrary code."
                ),
            },
        ),
        (
            "extension-pkg-whitelist",
            {
                "type": "csv",
                "metavar": "<pkg[,pkg]>",
                "default": [],
                "help": (
                    "A comma-separated list of package or module names"
                    " from where C extensions may be loaded. Extensions are"
                    " loading into the active Python interpreter and may run"
                    " arbitrary code. (This is an alternative name to"
                    " extension-pkg-allow-list for backward compatibility.)"
                ),
            },
        ),
        (
            "suggestion-mode",
            {
                "type": "yn",
                "metavar": "<y or n>",
                "default": True,
                "help": (
                    "When enabled, pylint would attempt to guess common "
                    "misconfiguration and emit user-friendly hints instead "
                    "of false-positive error messages."
                ),
            },
        ),
        (
            "exit-zero",
            {
                "action": "store_true",
                "default": False,
                "metavar": "<flag>",
                "help": (
                    "Always return a 0 (non-error) status code, even if "
                    "lint errors are found. This is primarily useful in "
                    "continuous integration scripts."
                ),
            },
        ),
        (
            "from-stdin",
            {
                "action": "store_true",
                "default": False,
                "metavar": "<flag>",
                "help": (
                    "Interpret the stdin as a python script, whose filename "
                    "needs to be passed as the module_or_package argument."
                ),
            },
        ),
        (
            "source-roots",
            {
                "type": "glob_paths_csv",
                "metavar": "<path>[,<path>...]",
                "default": (),
                "help": "Add paths to the list of the source roots. Supports globbing patterns. "
                "The source root is an absolute path or a path relative to the current working "
                "directory used to determine a package namespace for modules located under the "
                "source root.",
            },
        ),
        (
            "recursive",
            {
                "type": "yn",
                "metavar": "<yn>",
                "default": False,
                "help": "Discover python modules and packages in the file system subtree.",
            },
        ),
        (
            "py-version",
            {
                "default": sys.version_info[:2],
                "type": "py_version",
                "metavar": "<py_version>",
                "help": (
                    "Minimum Python version to use for version dependent checks. "
                    "Will default to the version used to run pylint."
                ),
            },
        ),
        (
            "ignored-modules",
            {
                "default": (),
                "type": "csv",
                "metavar": "<module names>",
                "help": "List of module names for which member attributes "
                "should not be checked (useful for modules/projects "
                "where namespaces are manipulated during runtime and "
                "thus existing member attributes cannot be "
                "deduced by static analysis). It supports qualified "
                "module names, as well as Unix pattern matching.",
            },
        ),
        (
            "analyse-fallback-blocks",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Analyse import fallback blocks. This can be used to "
                "support both Python 2 and 3 compatible code, which "
                "means that the block might have code that exists "
                "only in one or another interpreter, leading to false "
                "positives when analysed.",
            },
        ),
        (
            "clear-cache-post-run",
            {
                "default": False,
                "type": "yn",
                "metavar": "<y or n>",
                "help": "Clear in-memory caches upon conclusion of linting. "
                "Useful if running pylint in a server-like mode.",
            },
        ),
    )


def _make_run_options(self: Run) -> Options:
    """Return the options used in a Run class."""
    return (
        (
            "rcfile",
            {
                "action": _DoNothingAction,
                "kwargs": {},
                "group": "Commands",
                "help": "Specify a configuration file to load.",
                "hide_from_config_file": True,
            },
        ),
        (
            "output",
            {
                "action": _DoNothingAction,
                "kwargs": {},
                "group": "Commands",
                "help": "Specify an output file.",
                "hide_from_config_file": True,
            },
        ),
        (
            "init-hook",
            {
                "action": _DoNothingAction,
                "kwargs": {},
                "help": "Python code to execute, usually for sys.path "
                "manipulation such as pygtk.require().",
            },
        ),
        (
            "help-msg",
            {
                "action": _MessageHelpAction,
                "kwargs": {"Run": self},
                "group": "Commands",
                "help": "Display a help message for the given message id and "
                "exit. The value may be a comma separated list of message ids.",
                "hide_from_config_file": True,
            },
        ),
        (
            "list-msgs",
            {
                "action": _ListMessagesAction,
                "kwargs": {"Run": self},
                "group": "Commands",
                "help": "Display a list of all pylint's messages divided by whether "
                "they are emittable with the given interpreter.",
                "hide_from_config_file": True,
            },
        ),
        (
            "list-msgs-enabled",
            {
                "action": _ListMessagesEnabledAction,
                "kwargs": {"Run": self},
                "group": "Commands",
                "help": "Display a list of what messages are enabled, "
                "disabled and non-emittable with the given configuration.",
                "hide_from_config_file": True,
            },
        ),
        (
            "list-groups",
            {
                "action": _ListCheckGroupsAction,
                "kwargs": {"Run": self},
                "group": "Commands",
                "help": "List pylint's message groups.",
                "hide_from_config_file": True,
            },
        ),
        (
            "list-conf-levels",
            {
                "action": _ListConfidenceLevelsAction,
                "kwargs": {"Run": self},
                "group": "Commands",
                "help": "Generate pylint's confidence levels.",
                "hide_from_config_file": True,
            },
        ),
        (
            "list-extensions",
            {
                "action": _ListExtensionsAction,
                "kwargs": {"Run": self},
                "group": "Commands",
                "help": "List available extensions.",
                "hide_from_config_file": True,
            },
        ),
        (
            "full-documentation",
            {
                "action": _FullDocumentationAction,
                "kwargs": {"Run": self},
                "group": "Commands",
                "help": "Generate pylint's full documentation.",
                "hide_from_config_file": True,
            },
        ),
        (
            "generate-rcfile",
            {
                "action": _GenerateRCFileAction,
                "kwargs": {"Run": self},
                "group": "Commands",
                "help": "Generate a sample configuration file according to "
                "the current configuration. You can put other options "
                "before this one to get them in the generated "
                "configuration.",
                "hide_from_config_file": True,
            },
        ),
        (
            "generate-toml-config",
            {
                "action": _GenerateConfigFileAction,
                "kwargs": {"Run": self},
                "group": "Commands",
                "help": "Generate a sample configuration file according to "
                "the current configuration. You can put other options "
                "before this one to get them in the generated "
                "configuration. The config is in the .toml format.",
                "hide_from_config_file": True,
            },
        ),
        (
            "errors-only",
            {
                "action": _ErrorsOnlyModeAction,
                "kwargs": {"Run": self},
                "short": "E",
                "help": "In error mode, messages with a category besides "
                "ERROR or FATAL are suppressed, and no reports are done by default. "
                "Error mode is compatible with disabling specific errors. ",
                "hide_from_config_file": True,
            },
        ),
        (
            "verbose",
            {
                "action": _DoNothingAction,
                "kwargs": {},
                "short": "v",
                "help": "In verbose mode, extra non-checker-related info "
                "will be displayed.",
                "hide_from_config_file": True,
                "metavar": "",
            },
        ),
        (
            "enable-all-extensions",
            {
                "action": _DoNothingAction,
                "kwargs": {},
                "help": "Load and enable all available extensions. "
                "Use --list-extensions to see a list all available extensions.",
                "hide_from_config_file": True,
                "metavar": "",
            },
        ),
        (
            "long-help",
            {
                "action": _LongHelpAction,
                "kwargs": {"Run": self},
                "help": "Show more verbose help.",
                "group": "Commands",
                "hide_from_config_file": True,
            },
        ),
    )

back/venvmsg/Lib/site-packages/pylint/lint/base_options.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import pickle
import sys
import warnings
from pathlib import Path

from pylint.constants import PYLINT_HOME
from pylint.utils import LinterStats

PYLINT_HOME_AS_PATH = Path(PYLINT_HOME)


def _get_pdata_path(
    base_name: Path, recurs: int, pylint_home: Path = PYLINT_HOME_AS_PATH
) -> Path:
    # We strip all characters that can't be used in a filename. Also strip '/' and
    # '\\' because we want to create a single file, not sub-directories.
    underscored_name = "_".join(
        str(p.replace(":", "_").replace("/", "_").replace("\\", "_"))
        for p in base_name.parts
    )
    return pylint_home / f"{underscored_name}_{recurs}.stats"


def load_results(
    base: str | Path, pylint_home: str | Path = PYLINT_HOME
) -> LinterStats | None:
    base = Path(base)
    pylint_home = Path(pylint_home)
    data_file = _get_pdata_path(base, 1, pylint_home)

    if not data_file.exists():
        return None

    try:
        with open(data_file, "rb") as stream:
            data = pickle.load(stream)
            if not isinstance(data, LinterStats):
                warnings.warn(
                    "You're using an old pylint cache with invalid data following "
                    f"an upgrade, please delete '{data_file}'.",
                    UserWarning,
                    stacklevel=2,
                )
                raise TypeError
            return data
    except Exception:  # pylint: disable=broad-except
        # There's an issue with the cache but we just continue as if it isn't there
        return None


def save_results(
    results: LinterStats, base: str | Path, pylint_home: str | Path = PYLINT_HOME
) -> None:
    base = Path(base)
    pylint_home = Path(pylint_home)
    try:
        pylint_home.mkdir(parents=True, exist_ok=True)
    except OSError:  # pragma: no cover
        print(f"Unable to create directory {pylint_home}", file=sys.stderr)
    data_file = _get_pdata_path(base, 1)
    try:
        with open(data_file, "wb") as stream:
            pickle.dump(results, stream)
    except OSError as ex:  # pragma: no cover
        print(f"Unable to create file {data_file}: {ex}", file=sys.stderr)

back/venvmsg/Lib/site-packages/pylint/lint/caching.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import os
import sys
from collections.abc import Sequence
from re import Pattern

from astroid import modutils

from pylint.typing import ErrorDescriptionDict, ModuleDescriptionDict


def _modpath_from_file(filename: str, is_namespace: bool, path: list[str]) -> list[str]:
    def _is_package_cb(inner_path: str, parts: list[str]) -> bool:
        return modutils.check_modpath_has_init(inner_path, parts) or is_namespace

    return modutils.modpath_from_file_with_callback(  # type: ignore[no-any-return]
        filename, path=path, is_package_cb=_is_package_cb
    )


def discover_package_path(modulepath: str, source_roots: Sequence[str]) -> str:
    """Discover package path from one its modules and source roots."""
    dirname = os.path.realpath(os.path.expanduser(modulepath))
    if not os.path.isdir(dirname):
        dirname = os.path.dirname(dirname)

    # Look for a source root that contains the module directory
    for source_root in source_roots:
        source_root = os.path.realpath(os.path.expanduser(source_root))
        if os.path.commonpath([source_root, dirname]) == source_root:
            return source_root

    # Fall back to legacy discovery by looking for __init__.py upwards as
    # it's the only way given that source root was not found or was not provided
    while True:
        if not os.path.exists(os.path.join(dirname, "__init__.py")):
            return dirname
        old_dirname = dirname
        dirname = os.path.dirname(dirname)
        if old_dirname == dirname:
            return os.getcwd()


def _is_in_ignore_list_re(element: str, ignore_list_re: list[Pattern[str]]) -> bool:
    """Determines if the element is matched in a regex ignore-list."""
    return any(file_pattern.match(element) for file_pattern in ignore_list_re)


def _is_ignored_file(
    element: str,
    ignore_list: list[str],
    ignore_list_re: list[Pattern[str]],
    ignore_list_paths_re: list[Pattern[str]],
) -> bool:
    element = os.path.normpath(element)
    basename = os.path.basename(element)
    return (
        basename in ignore_list
        or _is_in_ignore_list_re(basename, ignore_list_re)
        or _is_in_ignore_list_re(element, ignore_list_paths_re)
    )


# pylint: disable = too-many-locals, too-many-statements
def expand_modules(
    files_or_modules: Sequence[str],
    source_roots: Sequence[str],
    ignore_list: list[str],
    ignore_list_re: list[Pattern[str]],
    ignore_list_paths_re: list[Pattern[str]],
) -> tuple[dict[str, ModuleDescriptionDict], list[ErrorDescriptionDict]]:
    """Take a list of files/modules/packages and return the list of tuple
    (file, module name) which have to be actually checked.
    """
    result: dict[str, ModuleDescriptionDict] = {}
    errors: list[ErrorDescriptionDict] = []
    path = sys.path.copy()

    for something in files_or_modules:
        basename = os.path.basename(something)
        if _is_ignored_file(
            something, ignore_list, ignore_list_re, ignore_list_paths_re
        ):
            continue
        module_package_path = discover_package_path(something, source_roots)
        additional_search_path = [".", module_package_path, *path]
        if os.path.exists(something):
            # this is a file or a directory
            try:
                modname = ".".join(
                    modutils.modpath_from_file(something, path=additional_search_path)
                )
            except ImportError:
                modname = os.path.splitext(basename)[0]
            if os.path.isdir(something):
                filepath = os.path.join(something, "__init__.py")
            else:
                filepath = something
        else:
            # suppose it's a module or package
            modname = something
            try:
                filepath = modutils.file_from_modpath(
                    modname.split("."), path=additional_search_path
                )
                if filepath is None:
                    continue
            except ImportError as ex:
                errors.append({"key": "fatal", "mod": modname, "ex": ex})
                continue
        filepath = os.path.normpath(filepath)
        modparts = (modname or something).split(".")
        try:
            spec = modutils.file_info_from_modpath(
                modparts, path=additional_search_path
            )
        except ImportError:
            # Might not be acceptable, don't crash.
            is_namespace = False
            is_directory = os.path.isdir(something)
        else:
            is_namespace = modutils.is_namespace(spec)
            is_directory = modutils.is_directory(spec)
        if not is_namespace:
            if filepath in result:
                # Always set arg flag if module explicitly given.
                result[filepath]["isarg"] = True
            else:
                result[filepath] = {
                    "path": filepath,
                    "name": modname,
                    "isarg": True,
                    "basepath": filepath,
                    "basename": modname,
                }
        has_init = (
            not (modname.endswith(".__init__") or modname == "__init__")
            and os.path.basename(filepath) == "__init__.py"
        )
        if has_init or is_namespace or is_directory:
            for subfilepath in modutils.get_module_files(
                os.path.dirname(filepath), ignore_list, list_all=is_namespace
            ):
                if filepath == subfilepath:
                    continue
                if _is_in_ignore_list_re(
                    os.path.basename(subfilepath), ignore_list_re
                ) or _is_in_ignore_list_re(subfilepath, ignore_list_paths_re):
                    continue

                modpath = _modpath_from_file(
                    subfilepath, is_namespace, path=additional_search_path
                )
                submodname = ".".join(modpath)
                # Preserve arg flag if module is also explicitly given.
                isarg = subfilepath in result and result[subfilepath]["isarg"]
                result[subfilepath] = {
                    "path": subfilepath,
                    "name": submodname,
                    "isarg": isarg,
                    "basepath": filepath,
                    "basename": modname,
                }
    return result, errors

back/venvmsg/Lib/site-packages/pylint/lint/expand_modules.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import tokenize
from collections import defaultdict
from typing import TYPE_CHECKING, Literal

from pylint import exceptions, interfaces
from pylint.constants import (
    MSG_STATE_CONFIDENCE,
    MSG_STATE_SCOPE_CONFIG,
    MSG_STATE_SCOPE_MODULE,
    MSG_TYPES,
    MSG_TYPES_LONG,
)
from pylint.interfaces import HIGH
from pylint.message import MessageDefinition
from pylint.typing import ManagedMessage
from pylint.utils.pragma_parser import (
    OPTION_PO,
    InvalidPragmaError,
    UnRecognizedOptionError,
    parse_pragma,
)

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


class _MessageStateHandler:
    """Class that handles message disabling & enabling and processing of inline
    pragma's.
    """

    def __init__(self, linter: PyLinter) -> None:
        self.linter = linter
        self._msgs_state: dict[str, bool] = {}
        self._options_methods = {
            "enable": self.enable,
            "disable": self.disable,
            "disable-next": self.disable_next,
        }
        self._bw_options_methods = {
            "disable-msg": self._options_methods["disable"],
            "enable-msg": self._options_methods["enable"],
        }
        self._pragma_lineno: dict[str, int] = {}
        self._stashed_messages: defaultdict[
            tuple[str, str], list[tuple[str | None, str]]
        ] = defaultdict(list)
        """Some messages in the options (for --enable and --disable) are encountered
        too early to warn about them.

        i.e. before all option providers have been fully parsed. Thus, this dict stores
        option_value and msg_id needed to (later) emit the messages keyed on module names.
        """

    def _set_one_msg_status(
        self, scope: str, msg: MessageDefinition, line: int | None, enable: bool
    ) -> None:
        """Set the status of an individual message."""
        if scope in {"module", "line"}:
            assert isinstance(line, int)  # should always be int inside module scope

            self.linter.file_state.set_msg_status(msg, line, enable, scope)
            if not enable and msg.symbol != "locally-disabled":
                self.linter.add_message(
                    "locally-disabled", line=line, args=(msg.symbol, msg.msgid)
                )
        else:
            msgs = self._msgs_state
            msgs[msg.msgid] = enable

    def _get_messages_to_set(
        self, msgid: str, enable: bool, ignore_unknown: bool = False
    ) -> list[MessageDefinition]:
        """Do some tests and find the actual messages of which the status should be set."""
        message_definitions: list[MessageDefinition] = []
        if msgid == "all":
            for _msgid in MSG_TYPES:
                message_definitions.extend(
                    self._get_messages_to_set(_msgid, enable, ignore_unknown)
                )
            return message_definitions

        # msgid is a category?
        category_id = msgid.upper()
        if category_id not in MSG_TYPES:
            category_id_formatted = MSG_TYPES_LONG.get(category_id)
        else:
            category_id_formatted = category_id
        if category_id_formatted is not None:
            for _msgid in self.linter.msgs_store._msgs_by_category[
                category_id_formatted
            ]:
                message_definitions.extend(
                    self._get_messages_to_set(_msgid, enable, ignore_unknown)
                )
            return message_definitions

        # msgid is a checker name?
        if msgid.lower() in self.linter._checkers:
            for checker in self.linter._checkers[msgid.lower()]:
                for _msgid in checker.msgs:
                    message_definitions.extend(
                        self._get_messages_to_set(_msgid, enable, ignore_unknown)
                    )
            return message_definitions

        # msgid is report id?
        if msgid.lower().startswith("rp"):
            if enable:
                self.linter.enable_report(msgid)
            else:
                self.linter.disable_report(msgid)
            return message_definitions

        try:
            # msgid is a symbolic or numeric msgid.
            message_definitions = self.linter.msgs_store.get_message_definitions(msgid)
        except exceptions.UnknownMessageError:
            if not ignore_unknown:
                raise
        return message_definitions

    def _set_msg_status(
        self,
        msgid: str,
        enable: bool,
        scope: str = "package",
        line: int | None = None,
        ignore_unknown: bool = False,
    ) -> None:
        """Do some tests and then iterate over message definitions to set state."""
        assert scope in {"package", "module", "line"}

        message_definitions = self._get_messages_to_set(msgid, enable, ignore_unknown)

        for message_definition in message_definitions:
            self._set_one_msg_status(scope, message_definition, line, enable)

        # sync configuration object
        self.linter.config.enable = []
        self.linter.config.disable = []
        for msgid_or_symbol, is_enabled in self._msgs_state.items():
            symbols = [
                m.symbol
                for m in self.linter.msgs_store.get_message_definitions(msgid_or_symbol)
            ]
            if is_enabled:
                self.linter.config.enable += symbols
            else:
                self.linter.config.disable += symbols

    def _register_by_id_managed_msg(
        self, msgid_or_symbol: str, line: int | None, is_disabled: bool = True
    ) -> None:
        """If the msgid is a numeric one, then register it to inform the user
        it could furnish instead a symbolic msgid.
        """
        if msgid_or_symbol[1:].isdigit():
            try:
                symbol = self.linter.msgs_store.message_id_store.get_symbol(
                    msgid=msgid_or_symbol
                )
            except exceptions.UnknownMessageError:
                return
            managed = ManagedMessage(
                self.linter.current_name, msgid_or_symbol, symbol, line, is_disabled
            )
            self.linter._by_id_managed_msgs.append(managed)

    def disable(
        self,
        msgid: str,
        scope: str = "package",
        line: int | None = None,
        ignore_unknown: bool = False,
    ) -> None:
        """Disable a message for a scope."""
        self._set_msg_status(
            msgid, enable=False, scope=scope, line=line, ignore_unknown=ignore_unknown
        )
        self._register_by_id_managed_msg(msgid, line)

    def disable_next(
        self,
        msgid: str,
        _: str = "package",
        line: int | None = None,
        ignore_unknown: bool = False,
    ) -> None:
        """Disable a message for the next line."""
        if not line:
            raise exceptions.NoLineSuppliedError
        self._set_msg_status(
            msgid,
            enable=False,
            scope="line",
            line=line + 1,
            ignore_unknown=ignore_unknown,
        )
        self._register_by_id_managed_msg(msgid, line + 1)

    def enable(
        self,
        msgid: str,
        scope: str = "package",
        line: int | None = None,
        ignore_unknown: bool = False,
    ) -> None:
        """Enable a message for a scope."""
        self._set_msg_status(
            msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown
        )
        self._register_by_id_managed_msg(msgid, line, is_disabled=False)

    def disable_noerror_messages(self) -> None:
        """Disable message categories other than `error` and `fatal`."""
        for msgcat in self.linter.msgs_store._msgs_by_category:
            if msgcat in {"E", "F"}:
                continue
            self.disable(msgcat)

    def list_messages_enabled(self) -> None:
        emittable, non_emittable = self.linter.msgs_store.find_emittable_messages()
        enabled: list[str] = []
        disabled: list[str] = []
        for message in emittable:
            if self.is_message_enabled(message.msgid):
                enabled.append(f"  {message.symbol} ({message.msgid})")
            else:
                disabled.append(f"  {message.symbol} ({message.msgid})")
        print("Enabled messages:")
        for msg in enabled:
            print(msg)
        print("\nDisabled messages:")
        for msg in disabled:
            print(msg)
        print("\nNon-emittable messages with current interpreter:")
        for msg_def in non_emittable:
            print(f"  {msg_def.symbol} ({msg_def.msgid})")
        print("")

    def _get_message_state_scope(
        self,
        msgid: str,
        line: int | None = None,
        confidence: interfaces.Confidence | None = None,
    ) -> Literal[0, 1, 2] | None:
        """Returns the scope at which a message was enabled/disabled."""
        if confidence is None:
            confidence = interfaces.UNDEFINED
        if confidence.name not in self.linter.config.confidence:
            return MSG_STATE_CONFIDENCE  # type: ignore[return-value] # mypy does not infer Literal correctly
        try:
            if line in self.linter.file_state._module_msgs_state[msgid]:
                return MSG_STATE_SCOPE_MODULE  # type: ignore[return-value]
        except (KeyError, TypeError):
            return MSG_STATE_SCOPE_CONFIG  # type: ignore[return-value]
        return None

    def _is_one_message_enabled(self, msgid: str, line: int | None) -> bool:
        """Checks state of a single message for the current file.

        This function can't be cached as it depends on self.file_state which can
        change.
        """
        if line is None:
            return self._msgs_state.get(msgid, True)
        try:
            return self.linter.file_state._module_msgs_state[msgid][line]
        except KeyError:
            # Check if the message's line is after the maximum line existing in ast tree.
            # This line won't appear in the ast tree and won't be referred in
            # self.file_state._module_msgs_state
            # This happens for example with a commented line at the end of a module.
            max_line_number = self.linter.file_state.get_effective_max_line_number()
            if max_line_number and line > max_line_number:
                fallback = True
                lines = self.linter.file_state._raw_module_msgs_state.get(msgid, {})

                # Doesn't consider scopes, as a 'disable' can be in a
                # different scope than that of the current line.
                closest_lines = reversed(
                    [
                        (message_line, enable)
                        for message_line, enable in lines.items()
                        if message_line <= line
                    ]
                )
                _, fallback_iter = next(closest_lines, (None, None))
                if fallback_iter is not None:
                    fallback = fallback_iter

                return self._msgs_state.get(msgid, fallback)
            return self._msgs_state.get(msgid, True)

    def is_message_enabled(
        self,
        msg_descr: str,
        line: int | None = None,
        confidence: interfaces.Confidence | None = None,
    ) -> bool:
        """Return whether this message is enabled for the current file, line and
        confidence level.

        This function can't be cached right now as the line is the line of
        the currently analysed file (self.file_state), if it changes, then the
        result for the same msg_descr/line might need to change.

        :param msg_descr: Either the msgid or the symbol for a MessageDefinition
        :param line: The line of the currently analysed file
        :param confidence: The confidence of the message
        """
        if confidence and confidence.name not in self.linter.config.confidence:
            return False
        try:
            msgids = self.linter.msgs_store.message_id_store.get_active_msgids(
                msg_descr
            )
        except exceptions.UnknownMessageError:
            # The linter checks for messages that are not registered
            # due to version mismatch, just treat them as message IDs
            # for now.
            msgids = [msg_descr]
        return any(self._is_one_message_enabled(msgid, line) for msgid in msgids)

    def process_tokens(self, tokens: list[tokenize.TokenInfo]) -> None:
        """Process tokens from the current module to search for module/block level
        options.

        See func_block_disable_msg.py test case for expected behaviour.
        """
        control_pragmas = {"disable", "disable-next", "enable"}
        prev_line = None
        saw_newline = True
        seen_newline = True
        for tok_type, content, start, _, _ in tokens:
            if prev_line and prev_line != start[0]:
                saw_newline = seen_newline
                seen_newline = False

            prev_line = start[0]
            if tok_type in (tokenize.NL, tokenize.NEWLINE):
                seen_newline = True

            if tok_type != tokenize.COMMENT:
                continue
            match = OPTION_PO.search(content)
            if match is None:
                continue
            try:  # pylint: disable = too-many-try-statements
                for pragma_repr in parse_pragma(match.group(2)):
                    if pragma_repr.action in {"disable-all", "skip-file"}:
                        if pragma_repr.action == "disable-all":
                            self.linter.add_message(
                                "deprecated-pragma",
                                line=start[0],
                                args=("disable-all", "skip-file"),
                            )
                        self.linter.add_message("file-ignored", line=start[0])
                        self._ignore_file = True
                        return
                    try:
                        meth = self._options_methods[pragma_repr.action]
                    except KeyError:
                        meth = self._bw_options_methods[pragma_repr.action]
                        # found a "(dis|en)able-msg" pragma deprecated suppression
                        self.linter.add_message(
                            "deprecated-pragma",
                            line=start[0],
                            args=(
                                pragma_repr.action,
                                pragma_repr.action.replace("-msg", ""),
                            ),
                        )
                    for msgid in pragma_repr.messages:
                        # Add the line where a control pragma was encountered.
                        if pragma_repr.action in control_pragmas:
                            self._pragma_lineno[msgid] = start[0]

                        if (pragma_repr.action, msgid) == ("disable", "all"):
                            self.linter.add_message(
                                "deprecated-pragma",
                                line=start[0],
                                args=("disable=all", "skip-file"),
                            )
                            self.linter.add_message("file-ignored", line=start[0])
                            self._ignore_file = True
                            return
                            # If we did not see a newline between the previous line and now,
                            # we saw a backslash so treat the two lines as one.
                        l_start = start[0]
                        if not saw_newline:
                            l_start -= 1
                        try:
                            meth(msgid, "module", l_start)
                        except (
                            exceptions.DeletedMessageError,
                            exceptions.MessageBecameExtensionError,
                        ) as e:
                            self.linter.add_message(
                                "useless-option-value",
                                args=(pragma_repr.action, e),
                                line=start[0],
                                confidence=HIGH,
                            )
                        except exceptions.UnknownMessageError:
                            self.linter.add_message(
                                "unknown-option-value",
                                args=(pragma_repr.action, msgid),
                                line=start[0],
                                confidence=HIGH,
                            )

            except UnRecognizedOptionError as err:
                self.linter.add_message(
                    "unrecognized-inline-option", args=err.token, line=start[0]
                )
                continue
            except InvalidPragmaError as err:
                self.linter.add_message(
                    "bad-inline-option", args=err.token, line=start[0]
                )
                continue

back/venvmsg/Lib/site-packages/pylint/lint/message_state_handler.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import functools
from collections import defaultdict
from collections.abc import Iterable, Sequence
from typing import TYPE_CHECKING, Any

import dill

from pylint import reporters
from pylint.lint.utils import _augment_sys_path
from pylint.message import Message
from pylint.typing import FileItem
from pylint.utils import LinterStats, merge_stats

try:
    import multiprocessing
except ImportError:
    multiprocessing = None  # type: ignore[assignment]

try:
    from concurrent.futures import ProcessPoolExecutor
except ImportError:
    ProcessPoolExecutor = None  # type: ignore[assignment,misc]

if TYPE_CHECKING:
    from pylint.lint import PyLinter

# PyLinter object used by worker processes when checking files using parallel mode
# should only be used by the worker processes
_worker_linter: PyLinter | None = None


def _worker_initialize(
    linter: bytes, extra_packages_paths: Sequence[str] | None = None
) -> None:
    """Function called to initialize a worker for a Process within a concurrent Pool.

    :param linter: A linter-class (PyLinter) instance pickled with dill
    :param extra_packages_paths: Extra entries to be added to `sys.path`
    """
    global _worker_linter  # pylint: disable=global-statement
    _worker_linter = dill.loads(linter)
    assert _worker_linter

    # On the worker process side the messages are just collected and passed back to
    # parent process as _worker_check_file function's return value
    _worker_linter.set_reporter(reporters.CollectingReporter())
    _worker_linter.open()

    # Re-register dynamic plugins, since the pool does not have access to the
    # astroid module that existed when the linter was pickled.
    _worker_linter.load_plugin_modules(_worker_linter._dynamic_plugins, force=True)
    _worker_linter.load_plugin_configuration()

    if extra_packages_paths:
        _augment_sys_path(extra_packages_paths)


def _worker_check_single_file(
    file_item: FileItem,
) -> tuple[
    int,
    str,
    str,
    str,
    list[Message],
    LinterStats,
    int,
    defaultdict[str, list[Any]],
]:
    if not _worker_linter:
        raise RuntimeError("Worker linter not yet initialised")
    _worker_linter.open()
    _worker_linter.check_single_file_item(file_item)
    mapreduce_data = defaultdict(list)
    for checker in _worker_linter.get_checkers():
        data = checker.get_map_data()
        if data is not None:
            mapreduce_data[checker.name].append(data)
    msgs = _worker_linter.reporter.messages
    assert isinstance(_worker_linter.reporter, reporters.CollectingReporter)
    _worker_linter.reporter.reset()
    return (
        id(multiprocessing.current_process()),
        _worker_linter.current_name,
        file_item.filepath,
        _worker_linter.file_state.base_name,
        msgs,
        _worker_linter.stats,
        _worker_linter.msg_status,
        mapreduce_data,
    )


def _merge_mapreduce_data(
    linter: PyLinter,
    all_mapreduce_data: defaultdict[int, list[defaultdict[str, list[Any]]]],
) -> None:
    """Merges map/reduce data across workers, invoking relevant APIs on checkers."""
    # First collate the data and prepare it, so we can send it to the checkers for
    # validation. The intent here is to collect all the mapreduce data for all checker-
    # runs across processes - that will then be passed to a static method on the
    # checkers to be reduced and further processed.
    collated_map_reduce_data: defaultdict[str, list[Any]] = defaultdict(list)
    for linter_data in all_mapreduce_data.values():
        for run_data in linter_data:
            for checker_name, data in run_data.items():
                collated_map_reduce_data[checker_name].extend(data)

    # Send the data to checkers that support/require consolidated data
    original_checkers = linter.get_checkers()
    for checker in original_checkers:
        if checker.name in collated_map_reduce_data:
            # Assume that if the check has returned map/reduce data that it has the
            # reducer function
            checker.reduce_map_data(linter, collated_map_reduce_data[checker.name])


def check_parallel(
    linter: PyLinter,
    jobs: int,
    files: Iterable[FileItem],
    extra_packages_paths: Sequence[str] | None = None,
) -> None:
    """Use the given linter to lint the files with given amount of workers (jobs).

    This splits the work filestream-by-filestream. If you need to do work across
    multiple files, as in the similarity-checker, then implement the map/reduce functionality.
    """
    # The linter is inherited by all the pool's workers, i.e. the linter
    # is identical to the linter object here. This is required so that
    # a custom PyLinter object can be used.
    initializer = functools.partial(
        _worker_initialize, extra_packages_paths=extra_packages_paths
    )
    with ProcessPoolExecutor(
        max_workers=jobs, initializer=initializer, initargs=(dill.dumps(linter),)
    ) as executor:
        linter.open()
        all_stats = []
        all_mapreduce_data: defaultdict[
            int, list[defaultdict[str, list[Any]]]
        ] = defaultdict(list)

        # Maps each file to be worked on by a single _worker_check_single_file() call,
        # collecting any map/reduce data by checker module so that we can 'reduce' it
        # later.
        for (
            worker_idx,  # used to merge map/reduce data across workers
            module,
            file_path,
            base_name,
            messages,
            stats,
            msg_status,
            mapreduce_data,
        ) in executor.map(_worker_check_single_file, files):
            linter.file_state.base_name = base_name
            linter.file_state._is_base_filestate = False
            linter.set_current_module(module, file_path)
            for msg in messages:
                linter.reporter.handle_message(msg)
            all_stats.append(stats)
            all_mapreduce_data[worker_idx].append(mapreduce_data)
            linter.msg_status |= msg_status

    _merge_mapreduce_data(linter, all_mapreduce_data)
    linter.stats = merge_stats([linter.stats, *all_stats])

back/venvmsg/Lib/site-packages/pylint/lint/parallel.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import argparse
import collections
import contextlib
import functools
import os
import sys
import tokenize
import traceback
from collections import defaultdict
from collections.abc import Callable, Iterable, Iterator, Sequence
from io import TextIOWrapper
from pathlib import Path
from re import Pattern
from types import ModuleType
from typing import Any, Protocol

import astroid
from astroid import nodes

from pylint import checkers, exceptions, interfaces, reporters
from pylint.checkers.base_checker import BaseChecker
from pylint.config.arguments_manager import _ArgumentsManager
from pylint.constants import (
    MAIN_CHECKER_NAME,
    MSG_TYPES,
    MSG_TYPES_STATUS,
    WarningScope,
)
from pylint.interfaces import HIGH
from pylint.lint.base_options import _make_linter_options
from pylint.lint.caching import load_results, save_results
from pylint.lint.expand_modules import (
    _is_ignored_file,
    discover_package_path,
    expand_modules,
)
from pylint.lint.message_state_handler import _MessageStateHandler
from pylint.lint.parallel import check_parallel
from pylint.lint.report_functions import (
    report_messages_by_module_stats,
    report_messages_stats,
    report_total_messages_stats,
)
from pylint.lint.utils import (
    _is_relative_to,
    augmented_sys_path,
    get_fatal_error_message,
    prepare_crash_report,
)
from pylint.message import Message, MessageDefinition, MessageDefinitionStore
from pylint.reporters.base_reporter import BaseReporter
from pylint.reporters.text import TextReporter
from pylint.reporters.ureports import nodes as report_nodes
from pylint.typing import (
    DirectoryNamespaceDict,
    FileItem,
    ManagedMessage,
    MessageDefinitionTuple,
    MessageLocationTuple,
    ModuleDescriptionDict,
    Options,
)
from pylint.utils import ASTWalker, FileState, LinterStats, utils

MANAGER = astroid.MANAGER


class GetAstProtocol(Protocol):
    def __call__(
        self, filepath: str, modname: str, data: str | None = None
    ) -> nodes.Module:
        ...


def _read_stdin() -> str:
    # See https://github.com/python/typeshed/pull/5623 for rationale behind assertion
    assert isinstance(sys.stdin, TextIOWrapper)
    sys.stdin = TextIOWrapper(sys.stdin.detach(), encoding="utf-8")
    return sys.stdin.read()


def _load_reporter_by_class(reporter_class: str) -> type[BaseReporter]:
    qname = reporter_class
    module_part = astroid.modutils.get_module_part(qname)
    module = astroid.modutils.load_module_from_name(module_part)
    class_name = qname.split(".")[-1]
    klass = getattr(module, class_name)
    assert issubclass(klass, BaseReporter), f"{klass} is not a BaseReporter"
    return klass  # type: ignore[no-any-return]


# Python Linter class #########################################################

# pylint: disable-next=consider-using-namedtuple-or-dataclass
MSGS: dict[str, MessageDefinitionTuple] = {
    "F0001": (
        "%s",
        "fatal",
        "Used when an error occurred preventing the analysis of a \
              module (unable to find it for instance).",
        {"scope": WarningScope.LINE},
    ),
    "F0002": (
        "%s: %s",
        "astroid-error",
        "Used when an unexpected error occurred while building the "
        "Astroid  representation. This is usually accompanied by a "
        "traceback. Please report such errors !",
        {"scope": WarningScope.LINE},
    ),
    "F0010": (
        "error while code parsing: %s",
        "parse-error",
        "Used when an exception occurred while building the Astroid "
        "representation which could be handled by astroid.",
        {"scope": WarningScope.LINE},
    ),
    "F0011": (
        "error while parsing the configuration: %s",
        "config-parse-error",
        "Used when an exception occurred while parsing a pylint configuration file.",
        {"scope": WarningScope.LINE},
    ),
    "I0001": (
        "Unable to run raw checkers on built-in module %s",
        "raw-checker-failed",
        "Used to inform that a built-in module has not been checked "
        "using the raw checkers.",
        {
            "scope": WarningScope.LINE,
            "default_enabled": False,
        },
    ),
    "I0010": (
        "Unable to consider inline option %r",
        "bad-inline-option",
        "Used when an inline option is either badly formatted or can't "
        "be used inside modules.",
        {
            "scope": WarningScope.LINE,
            "default_enabled": False,
        },
    ),
    "I0011": (
        "Locally disabling %s (%s)",
        "locally-disabled",
        "Used when an inline option disables a message or a messages category.",
        {
            "scope": WarningScope.LINE,
            "default_enabled": False,
        },
    ),
    "I0013": (
        "Ignoring entire file",
        "file-ignored",
        "Used to inform that the file will not be checked",
        {
            "scope": WarningScope.LINE,
            "default_enabled": False,
        },
    ),
    "I0020": (
        "Suppressed %s (from line %d)",
        "suppressed-message",
        "A message was triggered on a line, but suppressed explicitly "
        "by a disable= comment in the file. This message is not "
        "generated for messages that are ignored due to configuration "
        "settings.",
        {
            "scope": WarningScope.LINE,
            "default_enabled": False,
        },
    ),
    "I0021": (
        "Useless suppression of %s",
        "useless-suppression",
        "Reported when a message is explicitly disabled for a line or "
        "a block of code, but never triggered.",
        {
            "scope": WarningScope.LINE,
            "default_enabled": False,
        },
    ),
    "I0022": (
        'Pragma "%s" is deprecated, use "%s" instead',
        "deprecated-pragma",
        "Some inline pylint options have been renamed or reworked, "
        "only the most recent form should be used. "
        "NOTE:skip-all is only available with pylint >= 0.26",
        {
            "old_names": [("I0014", "deprecated-disable-all")],
            "scope": WarningScope.LINE,
            "default_enabled": False,
        },
    ),
    "E0001": (
        "%s",
        "syntax-error",
        "Used when a syntax error is raised for a module.",
        {"scope": WarningScope.LINE},
    ),
    "E0011": (
        "Unrecognized file option %r",
        "unrecognized-inline-option",
        "Used when an unknown inline option is encountered.",
        {"scope": WarningScope.LINE},
    ),
    "W0012": (
        "Unknown option value for '%s', expected a valid pylint message and got '%s'",
        "unknown-option-value",
        "Used when an unknown value is encountered for an option.",
        {
            "scope": WarningScope.LINE,
            "old_names": [("E0012", "bad-option-value")],
        },
    ),
    "R0022": (
        "Useless option value for '%s', %s",
        "useless-option-value",
        "Used when a value for an option that is now deleted from pylint"
        " is encountered.",
        {
            "scope": WarningScope.LINE,
            "old_names": [("E0012", "bad-option-value")],
        },
    ),
    "E0013": (
        "Plugin '%s' is impossible to load, is it installed ? ('%s')",
        "bad-plugin-value",
        "Used when a bad value is used in 'load-plugins'.",
        {"scope": WarningScope.LINE},
    ),
    "E0014": (
        "Out-of-place setting encountered in top level configuration-section '%s' : '%s'",
        "bad-configuration-section",
        "Used when we detect a setting in the top level of a toml configuration that"
        " shouldn't be there.",
        {"scope": WarningScope.LINE},
    ),
    "E0015": (
        "Unrecognized option found: %s",
        "unrecognized-option",
        "Used when we detect an option that we do not recognize.",
        {"scope": WarningScope.LINE},
    ),
}


# pylint: disable=too-many-instance-attributes,too-many-public-methods
class PyLinter(
    _ArgumentsManager,
    _MessageStateHandler,
    reporters.ReportsHandlerMixIn,
    checkers.BaseChecker,
):
    """Lint Python modules using external checkers.

    This is the main checker controlling the other ones and the reports
    generation. It is itself both a raw checker and an astroid checker in order
    to:
    * handle message activation / deactivation at the module level
    * handle some basic but necessary stats' data (number of classes, methods...)

    IDE plugin developers: you may have to call
    `astroid.MANAGER.clear_cache()` across runs if you want
    to ensure the latest code version is actually checked.

    This class needs to support pickling for parallel linting to work. The exception
    is reporter member; see check_parallel function for more details.
    """

    name = MAIN_CHECKER_NAME
    msgs = MSGS
    # Will be used like this : datetime.now().strftime(crash_file_path)
    crash_file_path: str = "pylint-crash-%Y-%m-%d-%H-%M-%S.txt"

    option_groups_descs = {
        "Messages control": "Options controlling analysis messages",
        "Reports": "Options related to output formatting and reporting",
    }

    def __init__(
        self,
        options: Options = (),
        reporter: reporters.BaseReporter | reporters.MultiReporter | None = None,
        option_groups: tuple[tuple[str, str], ...] = (),
        # TODO: Deprecate passing the pylintrc parameter
        pylintrc: str | None = None,  # pylint: disable=unused-argument
    ) -> None:
        _ArgumentsManager.__init__(self, prog="pylint")
        _MessageStateHandler.__init__(self, self)

        # Some stuff has to be done before initialization of other ancestors...
        # messages store / checkers / reporter / astroid manager

        # Attributes for reporters
        self.reporter: reporters.BaseReporter | reporters.MultiReporter
        if reporter:
            self.set_reporter(reporter)
        else:
            self.set_reporter(TextReporter())
        self._reporters: dict[str, type[reporters.BaseReporter]] = {}
        """Dictionary of possible but non-initialized reporters."""

        # Attributes for checkers and plugins
        self._checkers: defaultdict[
            str, list[checkers.BaseChecker]
        ] = collections.defaultdict(list)
        """Dictionary of registered and initialized checkers."""
        self._dynamic_plugins: dict[str, ModuleType | ModuleNotFoundError | bool] = {}
        """Set of loaded plugin names."""

        # Attributes related to stats
        self.stats = LinterStats()

        # Attributes related to (command-line) options and their parsing
        self.options: Options = options + _make_linter_options(self)
        for opt_group in option_groups:
            self.option_groups_descs[opt_group[0]] = opt_group[1]
        self._option_groups: tuple[tuple[str, str], ...] = (
            *option_groups,
            ("Messages control", "Options controlling analysis messages"),
            ("Reports", "Options related to output formatting and reporting"),
        )
        self.fail_on_symbols: list[str] = []
        """List of message symbols on which pylint should fail, set by --fail-on."""
        self._error_mode = False

        reporters.ReportsHandlerMixIn.__init__(self)
        checkers.BaseChecker.__init__(self, self)
        # provided reports
        self.reports = (
            ("RP0001", "Messages by category", report_total_messages_stats),
            (
                "RP0002",
                "% errors / warnings by module",
                report_messages_by_module_stats,
            ),
            ("RP0003", "Messages", report_messages_stats),
        )

        # Attributes related to registering messages and their handling
        self.msgs_store = MessageDefinitionStore(self.config.py_version)
        self.msg_status = 0
        self._by_id_managed_msgs: list[ManagedMessage] = []

        # Attributes related to visiting files
        self.file_state = FileState("", self.msgs_store, is_base_filestate=True)
        self.current_name: str = ""
        self.current_file: str | None = None
        self._ignore_file = False
        self._ignore_paths: list[Pattern[str]] = []

        self.register_checker(self)

    def load_default_plugins(self) -> None:
        checkers.initialize(self)
        reporters.initialize(self)

    def load_plugin_modules(self, modnames: Iterable[str], force: bool = False) -> None:
        """Check a list of pylint plugins modules, load and register them.

        If a module cannot be loaded, never try to load it again and instead
        store the error message for later use in ``load_plugin_configuration``
        below.

        If `force` is True (useful when multiprocessing), then the plugin is
        reloaded regardless if an entry exists in self._dynamic_plugins.
        """
        for modname in modnames:
            if modname in self._dynamic_plugins and not force:
                continue
            try:
                module = astroid.modutils.load_module_from_name(modname)
                module.register(self)
                self._dynamic_plugins[modname] = module
            except ModuleNotFoundError as mnf_e:
                self._dynamic_plugins[modname] = mnf_e

    def load_plugin_configuration(self) -> None:
        """Call the configuration hook for plugins.

        This walks through the list of plugins, grabs the "load_configuration"
        hook, if exposed, and calls it to allow plugins to configure specific
        settings.

        The result of attempting to load the plugin of the given name
        is stored in the dynamic plugins dictionary in ``load_plugin_modules`` above.

        ..note::
            This function previously always tried to load modules again, which
            led to some confusion and silent failure conditions as described
            in GitHub issue #7264. Making it use the stored result is more efficient, and
            means that we avoid the ``init-hook`` problems from before.
        """
        for modname, module_or_error in self._dynamic_plugins.items():
            if isinstance(module_or_error, ModuleNotFoundError):
                self.add_message(
                    "bad-plugin-value", args=(modname, module_or_error), line=0
                )
            elif hasattr(module_or_error, "load_configuration"):
                module_or_error.load_configuration(self)

        # We re-set all the dictionary values to True here to make sure the dict
        # is pickle-able. This is only a problem in multiprocessing/parallel mode.
        # (e.g. invoking pylint -j 2)
        self._dynamic_plugins = {
            modname: not isinstance(val, ModuleNotFoundError)
            for modname, val in self._dynamic_plugins.items()
        }

    def _load_reporters(self, reporter_names: str) -> None:
        """Load the reporters if they are available on _reporters."""
        if not self._reporters:
            return
        sub_reporters = []
        output_files = []
        with contextlib.ExitStack() as stack:
            for reporter_name in reporter_names.split(","):
                reporter_name, *reporter_output = reporter_name.split(":", 1)

                reporter = self._load_reporter_by_name(reporter_name)
                sub_reporters.append(reporter)
                if reporter_output:
                    output_file = stack.enter_context(
                        open(reporter_output[0], "w", encoding="utf-8")
                    )
                    reporter.out = output_file
                    output_files.append(output_file)

            # Extend the lifetime of all opened output files
            close_output_files = stack.pop_all().close

        if len(sub_reporters) > 1 or output_files:
            self.set_reporter(
                reporters.MultiReporter(
                    sub_reporters,
                    close_output_files,
                )
            )
        else:
            self.set_reporter(sub_reporters[0])

    def _load_reporter_by_name(self, reporter_name: str) -> reporters.BaseReporter:
        name = reporter_name.lower()
        if name in self._reporters:
            return self._reporters[name]()

        try:
            reporter_class = _load_reporter_by_class(reporter_name)
        except (ImportError, AttributeError, AssertionError) as e:
            raise exceptions.InvalidReporterError(name) from e

        return reporter_class()

    def set_reporter(
        self, reporter: reporters.BaseReporter | reporters.MultiReporter
    ) -> None:
        """Set the reporter used to display messages and reports."""
        self.reporter = reporter
        reporter.linter = self

    def register_reporter(self, reporter_class: type[reporters.BaseReporter]) -> None:
        """Registers a reporter class on the _reporters attribute."""
        self._reporters[reporter_class.name] = reporter_class

    def report_order(self) -> list[BaseChecker]:
        reports = sorted(self._reports, key=lambda x: getattr(x, "name", ""))
        try:
            # Remove the current reporter and add it
            # at the end of the list.
            reports.pop(reports.index(self))
        except ValueError:
            pass
        else:
            reports.append(self)
        return reports

    # checkers manipulation methods ############################################

    def register_checker(self, checker: checkers.BaseChecker) -> None:
        """This method auto registers the checker."""
        self._checkers[checker.name].append(checker)
        for r_id, r_title, r_cb in checker.reports:
            self.register_report(r_id, r_title, r_cb, checker)
        if hasattr(checker, "msgs"):
            self.msgs_store.register_messages_from_checker(checker)
            for message in checker.messages:
                if not message.default_enabled:
                    self.disable(message.msgid)
        # Register the checker, but disable all of its messages.
        if not getattr(checker, "enabled", True):
            self.disable(checker.name)

    def enable_fail_on_messages(self) -> None:
        """Enable 'fail on' msgs.

        Convert values in config.fail_on (which might be msg category, msg id,
        or symbol) to specific msgs, then enable and flag them for later.
        """
        fail_on_vals = self.config.fail_on
        if not fail_on_vals:
            return

        fail_on_cats = set()
        fail_on_msgs = set()
        for val in fail_on_vals:
            # If value is a category, add category, else add message
            if val in MSG_TYPES:
                fail_on_cats.add(val)
            else:
                fail_on_msgs.add(val)

        # For every message in every checker, if cat or msg flagged, enable check
        for all_checkers in self._checkers.values():
            for checker in all_checkers:
                for msg in checker.messages:
                    if msg.msgid in fail_on_msgs or msg.symbol in fail_on_msgs:
                        # message id/symbol matched, enable and flag it
                        self.enable(msg.msgid)
                        self.fail_on_symbols.append(msg.symbol)
                    elif msg.msgid[0] in fail_on_cats:
                        # message starts with a category value, flag (but do not enable) it
                        self.fail_on_symbols.append(msg.symbol)

    def any_fail_on_issues(self) -> bool:
        return any(x in self.fail_on_symbols for x in self.stats.by_msg.keys())

    def disable_reporters(self) -> None:
        """Disable all reporters."""
        for _reporters in self._reports.values():
            for report_id, _, _ in _reporters:
                self.disable_report(report_id)

    def _parse_error_mode(self) -> None:
        """Parse the current state of the error mode.

        Error mode: enable only errors; no reports, no persistent.
        """
        if not self._error_mode:
            return

        self.disable_noerror_messages()
        self.disable("miscellaneous")
        self.set_option("reports", False)
        self.set_option("persistent", False)
        self.set_option("score", False)

    # code checking methods ###################################################

    def get_checkers(self) -> list[BaseChecker]:
        """Return all available checkers as an ordered list."""
        return sorted(c for _checkers in self._checkers.values() for c in _checkers)

    def get_checker_names(self) -> list[str]:
        """Get all the checker names that this linter knows about."""
        return sorted(
            {
                checker.name
                for checker in self.get_checkers()
                if checker.name != MAIN_CHECKER_NAME
            }
        )

    def prepare_checkers(self) -> list[BaseChecker]:
        """Return checkers needed for activated messages and reports."""
        if not self.config.reports:
            self.disable_reporters()
        # get needed checkers
        needed_checkers: list[BaseChecker] = [self]
        for checker in self.get_checkers()[1:]:
            messages = {msg for msg in checker.msgs if self.is_message_enabled(msg)}
            if messages or any(self.report_is_enabled(r[0]) for r in checker.reports):
                needed_checkers.append(checker)
        return needed_checkers

    # pylint: disable=unused-argument
    @staticmethod
    def should_analyze_file(modname: str, path: str, is_argument: bool = False) -> bool:
        """Returns whether a module should be checked.

        This implementation returns True for all python source file, indicating
        that all files should be linted.

        Subclasses may override this method to indicate that modules satisfying
        certain conditions should not be linted.

        :param str modname: The name of the module to be checked.
        :param str path: The full path to the source code of the module.
        :param bool is_argument: Whether the file is an argument to pylint or not.
                                 Files which respect this property are always
                                 checked, since the user requested it explicitly.
        :returns: True if the module should be checked.
        """
        if is_argument:
            return True
        return path.endswith(".py")

    # pylint: enable=unused-argument

    def initialize(self) -> None:
        """Initialize linter for linting.

        This method is called before any linting is done.
        """
        self._ignore_paths = self.config.ignore_paths
        # initialize msgs_state now that all messages have been registered into
        # the store
        for msg in self.msgs_store.messages:
            if not msg.may_be_emitted(self.config.py_version):
                self._msgs_state[msg.msgid] = False

    def _discover_files(self, files_or_modules: Sequence[str]) -> Iterator[str]:
        """Discover python modules and packages in sub-directory.

        Returns iterator of paths to discovered modules and packages.
        """
        for something in files_or_modules:
            if os.path.isdir(something) and not os.path.isfile(
                os.path.join(something, "__init__.py")
            ):
                skip_subtrees: list[str] = []
                for root, _, files in os.walk(something):
                    if any(root.startswith(s) for s in skip_subtrees):
                        # Skip subtree of already discovered package.
                        continue

                    if _is_ignored_file(
                        root,
                        self.config.ignore,
                        self.config.ignore_patterns,
                        self.config.ignore_paths,
                    ):
                        skip_subtrees.append(root)
                        continue

                    if "__init__.py" in files:
                        skip_subtrees.append(root)
                        yield root
                    else:
                        yield from (
                            os.path.join(root, file)
                            for file in files
                            if file.endswith(".py")
                        )
            else:
                yield something

    def check(self, files_or_modules: Sequence[str]) -> None:
        """Main checking entry: check a list of files or modules from their name.

        files_or_modules is either a string or list of strings presenting modules to check.
        """
        self.initialize()
        if self.config.recursive:
            files_or_modules = tuple(self._discover_files(files_or_modules))
        if self.config.from_stdin:
            if len(files_or_modules) != 1:
                raise exceptions.InvalidArgsError(
                    "Missing filename required for --from-stdin"
                )

        extra_packages_paths = list(
            {
                discover_package_path(file_or_module, self.config.source_roots)
                for file_or_module in files_or_modules
            }
        )

        # TODO: Move the parallel invocation into step 3 of the checking process
        if not self.config.from_stdin and self.config.jobs > 1:
            original_sys_path = sys.path[:]
            check_parallel(
                self,
                self.config.jobs,
                self._iterate_file_descrs(files_or_modules),
                extra_packages_paths,
            )
            sys.path = original_sys_path
            return

        # 1) Get all FileItems
        with augmented_sys_path(extra_packages_paths):
            if self.config.from_stdin:
                fileitems = self._get_file_descr_from_stdin(files_or_modules[0])
                data: str | None = _read_stdin()
            else:
                fileitems = self._iterate_file_descrs(files_or_modules)
                data = None

        # The contextmanager also opens all checkers and sets up the PyLinter class
        with augmented_sys_path(extra_packages_paths):
            with self._astroid_module_checker() as check_astroid_module:
                # 2) Get the AST for each FileItem
                ast_per_fileitem = self._get_asts(fileitems, data)

                # 3) Lint each ast
                self._lint_files(ast_per_fileitem, check_astroid_module)

    def _get_asts(
        self, fileitems: Iterator[FileItem], data: str | None
    ) -> dict[FileItem, nodes.Module | None]:
        """Get the AST for all given FileItems."""
        ast_per_fileitem: dict[FileItem, nodes.Module | None] = {}

        for fileitem in fileitems:
            self.set_current_module(fileitem.name, fileitem.filepath)

            try:
                ast_per_fileitem[fileitem] = self.get_ast(
                    fileitem.filepath, fileitem.name, data
                )
            except astroid.AstroidBuildingError as ex:
                template_path = prepare_crash_report(
                    ex, fileitem.filepath, self.crash_file_path
                )
                msg = get_fatal_error_message(fileitem.filepath, template_path)
                self.add_message(
                    "astroid-error",
                    args=(fileitem.filepath, msg),
                    confidence=HIGH,
                )

        return ast_per_fileitem

    def check_single_file_item(self, file: FileItem) -> None:
        """Check single file item.

        The arguments are the same that are documented in _check_files

        initialize() should be called before calling this method
        """
        with self._astroid_module_checker() as check_astroid_module:
            self._check_file(self.get_ast, check_astroid_module, file)

    def _lint_files(
        self,
        ast_mapping: dict[FileItem, nodes.Module | None],
        check_astroid_module: Callable[[nodes.Module], bool | None],
    ) -> None:
        """Lint all AST modules from a mapping.."""
        for fileitem, module in ast_mapping.items():
            if module is None:
                continue
            try:
                self._lint_file(fileitem, module, check_astroid_module)
            except Exception as ex:  # pylint: disable=broad-except
                template_path = prepare_crash_report(
                    ex, fileitem.filepath, self.crash_file_path
                )
                msg = get_fatal_error_message(fileitem.filepath, template_path)
                if isinstance(ex, astroid.AstroidError):
                    self.add_message(
                        "astroid-error", args=(fileitem.filepath, msg), confidence=HIGH
                    )
                else:
                    self.add_message("fatal", args=msg, confidence=HIGH)

    def _lint_file(
        self,
        file: FileItem,
        module: nodes.Module,
        check_astroid_module: Callable[[nodes.Module], bool | None],
    ) -> None:
        """Lint a file using the passed utility function check_astroid_module).

        :param FileItem file: data about the file
        :param nodes.Module module: the ast module to lint
        :param Callable check_astroid_module: callable checking an AST taking the following
               arguments
        - ast: AST of the module
        :raises AstroidError: for any failures stemming from astroid
        """
        self.set_current_module(file.name, file.filepath)
        self._ignore_file = False
        self.file_state = FileState(file.modpath, self.msgs_store, module)
        # fix the current file (if the source file was not available or
        # if it's actually a c extension)
        self.current_file = module.file

        try:
            check_astroid_module(module)
        except Exception as e:
            raise astroid.AstroidError from e

        # warn about spurious inline messages handling
        spurious_messages = self.file_state.iter_spurious_suppression_messages(
            self.msgs_store
        )
        for msgid, line, args in spurious_messages:
            self.add_message(msgid, line, None, args)

    def _check_file(
        self,
        get_ast: GetAstProtocol,
        check_astroid_module: Callable[[nodes.Module], bool | None],
        file: FileItem,
    ) -> None:
        """Check a file using the passed utility functions (get_ast and
        check_astroid_module).

        :param callable get_ast: callable returning AST from defined file taking the
                                 following arguments
        - filepath: path to the file to check
        - name: Python module name
        :param callable check_astroid_module: callable checking an AST taking the following
               arguments
        - ast: AST of the module
        :param FileItem file: data about the file
        :raises AstroidError: for any failures stemming from astroid
        """
        self.set_current_module(file.name, file.filepath)
        # get the module representation
        ast_node = get_ast(file.filepath, file.name)
        if ast_node is None:
            return

        self._ignore_file = False

        self.file_state = FileState(file.modpath, self.msgs_store, ast_node)
        # fix the current file (if the source file was not available or
        # if it's actually a c extension)
        self.current_file = ast_node.file
        try:
            check_astroid_module(ast_node)
        except Exception as e:  # pragma: no cover
            raise astroid.AstroidError from e
        # warn about spurious inline messages handling
        spurious_messages = self.file_state.iter_spurious_suppression_messages(
            self.msgs_store
        )
        for msgid, line, args in spurious_messages:
            self.add_message(msgid, line, None, args)

    def _get_file_descr_from_stdin(self, filepath: str) -> Iterator[FileItem]:
        """Return file description (tuple of module name, file path, base name) from
        given file path.

        This method is used for creating suitable file description for _check_files when the
        source is standard input.
        """
        if _is_ignored_file(
            filepath,
            self.config.ignore,
            self.config.ignore_patterns,
            self.config.ignore_paths,
        ):
            return

        try:
            # Note that this function does not really perform an
            # __import__ but may raise an ImportError exception, which
            # we want to catch here.
            modname = ".".join(astroid.modutils.modpath_from_file(filepath))
        except ImportError:
            modname = os.path.splitext(os.path.basename(filepath))[0]

        yield FileItem(modname, filepath, filepath)

    def _iterate_file_descrs(
        self, files_or_modules: Sequence[str]
    ) -> Iterator[FileItem]:
        """Return generator yielding file descriptions (tuples of module name, file
        path, base name).

        The returned generator yield one item for each Python module that should be linted.
        """
        for descr in self._expand_files(files_or_modules).values():
            name, filepath, is_arg = descr["name"], descr["path"], descr["isarg"]
            if self.should_analyze_file(name, filepath, is_argument=is_arg):
                yield FileItem(name, filepath, descr["basename"])

    def _expand_files(
        self, files_or_modules: Sequence[str]
    ) -> dict[str, ModuleDescriptionDict]:
        """Get modules and errors from a list of modules and handle errors."""
        result, errors = expand_modules(
            files_or_modules,
            self.config.source_roots,
            self.config.ignore,
            self.config.ignore_patterns,
            self._ignore_paths,
        )
        for error in errors:
            message = modname = error["mod"]
            key = error["key"]
            self.set_current_module(modname)
            if key == "fatal":
                message = str(error["ex"]).replace(os.getcwd() + os.sep, "")
            self.add_message(key, args=message)
        return result

    def set_current_module(self, modname: str, filepath: str | None = None) -> None:
        """Set the name of the currently analyzed module and
        init statistics for it.
        """
        if not modname and filepath is None:
            return
        self.reporter.on_set_current_module(modname or "", filepath)
        self.current_name = modname
        self.current_file = filepath or modname
        self.stats.init_single_module(modname or "")

        # If there is an actual filepath we might need to update the config attribute
        if filepath:
            namespace = self._get_namespace_for_file(
                Path(filepath), self._directory_namespaces
            )
            if namespace:
                self.config = namespace or self._base_config

    def _get_namespace_for_file(
        self, filepath: Path, namespaces: DirectoryNamespaceDict
    ) -> argparse.Namespace | None:
        for directory in namespaces:
            if _is_relative_to(filepath, directory):
                namespace = self._get_namespace_for_file(
                    filepath, namespaces[directory][1]
                )
                if namespace is None:
                    return namespaces[directory][0]
        return None

    @contextlib.contextmanager
    def _astroid_module_checker(
        self,
    ) -> Iterator[Callable[[nodes.Module], bool | None]]:
        """Context manager for checking ASTs.

        The value in the context is callable accepting AST as its only argument.
        """
        walker = ASTWalker(self)
        _checkers = self.prepare_checkers()
        tokencheckers = [
            c for c in _checkers if isinstance(c, checkers.BaseTokenChecker)
        ]
        rawcheckers = [
            c for c in _checkers if isinstance(c, checkers.BaseRawFileChecker)
        ]
        for checker in _checkers:
            checker.open()
            walker.add_checker(checker)

        yield functools.partial(
            self.check_astroid_module,
            walker=walker,
            tokencheckers=tokencheckers,
            rawcheckers=rawcheckers,
        )

        # notify global end
        self.stats.statement = walker.nbstatements
        for checker in reversed(_checkers):
            checker.close()

    def get_ast(
        self, filepath: str, modname: str, data: str | None = None
    ) -> nodes.Module | None:
        """Return an ast(roid) representation of a module or a string.

        :param filepath: path to checked file.
        :param str modname: The name of the module to be checked.
        :param str data: optional contents of the checked file.
        :returns: the AST
        :rtype: astroid.nodes.Module
        :raises AstroidBuildingError: Whenever we encounter an unexpected exception
        """
        try:
            if data is None:
                return MANAGER.ast_from_file(filepath, modname, source=True)
            return astroid.builder.AstroidBuilder(MANAGER).string_build(
                data, modname, filepath
            )
        except astroid.AstroidSyntaxError as ex:
            line = getattr(ex.error, "lineno", None)
            if line is None:
                line = 0
            self.add_message(
                "syntax-error",
                line=line,
                col_offset=getattr(ex.error, "offset", None),
                args=f"Parsing failed: '{ex.error}'",
                confidence=HIGH,
            )
        except astroid.AstroidBuildingError as ex:
            self.add_message("parse-error", args=ex)
        except Exception as ex:
            traceback.print_exc()
            # We raise BuildingError here as this is essentially an astroid issue
            # Creating an issue template and adding the 'astroid-error' message is handled
            # by caller: _check_files
            raise astroid.AstroidBuildingError(
                "Building error when trying to create ast representation of module '{modname}'",
                modname=modname,
            ) from ex
        return None

    def check_astroid_module(
        self,
        ast_node: nodes.Module,
        walker: ASTWalker,
        rawcheckers: list[checkers.BaseRawFileChecker],
        tokencheckers: list[checkers.BaseTokenChecker],
    ) -> bool | None:
        """Check a module from its astroid representation.

        For return value see _check_astroid_module
        """
        before_check_statements = walker.nbstatements

        retval = self._check_astroid_module(
            ast_node, walker, rawcheckers, tokencheckers
        )
        self.stats.by_module[self.current_name]["statement"] = (
            walker.nbstatements - before_check_statements
        )

        return retval

    def _check_astroid_module(
        self,
        node: nodes.Module,
        walker: ASTWalker,
        rawcheckers: list[checkers.BaseRawFileChecker],
        tokencheckers: list[checkers.BaseTokenChecker],
    ) -> bool | None:
        """Check given AST node with given walker and checkers.

        :param astroid.nodes.Module node: AST node of the module to check
        :param pylint.utils.ast_walker.ASTWalker walker: AST walker
        :param list rawcheckers: List of token checkers to use
        :param list tokencheckers: List of raw checkers to use

        :returns: True if the module was checked, False if ignored,
            None if the module contents could not be parsed
        """
        try:
            tokens = utils.tokenize_module(node)
        except tokenize.TokenError as ex:
            self.add_message(
                "syntax-error",
                line=ex.args[1][0],
                col_offset=ex.args[1][1],
                args=ex.args[0],
                confidence=HIGH,
            )
            return None

        if not node.pure_python:
            self.add_message("raw-checker-failed", args=node.name)
        else:
            # assert astroid.file.endswith('.py')
            # Parse module/block level option pragma's
            self.process_tokens(tokens)
            if self._ignore_file:
                return False
            # run raw and tokens checkers
            for raw_checker in rawcheckers:
                raw_checker.process_module(node)
            for token_checker in tokencheckers:
                token_checker.process_tokens(tokens)
        # generate events to astroid checkers
        walker.walk(node)
        return True

    def open(self) -> None:
        """Initialize counters."""
        MANAGER.always_load_extensions = self.config.unsafe_load_any_extension
        MANAGER.max_inferable_values = self.config.limit_inference_results
        MANAGER.extension_package_whitelist.update(self.config.extension_pkg_allow_list)
        if self.config.extension_pkg_whitelist:
            MANAGER.extension_package_whitelist.update(
                self.config.extension_pkg_whitelist
            )
        self.stats.reset_message_count()

    def generate_reports(self) -> int | None:
        """Close the whole package /module, it's time to make reports !

        if persistent run, pickle results for later comparison
        """
        # Display whatever messages are left on the reporter.
        self.reporter.display_messages(report_nodes.Section())
        if not self.file_state._is_base_filestate:
            # load previous results if any
            previous_stats = load_results(self.file_state.base_name)
            self.reporter.on_close(self.stats, previous_stats)
            if self.config.reports:
                sect = self.make_reports(self.stats, previous_stats)
            else:
                sect = report_nodes.Section()

            if self.config.reports:
                self.reporter.display_reports(sect)
            score_value = self._report_evaluation()
            # save results if persistent run
            if self.config.persistent:
                save_results(self.stats, self.file_state.base_name)
        else:
            self.reporter.on_close(self.stats, LinterStats())
            score_value = None
        return score_value

    def _report_evaluation(self) -> int | None:
        """Make the global evaluation report."""
        # check with at least a statement (usually 0 when there is a
        # syntax error preventing pylint from further processing)
        note = None
        previous_stats = load_results(self.file_state.base_name)
        if self.stats.statement == 0:
            return note

        # get a global note for the code
        evaluation = self.config.evaluation
        try:
            stats_dict = {
                "fatal": self.stats.fatal,
                "error": self.stats.error,
                "warning": self.stats.warning,
                "refactor": self.stats.refactor,
                "convention": self.stats.convention,
                "statement": self.stats.statement,
                "info": self.stats.info,
            }
            note = eval(evaluation, {}, stats_dict)  # pylint: disable=eval-used
        except Exception as ex:  # pylint: disable=broad-except
            msg = f"An exception occurred while rating: {ex}"
        else:
            self.stats.global_note = note
            msg = f"Your code has been rated at {note:.2f}/10"
            if previous_stats:
                pnote = previous_stats.global_note
                if pnote is not None:
                    msg += f" (previous run: {pnote:.2f}/10, {note - pnote:+.2f})"

        if self.config.score:
            sect = report_nodes.EvaluationSection(msg)
            self.reporter.display_reports(sect)
        return note

    def _add_one_message(
        self,
        message_definition: MessageDefinition,
        line: int | None,
        node: nodes.NodeNG | None,
        args: Any | None,
        confidence: interfaces.Confidence | None,
        col_offset: int | None,
        end_lineno: int | None,
        end_col_offset: int | None,
    ) -> None:
        """After various checks have passed a single Message is
        passed to the reporter and added to stats.
        """
        message_definition.check_message_definition(line, node)

        # Look up "location" data of node if not yet supplied
        if node:
            if node.position:
                if not line:
                    line = node.position.lineno
                if not col_offset:
                    col_offset = node.position.col_offset
                if not end_lineno:
                    end_lineno = node.position.end_lineno
                if not end_col_offset:
                    end_col_offset = node.position.end_col_offset
            else:
                if not line:
                    line = node.fromlineno
                if not col_offset:
                    col_offset = node.col_offset
                if not end_lineno:
                    end_lineno = node.end_lineno
                if not end_col_offset:
                    end_col_offset = node.end_col_offset

        # should this message be displayed
        if not self.is_message_enabled(message_definition.msgid, line, confidence):
            self.file_state.handle_ignored_message(
                self._get_message_state_scope(
                    message_definition.msgid, line, confidence
                ),
                message_definition.msgid,
                line,
            )
            return

        # update stats
        msg_cat = MSG_TYPES[message_definition.msgid[0]]
        self.msg_status |= MSG_TYPES_STATUS[message_definition.msgid[0]]
        self.stats.increase_single_message_count(msg_cat, 1)
        self.stats.increase_single_module_message_count(self.current_name, msg_cat, 1)
        try:
            self.stats.by_msg[message_definition.symbol] += 1
        except KeyError:
            self.stats.by_msg[message_definition.symbol] = 1
        # Interpolate arguments into message string
        msg = message_definition.msg
        if args is not None:
            msg %= args
        # get module and object
        if node is None:
            module, obj = self.current_name, ""
            abspath = self.current_file
        else:
            module, obj = utils.get_module_and_frameid(node)
            abspath = node.root().file
        if abspath is not None:
            path = abspath.replace(self.reporter.path_strip_prefix, "", 1)
        else:
            path = "configuration"
        # add the message
        self.reporter.handle_message(
            Message(
                message_definition.msgid,
                message_definition.symbol,
                MessageLocationTuple(
                    abspath or "",
                    path,
                    module or "",
                    obj,
                    line or 1,
                    col_offset or 0,
                    end_lineno,
                    end_col_offset,
                ),
                msg,
                confidence,
            )
        )

    def add_message(
        self,
        msgid: str,
        line: int | None = None,
        node: nodes.NodeNG | None = None,
        args: Any | None = None,
        confidence: interfaces.Confidence | None = None,
        col_offset: int | None = None,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """Adds a message given by ID or name.

        If provided, the message string is expanded using args.

        AST checkers must provide the node argument (but may optionally
        provide line if the line number is different), raw and token checkers
        must provide the line argument.
        """
        if confidence is None:
            confidence = interfaces.UNDEFINED
        message_definitions = self.msgs_store.get_message_definitions(msgid)
        for message_definition in message_definitions:
            self._add_one_message(
                message_definition,
                line,
                node,
                args,
                confidence,
                col_offset,
                end_lineno,
                end_col_offset,
            )

    def add_ignored_message(
        self,
        msgid: str,
        line: int,
        node: nodes.NodeNG | None = None,
        confidence: interfaces.Confidence | None = interfaces.UNDEFINED,
    ) -> None:
        """Prepares a message to be added to the ignored message storage.

        Some checks return early in special cases and never reach add_message(),
        even though they would normally issue a message.
        This creates false positives for useless-suppression.
        This function avoids this by adding those message to the ignored msgs attribute
        """
        message_definitions = self.msgs_store.get_message_definitions(msgid)
        for message_definition in message_definitions:
            message_definition.check_message_definition(line, node)
            self.file_state.handle_ignored_message(
                self._get_message_state_scope(
                    message_definition.msgid, line, confidence
                ),
                message_definition.msgid,
                line,
            )

    def _emit_stashed_messages(self) -> None:
        for keys, values in self._stashed_messages.items():
            modname, symbol = keys
            self.linter.set_current_module(modname)
            for args in values:
                self.add_message(
                    symbol,
                    args=args,
                    line=0,
                    confidence=HIGH,
                )
        self._stashed_messages = collections.defaultdict(list)

back/venvmsg/Lib/site-packages/pylint/lint/pylinter.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import collections
from collections import defaultdict

from pylint import checkers, exceptions
from pylint.reporters.ureports.nodes import Section, Table
from pylint.utils import LinterStats


def report_total_messages_stats(
    sect: Section,
    stats: LinterStats,
    previous_stats: LinterStats | None,
) -> None:
    """Make total errors / warnings report."""
    lines = ["type", "number", "previous", "difference"]
    lines += checkers.table_lines_from_stats(stats, previous_stats, "message_types")
    sect.append(Table(children=lines, cols=4, rheaders=1))


def report_messages_stats(
    sect: Section,
    stats: LinterStats,
    _: LinterStats | None,
) -> None:
    """Make messages type report."""
    by_msg_stats = stats.by_msg
    in_order = sorted(
        (value, msg_id)
        for msg_id, value in by_msg_stats.items()
        if not msg_id.startswith("I")
    )
    in_order.reverse()
    lines = ["message id", "occurrences"]
    for value, msg_id in in_order:
        lines += [msg_id, str(value)]
    sect.append(Table(children=lines, cols=2, rheaders=1))


def report_messages_by_module_stats(
    sect: Section,
    stats: LinterStats,
    _: LinterStats | None,
) -> None:
    """Make errors / warnings by modules report."""
    module_stats = stats.by_module
    if len(module_stats) == 1:
        # don't print this report when we are analysing a single module
        raise exceptions.EmptyReportError()
    by_mod: defaultdict[str, dict[str, int | float]] = collections.defaultdict(dict)
    for m_type in ("fatal", "error", "warning", "refactor", "convention"):
        total = stats.get_global_message_count(m_type)
        for module in module_stats.keys():
            mod_total = stats.get_module_message_count(module, m_type)
            percent = 0 if total == 0 else float(mod_total * 100) / total
            by_mod[module][m_type] = percent
    sorted_result = []
    for module, mod_info in by_mod.items():
        sorted_result.append(
            (
                mod_info["error"],
                mod_info["warning"],
                mod_info["refactor"],
                mod_info["convention"],
                module,
            )
        )
    sorted_result.sort()
    sorted_result.reverse()
    lines = ["module", "error", "warning", "refactor", "convention"]
    for line in sorted_result:
        # Don't report clean modules.
        if all(entry == 0 for entry in line[:-1]):
            continue
        lines.append(line[-1])
        for val in line[:-1]:
            lines.append(f"{val:.2f}")
    if len(lines) == 5:
        raise exceptions.EmptyReportError()
    sect.append(Table(children=lines, cols=5, rheaders=1))

back/venvmsg/Lib/site-packages/pylint/lint/report_functions.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import os
import sys
import warnings
from collections.abc import Sequence
from pathlib import Path
from typing import ClassVar

from pylint import config
from pylint.checkers.utils import clear_lru_caches
from pylint.config._pylint_config import (
    _handle_pylint_config_commands,
    _register_generate_config_options,
)
from pylint.config.config_initialization import _config_initialization
from pylint.config.exceptions import ArgumentPreprocessingError
from pylint.config.utils import _preprocess_options
from pylint.constants import full_version
from pylint.lint.base_options import _make_run_options
from pylint.lint.pylinter import MANAGER, PyLinter
from pylint.reporters.base_reporter import BaseReporter

try:
    import multiprocessing
    from multiprocessing import synchronize  # noqa pylint: disable=unused-import
except ImportError:
    multiprocessing = None  # type: ignore[assignment]

try:
    from concurrent.futures import ProcessPoolExecutor
except ImportError:
    ProcessPoolExecutor = None  # type: ignore[assignment,misc]


def _query_cpu() -> int | None:
    """Try to determine number of CPUs allotted in a docker container.

    This is based on discussion and copied from suggestions in
    https://bugs.python.org/issue36054.
    """
    cpu_quota, avail_cpu = None, None

    if Path("/sys/fs/cgroup/cpu/cpu.cfs_quota_us").is_file():
        with open("/sys/fs/cgroup/cpu/cpu.cfs_quota_us", encoding="utf-8") as file:
            # Not useful for AWS Batch based jobs as result is -1, but works on local linux systems
            cpu_quota = int(file.read().rstrip())

    if (
        cpu_quota
        and cpu_quota != -1
        and Path("/sys/fs/cgroup/cpu/cpu.cfs_period_us").is_file()
    ):
        with open("/sys/fs/cgroup/cpu/cpu.cfs_period_us", encoding="utf-8") as file:
            cpu_period = int(file.read().rstrip())
        # Divide quota by period and you should get num of allotted CPU to the container,
        # rounded down if fractional.
        avail_cpu = int(cpu_quota / cpu_period)
    elif Path("/sys/fs/cgroup/cpu/cpu.shares").is_file():
        with open("/sys/fs/cgroup/cpu/cpu.shares", encoding="utf-8") as file:
            cpu_shares = int(file.read().rstrip())
        # For AWS, gives correct value * 1024.
        avail_cpu = int(cpu_shares / 1024)

    # In K8s Pods also a fraction of a single core could be available
    # As multiprocessing is not able to run only a "fraction" of process
    # assume we have 1 CPU available
    if avail_cpu == 0:
        avail_cpu = 1

    return avail_cpu


def _cpu_count() -> int:
    """Use sched_affinity if available for virtualized or containerized
    environments.
    """
    cpu_share = _query_cpu()
    cpu_count = None
    sched_getaffinity = getattr(os, "sched_getaffinity", None)
    # pylint: disable=not-callable,using-constant-test,useless-suppression
    if sched_getaffinity:
        cpu_count = len(sched_getaffinity(0))
    elif multiprocessing:
        cpu_count = multiprocessing.cpu_count()
    else:
        cpu_count = 1
    if sys.platform == "win32":
        # See also https://github.com/python/cpython/issues/94242
        cpu_count = min(cpu_count, 56)  # pragma: no cover
    if cpu_share is not None:
        return min(cpu_share, cpu_count)
    return cpu_count


class Run:
    """Helper class to use as main for pylint with 'run(*sys.argv[1:])'."""

    LinterClass = PyLinter
    option_groups = (
        (
            "Commands",
            "Options which are actually commands. Options in this \
group are mutually exclusive.",
        ),
    )
    _is_pylint_config: ClassVar[bool] = False
    """Boolean whether or not this is a 'pylint-config' run.

    Used by _PylintConfigRun to make the 'pylint-config' command work.
    """

    # pylint: disable = too-many-statements, too-many-branches
    def __init__(
        self,
        args: Sequence[str],
        reporter: BaseReporter | None = None,
        exit: bool = True,  # pylint: disable=redefined-builtin
    ) -> None:
        # Immediately exit if user asks for version
        if "--version" in args:
            print(full_version)
            sys.exit(0)

        self._rcfile: str | None = None
        self._output: str | None = None
        self._plugins: list[str] = []
        self.verbose: bool = False

        # Pre-process certain options and remove them from args list
        try:
            args = _preprocess_options(self, args)
        except ArgumentPreprocessingError as ex:
            print(ex, file=sys.stderr)
            sys.exit(32)

        # Determine configuration file
        if self._rcfile is None:
            default_file = next(config.find_default_config_files(), None)
            if default_file:
                self._rcfile = str(default_file)

        self.linter = linter = self.LinterClass(
            _make_run_options(self),
            option_groups=self.option_groups,
            pylintrc=self._rcfile,
        )
        # register standard checkers
        linter.load_default_plugins()
        # load command line plugins
        linter.load_plugin_modules(self._plugins)

        # Register the options needed for 'pylint-config'
        # By not registering them by default they don't show up in the normal usage message
        if self._is_pylint_config:
            _register_generate_config_options(linter._arg_parser)

        args = _config_initialization(
            linter, args, reporter, config_file=self._rcfile, verbose_mode=self.verbose
        )

        # Handle the 'pylint-config' command
        if self._is_pylint_config:
            warnings.warn(
                "NOTE: The 'pylint-config' command is experimental and usage can change",
                UserWarning,
                stacklevel=2,
            )
            code = _handle_pylint_config_commands(linter)
            if exit:
                sys.exit(code)
            return

        # Display help if there are no files to lint or no checks enabled
        if not args or len(linter.config.disable) == len(
            linter.msgs_store._messages_definitions
        ):
            print("No files to lint: exiting.")
            sys.exit(32)

        if linter.config.jobs < 0:
            print(
                f"Jobs number ({linter.config.jobs}) should be greater than or equal to 0",
                file=sys.stderr,
            )
            sys.exit(32)
        if linter.config.jobs > 1 or linter.config.jobs == 0:
            if ProcessPoolExecutor is None:
                print(
                    "concurrent.futures module is missing, fallback to single process",
                    file=sys.stderr,
                )
                linter.set_option("jobs", 1)
            elif linter.config.jobs == 0:
                linter.config.jobs = _cpu_count()

        if self._output:
            try:
                with open(self._output, "w", encoding="utf-8") as output:
                    linter.reporter.out = output
                    linter.check(args)
                    score_value = linter.generate_reports()
            except OSError as ex:
                print(ex, file=sys.stderr)
                sys.exit(32)
        else:
            linter.check(args)
            score_value = linter.generate_reports()
        if linter.config.clear_cache_post_run:
            clear_lru_caches()
            MANAGER.clear_cache()

        if exit:
            if linter.config.exit_zero:
                sys.exit(0)
            elif linter.any_fail_on_issues():
                # We need to make sure we return a failing exit code in this case.
                # So we use self.linter.msg_status if that is non-zero, otherwise we just return 1.
                sys.exit(self.linter.msg_status or 1)
            elif score_value is not None:
                if score_value >= linter.config.fail_under:
                    sys.exit(0)
                else:
                    # We need to make sure we return a failing exit code in this case.
                    # So we use self.linter.msg_status if that is non-zero, otherwise we just return 1.
                    sys.exit(self.linter.msg_status or 1)
            else:
                sys.exit(self.linter.msg_status)


class _PylintConfigRun(Run):
    """A private wrapper for the 'pylint-config' command."""

    _is_pylint_config: ClassVar[bool] = True
    """Boolean whether or not this is a 'pylint-config' run.

    Used by _PylintConfigRun to make the 'pylint-config' command work.
    """

back/venvmsg/Lib/site-packages/pylint/lint/run.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import contextlib
import platform
import sys
import traceback
from collections.abc import Iterator, Sequence
from datetime import datetime
from pathlib import Path

from pylint.constants import PYLINT_HOME, full_version


def prepare_crash_report(ex: Exception, filepath: str, crash_file_path: str) -> Path:
    issue_template_path = (
        Path(PYLINT_HOME) / datetime.now().strftime(str(crash_file_path))
    ).resolve()
    with open(filepath, encoding="utf8") as f:
        file_content = f.read()
    template = ""
    if not issue_template_path.exists():
        template = """\
First, please verify that the bug is not already filled:
https://github.com/pylint-dev/pylint/issues/

Then create a new issue:
https://github.com/pylint-dev/pylint/issues/new?labels=Crash 💥%2CNeeds triage 📥


"""
    template += f"""
Issue title:
Crash ``{ex}`` (if possible, be more specific about what made pylint crash)

### Bug description

When parsing the following ``a.py``:

<!--
 If sharing the code is not an option, please state so,
 but providing only the stacktrace would still be helpful.
 -->

```python
{file_content}
```

### Command used

```shell
pylint a.py
```

### Pylint output

<details open>
    <summary>
        pylint crashed with a ``{ex.__class__.__name__}`` and with the following stacktrace:
    </summary>

```python
"""
    template += traceback.format_exc()
    template += f"""
```


</details>

### Expected behavior

No crash.

### Pylint version

```shell
{full_version}
```

### OS / Environment

{sys.platform} ({platform.system()})

### Additional dependencies

<!--
Please remove this part if you're not using any of
your dependencies in the example.
 -->
"""
    try:
        with open(issue_template_path, "a", encoding="utf8") as f:
            f.write(template)
    except Exception as exc:  # pylint: disable=broad-except
        print(
            f"Can't write the issue template for the crash in {issue_template_path} "
            f"because of: '{exc}'\nHere's the content anyway:\n{template}.",
            file=sys.stderr,
        )
    return issue_template_path


def get_fatal_error_message(filepath: str, issue_template_path: Path) -> str:
    return (
        f"Fatal error while checking '{filepath}'. "
        f"Please open an issue in our bug tracker so we address this. "
        f"There is a pre-filled template that you can use in '{issue_template_path}'."
    )


def _augment_sys_path(additional_paths: Sequence[str]) -> list[str]:
    original = list(sys.path)
    changes = []
    seen = set()
    for additional_path in additional_paths:
        if additional_path not in seen:
            changes.append(additional_path)
            seen.add(additional_path)

    sys.path[:] = changes + sys.path
    return original


@contextlib.contextmanager
def augmented_sys_path(additional_paths: Sequence[str]) -> Iterator[None]:
    """Augment 'sys.path' by adding non-existent entries from additional_paths."""
    original = _augment_sys_path(additional_paths)
    try:
        yield
    finally:
        sys.path[:] = original


def _is_relative_to(self: Path, *other: Path) -> bool:
    """Checks if self is relative to other.

    Backport of pathlib.Path.is_relative_to for Python <3.9
    TODO: py39: Remove this backport and use stdlib function.
    """
    try:
        self.relative_to(*other)
        return True
    except ValueError:
        return False

back/venvmsg/Lib/site-packages/pylint/lint/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""All the classes related to Message handling."""

from pylint.message.message import Message
from pylint.message.message_definition import MessageDefinition
from pylint.message.message_definition_store import MessageDefinitionStore
from pylint.message.message_id_store import MessageIdStore

__all__ = [
    "Message",
    "MessageDefinition",
    "MessageDefinitionStore",
    "MessageIdStore",
]

back/venvmsg/Lib/site-packages/pylint/message/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from functools import lru_cache
from typing import NamedTuple


class DeletedMessage(NamedTuple):
    msgid: str
    symbol: str
    old_names: list[tuple[str, str]] = []


DELETED_MSGID_PREFIXES: list[int] = []

DELETED_MESSAGES_IDS = {
    # Everything until the next comment is from the PY3K+ checker
    "https://github.com/pylint-dev/pylint/pull/4942": [
        DeletedMessage("W1601", "apply-builtin"),
        DeletedMessage("E1601", "print-statement"),
        DeletedMessage("E1602", "parameter-unpacking"),
        DeletedMessage(
            "E1603", "unpacking-in-except", [("W0712", "old-unpacking-in-except")]
        ),
        DeletedMessage(
            "E1604", "old-raise-syntax", [("W0121", "old-old-raise-syntax")]
        ),
        DeletedMessage("E1605", "backtick", [("W0333", "old-backtick")]),
        DeletedMessage("E1609", "import-star-module-level"),
        DeletedMessage("W1601", "apply-builtin"),
        DeletedMessage("W1602", "basestring-builtin"),
        DeletedMessage("W1603", "buffer-builtin"),
        DeletedMessage("W1604", "cmp-builtin"),
        DeletedMessage("W1605", "coerce-builtin"),
        DeletedMessage("W1606", "execfile-builtin"),
        DeletedMessage("W1607", "file-builtin"),
        DeletedMessage("W1608", "long-builtin"),
        DeletedMessage("W1609", "raw_input-builtin"),
        DeletedMessage("W1610", "reduce-builtin"),
        DeletedMessage("W1611", "standarderror-builtin"),
        DeletedMessage("W1612", "unicode-builtin"),
        DeletedMessage("W1613", "xrange-builtin"),
        DeletedMessage("W1614", "coerce-method"),
        DeletedMessage("W1615", "delslice-method"),
        DeletedMessage("W1616", "getslice-method"),
        DeletedMessage("W1617", "setslice-method"),
        DeletedMessage("W1618", "no-absolute-import"),
        DeletedMessage("W1619", "old-division"),
        DeletedMessage("W1620", "dict-iter-method"),
        DeletedMessage("W1621", "dict-view-method"),
        DeletedMessage("W1622", "next-method-called"),
        DeletedMessage("W1623", "metaclass-assignment"),
        DeletedMessage(
            "W1624", "indexing-exception", [("W0713", "old-indexing-exception")]
        ),
        DeletedMessage("W1625", "raising-string", [("W0701", "old-raising-string")]),
        DeletedMessage("W1626", "reload-builtin"),
        DeletedMessage("W1627", "oct-method"),
        DeletedMessage("W1628", "hex-method"),
        DeletedMessage("W1629", "nonzero-method"),
        DeletedMessage("W1630", "cmp-method"),
        DeletedMessage("W1632", "input-builtin"),
        DeletedMessage("W1633", "round-builtin"),
        DeletedMessage("W1634", "intern-builtin"),
        DeletedMessage("W1635", "unichr-builtin"),
        DeletedMessage(
            "W1636", "map-builtin-not-iterating", [("W1631", "implicit-map-evaluation")]
        ),
        DeletedMessage("W1637", "zip-builtin-not-iterating"),
        DeletedMessage("W1638", "range-builtin-not-iterating"),
        DeletedMessage("W1639", "filter-builtin-not-iterating"),
        DeletedMessage("W1640", "using-cmp-argument"),
        DeletedMessage("W1642", "div-method"),
        DeletedMessage("W1643", "idiv-method"),
        DeletedMessage("W1644", "rdiv-method"),
        DeletedMessage("W1645", "exception-message-attribute"),
        DeletedMessage("W1646", "invalid-str-codec"),
        DeletedMessage("W1647", "sys-max-int"),
        DeletedMessage("W1648", "bad-python3-import"),
        DeletedMessage("W1649", "deprecated-string-function"),
        DeletedMessage("W1650", "deprecated-str-translate-call"),
        DeletedMessage("W1651", "deprecated-itertools-function"),
        DeletedMessage("W1652", "deprecated-types-field"),
        DeletedMessage("W1653", "next-method-defined"),
        DeletedMessage("W1654", "dict-items-not-iterating"),
        DeletedMessage("W1655", "dict-keys-not-iterating"),
        DeletedMessage("W1656", "dict-values-not-iterating"),
        DeletedMessage("W1657", "deprecated-operator-function"),
        DeletedMessage("W1658", "deprecated-urllib-function"),
        DeletedMessage("W1659", "xreadlines-attribute"),
        DeletedMessage("W1660", "deprecated-sys-function"),
        DeletedMessage("W1661", "exception-escape"),
        DeletedMessage("W1662", "comprehension-escape"),
    ],
    "https://github.com/pylint-dev/pylint/pull/3578": [
        DeletedMessage("W0312", "mixed-indentation"),
    ],
    "https://github.com/pylint-dev/pylint/pull/3577": [
        DeletedMessage(
            "C0326",
            "bad-whitespace",
            [
                ("C0323", "no-space-after-operator"),
                ("C0324", "no-space-after-comma"),
                ("C0322", "no-space-before-operator"),
            ],
        ),
    ],
    "https://github.com/pylint-dev/pylint/pull/3571": [
        DeletedMessage("C0330", "bad-continuation")
    ],
    "https://pylint.readthedocs.io/en/latest/whatsnew/1/1.4.html#what-s-new-in-pylint-1-4-3": [
        DeletedMessage("R0921", "abstract-class-not-used"),
        DeletedMessage("R0922", "abstract-class-little-used"),
        DeletedMessage("W0142", "star-args"),
    ],
    "https://github.com/pylint-dev/pylint/issues/2409": [
        DeletedMessage("W0232", "no-init"),
    ],
    "https://github.com/pylint-dev/pylint/pull/6421": [
        DeletedMessage("W0111", "assign-to-new-keyword"),
    ],
}
MOVED_TO_EXTENSIONS = {
    "https://pylint.readthedocs.io/en/latest/whatsnew/2/2.14/summary.html#removed-checkers": [
        DeletedMessage("R0201", "no-self-use")
    ],
}


@lru_cache(maxsize=None)
def is_deleted_symbol(symbol: str) -> str | None:
    """Return the explanation for removal if the message was removed."""
    for explanation, deleted_messages in DELETED_MESSAGES_IDS.items():
        for deleted_message in deleted_messages:
            if symbol == deleted_message.symbol or any(
                symbol == m[1] for m in deleted_message.old_names
            ):
                return explanation
    return None


@lru_cache(maxsize=None)
def is_deleted_msgid(msgid: str) -> str | None:
    """Return the explanation for removal if the message was removed."""
    for explanation, deleted_messages in DELETED_MESSAGES_IDS.items():
        for deleted_message in deleted_messages:
            if msgid == deleted_message.msgid or any(
                msgid == m[0] for m in deleted_message.old_names
            ):
                return explanation
    return None


@lru_cache(maxsize=None)
def is_moved_symbol(symbol: str) -> str | None:
    """Return the explanation for moving if the message was moved to extensions."""
    for explanation, moved_messages in MOVED_TO_EXTENSIONS.items():
        for moved_message in moved_messages:
            if symbol == moved_message.symbol or any(
                symbol == m[1] for m in moved_message.old_names
            ):
                return explanation
    return None


@lru_cache(maxsize=None)
def is_moved_msgid(msgid: str) -> str | None:
    """Return the explanation for moving if the message was moved to extensions."""
    for explanation, moved_messages in MOVED_TO_EXTENSIONS.items():
        for moved_message in moved_messages:
            if msgid == moved_message.msgid or any(
                msgid == m[0] for m in moved_message.old_names
            ):
                return explanation
    return None

back/venvmsg/Lib/site-packages/pylint/message/_deleted_message_ids.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from dataclasses import asdict, dataclass

from pylint.constants import MSG_TYPES
from pylint.interfaces import UNDEFINED, Confidence
from pylint.typing import MessageLocationTuple


@dataclass(unsafe_hash=True)
class Message:  # pylint: disable=too-many-instance-attributes
    """This class represent a message to be issued by the reporters."""

    msg_id: str
    symbol: str
    msg: str
    C: str
    category: str
    confidence: Confidence
    abspath: str
    path: str
    module: str
    obj: str
    line: int
    column: int
    end_line: int | None
    end_column: int | None

    def __init__(
        self,
        msg_id: str,
        symbol: str,
        location: MessageLocationTuple,
        msg: str,
        confidence: Confidence | None,
    ) -> None:
        self.msg_id = msg_id
        self.symbol = symbol
        self.msg = msg
        self.C = msg_id[0]
        self.category = MSG_TYPES[msg_id[0]]
        self.confidence = confidence or UNDEFINED
        self.abspath = location.abspath
        self.path = location.path
        self.module = location.module
        self.obj = location.obj
        self.line = location.line
        self.column = location.column
        self.end_line = location.end_line
        self.end_column = location.end_column

    def format(self, template: str) -> str:
        """Format the message according to the given template.

        The template format is the one of the format method :
        cf. https://docs.python.org/2/library/string.html#formatstrings
        """
        return template.format(**asdict(self))

    @property
    def location(self) -> MessageLocationTuple:
        return MessageLocationTuple(
            self.abspath,
            self.path,
            self.module,
            self.obj,
            self.line,
            self.column,
            self.end_line,
            self.end_column,
        )

back/venvmsg/Lib/site-packages/pylint/message/message.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import sys
from typing import TYPE_CHECKING, Any

from astroid import nodes

from pylint.constants import _SCOPE_EXEMPT, MSG_TYPES, WarningScope
from pylint.exceptions import InvalidMessageError
from pylint.utils import normalize_text

if TYPE_CHECKING:
    from pylint.checkers import BaseChecker


class MessageDefinition:
    # pylint: disable-next=too-many-arguments
    def __init__(
        self,
        checker: BaseChecker,
        msgid: str,
        msg: str,
        description: str,
        symbol: str,
        scope: str,
        minversion: tuple[int, int] | None = None,
        maxversion: tuple[int, int] | None = None,
        old_names: list[tuple[str, str]] | None = None,
        shared: bool = False,
        default_enabled: bool = True,
    ) -> None:
        self.checker_name = checker.name
        self.check_msgid(msgid)
        self.msgid = msgid
        self.symbol = symbol
        self.msg = msg
        self.description = description
        self.scope = scope
        self.minversion = minversion
        self.maxversion = maxversion
        self.shared = shared
        self.default_enabled = default_enabled
        self.old_names: list[tuple[str, str]] = []
        if old_names:
            for old_msgid, old_symbol in old_names:
                self.check_msgid(old_msgid)
                self.old_names.append(
                    (old_msgid, old_symbol),
                )

    @staticmethod
    def check_msgid(msgid: str) -> None:
        if len(msgid) != 5:
            raise InvalidMessageError(f"Invalid message id {msgid!r}")
        if msgid[0] not in MSG_TYPES:
            raise InvalidMessageError(f"Bad message type {msgid[0]} in {msgid!r}")

    def __eq__(self, other: Any) -> bool:
        return (
            isinstance(other, MessageDefinition)
            and self.msgid == other.msgid
            and self.symbol == other.symbol
        )

    def __repr__(self) -> str:
        return f"MessageDefinition:{self.symbol} ({self.msgid})"

    def __str__(self) -> str:
        return f"{self!r}:\n{self.msg} {self.description}"

    def may_be_emitted(self, py_version: tuple[int, ...] | sys._version_info) -> bool:
        """May the message be emitted using the configured py_version?"""
        if self.minversion is not None and self.minversion > py_version:
            return False
        if self.maxversion is not None and self.maxversion <= py_version:
            return False
        return True

    def format_help(self, checkerref: bool = False) -> str:
        """Return the help string for the given message id."""
        desc = self.description
        if checkerref:
            desc += f" This message belongs to the {self.checker_name} checker."
        title = self.msg
        if self.minversion or self.maxversion:
            restr = []
            if self.minversion:
                restr.append(f"< {'.'.join(str(n) for n in self.minversion)}")
            if self.maxversion:
                restr.append(f">= {'.'.join(str(n) for n in self.maxversion)}")
            restriction = " or ".join(restr)
            if checkerref:
                desc += f" It can't be emitted when using Python {restriction}."
            else:
                desc += (
                    f" This message can't be emitted when using Python {restriction}."
                )
        msg_help = normalize_text(" ".join(desc.split()), indent="  ")
        message_id = f"{self.symbol} ({self.msgid})"
        if title != "%s":
            title = title.splitlines()[0]
            return f":{message_id}: *{title.rstrip(' ')}*\n{msg_help}"
        return f":{message_id}:\n{msg_help}"

    def check_message_definition(
        self, line: int | None, node: nodes.NodeNG | None
    ) -> None:
        """Check MessageDefinition for possible errors."""
        if self.msgid[0] not in _SCOPE_EXEMPT:
            # Fatal messages and reports are special, the node/scope distinction
            # does not apply to them.
            if self.scope == WarningScope.LINE:
                if line is None:
                    raise InvalidMessageError(
                        f"Message {self.msgid} must provide line, got None"
                    )
                if node is not None:
                    raise InvalidMessageError(
                        f"Message {self.msgid} must only provide line, "
                        f"got line={line}, node={node}"
                    )
            elif self.scope == WarningScope.NODE:
                # Node-based warnings may provide an override line.
                if node is None:
                    raise InvalidMessageError(
                        f"Message {self.msgid} must provide Node, got None"
                    )

back/venvmsg/Lib/site-packages/pylint/message/message_definition.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import collections
import functools
import sys
from collections.abc import Sequence, ValuesView
from typing import TYPE_CHECKING

from pylint.exceptions import UnknownMessageError
from pylint.message.message_definition import MessageDefinition
from pylint.message.message_id_store import MessageIdStore

if TYPE_CHECKING:
    from pylint.checkers import BaseChecker


class MessageDefinitionStore:

    """The messages store knows information about every possible message definition but
    has no particular state during analysis.
    """

    def __init__(
        self, py_version: tuple[int, ...] | sys._version_info = sys.version_info
    ) -> None:
        self.message_id_store: MessageIdStore = MessageIdStore()
        # Primary registry for all active messages definitions.
        # It contains the 1:1 mapping from msgid to MessageDefinition.
        # Keys are msgid, values are MessageDefinition
        self._messages_definitions: dict[str, MessageDefinition] = {}
        # MessageDefinition kept by category
        self._msgs_by_category: dict[str, list[str]] = collections.defaultdict(list)
        self.py_version = py_version

    @property
    def messages(self) -> ValuesView[MessageDefinition]:
        """The list of all active messages."""
        return self._messages_definitions.values()

    def register_messages_from_checker(self, checker: BaseChecker) -> None:
        """Register all messages definitions from a checker."""
        checker.check_consistency()
        for message in checker.messages:
            self.register_message(message)

    def register_message(self, message: MessageDefinition) -> None:
        """Register a MessageDefinition with consistency in mind."""
        self.message_id_store.register_message_definition(
            message.msgid, message.symbol, message.old_names
        )
        self._messages_definitions[message.msgid] = message
        self._msgs_by_category[message.msgid[0]].append(message.msgid)

    # Since MessageDefinitionStore is only initialized once
    # and the arguments are relatively small we do not run the
    # risk of creating a large memory leak.
    # See discussion in: https://github.com/pylint-dev/pylint/pull/5673
    @functools.lru_cache(  # pylint: disable=method-cache-max-size-none # noqa: B019
        maxsize=None
    )
    def get_message_definitions(self, msgid_or_symbol: str) -> list[MessageDefinition]:
        """Returns the Message definition for either a numeric or symbolic id.

        The cache has no limit as its size will likely stay minimal. For each message we store
        about 1000 characters, so even if we would have 1000 messages the cache would only
        take up ~= 1 Mb.
        """
        return [
            self._messages_definitions[m]
            for m in self.message_id_store.get_active_msgids(msgid_or_symbol)
        ]

    def get_msg_display_string(self, msgid_or_symbol: str) -> str:
        """Generates a user-consumable representation of a message."""
        message_definitions = self.get_message_definitions(msgid_or_symbol)
        if len(message_definitions) == 1:
            return repr(message_definitions[0].symbol)
        return repr([md.symbol for md in message_definitions])

    def help_message(self, msgids_or_symbols: Sequence[str]) -> None:
        """Display help messages for the given message identifiers."""
        for msgids_or_symbol in msgids_or_symbols:
            try:
                for message_definition in self.get_message_definitions(
                    msgids_or_symbol
                ):
                    print(message_definition.format_help(checkerref=True))
                    print("")
            except UnknownMessageError as ex:
                print(ex)
                print("")
                continue

    def list_messages(self) -> None:
        """Output full messages list documentation in ReST format."""
        emittable, non_emittable = self.find_emittable_messages()
        print("Emittable messages with current interpreter:")
        for msg in emittable:
            print(msg.format_help(checkerref=False))
        print("\nNon-emittable messages with current interpreter:")
        for msg in non_emittable:
            print(msg.format_help(checkerref=False))
        print("")

    def find_emittable_messages(
        self,
    ) -> tuple[list[MessageDefinition], list[MessageDefinition]]:
        """Finds all emittable and non-emittable messages."""
        messages = sorted(self._messages_definitions.values(), key=lambda m: m.msgid)
        emittable = []
        non_emittable = []
        for message in messages:
            if message.may_be_emitted(self.py_version):
                emittable.append(message)
            else:
                non_emittable.append(message)
        return emittable, non_emittable

back/venvmsg/Lib/site-packages/pylint/message/message_definition_store.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import NoReturn

from pylint.exceptions import (
    DeletedMessageError,
    InvalidMessageError,
    MessageBecameExtensionError,
    UnknownMessageError,
)
from pylint.message._deleted_message_ids import (
    is_deleted_msgid,
    is_deleted_symbol,
    is_moved_msgid,
    is_moved_symbol,
)


class MessageIdStore:

    """The MessageIdStore store MessageId and make sure that there is a 1-1 relation
    between msgid and symbol.
    """

    def __init__(self) -> None:
        self.__msgid_to_symbol: dict[str, str] = {}
        self.__symbol_to_msgid: dict[str, str] = {}
        self.__old_names: dict[str, list[str]] = {}
        self.__active_msgids: dict[str, list[str]] = {}

    def __len__(self) -> int:
        return len(self.__msgid_to_symbol)

    def __repr__(self) -> str:
        result = "MessageIdStore: [\n"
        for msgid, symbol in self.__msgid_to_symbol.items():
            result += f"  - {msgid} ({symbol})\n"
        result += "]"
        return result

    def get_symbol(self, msgid: str) -> str:
        try:
            return self.__msgid_to_symbol[msgid.upper()]
        except KeyError as e:
            msg = f"'{msgid}' is not stored in the message store."
            raise UnknownMessageError(msg) from e

    def get_msgid(self, symbol: str) -> str:
        try:
            return self.__symbol_to_msgid[symbol]
        except KeyError as e:
            msg = f"'{symbol}' is not stored in the message store."
            raise UnknownMessageError(msg) from e

    def register_message_definition(
        self, msgid: str, symbol: str, old_names: list[tuple[str, str]]
    ) -> None:
        self.check_msgid_and_symbol(msgid, symbol)
        self.add_msgid_and_symbol(msgid, symbol)
        for old_msgid, old_symbol in old_names:
            self.check_msgid_and_symbol(old_msgid, old_symbol)
            self.add_legacy_msgid_and_symbol(old_msgid, old_symbol, msgid)

    def add_msgid_and_symbol(self, msgid: str, symbol: str) -> None:
        """Add valid message id.

        There is a little duplication with add_legacy_msgid_and_symbol to avoid a function call,
        this is called a lot at initialization.
        """
        self.__msgid_to_symbol[msgid] = symbol
        self.__symbol_to_msgid[symbol] = msgid

    def add_legacy_msgid_and_symbol(
        self, msgid: str, symbol: str, new_msgid: str
    ) -> None:
        """Add valid legacy message id.

        There is a little duplication with add_msgid_and_symbol to avoid a function call,
        this is called a lot at initialization.
        """
        self.__msgid_to_symbol[msgid] = symbol
        self.__symbol_to_msgid[symbol] = msgid
        existing_old_names = self.__old_names.get(msgid, [])
        existing_old_names.append(new_msgid)
        self.__old_names[msgid] = existing_old_names

    def check_msgid_and_symbol(self, msgid: str, symbol: str) -> None:
        existing_msgid: str | None = self.__symbol_to_msgid.get(symbol)
        existing_symbol: str | None = self.__msgid_to_symbol.get(msgid)
        if existing_symbol is None and existing_msgid is None:
            return  # both symbol and msgid are usable
        if existing_msgid is not None:
            if existing_msgid != msgid:
                self._raise_duplicate_msgid(symbol, msgid, existing_msgid)
        if existing_symbol and existing_symbol != symbol:
            # See https://github.com/python/mypy/issues/10559
            self._raise_duplicate_symbol(msgid, symbol, existing_symbol)

    @staticmethod
    def _raise_duplicate_symbol(msgid: str, symbol: str, other_symbol: str) -> NoReturn:
        """Raise an error when a symbol is duplicated."""
        symbols = [symbol, other_symbol]
        symbols.sort()
        error_message = f"Message id '{msgid}' cannot have both "
        error_message += f"'{symbols[0]}' and '{symbols[1]}' as symbolic name."
        raise InvalidMessageError(error_message)

    @staticmethod
    def _raise_duplicate_msgid(symbol: str, msgid: str, other_msgid: str) -> NoReturn:
        """Raise an error when a msgid is duplicated."""
        msgids = [msgid, other_msgid]
        msgids.sort()
        error_message = (
            f"Message symbol '{symbol}' cannot be used for "
            f"'{msgids[0]}' and '{msgids[1]}' at the same time."
            f" If you're creating an 'old_names' use 'old-{symbol}' as the old symbol."
        )
        raise InvalidMessageError(error_message)

    def get_active_msgids(self, msgid_or_symbol: str) -> list[str]:
        """Return msgids but the input can be a symbol.

        self.__active_msgids is used to implement a primitive cache for this function.
        """
        try:
            return self.__active_msgids[msgid_or_symbol]
        except KeyError:
            pass

        # If we don't have a cached value yet we compute it
        msgid: str | None
        deletion_reason = None
        moved_reason = None
        if msgid_or_symbol[1:].isdigit():
            # Only msgid can have a digit as second letter
            msgid = msgid_or_symbol.upper()
            symbol = self.__msgid_to_symbol.get(msgid)
            if not symbol:
                deletion_reason = is_deleted_msgid(msgid)
                if deletion_reason is None:
                    moved_reason = is_moved_msgid(msgid)
        else:
            symbol = msgid_or_symbol
            msgid = self.__symbol_to_msgid.get(msgid_or_symbol)
            if not msgid:
                deletion_reason = is_deleted_symbol(symbol)
                if deletion_reason is None:
                    moved_reason = is_moved_symbol(symbol)
        if not msgid or not symbol:
            if deletion_reason is not None:
                raise DeletedMessageError(msgid_or_symbol, deletion_reason)
            if moved_reason is not None:
                raise MessageBecameExtensionError(msgid_or_symbol, moved_reason)
            error_msg = f"No such message id or symbol '{msgid_or_symbol}'."
            raise UnknownMessageError(error_msg)
        ids = self.__old_names.get(msgid, [msgid])
        # Add to cache
        self.__active_msgids[msgid_or_symbol] = ids
        return ids

back/venvmsg/Lib/site-packages/pylint/message/message_id_store.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Pyreverse.extensions."""

__revision__ = "$Id $"

back/venvmsg/Lib/site-packages/pylint/pyreverse/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Handle diagram generation options for class diagram or default diagrams."""

from __future__ import annotations

import argparse
from collections.abc import Generator
from typing import Any

import astroid
from astroid import nodes
from astroid.modutils import is_stdlib_module

from pylint.pyreverse.diagrams import ClassDiagram, PackageDiagram
from pylint.pyreverse.inspector import Linker, Project
from pylint.pyreverse.utils import LocalsVisitor

# diagram generators ##########################################################


class DiaDefGenerator:
    """Handle diagram generation options."""

    def __init__(self, linker: Linker, handler: DiadefsHandler) -> None:
        """Common Diagram Handler initialization."""
        self.config = handler.config
        self.module_names: bool = False
        self._set_default_options()
        self.linker = linker
        self.classdiagram: ClassDiagram  # defined by subclasses

    def get_title(self, node: nodes.ClassDef) -> str:
        """Get title for objects."""
        title = node.name
        if self.module_names:
            title = f"{node.root().name}.{title}"
        return title  # type: ignore[no-any-return]

    def _set_option(self, option: bool | None) -> bool:
        """Activate some options if not explicitly deactivated."""
        # if we have a class diagram, we want more information by default;
        # so if the option is None, we return True
        if option is None:
            return bool(self.config.classes)
        return option

    def _set_default_options(self) -> None:
        """Set different default options with _default dictionary."""
        self.module_names = self._set_option(self.config.module_names)
        all_ancestors = self._set_option(self.config.all_ancestors)
        all_associated = self._set_option(self.config.all_associated)
        anc_level, association_level = (0, 0)
        if all_ancestors:
            anc_level = -1
        if all_associated:
            association_level = -1
        if self.config.show_ancestors is not None:
            anc_level = self.config.show_ancestors
        if self.config.show_associated is not None:
            association_level = self.config.show_associated
        self.anc_level, self.association_level = anc_level, association_level

    def _get_levels(self) -> tuple[int, int]:
        """Help function for search levels."""
        return self.anc_level, self.association_level

    def show_node(self, node: nodes.ClassDef) -> bool:
        """Determine if node should be shown based on config."""
        if node.root().name == "builtins":
            return self.config.show_builtin  # type: ignore[no-any-return]

        if is_stdlib_module(node.root().name):
            return self.config.show_stdlib  # type: ignore[no-any-return]

        return True

    def add_class(self, node: nodes.ClassDef) -> None:
        """Visit one class and add it to diagram."""
        self.linker.visit(node)
        self.classdiagram.add_object(self.get_title(node), node)

    def get_ancestors(
        self, node: nodes.ClassDef, level: int
    ) -> Generator[nodes.ClassDef, None, None]:
        """Return ancestor nodes of a class node."""
        if level == 0:
            return
        for ancestor in node.ancestors(recurs=False):
            if not self.show_node(ancestor):
                continue
            yield ancestor

    def get_associated(
        self, klass_node: nodes.ClassDef, level: int
    ) -> Generator[nodes.ClassDef, None, None]:
        """Return associated nodes of a class node."""
        if level == 0:
            return
        for association_nodes in list(klass_node.instance_attrs_type.values()) + list(
            klass_node.locals_type.values()
        ):
            for node in association_nodes:
                if isinstance(node, astroid.Instance):
                    node = node._proxied
                if not (isinstance(node, nodes.ClassDef) and self.show_node(node)):
                    continue
                yield node

    def extract_classes(
        self, klass_node: nodes.ClassDef, anc_level: int, association_level: int
    ) -> None:
        """Extract recursively classes related to klass_node."""
        if self.classdiagram.has_node(klass_node) or not self.show_node(klass_node):
            return
        self.add_class(klass_node)

        for ancestor in self.get_ancestors(klass_node, anc_level):
            self.extract_classes(ancestor, anc_level - 1, association_level)

        for node in self.get_associated(klass_node, association_level):
            self.extract_classes(node, anc_level, association_level - 1)


class DefaultDiadefGenerator(LocalsVisitor, DiaDefGenerator):
    """Generate minimum diagram definition for the project :

    * a package diagram including project's modules
    * a class diagram including project's classes
    """

    def __init__(self, linker: Linker, handler: DiadefsHandler) -> None:
        DiaDefGenerator.__init__(self, linker, handler)
        LocalsVisitor.__init__(self)

    def visit_project(self, node: Project) -> None:
        """Visit a pyreverse.utils.Project node.

        create a diagram definition for packages
        """
        mode = self.config.mode
        if len(node.modules) > 1:
            self.pkgdiagram: PackageDiagram | None = PackageDiagram(
                f"packages {node.name}", mode
            )
        else:
            self.pkgdiagram = None
        self.classdiagram = ClassDiagram(f"classes {node.name}", mode)

    def leave_project(self, _: Project) -> Any:
        """Leave the pyreverse.utils.Project node.

        return the generated diagram definition
        """
        if self.pkgdiagram:
            return self.pkgdiagram, self.classdiagram
        return (self.classdiagram,)

    def visit_module(self, node: nodes.Module) -> None:
        """Visit an astroid.Module node.

        add this class to the package diagram definition
        """
        if self.pkgdiagram:
            self.linker.visit(node)
            self.pkgdiagram.add_object(node.name, node)

    def visit_classdef(self, node: nodes.ClassDef) -> None:
        """Visit an astroid.Class node.

        add this class to the class diagram definition
        """
        anc_level, association_level = self._get_levels()
        self.extract_classes(node, anc_level, association_level)

    def visit_importfrom(self, node: nodes.ImportFrom) -> None:
        """Visit astroid.ImportFrom  and catch modules for package diagram."""
        if self.pkgdiagram:
            self.pkgdiagram.add_from_depend(node, node.modname)


class ClassDiadefGenerator(DiaDefGenerator):
    """Generate a class diagram definition including all classes related to a
    given class.
    """

    def class_diagram(self, project: Project, klass: nodes.ClassDef) -> ClassDiagram:
        """Return a class diagram definition for the class and related classes."""
        self.classdiagram = ClassDiagram(klass, self.config.mode)
        if len(project.modules) > 1:
            module, klass = klass.rsplit(".", 1)
            module = project.get_module(module)
        else:
            module = project.modules[0]
            klass = klass.split(".")[-1]
        klass = next(module.ilookup(klass))

        anc_level, association_level = self._get_levels()
        self.extract_classes(klass, anc_level, association_level)
        return self.classdiagram


# diagram handler #############################################################


class DiadefsHandler:
    """Get diagram definitions from user (i.e. xml files) or generate them."""

    def __init__(self, config: argparse.Namespace) -> None:
        self.config = config

    def get_diadefs(self, project: Project, linker: Linker) -> list[ClassDiagram]:
        """Get the diagram's configuration data.

        :param project:The pyreverse project
        :type project: pyreverse.utils.Project
        :param linker: The linker
        :type linker: pyreverse.inspector.Linker(IdGeneratorMixIn, LocalsVisitor)

        :returns: The list of diagram definitions
        :rtype: list(:class:`pylint.pyreverse.diagrams.ClassDiagram`)
        """

        #  read and interpret diagram definitions (Diadefs)
        diagrams = []
        generator = ClassDiadefGenerator(linker, self)
        for klass in self.config.classes:
            diagrams.append(generator.class_diagram(project, klass))
        if not diagrams:
            diagrams = DefaultDiadefGenerator(linker, self).visit(project)
        for diagram in diagrams:
            diagram.extract_relationships()
        return diagrams

back/venvmsg/Lib/site-packages/pylint/pyreverse/diadefslib.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Diagram objects."""

from __future__ import annotations

from collections.abc import Iterable
from typing import Any

import astroid
from astroid import nodes, util

from pylint.checkers.utils import decorated_with_property, in_type_checking_block
from pylint.pyreverse.utils import FilterMixIn


class Figure:
    """Base class for counter handling."""

    def __init__(self) -> None:
        self.fig_id: str = ""


class Relationship(Figure):
    """A relationship from an object in the diagram to another."""

    def __init__(
        self,
        from_object: DiagramEntity,
        to_object: DiagramEntity,
        relation_type: str,
        name: str | None = None,
    ):
        super().__init__()
        self.from_object = from_object
        self.to_object = to_object
        self.type = relation_type
        self.name = name


class DiagramEntity(Figure):
    """A diagram object, i.e. a label associated to an astroid node."""

    default_shape = ""

    def __init__(
        self, title: str = "No name", node: nodes.NodeNG | None = None
    ) -> None:
        super().__init__()
        self.title = title
        self.node: nodes.NodeNG = node or nodes.NodeNG(
            lineno=None,
            col_offset=None,
            end_lineno=None,
            end_col_offset=None,
            parent=None,
        )
        self.shape = self.default_shape


class PackageEntity(DiagramEntity):
    """A diagram object representing a package."""

    default_shape = "package"


class ClassEntity(DiagramEntity):
    """A diagram object representing a class."""

    default_shape = "class"

    def __init__(self, title: str, node: nodes.ClassDef) -> None:
        super().__init__(title=title, node=node)
        self.attrs: list[str] = []
        self.methods: list[nodes.FunctionDef] = []


class ClassDiagram(Figure, FilterMixIn):
    """Main class diagram handling."""

    TYPE = "class"

    def __init__(self, title: str, mode: str) -> None:
        FilterMixIn.__init__(self, mode)
        Figure.__init__(self)
        self.title = title
        # TODO: Specify 'Any' after refactor of `DiagramEntity`
        self.objects: list[Any] = []
        self.relationships: dict[str, list[Relationship]] = {}
        self._nodes: dict[nodes.NodeNG, DiagramEntity] = {}

    def get_relationships(self, role: str) -> Iterable[Relationship]:
        # sorted to get predictable (hence testable) results
        return sorted(
            self.relationships.get(role, ()),
            key=lambda x: (x.from_object.fig_id, x.to_object.fig_id),
        )

    def add_relationship(
        self,
        from_object: DiagramEntity,
        to_object: DiagramEntity,
        relation_type: str,
        name: str | None = None,
    ) -> None:
        """Create a relationship."""
        rel = Relationship(from_object, to_object, relation_type, name)
        self.relationships.setdefault(relation_type, []).append(rel)

    def get_relationship(
        self, from_object: DiagramEntity, relation_type: str
    ) -> Relationship:
        """Return a relationship or None."""
        for rel in self.relationships.get(relation_type, ()):
            if rel.from_object is from_object:
                return rel
        raise KeyError(relation_type)

    def get_attrs(self, node: nodes.ClassDef) -> list[str]:
        """Return visible attributes, possibly with class name."""
        attrs = []
        properties = {
            local_name: local_node
            for local_name, local_node in node.items()
            if isinstance(local_node, nodes.FunctionDef)
            and decorated_with_property(local_node)
        }
        for attr_name, attr_type in list(node.locals_type.items()) + list(
            node.instance_attrs_type.items()
        ):
            if attr_name not in properties:
                properties[attr_name] = attr_type

        for node_name, associated_nodes in properties.items():
            if not self.show_attr(node_name):
                continue
            names = self.class_names(associated_nodes)
            if names:
                node_name = f"{node_name} : {', '.join(names)}"
            attrs.append(node_name)
        return sorted(attrs)

    def get_methods(self, node: nodes.ClassDef) -> list[nodes.FunctionDef]:
        """Return visible methods."""
        methods = [
            m
            for m in node.values()
            if isinstance(m, nodes.FunctionDef)
            and not isinstance(m, astroid.objects.Property)
            and not decorated_with_property(m)
            and self.show_attr(m.name)
        ]
        return sorted(methods, key=lambda n: n.name)

    def add_object(self, title: str, node: nodes.ClassDef) -> None:
        """Create a diagram object."""
        assert node not in self._nodes
        ent = ClassEntity(title, node)
        self._nodes[node] = ent
        self.objects.append(ent)

    def class_names(self, nodes_lst: Iterable[nodes.NodeNG]) -> list[str]:
        """Return class names if needed in diagram."""
        names = []
        for node in nodes_lst:
            if isinstance(node, astroid.Instance):
                node = node._proxied
            if (
                isinstance(
                    node, (nodes.ClassDef, nodes.Name, nodes.Subscript, nodes.BinOp)
                )
                and hasattr(node, "name")
                and not self.has_node(node)
            ):
                if node.name not in names:
                    node_name = node.name
                    names.append(node_name)
        # sorted to get predictable (hence testable) results
        return sorted(
            name
            for name in names
            if all(name not in other or name == other for other in names)
        )

    def has_node(self, node: nodes.NodeNG) -> bool:
        """Return true if the given node is included in the diagram."""
        return node in self._nodes

    def object_from_node(self, node: nodes.NodeNG) -> DiagramEntity:
        """Return the diagram object mapped to node."""
        return self._nodes[node]

    def classes(self) -> list[ClassEntity]:
        """Return all class nodes in the diagram."""
        return [o for o in self.objects if isinstance(o, ClassEntity)]

    def classe(self, name: str) -> ClassEntity:
        """Return a class by its name, raise KeyError if not found."""
        for klass in self.classes():
            if klass.node.name == name:
                return klass
        raise KeyError(name)

    def extract_relationships(self) -> None:
        """Extract relationships between nodes in the diagram."""
        for obj in self.classes():
            node = obj.node
            obj.attrs = self.get_attrs(node)
            obj.methods = self.get_methods(node)
            obj.shape = "class"
            # inheritance link
            for par_node in node.ancestors(recurs=False):
                try:
                    par_obj = self.object_from_node(par_node)
                    self.add_relationship(obj, par_obj, "specialization")
                except KeyError:
                    continue

            # associations & aggregations links
            for name, values in list(node.aggregations_type.items()):
                for value in values:
                    self.assign_association_relationship(
                        value, obj, name, "aggregation"
                    )

            associations = node.associations_type.copy()

            for name, values in node.locals_type.items():
                if name not in associations:
                    associations[name] = values

            for name, values in associations.items():
                for value in values:
                    self.assign_association_relationship(
                        value, obj, name, "association"
                    )

    def assign_association_relationship(
        self, value: astroid.NodeNG, obj: ClassEntity, name: str, type_relationship: str
    ) -> None:
        if isinstance(value, util.UninferableBase):
            return
        if isinstance(value, astroid.Instance):
            value = value._proxied
        try:
            associated_obj = self.object_from_node(value)
            self.add_relationship(associated_obj, obj, type_relationship, name)
        except KeyError:
            return


class PackageDiagram(ClassDiagram):
    """Package diagram handling."""

    TYPE = "package"

    def modules(self) -> list[PackageEntity]:
        """Return all module nodes in the diagram."""
        return [o for o in self.objects if isinstance(o, PackageEntity)]

    def module(self, name: str) -> PackageEntity:
        """Return a module by its name, raise KeyError if not found."""
        for mod in self.modules():
            if mod.node.name == name:
                return mod
        raise KeyError(name)

    def add_object(self, title: str, node: nodes.Module) -> None:
        """Create a diagram object."""
        assert node not in self._nodes
        ent = PackageEntity(title, node)
        self._nodes[node] = ent
        self.objects.append(ent)

    def get_module(self, name: str, node: nodes.Module) -> PackageEntity:
        """Return a module by its name, looking also for relative imports;
        raise KeyError if not found.
        """
        for mod in self.modules():
            mod_name = mod.node.name
            if mod_name == name:
                return mod
            # search for fullname of relative import modules
            package = node.root().name
            if mod_name == f"{package}.{name}":
                return mod
            if mod_name == f"{package.rsplit('.', 1)[0]}.{name}":
                return mod
        raise KeyError(name)

    def add_from_depend(self, node: nodes.ImportFrom, from_module: str) -> None:
        """Add dependencies created by from-imports."""
        mod_name = node.root().name
        package = self.module(mod_name).node

        if from_module in package.depends:
            return

        if not in_type_checking_block(node):
            package.depends.append(from_module)
        elif from_module not in package.type_depends:
            package.type_depends.append(from_module)

    def extract_relationships(self) -> None:
        """Extract relationships between nodes in the diagram."""
        super().extract_relationships()
        for class_obj in self.classes():
            # ownership
            try:
                mod = self.object_from_node(class_obj.node.root())
                self.add_relationship(class_obj, mod, "ownership")
            except KeyError:
                continue
        for package_obj in self.modules():
            package_obj.shape = "package"
            # dependencies
            for dep_name in package_obj.node.depends:
                try:
                    dep = self.get_module(dep_name, package_obj.node)
                except KeyError:
                    continue
                self.add_relationship(package_obj, dep, "depends")

            for dep_name in package_obj.node.type_depends:
                try:
                    dep = self.get_module(dep_name, package_obj.node)
                except KeyError:  # pragma: no cover
                    continue
                self.add_relationship(package_obj, dep, "type_depends")

back/venvmsg/Lib/site-packages/pylint/pyreverse/diagrams.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Class to generate files in dot format and image formats supported by Graphviz."""

from __future__ import annotations

import os
import subprocess
import tempfile
from enum import Enum
from pathlib import Path

from astroid import nodes

from pylint.pyreverse.printer import EdgeType, Layout, NodeProperties, NodeType, Printer
from pylint.pyreverse.utils import get_annotation_label


class HTMLLabels(Enum):
    LINEBREAK_LEFT = '<br ALIGN="LEFT"/>'


ALLOWED_CHARSETS: frozenset[str] = frozenset(("utf-8", "iso-8859-1", "latin1"))
SHAPES: dict[NodeType, str] = {
    NodeType.PACKAGE: "box",
    NodeType.CLASS: "record",
}
# pylint: disable-next=consider-using-namedtuple-or-dataclass
ARROWS: dict[EdgeType, dict[str, str]] = {
    EdgeType.INHERITS: {"arrowtail": "none", "arrowhead": "empty"},
    EdgeType.ASSOCIATION: {
        "fontcolor": "green",
        "arrowtail": "none",
        "arrowhead": "diamond",
        "style": "solid",
    },
    EdgeType.AGGREGATION: {
        "fontcolor": "green",
        "arrowtail": "none",
        "arrowhead": "odiamond",
        "style": "solid",
    },
    EdgeType.USES: {"arrowtail": "none", "arrowhead": "open"},
    EdgeType.TYPE_DEPENDENCY: {
        "arrowtail": "none",
        "arrowhead": "open",
        "style": "dashed",
    },
}


class DotPrinter(Printer):
    DEFAULT_COLOR = "black"

    def __init__(
        self,
        title: str,
        layout: Layout | None = None,
        use_automatic_namespace: bool | None = None,
    ):
        layout = layout or Layout.BOTTOM_TO_TOP
        self.charset = "utf-8"
        super().__init__(title, layout, use_automatic_namespace)

    def _open_graph(self) -> None:
        """Emit the header lines."""
        self.emit(f'digraph "{self.title}" {{')
        if self.layout:
            self.emit(f"rankdir={self.layout.value}")
        if self.charset:
            assert (
                self.charset.lower() in ALLOWED_CHARSETS
            ), f"unsupported charset {self.charset}"
            self.emit(f'charset="{self.charset}"')

    def emit_node(
        self,
        name: str,
        type_: NodeType,
        properties: NodeProperties | None = None,
    ) -> None:
        """Create a new node.

        Nodes can be classes, packages, participants etc.
        """
        if properties is None:
            properties = NodeProperties(label=name)
        shape = SHAPES[type_]
        color = properties.color if properties.color is not None else self.DEFAULT_COLOR
        style = "filled" if color != self.DEFAULT_COLOR else "solid"
        label = self._build_label_for_node(properties)
        label_part = f", label=<{label}>" if label else ""
        fontcolor_part = (
            f', fontcolor="{properties.fontcolor}"' if properties.fontcolor else ""
        )
        self.emit(
            f'"{name}" [color="{color}"{fontcolor_part}{label_part}, shape="{shape}", style="{style}"];'
        )

    def _build_label_for_node(self, properties: NodeProperties) -> str:
        if not properties.label:
            return ""

        label: str = properties.label
        if properties.attrs is None and properties.methods is None:
            # return a "compact" form which only displays the class name in a box
            return label

        # Add class attributes
        attrs: list[str] = properties.attrs or []
        attrs_string = rf"{HTMLLabels.LINEBREAK_LEFT.value}".join(
            attr.replace("|", r"\|") for attr in attrs
        )
        label = rf"{{{label}|{attrs_string}{HTMLLabels.LINEBREAK_LEFT.value}|"

        # Add class methods
        methods: list[nodes.FunctionDef] = properties.methods or []
        for func in methods:
            args = ", ".join(self._get_method_arguments(func)).replace("|", r"\|")
            method_name = (
                f"<I>{func.name}</I>" if func.is_abstract() else f"{func.name}"
            )
            label += rf"{method_name}({args})"
            if func.returns:
                annotation_label = get_annotation_label(func.returns)
                label += ": " + self._escape_annotation_label(annotation_label)
            label += rf"{HTMLLabels.LINEBREAK_LEFT.value}"
        label += "}"
        return label

    def _escape_annotation_label(self, annotation_label: str) -> str:
        # Escape vertical bar characters to make them appear as a literal characters
        # otherwise it gets treated as field separator of record-based nodes
        annotation_label = annotation_label.replace("|", r"\|")

        return annotation_label

    def emit_edge(
        self,
        from_node: str,
        to_node: str,
        type_: EdgeType,
        label: str | None = None,
    ) -> None:
        """Create an edge from one node to another to display relationships."""
        arrowstyle = ARROWS[type_]
        attrs = [f'{prop}="{value}"' for prop, value in arrowstyle.items()]
        if label:
            attrs.append(f'label="{label}"')
        self.emit(f'"{from_node}" -> "{to_node}" [{", ".join(sorted(attrs))}];')

    def generate(self, outputfile: str) -> None:
        self._close_graph()
        graphviz_extensions = ("dot", "gv")
        name = self.title
        if outputfile is None:
            target = "png"
            pdot, dot_sourcepath = tempfile.mkstemp(".gv", name)
            ppng, outputfile = tempfile.mkstemp(".png", name)
            os.close(pdot)
            os.close(ppng)
        else:
            target = Path(outputfile).suffix.lstrip(".")
            if not target:
                target = "png"
                outputfile = outputfile + "." + target
            if target not in graphviz_extensions:
                pdot, dot_sourcepath = tempfile.mkstemp(".gv", name)
                os.close(pdot)
            else:
                dot_sourcepath = outputfile
        with open(dot_sourcepath, "w", encoding="utf8") as outfile:
            outfile.writelines(self.lines)
        if target not in graphviz_extensions:
            subprocess.run(
                ["dot", "-T", target, dot_sourcepath, "-o", outputfile], check=True
            )
            os.unlink(dot_sourcepath)

    def _close_graph(self) -> None:
        """Emit the lines needed to properly close the graph."""
        self.emit("}\n")

back/venvmsg/Lib/site-packages/pylint/pyreverse/dot_printer.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Visitor doing some post-processing on the astroid tree.

Try to resolve definitions (namespace) dictionary, relationship...
"""

from __future__ import annotations

import collections
import os
import traceback
from abc import ABC, abstractmethod
from typing import Callable, Optional

import astroid
from astroid import nodes

from pylint import constants
from pylint.pyreverse import utils

_WrapperFuncT = Callable[
    [Callable[[str], nodes.Module], str, bool], Optional[nodes.Module]
]


def _astroid_wrapper(
    func: Callable[[str], nodes.Module],
    modname: str,
    verbose: bool = False,
) -> nodes.Module | None:
    if verbose:
        print(f"parsing {modname}...")
    try:
        return func(modname)
    except astroid.exceptions.AstroidBuildingException as exc:
        print(exc)
    except Exception:  # pylint: disable=broad-except
        traceback.print_exc()
    return None


class IdGeneratorMixIn:
    """Mixin adding the ability to generate integer uid."""

    def __init__(self, start_value: int = 0) -> None:
        self.id_count = start_value

    def init_counter(self, start_value: int = 0) -> None:
        """Init the id counter."""
        self.id_count = start_value

    def generate_id(self) -> int:
        """Generate a new identifier."""
        self.id_count += 1
        return self.id_count


class Project:
    """A project handle a set of modules / packages."""

    def __init__(self, name: str = ""):
        self.name = name
        self.uid: int | None = None
        self.path: str = ""
        self.modules: list[nodes.Module] = []
        self.locals: dict[str, nodes.Module] = {}
        self.__getitem__ = self.locals.__getitem__
        self.__iter__ = self.locals.__iter__
        self.values = self.locals.values
        self.keys = self.locals.keys
        self.items = self.locals.items

    def add_module(self, node: nodes.Module) -> None:
        self.locals[node.name] = node
        self.modules.append(node)

    def get_module(self, name: str) -> nodes.Module:
        return self.locals[name]

    def get_children(self) -> list[nodes.Module]:
        return self.modules

    def __repr__(self) -> str:
        return f"<Project {self.name!r} at {id(self)} ({len(self.modules)} modules)>"


class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
    """Walk on the project tree and resolve relationships.

    According to options the following attributes may be
    added to visited nodes:

    * uid,
      a unique identifier for the node (on astroid.Project, astroid.Module,
      astroid.Class and astroid.locals_type). Only if the linker
      has been instantiated with tag=True parameter (False by default).

    * Function
      a mapping from locals names to their bounded value, which may be a
      constant like a string or an integer, or an astroid node
      (on astroid.Module, astroid.Class and astroid.Function).

    * instance_attrs_type
      as locals_type but for klass member attributes (only on astroid.Class)

    * associations_type
      as instance_attrs_type but for association relationships

    * aggregations_type
      as instance_attrs_type but for aggregations relationships
    """

    def __init__(self, project: Project, tag: bool = False) -> None:
        IdGeneratorMixIn.__init__(self)
        utils.LocalsVisitor.__init__(self)
        # tag nodes or not
        self.tag = tag
        # visited project
        self.project = project
        self.associations_handler = AggregationsHandler()
        self.associations_handler.set_next(OtherAssociationsHandler())

    def visit_project(self, node: Project) -> None:
        """Visit a pyreverse.utils.Project node.

        * optionally tag the node with a unique id
        """
        if self.tag:
            node.uid = self.generate_id()
        for module in node.modules:
            self.visit(module)

    def visit_module(self, node: nodes.Module) -> None:
        """Visit an astroid.Module node.

        * set the locals_type mapping
        * set the depends mapping
        * optionally tag the node with a unique id
        """
        if hasattr(node, "locals_type"):
            return
        node.locals_type = collections.defaultdict(list)
        node.depends = []
        node.type_depends = []
        if self.tag:
            node.uid = self.generate_id()

    def visit_classdef(self, node: nodes.ClassDef) -> None:
        """Visit an astroid.Class node.

        * set the locals_type and instance_attrs_type mappings
        * optionally tag the node with a unique id
        """
        if hasattr(node, "locals_type"):
            return
        node.locals_type = collections.defaultdict(list)
        if self.tag:
            node.uid = self.generate_id()
        # resolve ancestors
        for baseobj in node.ancestors(recurs=False):
            specializations = getattr(baseobj, "specializations", [])
            specializations.append(node)
            baseobj.specializations = specializations
        # resolve instance attributes
        node.instance_attrs_type = collections.defaultdict(list)
        node.aggregations_type = collections.defaultdict(list)
        node.associations_type = collections.defaultdict(list)
        for assignattrs in tuple(node.instance_attrs.values()):
            for assignattr in assignattrs:
                if not isinstance(assignattr, nodes.Unknown):
                    self.associations_handler.handle(assignattr, node)
                    self.handle_assignattr_type(assignattr, node)

    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
        """Visit an astroid.Function node.

        * set the locals_type mapping
        * optionally tag the node with a unique id
        """
        if hasattr(node, "locals_type"):
            return
        node.locals_type = collections.defaultdict(list)
        if self.tag:
            node.uid = self.generate_id()

    def visit_assignname(self, node: nodes.AssignName) -> None:
        """Visit an astroid.AssignName node.

        handle locals_type
        """
        # avoid double parsing done by different Linkers.visit
        # running over the same project:
        if hasattr(node, "_handled"):
            return
        node._handled = True
        if node.name in node.frame():
            frame = node.frame()
        else:
            # the name has been defined as 'global' in the frame and belongs
            # there.
            frame = node.root()
        if not hasattr(frame, "locals_type"):
            # If the frame doesn't have a locals_type yet,
            # it means it wasn't yet visited. Visit it now
            # to add what's missing from it.
            if isinstance(frame, nodes.ClassDef):
                self.visit_classdef(frame)
            elif isinstance(frame, nodes.FunctionDef):
                self.visit_functiondef(frame)
            else:
                self.visit_module(frame)

        current = frame.locals_type[node.name]
        frame.locals_type[node.name] = list(set(current) | utils.infer_node(node))

    @staticmethod
    def handle_assignattr_type(node: nodes.AssignAttr, parent: nodes.ClassDef) -> None:
        """Handle an astroid.assignattr node.

        handle instance_attrs_type
        """
        current = set(parent.instance_attrs_type[node.attrname])
        parent.instance_attrs_type[node.attrname] = list(
            current | utils.infer_node(node)
        )

    def visit_import(self, node: nodes.Import) -> None:
        """Visit an astroid.Import node.

        resolve module dependencies
        """
        context_file = node.root().file
        for name in node.names:
            relative = astroid.modutils.is_relative(name[0], context_file)
            self._imported_module(node, name[0], relative)

    def visit_importfrom(self, node: nodes.ImportFrom) -> None:
        """Visit an astroid.ImportFrom node.

        resolve module dependencies
        """
        basename = node.modname
        context_file = node.root().file
        if context_file is not None:
            relative = astroid.modutils.is_relative(basename, context_file)
        else:
            relative = False
        for name in node.names:
            if name[0] == "*":
                continue
            # analyze dependencies
            fullname = f"{basename}.{name[0]}"
            if fullname.find(".") > -1:
                try:
                    fullname = astroid.modutils.get_module_part(fullname, context_file)
                except ImportError:
                    continue
            if fullname != basename:
                self._imported_module(node, fullname, relative)

    def compute_module(self, context_name: str, mod_path: str) -> bool:
        """Should the module be added to dependencies ?"""
        package_dir = os.path.dirname(self.project.path)
        if context_name == mod_path:
            return False
        # astroid does return a boolean but is not typed correctly yet

        return astroid.modutils.module_in_path(mod_path, (package_dir,))  # type: ignore[no-any-return]

    def _imported_module(
        self, node: nodes.Import | nodes.ImportFrom, mod_path: str, relative: bool
    ) -> None:
        """Notify an imported module, used to analyze dependencies."""
        module = node.root()
        context_name = module.name
        if relative:
            mod_path = f"{'.'.join(context_name.split('.')[:-1])}.{mod_path}"
        if self.compute_module(context_name, mod_path):
            # handle dependencies
            if not hasattr(module, "depends"):
                module.depends = []
            mod_paths = module.depends
            if mod_path not in mod_paths:
                mod_paths.append(mod_path)


class AssociationHandlerInterface(ABC):
    @abstractmethod
    def set_next(
        self, handler: AssociationHandlerInterface
    ) -> AssociationHandlerInterface:
        pass

    @abstractmethod
    def handle(self, node: nodes.AssignAttr, parent: nodes.ClassDef) -> None:
        pass


class AbstractAssociationHandler(AssociationHandlerInterface):
    """
    Chain of Responsibility for handling types of association, useful
    to expand in the future if we want to add more distinct associations.

    Every link of the chain checks if it's a certain type of association.
    If no association is found it's set as a generic association in `associations_type`.

    The default chaining behavior is implemented inside the base handler
    class.
    """

    _next_handler: AssociationHandlerInterface

    def set_next(
        self, handler: AssociationHandlerInterface
    ) -> AssociationHandlerInterface:
        self._next_handler = handler
        return handler

    @abstractmethod
    def handle(self, node: nodes.AssignAttr, parent: nodes.ClassDef) -> None:
        if self._next_handler:
            self._next_handler.handle(node, parent)


class AggregationsHandler(AbstractAssociationHandler):
    def handle(self, node: nodes.AssignAttr, parent: nodes.ClassDef) -> None:
        if isinstance(node.parent, (nodes.AnnAssign, nodes.Assign)) and isinstance(
            node.parent.value, astroid.node_classes.Name
        ):
            current = set(parent.aggregations_type[node.attrname])
            parent.aggregations_type[node.attrname] = list(
                current | utils.infer_node(node)
            )
        else:
            super().handle(node, parent)


class OtherAssociationsHandler(AbstractAssociationHandler):
    def handle(self, node: nodes.AssignAttr, parent: nodes.ClassDef) -> None:
        current = set(parent.associations_type[node.attrname])
        parent.associations_type[node.attrname] = list(current | utils.infer_node(node))


def project_from_files(
    files: list[str],
    func_wrapper: _WrapperFuncT = _astroid_wrapper,
    project_name: str = "no name",
    black_list: tuple[str, ...] = constants.DEFAULT_IGNORE_LIST,
    verbose: bool = False,
) -> Project:
    """Return a Project from a list of files or modules."""
    # build the project representation
    astroid_manager = astroid.MANAGER
    project = Project(project_name)
    for something in files:
        if not os.path.exists(something):
            fpath = astroid.modutils.file_from_modpath(something.split("."))
        elif os.path.isdir(something):
            fpath = os.path.join(something, "__init__.py")
        else:
            fpath = something
        ast = func_wrapper(astroid_manager.ast_from_file, fpath, verbose)
        if ast is None:
            continue
        project.path = project.path or ast.file
        project.add_module(ast)
        base_name = ast.name
        # recurse in package except if __init__ was explicitly given
        if ast.package and something.find("__init__") == -1:
            # recurse on others packages / modules if this is a package
            for fpath in astroid.modutils.get_module_files(
                os.path.dirname(ast.file), black_list
            ):
                ast = func_wrapper(astroid_manager.ast_from_file, fpath, verbose)
                if ast is None or ast.name == base_name:
                    continue
                project.add_module(ast)
    return project

back/venvmsg/Lib/site-packages/pylint/pyreverse/inspector.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Create UML diagrams for classes and modules in <packages>."""

from __future__ import annotations

import sys
from collections.abc import Sequence
from typing import NoReturn

from pylint import constants
from pylint.config.arguments_manager import _ArgumentsManager
from pylint.config.arguments_provider import _ArgumentsProvider
from pylint.lint import discover_package_path
from pylint.lint.utils import augmented_sys_path
from pylint.pyreverse import writer
from pylint.pyreverse.diadefslib import DiadefsHandler
from pylint.pyreverse.inspector import Linker, project_from_files
from pylint.pyreverse.utils import (
    check_graphviz_availability,
    check_if_graphviz_supports_format,
    insert_default_options,
)
from pylint.typing import Options

DIRECTLY_SUPPORTED_FORMATS = (
    "dot",
    "puml",
    "plantuml",
    "mmd",
    "html",
)

DEFAULT_COLOR_PALETTE = (
    # colorblind scheme taken from https://personal.sron.nl/~pault/
    "#77AADD",  # light blue
    "#99DDFF",  # light cyan
    "#44BB99",  # mint
    "#BBCC33",  # pear
    "#AAAA00",  # olive
    "#EEDD88",  # light yellow
    "#EE8866",  # orange
    "#FFAABB",  # pink
    "#DDDDDD",  # pale grey
)

OPTIONS: Options = (
    (
        "filter-mode",
        {
            "short": "f",
            "default": "PUB_ONLY",
            "dest": "mode",
            "type": "string",
            "action": "store",
            "metavar": "<mode>",
            "help": """filter attributes and functions according to
    <mode>. Correct modes are :
                            'PUB_ONLY' filter all non public attributes
                                [DEFAULT], equivalent to PRIVATE+SPECIAL_A
                            'ALL' no filter
                            'SPECIAL' filter Python special functions
                                except constructor
                            'OTHER' filter protected and private
                                attributes""",
        },
    ),
    (
        "class",
        {
            "short": "c",
            "action": "extend",
            "metavar": "<class>",
            "type": "csv",
            "dest": "classes",
            "default": None,
            "help": "create a class diagram with all classes related to <class>;\
 this uses by default the options -ASmy",
        },
    ),
    (
        "show-ancestors",
        {
            "short": "a",
            "action": "store",
            "metavar": "<ancestor>",
            "type": "int",
            "default": None,
            "help": "show <ancestor> generations of ancestor classes not in <projects>",
        },
    ),
    (
        "all-ancestors",
        {
            "short": "A",
            "default": None,
            "action": "store_true",
            "help": "show all ancestors off all classes in <projects>",
        },
    ),
    (
        "show-associated",
        {
            "short": "s",
            "action": "store",
            "metavar": "<association_level>",
            "type": "int",
            "default": None,
            "help": "show <association_level> levels of associated classes not in <projects>",
        },
    ),
    (
        "all-associated",
        {
            "short": "S",
            "default": None,
            "action": "store_true",
            "help": "show recursively all associated off all associated classes",
        },
    ),
    (
        "show-builtin",
        {
            "short": "b",
            "action": "store_true",
            "default": False,
            "help": "include builtin objects in representation of classes",
        },
    ),
    (
        "show-stdlib",
        {
            "short": "L",
            "action": "store_true",
            "default": False,
            "help": "include standard library objects in representation of classes",
        },
    ),
    (
        "module-names",
        {
            "short": "m",
            "default": None,
            "type": "yn",
            "metavar": "<y or n>",
            "help": "include module name in representation of classes",
        },
    ),
    (
        "only-classnames",
        {
            "short": "k",
            "action": "store_true",
            "default": False,
            "help": "don't show attributes and methods in the class boxes; this disables -f values",
        },
    ),
    (
        "no-standalone",
        {
            "action": "store_true",
            "default": False,
            "help": "only show nodes with connections",
        },
    ),
    (
        "output",
        {
            "short": "o",
            "dest": "output_format",
            "action": "store",
            "default": "dot",
            "metavar": "<format>",
            "type": "string",
            "help": (
                "create a *.<format> output file if format is available. Available "
                f"formats are: {', '.join(DIRECTLY_SUPPORTED_FORMATS)}. Any other "
                f"format will be tried to create by means of the 'dot' command line "
                f"tool, which requires a graphviz installation."
            ),
        },
    ),
    (
        "colorized",
        {
            "dest": "colorized",
            "action": "store_true",
            "default": False,
            "help": "Use colored output. Classes/modules of the same package get the same color.",
        },
    ),
    (
        "max-color-depth",
        {
            "dest": "max_color_depth",
            "action": "store",
            "default": 2,
            "metavar": "<depth>",
            "type": "int",
            "help": "Use separate colors up to package depth of <depth>",
        },
    ),
    (
        "color-palette",
        {
            "dest": "color_palette",
            "action": "store",
            "default": DEFAULT_COLOR_PALETTE,
            "metavar": "<color1,color2,...>",
            "type": "csv",
            "help": "Comma separated list of colors to use",
        },
    ),
    (
        "ignore",
        {
            "type": "csv",
            "metavar": "<file[,file...]>",
            "dest": "ignore_list",
            "default": constants.DEFAULT_IGNORE_LIST,
            "help": "Files or directories to be skipped. They should be base names, not paths.",
        },
    ),
    (
        "project",
        {
            "default": "",
            "type": "string",
            "short": "p",
            "metavar": "<project name>",
            "help": "set the project name.",
        },
    ),
    (
        "output-directory",
        {
            "default": "",
            "type": "path",
            "short": "d",
            "action": "store",
            "metavar": "<output_directory>",
            "help": "set the output directory path.",
        },
    ),
    (
        "source-roots",
        {
            "type": "glob_paths_csv",
            "metavar": "<path>[,<path>...]",
            "default": (),
            "help": "Add paths to the list of the source roots. Supports globbing patterns. The "
            "source root is an absolute path or a path relative to the current working directory "
            "used to determine a package namespace for modules located under the source root.",
        },
    ),
    (
        "verbose",
        {
            "action": "store_true",
            "default": False,
            "help": "Makes pyreverse more verbose/talkative. Mostly useful for debugging.",
        },
    ),
)


class Run(_ArgumentsManager, _ArgumentsProvider):
    """Base class providing common behaviour for pyreverse commands."""

    options = OPTIONS
    name = "pyreverse"

    def __init__(self, args: Sequence[str]) -> NoReturn:
        # Immediately exit if user asks for version
        if "--version" in args:
            print("pyreverse is included in pylint:")
            print(constants.full_version)
            sys.exit(0)

        _ArgumentsManager.__init__(self, prog="pyreverse", description=__doc__)
        _ArgumentsProvider.__init__(self, self)

        # Parse options
        insert_default_options()
        args = self._parse_command_line_configuration(args)

        if self.config.output_format not in DIRECTLY_SUPPORTED_FORMATS:
            check_graphviz_availability()
            print(
                f"Format {self.config.output_format} is not supported natively."
                " Pyreverse will try to generate it using Graphviz..."
            )
            check_if_graphviz_supports_format(self.config.output_format)

        sys.exit(self.run(args))

    def run(self, args: list[str]) -> int:
        """Checking arguments and run project."""
        if not args:
            print(self.help())
            return 1
        extra_packages_paths = list(
            {discover_package_path(arg, self.config.source_roots) for arg in args}
        )
        with augmented_sys_path(extra_packages_paths):
            project = project_from_files(
                args,
                project_name=self.config.project,
                black_list=self.config.ignore_list,
                verbose=self.config.verbose,
            )
            linker = Linker(project, tag=True)
            handler = DiadefsHandler(self.config)
            diadefs = handler.get_diadefs(project, linker)
        writer.DiagramWriter(self.config).write(diadefs)
        return 0


if __name__ == "__main__":
    Run(sys.argv[1:])

back/venvmsg/Lib/site-packages/pylint/pyreverse/main.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Class to generate files in mermaidjs format."""

from __future__ import annotations

from pylint.pyreverse.printer import EdgeType, NodeProperties, NodeType, Printer
from pylint.pyreverse.utils import get_annotation_label


class MermaidJSPrinter(Printer):
    """Printer for MermaidJS diagrams."""

    DEFAULT_COLOR = "black"

    NODES: dict[NodeType, str] = {
        NodeType.CLASS: "class",
        NodeType.PACKAGE: "class",
    }
    ARROWS: dict[EdgeType, str] = {
        EdgeType.INHERITS: "--|>",
        EdgeType.ASSOCIATION: "--*",
        EdgeType.AGGREGATION: "--o",
        EdgeType.USES: "-->",
        EdgeType.TYPE_DEPENDENCY: "..>",
    }

    def _open_graph(self) -> None:
        """Emit the header lines."""
        self.emit("classDiagram")
        self._inc_indent()

    def emit_node(
        self,
        name: str,
        type_: NodeType,
        properties: NodeProperties | None = None,
    ) -> None:
        """Create a new node.

        Nodes can be classes, packages, participants etc.
        """
        # pylint: disable=duplicate-code
        if properties is None:
            properties = NodeProperties(label=name)
        nodetype = self.NODES[type_]
        body = []
        if properties.attrs:
            body.extend(properties.attrs)
        if properties.methods:
            for func in properties.methods:
                args = self._get_method_arguments(func)
                line = f"{func.name}({', '.join(args)})"
                line += "*" if func.is_abstract() else ""
                if func.returns:
                    line += f" {get_annotation_label(func.returns)}"
                body.append(line)
        name = name.split(".")[-1]
        self.emit(f"{nodetype} {name} {{")
        self._inc_indent()
        for line in body:
            self.emit(line)
        self._dec_indent()
        self.emit("}")

    def emit_edge(
        self,
        from_node: str,
        to_node: str,
        type_: EdgeType,
        label: str | None = None,
    ) -> None:
        """Create an edge from one node to another to display relationships."""
        from_node = from_node.split(".")[-1]
        to_node = to_node.split(".")[-1]
        edge = f"{from_node} {self.ARROWS[type_]} {to_node}"
        if label:
            edge += f" : {label}"
        self.emit(edge)

    def _close_graph(self) -> None:
        """Emit the lines needed to properly close the graph."""
        self._dec_indent()


class HTMLMermaidJSPrinter(MermaidJSPrinter):
    """Printer for MermaidJS diagrams wrapped in a html boilerplate."""

    HTML_OPEN_BOILERPLATE = """<html>
  <body>
    <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
      <div class="mermaid">
    """
    HTML_CLOSE_BOILERPLATE = """
       </div>
  </body>
</html>
"""
    GRAPH_INDENT_LEVEL = 4

    def _open_graph(self) -> None:
        self.emit(self.HTML_OPEN_BOILERPLATE)
        for _ in range(self.GRAPH_INDENT_LEVEL):
            self._inc_indent()
        super()._open_graph()

    def _close_graph(self) -> None:
        for _ in range(self.GRAPH_INDENT_LEVEL):
            self._dec_indent()
        self.emit(self.HTML_CLOSE_BOILERPLATE)

back/venvmsg/Lib/site-packages/pylint/pyreverse/mermaidjs_printer.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Class to generate files in dot format and image formats supported by Graphviz."""

from __future__ import annotations

from pylint.pyreverse.printer import EdgeType, Layout, NodeProperties, NodeType, Printer
from pylint.pyreverse.utils import get_annotation_label


class PlantUmlPrinter(Printer):
    """Printer for PlantUML diagrams."""

    DEFAULT_COLOR = "black"

    NODES: dict[NodeType, str] = {
        NodeType.CLASS: "class",
        NodeType.PACKAGE: "package",
    }
    ARROWS: dict[EdgeType, str] = {
        EdgeType.INHERITS: "--|>",
        EdgeType.ASSOCIATION: "--*",
        EdgeType.AGGREGATION: "--o",
        EdgeType.USES: "-->",
        EdgeType.TYPE_DEPENDENCY: "..>",
    }

    def _open_graph(self) -> None:
        """Emit the header lines."""
        self.emit("@startuml " + self.title)
        if not self.use_automatic_namespace:
            self.emit("set namespaceSeparator none")
        if self.layout:
            if self.layout is Layout.LEFT_TO_RIGHT:
                self.emit("left to right direction")
            elif self.layout is Layout.TOP_TO_BOTTOM:
                self.emit("top to bottom direction")
            else:
                raise ValueError(
                    f"Unsupported layout {self.layout}. PlantUmlPrinter only "
                    "supports left to right and top to bottom layout."
                )

    def emit_node(
        self,
        name: str,
        type_: NodeType,
        properties: NodeProperties | None = None,
    ) -> None:
        """Create a new node.

        Nodes can be classes, packages, participants etc.
        """
        if properties is None:
            properties = NodeProperties(label=name)
        nodetype = self.NODES[type_]
        if properties.color and properties.color != self.DEFAULT_COLOR:
            color = f" #{properties.color.lstrip('#')}"
        else:
            color = ""
        body = []
        if properties.attrs:
            body.extend(properties.attrs)
        if properties.methods:
            for func in properties.methods:
                args = self._get_method_arguments(func)
                line = "{abstract}" if func.is_abstract() else ""
                line += f"{func.name}({', '.join(args)})"
                if func.returns:
                    line += " -> " + get_annotation_label(func.returns)
                body.append(line)
        label = properties.label if properties.label is not None else name
        if properties.fontcolor and properties.fontcolor != self.DEFAULT_COLOR:
            label = f"<color:{properties.fontcolor}>{label}</color>"
        self.emit(f'{nodetype} "{label}" as {name}{color} {{')
        self._inc_indent()
        for line in body:
            self.emit(line)
        self._dec_indent()
        self.emit("}")

    def emit_edge(
        self,
        from_node: str,
        to_node: str,
        type_: EdgeType,
        label: str | None = None,
    ) -> None:
        """Create an edge from one node to another to display relationships."""
        edge = f"{from_node} {self.ARROWS[type_]} {to_node}"
        if label:
            edge += f" : {label}"
        self.emit(edge)

    def _close_graph(self) -> None:
        """Emit the lines needed to properly close the graph."""
        self.emit("@enduml")

back/venvmsg/Lib/site-packages/pylint/pyreverse/plantuml_printer.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Base class defining the interface for a printer."""

from __future__ import annotations

from abc import ABC, abstractmethod
from enum import Enum
from typing import NamedTuple

from astroid import nodes

from pylint.pyreverse.utils import get_annotation_label


class NodeType(Enum):
    CLASS = "class"
    PACKAGE = "package"


class EdgeType(Enum):
    INHERITS = "inherits"
    ASSOCIATION = "association"
    AGGREGATION = "aggregation"
    USES = "uses"
    TYPE_DEPENDENCY = "type_dependency"


class Layout(Enum):
    LEFT_TO_RIGHT = "LR"
    RIGHT_TO_LEFT = "RL"
    TOP_TO_BOTTOM = "TB"
    BOTTOM_TO_TOP = "BT"


class NodeProperties(NamedTuple):
    label: str
    attrs: list[str] | None = None
    methods: list[nodes.FunctionDef] | None = None
    color: str | None = None
    fontcolor: str | None = None


class Printer(ABC):
    """Base class defining the interface for a printer."""

    def __init__(
        self,
        title: str,
        layout: Layout | None = None,
        use_automatic_namespace: bool | None = None,
    ) -> None:
        self.title: str = title
        self.layout = layout
        self.use_automatic_namespace = use_automatic_namespace
        self.lines: list[str] = []
        self._indent = ""
        self._open_graph()

    def _inc_indent(self) -> None:
        """Increment indentation."""
        self._indent += "  "

    def _dec_indent(self) -> None:
        """Decrement indentation."""
        self._indent = self._indent[:-2]

    @abstractmethod
    def _open_graph(self) -> None:
        """Emit the header lines, i.e. all boilerplate code that defines things like
        layout etc.
        """

    def emit(self, line: str, force_newline: bool | None = True) -> None:
        if force_newline and not line.endswith("\n"):
            line += "\n"
        self.lines.append(self._indent + line)

    @abstractmethod
    def emit_node(
        self,
        name: str,
        type_: NodeType,
        properties: NodeProperties | None = None,
    ) -> None:
        """Create a new node.

        Nodes can be classes, packages, participants etc.
        """

    @abstractmethod
    def emit_edge(
        self,
        from_node: str,
        to_node: str,
        type_: EdgeType,
        label: str | None = None,
    ) -> None:
        """Create an edge from one node to another to display relationships."""

    @staticmethod
    def _get_method_arguments(method: nodes.FunctionDef) -> list[str]:
        if method.args.args is None:
            return []

        first_arg = 0 if method.type in {"function", "staticmethod"} else 1
        arguments: list[nodes.AssignName] = method.args.args[first_arg:]

        annotations = dict(zip(arguments, method.args.annotations[first_arg:]))
        for arg in arguments:
            annotation_label = ""
            ann = annotations.get(arg)
            if ann:
                annotation_label = get_annotation_label(ann)
            annotations[arg] = annotation_label

        return [
            f"{arg.name}: {ann}" if ann else f"{arg.name}"
            for arg, ann in annotations.items()
        ]

    def generate(self, outputfile: str) -> None:
        """Generate and save the final outputfile."""
        self._close_graph()
        with open(outputfile, "w", encoding="utf-8") as outfile:
            outfile.writelines(self.lines)

    @abstractmethod
    def _close_graph(self) -> None:
        """Emit the lines needed to properly close the graph."""

back/venvmsg/Lib/site-packages/pylint/pyreverse/printer.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from pylint.pyreverse.dot_printer import DotPrinter
from pylint.pyreverse.mermaidjs_printer import HTMLMermaidJSPrinter, MermaidJSPrinter
from pylint.pyreverse.plantuml_printer import PlantUmlPrinter
from pylint.pyreverse.printer import Printer

filetype_to_printer: dict[str, type[Printer]] = {
    "plantuml": PlantUmlPrinter,
    "puml": PlantUmlPrinter,
    "mmd": MermaidJSPrinter,
    "html": HTMLMermaidJSPrinter,
    "dot": DotPrinter,
}


def get_printer_for_filetype(filetype: str) -> type[Printer]:
    return filetype_to_printer.get(filetype, DotPrinter)

back/venvmsg/Lib/site-packages/pylint/pyreverse/printer_factory.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Generic classes/functions for pyreverse core/extensions."""

from __future__ import annotations

import os
import re
import shutil
import subprocess
import sys
from typing import TYPE_CHECKING, Any, Callable, Optional, Tuple, Union

import astroid
from astroid import nodes
from astroid.typing import InferenceResult

if TYPE_CHECKING:
    from pylint.pyreverse.diagrams import ClassDiagram, PackageDiagram

    _CallbackT = Callable[
        [nodes.NodeNG],
        Union[Tuple[ClassDiagram], Tuple[PackageDiagram, ClassDiagram], None],
    ]
    _CallbackTupleT = Tuple[Optional[_CallbackT], Optional[_CallbackT]]


RCFILE = ".pyreverserc"


def get_default_options() -> list[str]:
    """Read config file and return list of options."""
    options = []
    home = os.environ.get("HOME", "")
    if home:
        rcfile = os.path.join(home, RCFILE)
        try:
            with open(rcfile, encoding="utf-8") as file_handle:
                options = file_handle.read().split()
        except OSError:
            pass  # ignore if no config file found
    return options


def insert_default_options() -> None:
    """Insert default options to sys.argv."""
    options = get_default_options()
    options.reverse()
    for arg in options:
        sys.argv.insert(1, arg)


# astroid utilities ###########################################################
SPECIAL = re.compile(r"^__([^\W_]_*)+__$")
PRIVATE = re.compile(r"^__(_*[^\W_])+_?$")
PROTECTED = re.compile(r"^_\w*$")


def get_visibility(name: str) -> str:
    """Return the visibility from a name: public, protected, private or special."""
    if SPECIAL.match(name):
        visibility = "special"
    elif PRIVATE.match(name):
        visibility = "private"
    elif PROTECTED.match(name):
        visibility = "protected"

    else:
        visibility = "public"
    return visibility


def is_exception(node: nodes.ClassDef) -> bool:
    # bw compatibility
    return node.type == "exception"  # type: ignore[no-any-return]


# Helpers #####################################################################

_SPECIAL = 2
_PROTECTED = 4
_PRIVATE = 8
MODES = {
    "ALL": 0,
    "PUB_ONLY": _SPECIAL + _PROTECTED + _PRIVATE,
    "SPECIAL": _SPECIAL,
    "OTHER": _PROTECTED + _PRIVATE,
}
VIS_MOD = {
    "special": _SPECIAL,
    "protected": _PROTECTED,
    "private": _PRIVATE,
    "public": 0,
}


class FilterMixIn:
    """Filter nodes according to a mode and nodes' visibility."""

    def __init__(self, mode: str) -> None:
        """Init filter modes."""
        __mode = 0
        for nummod in mode.split("+"):
            try:
                __mode += MODES[nummod]
            except KeyError as ex:
                print(f"Unknown filter mode {ex}", file=sys.stderr)
        self.__mode = __mode

    def show_attr(self, node: nodes.NodeNG | str) -> bool:
        """Return true if the node should be treated."""
        visibility = get_visibility(getattr(node, "name", node))
        return not self.__mode & VIS_MOD[visibility]


class LocalsVisitor:
    """Visit a project by traversing the locals dictionary.

    * visit_<class name> on entering a node, where class name is the class of
    the node in lower case

    * leave_<class name> on leaving a node, where class name is the class of
    the node in lower case
    """

    def __init__(self) -> None:
        self._cache: dict[type[nodes.NodeNG], _CallbackTupleT] = {}
        self._visited: set[nodes.NodeNG] = set()

    def get_callbacks(self, node: nodes.NodeNG) -> _CallbackTupleT:
        """Get callbacks from handler for the visited node."""
        klass = node.__class__
        methods = self._cache.get(klass)
        if methods is None:
            kid = klass.__name__.lower()
            e_method = getattr(
                self, f"visit_{kid}", getattr(self, "visit_default", None)
            )
            l_method = getattr(
                self, f"leave_{kid}", getattr(self, "leave_default", None)
            )
            self._cache[klass] = (e_method, l_method)
        else:
            e_method, l_method = methods
        return e_method, l_method

    def visit(self, node: nodes.NodeNG) -> Any:
        """Launch the visit starting from the given node."""
        if node in self._visited:
            return None

        self._visited.add(node)
        methods = self.get_callbacks(node)
        if methods[0] is not None:
            methods[0](node)
        if hasattr(node, "locals"):  # skip Instance and other proxy
            for local_node in node.values():
                self.visit(local_node)
        if methods[1] is not None:
            return methods[1](node)
        return None


def get_annotation_label(ann: nodes.Name | nodes.NodeNG) -> str:
    if isinstance(ann, nodes.Name) and ann.name is not None:
        return ann.name  # type: ignore[no-any-return]
    if isinstance(ann, nodes.NodeNG):
        return ann.as_string()  # type: ignore[no-any-return]
    return ""


def get_annotation(
    node: nodes.AssignAttr | nodes.AssignName,
) -> nodes.Name | nodes.Subscript | None:
    """Return the annotation for `node`."""
    ann = None
    if isinstance(node.parent, nodes.AnnAssign):
        ann = node.parent.annotation
    elif isinstance(node, nodes.AssignAttr):
        init_method = node.parent.parent
        try:
            annotations = dict(zip(init_method.locals, init_method.args.annotations))
            ann = annotations.get(node.parent.value.name)
        except AttributeError:
            pass
    else:
        return ann

    try:
        default, *_ = node.infer()
    except astroid.InferenceError:
        default = ""

    label = get_annotation_label(ann)

    if (
        ann
        and getattr(default, "value", "value") is None
        and not label.startswith("Optional")
        and (
            not isinstance(ann, nodes.BinOp)
            or not any(
                isinstance(child, nodes.Const) and child.value is None
                for child in ann.get_children()
            )
        )
    ):
        label = rf"Optional[{label}]"

    if label and ann:
        ann.name = label
    return ann


def infer_node(node: nodes.AssignAttr | nodes.AssignName) -> set[InferenceResult]:
    """Return a set containing the node annotation if it exists
    otherwise return a set of the inferred types using the NodeNG.infer method.
    """

    ann = get_annotation(node)
    try:
        if ann:
            if isinstance(ann, nodes.Subscript) or (
                isinstance(ann, nodes.BinOp) and ann.op == "|"
            ):
                return {ann}
            return set(ann.infer())
        return set(node.infer())
    except astroid.InferenceError:
        return {ann} if ann else set()


def check_graphviz_availability() -> None:
    """Check if the ``dot`` command is available on the machine.

    This is needed if image output is desired and ``dot`` is used to convert
    from *.dot or *.gv into the final output format.
    """
    if shutil.which("dot") is None:
        print("'Graphviz' needs to be installed for your chosen output format.")
        sys.exit(32)


def check_if_graphviz_supports_format(output_format: str) -> None:
    """Check if the ``dot`` command supports the requested output format.

    This is needed if image output is desired and ``dot`` is used to convert
    from *.gv into the final output format.
    """
    dot_output = subprocess.run(
        ["dot", "-T?"], capture_output=True, check=False, encoding="utf-8"
    )
    match = re.match(
        pattern=r".*Use one of: (?P<formats>(\S*\s?)+)",
        string=dot_output.stderr.strip(),
    )
    if not match:
        print(
            "Unable to determine Graphviz supported output formats. "
            "Pyreverse will continue, but subsequent error messages "
            "regarding the output format may come from Graphviz directly."
        )
        return
    supported_formats = match.group("formats")
    if output_format not in supported_formats.split():
        print(
            f"Format {output_format} is not supported by Graphviz. It supports: {supported_formats}"
        )
        sys.exit(32)

back/venvmsg/Lib/site-packages/pylint/pyreverse/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Utilities for creating diagrams."""

from __future__ import annotations

import argparse
import itertools
import os
from collections import defaultdict
from collections.abc import Iterable

from astroid import modutils, nodes

from pylint.pyreverse.diagrams import (
    ClassDiagram,
    ClassEntity,
    DiagramEntity,
    PackageDiagram,
    PackageEntity,
)
from pylint.pyreverse.printer import EdgeType, NodeProperties, NodeType, Printer
from pylint.pyreverse.printer_factory import get_printer_for_filetype
from pylint.pyreverse.utils import is_exception


class DiagramWriter:
    """Base class for writing project diagrams."""

    def __init__(self, config: argparse.Namespace) -> None:
        self.config = config
        self.printer_class = get_printer_for_filetype(self.config.output_format)
        self.printer: Printer  # defined in set_printer
        self.file_name = ""  # defined in set_printer
        self.depth = self.config.max_color_depth
        # default colors are an adaption of the seaborn colorblind palette
        self.available_colors = itertools.cycle(self.config.color_palette)
        self.used_colors: dict[str, str] = {}

    def write(self, diadefs: Iterable[ClassDiagram | PackageDiagram]) -> None:
        """Write files for <project> according to <diadefs>."""
        for diagram in diadefs:
            basename = diagram.title.strip().replace("/", "_").replace(" ", "_")
            file_name = f"{basename}.{self.config.output_format}"
            if os.path.exists(self.config.output_directory):
                file_name = os.path.join(self.config.output_directory, file_name)
            self.set_printer(file_name, basename)
            if isinstance(diagram, PackageDiagram):
                self.write_packages(diagram)
            else:
                self.write_classes(diagram)
            self.save()

    def write_packages(self, diagram: PackageDiagram) -> None:
        """Write a package diagram."""
        module_info: dict[str, dict[str, int]] = {}

        # sorted to get predictable (hence testable) results
        for module in sorted(diagram.modules(), key=lambda x: x.title):
            module.fig_id = module.node.qname()

            if self.config.no_standalone and not any(
                module in (rel.from_object, rel.to_object)
                for rel in diagram.get_relationships("depends")
            ):
                continue

            self.printer.emit_node(
                module.fig_id,
                type_=NodeType.PACKAGE,
                properties=self.get_package_properties(module),
            )

            module_info[module.fig_id] = {
                "imports": 0,
                "imported": 0,
            }

        # package dependencies
        for rel in diagram.get_relationships("depends"):
            from_id = rel.from_object.fig_id
            to_id = rel.to_object.fig_id

            self.printer.emit_edge(
                from_id,
                to_id,
                type_=EdgeType.USES,
            )

            module_info[from_id]["imports"] += 1
            module_info[to_id]["imported"] += 1

        for rel in diagram.get_relationships("type_depends"):
            from_id = rel.from_object.fig_id
            to_id = rel.to_object.fig_id

            self.printer.emit_edge(
                from_id,
                to_id,
                type_=EdgeType.TYPE_DEPENDENCY,
            )

            module_info[from_id]["imports"] += 1
            module_info[to_id]["imported"] += 1

        print(
            f"Analysed {len(module_info)} modules with a total "
            f"of {sum(mod['imports'] for mod in module_info.values())} imports"
        )

    def write_classes(self, diagram: ClassDiagram) -> None:
        """Write a class diagram."""
        # sorted to get predictable (hence testable) results
        for obj in sorted(diagram.objects, key=lambda x: x.title):
            obj.fig_id = obj.node.qname()
            if self.config.no_standalone and not any(
                obj in (rel.from_object, rel.to_object)
                for rel_type in ("specialization", "association", "aggregation")
                for rel in diagram.get_relationships(rel_type)
            ):
                continue

            self.printer.emit_node(
                obj.fig_id,
                type_=NodeType.CLASS,
                properties=self.get_class_properties(obj),
            )
        # inheritance links
        for rel in diagram.get_relationships("specialization"):
            self.printer.emit_edge(
                rel.from_object.fig_id,
                rel.to_object.fig_id,
                type_=EdgeType.INHERITS,
            )
        associations: dict[str, set[str]] = defaultdict(set)
        # generate associations
        for rel in diagram.get_relationships("association"):
            associations[rel.from_object.fig_id].add(rel.to_object.fig_id)
            self.printer.emit_edge(
                rel.from_object.fig_id,
                rel.to_object.fig_id,
                label=rel.name,
                type_=EdgeType.ASSOCIATION,
            )
        # generate aggregations
        for rel in diagram.get_relationships("aggregation"):
            if rel.to_object.fig_id in associations[rel.from_object.fig_id]:
                continue
            self.printer.emit_edge(
                rel.from_object.fig_id,
                rel.to_object.fig_id,
                label=rel.name,
                type_=EdgeType.AGGREGATION,
            )

    def set_printer(self, file_name: str, basename: str) -> None:
        """Set printer."""
        self.printer = self.printer_class(basename)
        self.file_name = file_name

    def get_package_properties(self, obj: PackageEntity) -> NodeProperties:
        """Get label and shape for packages."""
        return NodeProperties(
            label=obj.title,
            color=self.get_shape_color(obj) if self.config.colorized else "black",
        )

    def get_class_properties(self, obj: ClassEntity) -> NodeProperties:
        """Get label and shape for classes."""
        properties = NodeProperties(
            label=obj.title,
            attrs=obj.attrs if not self.config.only_classnames else None,
            methods=obj.methods if not self.config.only_classnames else None,
            fontcolor="red" if is_exception(obj.node) else "black",
            color=self.get_shape_color(obj) if self.config.colorized else "black",
        )
        return properties

    def get_shape_color(self, obj: DiagramEntity) -> str:
        """Get shape color."""
        qualified_name = obj.node.qname()
        if modutils.is_stdlib_module(qualified_name.split(".", maxsplit=1)[0]):
            return "grey"
        if isinstance(obj.node, nodes.ClassDef):
            package = qualified_name.rsplit(".", maxsplit=2)[0]
        elif obj.node.package:
            package = qualified_name
        else:
            package = qualified_name.rsplit(".", maxsplit=1)[0]
        base_name = ".".join(package.split(".", self.depth)[: self.depth])
        if base_name not in self.used_colors:
            self.used_colors[base_name] = next(self.available_colors)
        return self.used_colors[base_name]

    def save(self) -> None:
        """Write to disk."""
        self.printer.generate(self.file_name)

back/venvmsg/Lib/site-packages/pylint/pyreverse/writer.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Utilities methods and classes for reporters."""

from __future__ import annotations

from typing import TYPE_CHECKING

from pylint import utils
from pylint.reporters.base_reporter import BaseReporter
from pylint.reporters.collecting_reporter import CollectingReporter
from pylint.reporters.json_reporter import JSON2Reporter, JSONReporter
from pylint.reporters.multi_reporter import MultiReporter
from pylint.reporters.reports_handler_mix_in import ReportsHandlerMixIn

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


def initialize(linter: PyLinter) -> None:
    """Initialize linter with reporters in this package."""
    utils.register_plugins(linter, __path__[0])


__all__ = [
    "BaseReporter",
    "ReportsHandlerMixIn",
    "JSONReporter",
    "JSON2Reporter",
    "CollectingReporter",
    "MultiReporter",
]

back/venvmsg/Lib/site-packages/pylint/reporters/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import os
import sys
from typing import TYPE_CHECKING, TextIO

from pylint.message import Message
from pylint.reporters.ureports.nodes import Text
from pylint.utils import LinterStats

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter
    from pylint.reporters.ureports.nodes import Section


class BaseReporter:
    """Base class for reporters.

    symbols: show short symbolic names for messages.
    """

    extension = ""

    name = "base"
    """Name of the reporter."""

    def __init__(self, output: TextIO | None = None) -> None:
        self.linter: PyLinter
        self.section = 0
        self.out: TextIO = output or sys.stdout
        self.messages: list[Message] = []
        # Build the path prefix to strip to get relative paths
        self.path_strip_prefix = os.getcwd() + os.sep

    def handle_message(self, msg: Message) -> None:
        """Handle a new message triggered on the current file."""
        self.messages.append(msg)

    def writeln(self, string: str = "") -> None:
        """Write a line in the output buffer."""
        print(string, file=self.out)

    def display_reports(self, layout: Section) -> None:
        """Display results encapsulated in the layout tree."""
        self.section = 0
        if layout.report_id:
            if isinstance(layout.children[0].children[0], Text):
                layout.children[0].children[0].data += f" ({layout.report_id})"
            else:
                raise ValueError(f"Incorrect child for {layout.children[0].children}")
        self._display(layout)

    def _display(self, layout: Section) -> None:
        """Display the layout."""
        raise NotImplementedError()

    def display_messages(self, layout: Section | None) -> None:
        """Hook for displaying the messages of the reporter.

        This will be called whenever the underlying messages
        needs to be displayed. For some reporters, it probably
        doesn't make sense to display messages as soon as they
        are available, so some mechanism of storing them could be used.
        This method can be implemented to display them after they've
        been aggregated.
        """

    # Event callbacks

    def on_set_current_module(self, module: str, filepath: str | None) -> None:
        """Hook called when a module starts to be analysed."""

    def on_close(
        self,
        stats: LinterStats,
        previous_stats: LinterStats | None,
    ) -> None:
        """Hook called when a module finished analyzing."""

back/venvmsg/Lib/site-packages/pylint/reporters/base_reporter.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import TYPE_CHECKING

from pylint.reporters.base_reporter import BaseReporter

if TYPE_CHECKING:
    from pylint.reporters.ureports.nodes import Section


class CollectingReporter(BaseReporter):
    """Collects messages."""

    name = "collector"

    def __init__(self) -> None:
        super().__init__()
        self.messages = []

    def reset(self) -> None:
        self.messages = []

    def _display(self, layout: Section) -> None:
        pass

back/venvmsg/Lib/site-packages/pylint/reporters/collecting_reporter.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""JSON reporter."""

from __future__ import annotations

import json
from typing import TYPE_CHECKING, Optional, TypedDict

from pylint.interfaces import CONFIDENCE_MAP, UNDEFINED
from pylint.message import Message
from pylint.reporters.base_reporter import BaseReporter
from pylint.typing import MessageLocationTuple

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter
    from pylint.reporters.ureports.nodes import Section

# Since message-id is an invalid name we need to use the alternative syntax
OldJsonExport = TypedDict(
    "OldJsonExport",
    {
        "type": str,
        "module": str,
        "obj": str,
        "line": int,
        "column": int,
        "endLine": Optional[int],
        "endColumn": Optional[int],
        "path": str,
        "symbol": str,
        "message": str,
        "message-id": str,
    },
)


class JSONReporter(BaseReporter):
    """Report messages and layouts in JSON.

    Consider using JSON2Reporter instead, as it is superior and this reporter
    is no longer maintained.
    """

    name = "json"
    extension = "json"

    def display_messages(self, layout: Section | None) -> None:
        """Launch layouts display."""
        json_dumpable = [self.serialize(message) for message in self.messages]
        print(json.dumps(json_dumpable, indent=4), file=self.out)

    def display_reports(self, layout: Section) -> None:
        """Don't do anything in this reporter."""

    def _display(self, layout: Section) -> None:
        """Do nothing."""

    @staticmethod
    def serialize(message: Message) -> OldJsonExport:
        return {
            "type": message.category,
            "module": message.module,
            "obj": message.obj,
            "line": message.line,
            "column": message.column,
            "endLine": message.end_line,
            "endColumn": message.end_column,
            "path": message.path,
            "symbol": message.symbol,
            "message": message.msg or "",
            "message-id": message.msg_id,
        }

    @staticmethod
    def deserialize(message_as_json: OldJsonExport) -> Message:
        return Message(
            msg_id=message_as_json["message-id"],
            symbol=message_as_json["symbol"],
            msg=message_as_json["message"],
            location=MessageLocationTuple(
                abspath=message_as_json["path"],
                path=message_as_json["path"],
                module=message_as_json["module"],
                obj=message_as_json["obj"],
                line=message_as_json["line"],
                column=message_as_json["column"],
                end_line=message_as_json["endLine"],
                end_column=message_as_json["endColumn"],
            ),
            confidence=UNDEFINED,
        )


class JSONMessage(TypedDict):
    type: str
    message: str
    messageId: str
    symbol: str
    confidence: str
    module: str
    path: str
    absolutePath: str
    line: int
    endLine: int | None
    column: int
    endColumn: int | None
    obj: str


class JSON2Reporter(BaseReporter):
    name = "json2"
    extension = "json2"

    def display_reports(self, layout: Section) -> None:
        """Don't do anything in this reporter."""

    def _display(self, layout: Section) -> None:
        """Do nothing."""

    def display_messages(self, layout: Section | None) -> None:
        """Launch layouts display."""
        output = {
            "messages": [self.serialize(message) for message in self.messages],
            "statistics": self.serialize_stats(),
        }
        print(json.dumps(output, indent=4), file=self.out)

    @staticmethod
    def serialize(message: Message) -> JSONMessage:
        return JSONMessage(
            type=message.category,
            symbol=message.symbol,
            message=message.msg or "",
            messageId=message.msg_id,
            confidence=message.confidence.name,
            module=message.module,
            obj=message.obj,
            line=message.line,
            column=message.column,
            endLine=message.end_line,
            endColumn=message.end_column,
            path=message.path,
            absolutePath=message.abspath,
        )

    @staticmethod
    def deserialize(message_as_json: JSONMessage) -> Message:
        return Message(
            msg_id=message_as_json["messageId"],
            symbol=message_as_json["symbol"],
            msg=message_as_json["message"],
            location=MessageLocationTuple(
                abspath=message_as_json["absolutePath"],
                path=message_as_json["path"],
                module=message_as_json["module"],
                obj=message_as_json["obj"],
                line=message_as_json["line"],
                column=message_as_json["column"],
                end_line=message_as_json["endLine"],
                end_column=message_as_json["endColumn"],
            ),
            confidence=CONFIDENCE_MAP[message_as_json["confidence"]],
        )

    def serialize_stats(self) -> dict[str, str | int | dict[str, int]]:
        """Serialize the linter stats into something JSON dumpable."""
        stats = self.linter.stats

        counts_dict = {
            "fatal": stats.fatal,
            "error": stats.error,
            "warning": stats.warning,
            "refactor": stats.refactor,
            "convention": stats.convention,
            "info": stats.info,
        }

        # Calculate score based on the evaluation option
        evaluation = self.linter.config.evaluation
        try:
            note: int = eval(  # pylint: disable=eval-used
                evaluation, {}, {**counts_dict, "statement": stats.statement or 1}
            )
        except Exception as ex:  # pylint: disable=broad-except
            score: str | int = f"An exception occurred while rating: {ex}"
        else:
            score = round(note, 2)

        return {
            "messageTypeCount": counts_dict,
            "modulesLinted": len(stats.by_module),
            "score": score,
        }


def register(linter: PyLinter) -> None:
    linter.register_reporter(JSONReporter)
    linter.register_reporter(JSON2Reporter)

back/venvmsg/Lib/site-packages/pylint/reporters/json_reporter.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import os
from collections.abc import Callable
from copy import copy
from typing import TYPE_CHECKING, TextIO

from pylint.message import Message
from pylint.reporters.base_reporter import BaseReporter
from pylint.utils import LinterStats

if TYPE_CHECKING:
    from pylint.lint import PyLinter
    from pylint.reporters.ureports.nodes import Section


class MultiReporter:
    """Reports messages and layouts in plain text."""

    name = "_internal_multi_reporter"
    # Note: do not register this reporter with linter.register_reporter as it is
    #       not intended to be used directly like a regular reporter, but is
    #       instead used to implement the
    #       `--output-format=json:somefile.json,colorized`
    #       multiple output formats feature

    extension = ""

    def __init__(
        self,
        sub_reporters: list[BaseReporter],
        close_output_files: Callable[[], None],
        output: TextIO | None = None,
    ):
        self._sub_reporters = sub_reporters
        self.close_output_files = close_output_files
        self._path_strip_prefix = os.getcwd() + os.sep
        self._linter: PyLinter | None = None
        self.out = output
        self.messages: list[Message] = []

    @property
    def out(self) -> TextIO | None:
        return self.__out

    @out.setter
    def out(self, output: TextIO | None = None) -> None:
        """MultiReporter doesn't have its own output.

        This method is only provided for API parity with BaseReporter
        and should not be called with non-None values for 'output'.
        """
        self.__out = None
        if output is not None:
            raise NotImplementedError("MultiReporter does not support direct output.")

    def __del__(self) -> None:
        self.close_output_files()

    @property
    def path_strip_prefix(self) -> str:
        return self._path_strip_prefix

    @property
    def linter(self) -> PyLinter | None:
        return self._linter

    @linter.setter
    def linter(self, value: PyLinter) -> None:
        self._linter = value
        for rep in self._sub_reporters:
            rep.linter = value

    def handle_message(self, msg: Message) -> None:
        """Handle a new message triggered on the current file."""
        for rep in self._sub_reporters:
            # We provide a copy so reporters can't modify message for others.
            rep.handle_message(copy(msg))

    def writeln(self, string: str = "") -> None:
        """Write a line in the output buffer."""
        for rep in self._sub_reporters:
            rep.writeln(string)

    def display_reports(self, layout: Section) -> None:
        """Display results encapsulated in the layout tree."""
        for rep in self._sub_reporters:
            rep.display_reports(layout)

    def display_messages(self, layout: Section | None) -> None:
        """Hook for displaying the messages of the reporter."""
        for rep in self._sub_reporters:
            rep.display_messages(layout)

    def on_set_current_module(self, module: str, filepath: str | None) -> None:
        """Hook called when a module starts to be analysed."""
        for rep in self._sub_reporters:
            rep.on_set_current_module(module, filepath)

    def on_close(
        self,
        stats: LinterStats,
        previous_stats: LinterStats | None,
    ) -> None:
        """Hook called when a module finished analyzing."""
        for rep in self._sub_reporters:
            rep.on_close(stats, previous_stats)

back/venvmsg/Lib/site-packages/pylint/reporters/multi_reporter.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import collections
from collections.abc import MutableSequence
from typing import TYPE_CHECKING, DefaultDict, List, Tuple

from pylint.exceptions import EmptyReportError
from pylint.reporters.ureports.nodes import Section
from pylint.typing import ReportsCallable
from pylint.utils import LinterStats

if TYPE_CHECKING:
    from pylint.checkers import BaseChecker
    from pylint.lint.pylinter import PyLinter

ReportsDict = DefaultDict["BaseChecker", List[Tuple[str, str, ReportsCallable]]]


class ReportsHandlerMixIn:
    """A mix-in class containing all the reports and stats manipulation
    related methods for the main lint class.
    """

    def __init__(self) -> None:
        self._reports: ReportsDict = collections.defaultdict(list)
        self._reports_state: dict[str, bool] = {}

    def report_order(self) -> MutableSequence[BaseChecker]:
        """Return a list of reporters."""
        return list(self._reports)

    def register_report(
        self, reportid: str, r_title: str, r_cb: ReportsCallable, checker: BaseChecker
    ) -> None:
        """Register a report.

        :param reportid: The unique identifier for the report
        :param r_title: The report's title
        :param r_cb: The method to call to make the report
        :param checker: The checker defining the report
        """
        reportid = reportid.upper()
        self._reports[checker].append((reportid, r_title, r_cb))

    def enable_report(self, reportid: str) -> None:
        """Enable the report of the given id."""
        reportid = reportid.upper()
        self._reports_state[reportid] = True

    def disable_report(self, reportid: str) -> None:
        """Disable the report of the given id."""
        reportid = reportid.upper()
        self._reports_state[reportid] = False

    def report_is_enabled(self, reportid: str) -> bool:
        """Is the report associated to the given identifier enabled ?"""
        return self._reports_state.get(reportid, True)

    def make_reports(  # type: ignore[misc] # ReportsHandlerMixIn is always mixed with PyLinter
        self: PyLinter,
        stats: LinterStats,
        old_stats: LinterStats | None,
    ) -> Section:
        """Render registered reports."""
        sect = Section("Report", f"{self.stats.statement} statements analysed.")
        for checker in self.report_order():
            for reportid, r_title, r_cb in self._reports[checker]:
                if not self.report_is_enabled(reportid):
                    continue
                report_sect = Section(r_title)
                try:
                    r_cb(report_sect, stats, old_stats)
                except EmptyReportError:
                    continue
                report_sect.report_id = reportid
                sect.append(report_sect)
        return sect

back/venvmsg/Lib/site-packages/pylint/reporters/reports_handler_mix_in.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Plain text reporters:.

:text: the default one grouping messages by module
:colorized: an ANSI colorized text reporter
"""

from __future__ import annotations

import os
import re
import sys
import warnings
from dataclasses import asdict, fields
from typing import TYPE_CHECKING, Dict, NamedTuple, TextIO

from pylint.message import Message
from pylint.reporters import BaseReporter
from pylint.reporters.ureports.text_writer import TextWriter

if TYPE_CHECKING:
    from pylint.lint import PyLinter
    from pylint.reporters.ureports.nodes import Section


class MessageStyle(NamedTuple):
    """Styling of a message."""

    color: str | None
    """The color name (see `ANSI_COLORS` for available values)
    or the color number when 256 colors are available.
    """
    style: tuple[str, ...] = ()
    """Tuple of style strings (see `ANSI_COLORS` for available values)."""

    def __get_ansi_code(self) -> str:
        """Return ANSI escape code corresponding to color and style.

        :raise KeyError: if a nonexistent color or style identifier is given

        :return: the built escape code
        """
        ansi_code = [ANSI_STYLES[effect] for effect in self.style]
        if self.color:
            if self.color.isdigit():
                ansi_code.extend(["38", "5"])
                ansi_code.append(self.color)
            else:
                ansi_code.append(ANSI_COLORS[self.color])
        if ansi_code:
            return ANSI_PREFIX + ";".join(ansi_code) + ANSI_END
        return ""

    def _colorize_ansi(self, msg: str) -> str:
        if self.color is None and len(self.style) == 0:
            # If both color and style are not defined, then leave the text as is.
            return msg
        escape_code = self.__get_ansi_code()
        # If invalid (or unknown) color, don't wrap msg with ANSI codes
        if escape_code:
            return f"{escape_code}{msg}{ANSI_RESET}"
        return msg


ColorMappingDict = Dict[str, MessageStyle]

TITLE_UNDERLINES = ["", "=", "-", "."]

ANSI_PREFIX = "\033["
ANSI_END = "m"
ANSI_RESET = "\033[0m"
ANSI_STYLES = {
    "reset": "0",
    "bold": "1",
    "italic": "3",
    "underline": "4",
    "blink": "5",
    "inverse": "7",
    "strike": "9",
}
ANSI_COLORS = {
    "reset": "0",
    "black": "30",
    "red": "31",
    "green": "32",
    "yellow": "33",
    "blue": "34",
    "magenta": "35",
    "cyan": "36",
    "white": "37",
}

MESSAGE_FIELDS = {i.name for i in fields(Message)}
"""All fields of the Message class."""


def colorize_ansi(msg: str, msg_style: MessageStyle) -> str:
    """Colorize message by wrapping it with ANSI escape codes."""
    return msg_style._colorize_ansi(msg)


def make_header(msg: Message) -> str:
    return f"************* Module {msg.module}"


class TextReporter(BaseReporter):
    """Reports messages and layouts in plain text."""

    name = "text"
    extension = "txt"
    line_format = "{path}:{line}:{column}: {msg_id}: {msg} ({symbol})"

    def __init__(self, output: TextIO | None = None) -> None:
        super().__init__(output)
        self._modules: set[str] = set()
        self._template = self.line_format
        self._fixed_template = self.line_format
        """The output format template with any unrecognized arguments removed."""

    def on_set_current_module(self, module: str, filepath: str | None) -> None:
        """Set the format template to be used and check for unrecognized arguments."""
        template = str(self.linter.config.msg_template or self._template)

        # Return early if the template is the same as the previous one
        if template == self._template:
            return

        # Set template to the currently selected template
        self._template = template

        # Check to see if all parameters in the template are attributes of the Message
        arguments = re.findall(r"\{(\w+?)(:.*)?\}", template)
        for argument in arguments:
            if argument[0] not in MESSAGE_FIELDS:
                warnings.warn(
                    f"Don't recognize the argument '{argument[0]}' in the --msg-template. "
                    "Are you sure it is supported on the current version of pylint?",
                    stacklevel=2,
                )
                template = re.sub(r"\{" + argument[0] + r"(:.*?)?\}", "", template)
        self._fixed_template = template

    def write_message(self, msg: Message) -> None:
        """Convenience method to write a formatted message with class default
        template.
        """
        self_dict = asdict(msg)
        for key in ("end_line", "end_column"):
            self_dict[key] = self_dict[key] or ""

        self.writeln(self._fixed_template.format(**self_dict))

    def handle_message(self, msg: Message) -> None:
        """Manage message of different type and in the context of path."""
        if msg.module not in self._modules:
            self.writeln(make_header(msg))
            self._modules.add(msg.module)
        self.write_message(msg)

    def _display(self, layout: Section) -> None:
        """Launch layouts display."""
        print(file=self.out)
        TextWriter().format(layout, self.out)


class NoHeaderReporter(TextReporter):
    """Reports messages and layouts in plain text without a module header."""

    name = "no-header"

    def handle_message(self, msg: Message) -> None:
        """Write message(s) without module header."""
        if msg.module not in self._modules:
            self._modules.add(msg.module)
        self.write_message(msg)


class ParseableTextReporter(TextReporter):
    """A reporter very similar to TextReporter, but display messages in a form
    recognized by most text editors :

    <filename>:<linenum>:<msg>
    """

    name = "parseable"
    line_format = "{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}"

    def __init__(self, output: TextIO | None = None) -> None:
        warnings.warn(
            f"{self.name} output format is deprecated. This is equivalent to --msg-template={self.line_format}",
            DeprecationWarning,
            stacklevel=2,
        )
        super().__init__(output)


class VSTextReporter(ParseableTextReporter):
    """Visual studio text reporter."""

    name = "msvs"
    line_format = "{path}({line}): [{msg_id}({symbol}){obj}] {msg}"


class ColorizedTextReporter(TextReporter):
    """Simple TextReporter that colorizes text output."""

    name = "colorized"
    COLOR_MAPPING: ColorMappingDict = {
        "I": MessageStyle("green"),
        "C": MessageStyle(None, ("bold",)),
        "R": MessageStyle("magenta", ("bold", "italic")),
        "W": MessageStyle("magenta"),
        "E": MessageStyle("red", ("bold",)),
        "F": MessageStyle("red", ("bold", "underline")),
        "S": MessageStyle("yellow", ("inverse",)),  # S stands for module Separator
    }

    def __init__(
        self,
        output: TextIO | None = None,
        color_mapping: ColorMappingDict | None = None,
    ) -> None:
        super().__init__(output)
        self.color_mapping = color_mapping or ColorizedTextReporter.COLOR_MAPPING
        ansi_terms = ["xterm-16color", "xterm-256color"]
        if os.environ.get("TERM") not in ansi_terms:
            if sys.platform == "win32":
                # pylint: disable=import-outside-toplevel
                import colorama

                self.out = colorama.AnsiToWin32(self.out)

    def _get_decoration(self, msg_id: str) -> MessageStyle:
        """Returns the message style as defined in self.color_mapping."""
        return self.color_mapping.get(msg_id[0]) or MessageStyle(None)

    def handle_message(self, msg: Message) -> None:
        """Manage message of different types, and colorize output
        using ANSI escape codes.
        """
        if msg.module not in self._modules:
            msg_style = self._get_decoration("S")
            modsep = colorize_ansi(make_header(msg), msg_style)
            self.writeln(modsep)
            self._modules.add(msg.module)
        msg_style = self._get_decoration(msg.C)

        msg.msg = colorize_ansi(msg.msg, msg_style)
        msg.symbol = colorize_ansi(msg.symbol, msg_style)
        msg.category = colorize_ansi(msg.category, msg_style)
        msg.C = colorize_ansi(msg.C, msg_style)
        self.write_message(msg)


def register(linter: PyLinter) -> None:
    linter.register_reporter(TextReporter)
    linter.register_reporter(NoHeaderReporter)
    linter.register_reporter(ParseableTextReporter)
    linter.register_reporter(VSTextReporter)
    linter.register_reporter(ColorizedTextReporter)

back/venvmsg/Lib/site-packages/pylint/reporters/text.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

__all__ = ("BaseWriter",)

from pylint.reporters.ureports.base_writer import BaseWriter

back/venvmsg/Lib/site-packages/pylint/reporters/ureports/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Universal report objects and some formatting drivers.

A way to create simple reports using python objects, primarily designed to be
formatted as text and html.
"""

from __future__ import annotations

import sys
from collections.abc import Iterator
from io import StringIO
from typing import TYPE_CHECKING, TextIO

if TYPE_CHECKING:
    from pylint.reporters.ureports.nodes import (
        BaseLayout,
        EvaluationSection,
        Paragraph,
        Section,
        Table,
    )


class BaseWriter:
    """Base class for ureport writers."""

    def format(
        self,
        layout: BaseLayout,
        stream: TextIO = sys.stdout,
        encoding: str | None = None,
    ) -> None:
        """Format and write the given layout into the stream object.

        unicode policy: unicode strings may be found in the layout;
        try to call 'stream.write' with it, but give it back encoded using
        the given encoding if it fails
        """
        if not encoding:
            encoding = getattr(stream, "encoding", "UTF-8")
        self.encoding = encoding or "UTF-8"
        self.out = stream
        self.begin_format()
        layout.accept(self)
        self.end_format()

    def format_children(self, layout: EvaluationSection | Paragraph | Section) -> None:
        """Recurse on the layout children and call their accept method
        (see the Visitor pattern).
        """
        for child in getattr(layout, "children", ()):
            child.accept(self)

    def writeln(self, string: str = "") -> None:
        """Write a line in the output buffer."""
        self.write(string + "\n")

    def write(self, string: str) -> None:
        """Write a string in the output buffer."""
        self.out.write(string)

    def begin_format(self) -> None:
        """Begin to format a layout."""
        self.section = 0

    def end_format(self) -> None:
        """Finished formatting a layout."""

    def get_table_content(self, table: Table) -> list[list[str]]:
        """Trick to get table content without actually writing it.

        return an aligned list of lists containing table cells values as string
        """
        result: list[list[str]] = [[]]
        cols = table.cols
        for cell in self.compute_content(table):
            if cols == 0:
                result.append([])
                cols = table.cols
            cols -= 1
            result[-1].append(cell)
        # fill missing cells
        result[-1] += [""] * (cols - len(result[-1]))
        return result

    def compute_content(self, layout: BaseLayout) -> Iterator[str]:
        """Trick to compute the formatting of children layout before actually
        writing it.

        return an iterator on strings (one for each child element)
        """
        # Patch the underlying output stream with a fresh-generated stream,
        # which is used to store a temporary representation of a child
        # node.
        out = self.out
        try:
            for child in layout.children:
                stream = StringIO()
                self.out = stream
                child.accept(self)
                yield stream.getvalue()
        finally:
            self.out = out

back/venvmsg/Lib/site-packages/pylint/reporters/ureports/base_writer.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Micro reports objects.

A micro report is a tree of layout and content objects.
"""

from __future__ import annotations

from collections.abc import Iterable, Iterator
from typing import Any, Callable, TypeVar

from pylint.reporters.ureports.base_writer import BaseWriter

_T = TypeVar("_T")
_VNodeT = TypeVar("_VNodeT", bound="VNode")
VisitLeaveFunction = Callable[[_T, Any, Any], None]


class VNode:
    def __init__(self) -> None:
        self.parent: BaseLayout | None = None
        self.children: list[VNode] = []
        self.visitor_name: str = self.__class__.__name__.lower()

    def __iter__(self) -> Iterator[VNode]:
        return iter(self.children)

    def accept(self: _VNodeT, visitor: BaseWriter, *args: Any, **kwargs: Any) -> None:
        func: VisitLeaveFunction[_VNodeT] = getattr(
            visitor, f"visit_{self.visitor_name}"
        )
        return func(self, *args, **kwargs)

    def leave(self: _VNodeT, visitor: BaseWriter, *args: Any, **kwargs: Any) -> None:
        func: VisitLeaveFunction[_VNodeT] = getattr(
            visitor, f"leave_{self.visitor_name}"
        )
        return func(self, *args, **kwargs)


class BaseLayout(VNode):
    """Base container node.

    attributes
    * children : components in this table (i.e. the table's cells)
    """

    def __init__(self, children: Iterable[Text | str] = ()) -> None:
        super().__init__()
        for child in children:
            if isinstance(child, VNode):
                self.append(child)
            else:
                self.add_text(child)

    def append(self, child: VNode) -> None:
        """Add a node to children."""
        assert child not in self.parents()
        self.children.append(child)
        child.parent = self

    def insert(self, index: int, child: VNode) -> None:
        """Insert a child node."""
        self.children.insert(index, child)
        child.parent = self

    def parents(self) -> list[BaseLayout]:
        """Return the ancestor nodes."""
        assert self.parent is not self
        if self.parent is None:
            return []
        return [self.parent, *self.parent.parents()]

    def add_text(self, text: str) -> None:
        """Shortcut to add text data."""
        self.children.append(Text(text))


# non container nodes #########################################################


class Text(VNode):
    """A text portion.

    attributes :
    * data : the text value as an encoded or unicode string
    """

    def __init__(self, data: str, escaped: bool = True) -> None:
        super().__init__()
        self.escaped = escaped
        self.data = data


class VerbatimText(Text):
    """A verbatim text, display the raw data.

    attributes :
    * data : the text value as an encoded or unicode string
    """


# container nodes #############################################################


class Section(BaseLayout):
    """A section.

    attributes :
    * BaseLayout attributes

    a title may also be given to the constructor, it'll be added
    as a first element
    a description may also be given to the constructor, it'll be added
    as a first paragraph
    """

    def __init__(
        self,
        title: str | None = None,
        description: str | None = None,
        children: Iterable[Text | str] = (),
    ) -> None:
        super().__init__(children=children)
        if description:
            self.insert(0, Paragraph([Text(description)]))
        if title:
            self.insert(0, Title(children=(title,)))
        self.report_id: str = ""  # Used in ReportHandlerMixin.make_reports


class EvaluationSection(Section):
    def __init__(self, message: str, children: Iterable[Text | str] = ()) -> None:
        super().__init__(children=children)
        title = Paragraph()
        title.append(Text("-" * len(message)))
        self.append(title)
        message_body = Paragraph()
        message_body.append(Text(message))
        self.append(message_body)


class Title(BaseLayout):
    """A title.

    attributes :
    * BaseLayout attributes

    A title must not contain a section nor a paragraph!
    """


class Paragraph(BaseLayout):
    """A simple text paragraph.

    attributes :
    * BaseLayout attributes

    A paragraph must not contains a section !
    """


class Table(BaseLayout):
    """Some tabular data.

    attributes :
    * BaseLayout attributes
    * cols : the number of columns of the table (REQUIRED)
    * rheaders : the first row's elements are table's header
    * cheaders : the first col's elements are table's header
    * title : the table's optional title
    """

    def __init__(
        self,
        cols: int,
        title: str | None = None,
        rheaders: int = 0,
        cheaders: int = 0,
        children: Iterable[Text | str] = (),
    ) -> None:
        super().__init__(children=children)
        assert isinstance(cols, int)
        self.cols = cols
        self.title = title
        self.rheaders = rheaders
        self.cheaders = cheaders

back/venvmsg/Lib/site-packages/pylint/reporters/ureports/nodes.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Text formatting drivers for ureports."""

from __future__ import annotations

from typing import TYPE_CHECKING

from pylint.reporters.ureports.base_writer import BaseWriter

if TYPE_CHECKING:
    from pylint.reporters.ureports.nodes import (
        EvaluationSection,
        Paragraph,
        Section,
        Table,
        Text,
        Title,
        VerbatimText,
    )

TITLE_UNDERLINES = ["", "=", "-", "`", ".", "~", "^"]
BULLETS = ["*", "-"]


class TextWriter(BaseWriter):
    """Format layouts as text
    (ReStructured inspiration but not totally handled yet).
    """

    def __init__(self) -> None:
        super().__init__()
        self.list_level = 0

    def visit_section(self, layout: Section) -> None:
        """Display a section as text."""
        self.section += 1
        self.writeln()
        self.format_children(layout)
        self.section -= 1
        self.writeln()

    def visit_evaluationsection(self, layout: EvaluationSection) -> None:
        """Display an evaluation section as a text."""
        self.section += 1
        self.format_children(layout)
        self.section -= 1
        self.writeln()

    def visit_title(self, layout: Title) -> None:
        title = "".join(list(self.compute_content(layout)))
        self.writeln(title)
        try:
            self.writeln(TITLE_UNDERLINES[self.section] * len(title))
        except IndexError:
            print("FIXME TITLE TOO DEEP. TURNING TITLE INTO TEXT")

    def visit_paragraph(self, layout: Paragraph) -> None:
        """Enter a paragraph."""
        self.format_children(layout)
        self.writeln()

    def visit_table(self, layout: Table) -> None:
        """Display a table as text."""
        table_content = self.get_table_content(layout)
        # get columns width
        cols_width = [0] * len(table_content[0])
        for row in table_content:
            for index, col in enumerate(row):
                cols_width[index] = max(cols_width[index], len(col))
        self.default_table(layout, table_content, cols_width)
        self.writeln()

    def default_table(
        self, layout: Table, table_content: list[list[str]], cols_width: list[int]
    ) -> None:
        """Format a table."""
        cols_width = [size + 1 for size in cols_width]
        format_strings = " ".join(["%%-%ss"] * len(cols_width))
        format_strings %= tuple(cols_width)

        table_linesep = "\n+" + "+".join("-" * w for w in cols_width) + "+\n"
        headsep = "\n+" + "+".join("=" * w for w in cols_width) + "+\n"

        self.write(table_linesep)
        split_strings = format_strings.split(" ")
        for index, line in enumerate(table_content):
            self.write("|")
            for line_index, at_index in enumerate(line):
                self.write(split_strings[line_index] % at_index)
                self.write("|")
            if index == 0 and layout.rheaders:
                self.write(headsep)
            else:
                self.write(table_linesep)

    def visit_verbatimtext(self, layout: VerbatimText) -> None:
        """Display a verbatim layout as text (so difficult ;)."""
        self.writeln("::\n")
        for line in layout.data.splitlines():
            self.writeln("    " + line)
        self.writeln()

    def visit_text(self, layout: Text) -> None:
        """Add some text."""
        self.write(f"{layout.data}")

back/venvmsg/Lib/site-packages/pylint/reporters/ureports/text_writer.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Functional/non regression tests for pylint."""

__all__ = [
    "_get_tests_info",
    "_tokenize_str",
    "CheckerTestCase",
    "FunctionalTestFile",
    "linter",
    "LintModuleTest",
    "MessageTest",
    "MinimalTestReporter",
    "set_config",
    "GenericTestReporter",
    "UPDATE_FILE",
    "UPDATE_OPTION",
    "UnittestLinter",
    "create_files",
]

from pylint.testutils.checker_test_case import CheckerTestCase
from pylint.testutils.constants import UPDATE_FILE, UPDATE_OPTION
from pylint.testutils.decorator import set_config
from pylint.testutils.functional import FunctionalTestFile
from pylint.testutils.get_test_info import _get_tests_info
from pylint.testutils.global_test_linter import linter
from pylint.testutils.lint_module_test import LintModuleTest
from pylint.testutils.output_line import MessageTest
from pylint.testutils.reporter_for_tests import GenericTestReporter, MinimalTestReporter
from pylint.testutils.tokenize_str import _tokenize_str
from pylint.testutils.unittest_linter import UnittestLinter
from pylint.testutils.utils import create_files

back/venvmsg/Lib/site-packages/pylint/testutils/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

__all__ = ["PackageToLint", "PRIMER_DIRECTORY_PATH"]

from pylint.testutils._primer.package_to_lint import (
    PRIMER_DIRECTORY_PATH,
    PackageToLint,
)

back/venvmsg/Lib/site-packages/pylint/testutils/_primer/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import logging
from pathlib import Path
from typing import Literal

from git import GitCommandError
from git.cmd import Git
from git.repo import Repo

PRIMER_DIRECTORY_PATH = Path("tests") / ".pylint_primer_tests"


class DirtyPrimerDirectoryException(Exception):
    """We can't pull if there's local changes."""

    def __init__(self, path: Path | str):
        super().__init__(
            rf"""

/!\ Can't pull /!\

In order for the prepare command to be able to pull please cleanup your local repo:
cd {path}
git diff
"""
        )


class PackageToLint:
    """Represents data about a package to be tested during primer tests."""

    url: str
    """URL of the repository to clone."""

    branch: str
    """Branch of the repository to clone."""

    directories: list[str]
    """Directories within the repository to run pylint over."""

    commit: str | None
    """Commit hash to pin the repository on."""

    pylint_additional_args: list[str]
    """Arguments to give to pylint."""

    pylintrc_relpath: str | None
    """Path relative to project's main directory to the pylintrc if it exists."""

    minimum_python: str | None
    """Minimum python version supported by the package."""

    def __init__(
        self,
        url: str,
        branch: str,
        directories: list[str],
        commit: str | None = None,
        pylint_additional_args: list[str] | None = None,
        pylintrc_relpath: str | None = None,
        minimum_python: str | None = None,
    ) -> None:
        self.url = url
        self.branch = branch
        self.directories = directories
        self.commit = commit
        self.pylint_additional_args = pylint_additional_args or []
        self.pylintrc_relpath = pylintrc_relpath
        self.minimum_python = minimum_python

    @property
    def pylintrc(self) -> Path | Literal[""]:
        if self.pylintrc_relpath is None:
            # Fall back to "" to ensure pylint's own pylintrc is not discovered
            return ""
        return self.clone_directory / self.pylintrc_relpath

    @property
    def clone_directory(self) -> Path:
        """Directory to clone repository into."""
        clone_name = "/".join(self.url.split("/")[-2:]).replace(".git", "")
        return PRIMER_DIRECTORY_PATH / clone_name

    @property
    def paths_to_lint(self) -> list[str]:
        """The paths we need to lint."""
        return [str(self.clone_directory / path) for path in self.directories]

    @property
    def pylint_args(self) -> list[str]:
        options: list[str] = []
        # There is an error if rcfile is given but does not exist
        options += [f"--rcfile={self.pylintrc}"]
        return self.paths_to_lint + options + self.pylint_additional_args

    def lazy_clone(self) -> str:  # pragma: no cover
        """Concatenates the target directory and clones the file.

        Not expected to be tested as the primer won't work if it doesn't.
        It's tested in the continuous integration primers, only the coverage
        is not calculated on everything. If lazy clone breaks for local use
        we'll probably notice because we'll have a fatal when launching the
        primer locally.
        """
        logging.info("Lazy cloning %s", self.url)
        if not self.clone_directory.exists():
            return self._clone_repository()
        return self._pull_repository()

    def _clone_repository(self) -> str:
        options: dict[str, str | int] = {
            "url": self.url,
            "to_path": str(self.clone_directory),
            "branch": self.branch,
            "depth": 1,
        }
        logging.info("Directory does not exists, cloning: %s", options)
        repo = Repo.clone_from(
            url=self.url, to_path=self.clone_directory, branch=self.branch, depth=1
        )
        return str(repo.head.object.hexsha)

    def _pull_repository(self) -> str:
        remote_sha1_commit = Git().ls_remote(self.url, self.branch).split("\t")[0]
        local_sha1_commit = Repo(self.clone_directory).head.object.hexsha
        if remote_sha1_commit != local_sha1_commit:
            logging.info(
                "Remote sha is '%s' while local sha is '%s': pulling new commits",
                remote_sha1_commit,
                local_sha1_commit,
            )
            try:
                repo = Repo(self.clone_directory)
                if repo.is_dirty():
                    raise DirtyPrimerDirectoryException(self.clone_directory)
                origin = repo.remotes.origin
                origin.pull()
            except GitCommandError as e:
                raise SystemError(
                    f"Failed to clone repository for {self.clone_directory}"
                ) from e
        else:
            logging.info("Repository already up to date.")
        return str(remote_sha1_commit)

back/venvmsg/Lib/site-packages/pylint/testutils/_primer/package_to_lint.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

from pylint.testutils._primer import PackageToLint
from pylint.testutils._primer.primer_command import PrimerCommand
from pylint.testutils._primer.primer_compare_command import CompareCommand
from pylint.testutils._primer.primer_prepare_command import PrepareCommand
from pylint.testutils._primer.primer_run_command import RunCommand


class Primer:
    """Main class to handle priming of packages."""

    def __init__(self, primer_directory: Path, json_path: Path) -> None:
        # Preparing arguments
        self.primer_directory = primer_directory
        self._argument_parser = argparse.ArgumentParser(prog="Pylint Primer")
        self._subparsers = self._argument_parser.add_subparsers(
            dest="command", required=True
        )

        # All arguments for the prepare parser
        prepare_parser = self._subparsers.add_parser("prepare")
        prepare_parser.add_argument(
            "--clone", help="Clone all packages.", action="store_true", default=False
        )
        prepare_parser.add_argument(
            "--check",
            help="Check consistencies and commits of all packages.",
            action="store_true",
            default=False,
        )
        prepare_parser.add_argument(
            "--make-commit-string",
            help="Get latest commit string.",
            action="store_true",
            default=False,
        )
        prepare_parser.add_argument(
            "--read-commit-string",
            help="Print latest commit string.",
            action="store_true",
            default=False,
        )

        # All arguments for the run parser
        run_parser = self._subparsers.add_parser("run")
        run_parser.add_argument(
            "--type", choices=["main", "pr"], required=True, help="Type of primer run."
        )
        run_parser.add_argument(
            "--batches",
            required=False,
            type=int,
            help="Number of batches",
        )
        run_parser.add_argument(
            "--batchIdx",
            required=False,
            type=int,
            help="Portion of primer packages to run.",
        )

        # All arguments for the compare parser
        compare_parser = self._subparsers.add_parser("compare")
        compare_parser.add_argument(
            "--base-file",
            required=True,
            help="Location of output file of the base run.",
        )
        compare_parser.add_argument(
            "--new-file",
            required=True,
            help="Location of output file of the new run.",
        )
        compare_parser.add_argument(
            "--commit",
            required=True,
            help="Commit hash of the PR commit being checked.",
        )
        compare_parser.add_argument(
            "--batches",
            required=False,
            type=int,
            help="Number of batches (filepaths with the placeholder BATCHIDX will be numbered)",
        )

        # Storing arguments
        self.config = self._argument_parser.parse_args()

        self.packages = self._get_packages_to_lint_from_json(json_path)
        """All packages to prime."""

        if self.config.command == "prepare":
            command_class: type[PrimerCommand] = PrepareCommand
        elif self.config.command == "run":
            command_class = RunCommand
        elif self.config.command == "compare":
            command_class = CompareCommand
        self.command = command_class(self.primer_directory, self.packages, self.config)

    def run(self) -> None:
        self.command.run()

    @staticmethod
    def _minimum_python_supported(package_data: dict[str, str]) -> bool:
        min_python_str = package_data.get("minimum_python", None)
        if not min_python_str:
            return True
        min_python_tuple = tuple(int(n) for n in min_python_str.split("."))
        return min_python_tuple <= sys.version_info[:2]

    @staticmethod
    def _get_packages_to_lint_from_json(json_path: Path) -> dict[str, PackageToLint]:
        with open(json_path, encoding="utf8") as f:
            return {
                name: PackageToLint(**package_data)
                for name, package_data in json.load(f).items()
                if Primer._minimum_python_supported(package_data)
            }

back/venvmsg/Lib/site-packages/pylint/testutils/_primer/primer.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import abc
import argparse
from pathlib import Path
from typing import Dict, TypedDict

from pylint.reporters.json_reporter import OldJsonExport
from pylint.testutils._primer import PackageToLint


class PackageData(TypedDict):
    commit: str
    messages: list[OldJsonExport]


PackageMessages = Dict[str, PackageData]


class PrimerCommand:
    """Generic primer action with required arguments."""

    def __init__(
        self,
        primer_directory: Path,
        packages: dict[str, PackageToLint],
        config: argparse.Namespace,
    ) -> None:
        self.primer_directory = primer_directory
        self.packages = packages
        self.config = config

    @abc.abstractmethod
    def run(self) -> None:
        pass

back/venvmsg/Lib/site-packages/pylint/testutils/_primer/primer_command.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations

import json
from pathlib import Path, PurePosixPath

from pylint.reporters.json_reporter import OldJsonExport
from pylint.testutils._primer.primer_command import (
    PackageData,
    PackageMessages,
    PrimerCommand,
)

MAX_GITHUB_COMMENT_LENGTH = 65536


class CompareCommand(PrimerCommand):
    def run(self) -> None:
        if self.config.batches is None:
            main_data = self._load_json(self.config.base_file)
            pr_data = self._load_json(self.config.new_file)
        else:
            main_data = {}
            pr_data = {}
            for idx in range(self.config.batches):
                main_data.update(
                    self._load_json(
                        self.config.base_file.replace("BATCHIDX", "batch" + str(idx))
                    )
                )
                pr_data.update(
                    self._load_json(
                        self.config.new_file.replace("BATCHIDX", "batch" + str(idx))
                    )
                )

        missing_messages_data, new_messages_data = self._cross_reference(
            main_data, pr_data
        )
        comment = self._create_comment(missing_messages_data, new_messages_data)
        with open(self.primer_directory / "comment.txt", "w", encoding="utf-8") as f:
            f.write(comment)

    @staticmethod
    def _cross_reference(
        main_data: PackageMessages, pr_data: PackageMessages
    ) -> tuple[PackageMessages, PackageMessages]:
        missing_messages_data: PackageMessages = {}
        for package, data in main_data.items():
            package_missing_messages: list[OldJsonExport] = []
            for message in data["messages"]:
                try:
                    pr_data[package]["messages"].remove(message)
                except ValueError:
                    package_missing_messages.append(message)
            missing_messages_data[package] = PackageData(
                commit=pr_data[package]["commit"], messages=package_missing_messages
            )
        return missing_messages_data, pr_data

    @staticmethod
    def _load_json(file_path: Path | str) -> PackageMessages:
        with open(file_path, encoding="utf-8") as f:
            result: PackageMessages = json.load(f)
        return result

    def _create_comment(
        self, all_missing_messages: PackageMessages, all_new_messages: PackageMessages
    ) -> str:
        comment = ""
        for package, missing_messages in all_missing_messages.items():
            if len(comment) >= MAX_GITHUB_COMMENT_LENGTH:
                break
            new_messages = all_new_messages[package]
            if not missing_messages["messages"] and not new_messages["messages"]:
                continue
            comment += self._create_comment_for_package(
                package, new_messages, missing_messages
            )
        comment = (
            f"🤖 **Effect of this PR on checked open source code:** 🤖\n\n{comment}"
            if comment
            else (
                "🤖 According to the primer, this change has **no effect** on the"
                " checked open source code. 🤖🎉\n\n"
            )
        )
        return self._truncate_comment(comment)

    def _create_comment_for_package(
        self, package: str, new_messages: PackageData, missing_messages: PackageData
    ) -> str:
        comment = f"\n\n**Effect on [{package}]({self.packages[package].url}):**\n"
        # Create comment for new messages
        count = 1
        astroid_errors = 0
        new_non_astroid_messages = ""
        if new_messages["messages"]:
            print("Now emitted:")
        for message in new_messages["messages"]:
            filepath = str(
                PurePosixPath(message["path"]).relative_to(
                    self.packages[package].clone_directory
                )
            )
            # Existing astroid errors may still show up as "new" because the timestamp
            # in the message is slightly different.
            if message["symbol"] == "astroid-error":
                astroid_errors += 1
            else:
                new_non_astroid_messages += (
                    f"{count}) {message['symbol']}:\n*{message['message']}*\n"
                    f"{self.packages[package].url}/blob/{new_messages['commit']}/{filepath}#L{message['line']}\n"
                )
                print(message)
                count += 1

        if astroid_errors:
            comment += (
                f'{astroid_errors} "astroid error(s)" were found. '
                "Please open the GitHub Actions log to see what failed or crashed.\n\n"
            )
        if new_non_astroid_messages:
            comment += (
                "The following messages are now emitted:\n\n<details>\n\n"
                + new_non_astroid_messages
                + "\n</details>\n\n"
            )

        # Create comment for missing messages
        count = 1
        if missing_messages["messages"]:
            comment += "The following messages are no longer emitted:\n\n<details>\n\n"
            print("No longer emitted:")
        for message in missing_messages["messages"]:
            comment += f"{count}) {message['symbol']}:\n*{message['message']}*\n"
            filepath = str(
                PurePosixPath(message["path"]).relative_to(
                    self.packages[package].clone_directory
                )
            )
            assert not self.packages[package].url.endswith(
                ".git"
            ), "You don't need the .git at the end of the github url."
            comment += (
                f"{self.packages[package].url}"
                f"/blob/{new_messages['commit']}/{filepath}#L{message['line']}\n"
            )
            count += 1
            print(message)
        if missing_messages:
            comment += "\n</details>\n\n"
        return comment

    def _truncate_comment(self, comment: str) -> str:
        """GitHub allows only a set number of characters in a comment."""
        hash_information = (
            f"*This comment was generated for commit {self.config.commit}*"
        )
        if len(comment) + len(hash_information) >= MAX_GITHUB_COMMENT_LENGTH:
            truncation_information = (
                f"*This comment was truncated because GitHub allows only"
                f" {MAX_GITHUB_COMMENT_LENGTH} characters in a comment.*"
            )
            max_len = (
                MAX_GITHUB_COMMENT_LENGTH
                - len(hash_information)
                - len(truncation_information)
            )
            comment = f"{comment[:max_len - 10]}...\n\n{truncation_information}\n\n"
        comment += hash_information
        return comment

back/venvmsg/Lib/site-packages/pylint/testutils/_primer/primer_compare_command.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations

import sys

from git.cmd import Git
from git.repo import Repo

from pylint.testutils._primer.primer_command import PrimerCommand


class PrepareCommand(PrimerCommand):
    def run(self) -> None:
        commit_string = ""
        version_string = ".".join(str(x) for x in sys.version_info[:2])
        # Shorten the SHA to avoid exceeding GitHub's 512 char ceiling
        if self.config.clone:
            for package, data in self.packages.items():
                local_commit = data.lazy_clone()
                print(f"Cloned '{package}' at commit '{local_commit}'.")
                commit_string += local_commit[:8] + "_"
        elif self.config.check:
            for package, data in self.packages.items():
                local_commit = Repo(data.clone_directory).head.object.hexsha
                print(f"Found '{package}' at commit '{local_commit}'.")
                commit_string += local_commit[:8] + "_"
        elif self.config.make_commit_string:
            for package, data in self.packages.items():
                remote_sha1_commit = (
                    Git().ls_remote(data.url, data.branch).split("\t")[0][:8]
                )
                print(f"'{package}' remote is at commit '{remote_sha1_commit}'.")
                commit_string += remote_sha1_commit + "_"
        elif self.config.read_commit_string:
            with open(
                self.primer_directory / f"commit_string_{version_string}.txt",
                encoding="utf-8",
            ) as f:
                print(f.read())
        if commit_string:
            with open(
                self.primer_directory / f"commit_string_{version_string}.txt",
                "w",
                encoding="utf-8",
            ) as f:
                f.write(commit_string)

back/venvmsg/Lib/site-packages/pylint/testutils/_primer/primer_prepare_command.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import json
import sys
import warnings
from io import StringIO

from git.repo import Repo

from pylint.lint import Run
from pylint.message import Message
from pylint.reporters.json_reporter import JSONReporter, OldJsonExport
from pylint.testutils._primer.package_to_lint import PackageToLint
from pylint.testutils._primer.primer_command import (
    PackageData,
    PackageMessages,
    PrimerCommand,
)

GITHUB_CRASH_TEMPLATE_LOCATION = "/home/runner/.cache"
CRASH_TEMPLATE_INTRO = "There is a pre-filled template"


class RunCommand(PrimerCommand):
    def run(self) -> None:
        packages: PackageMessages = {}
        fatal_msgs: list[Message] = []
        package_data_iter = (
            self.packages.items()
            if self.config.batches is None
            else list(self.packages.items())[
                self.config.batchIdx :: self.config.batches
            ]
        )
        for package, data in package_data_iter:
            messages, p_fatal_msgs = self._lint_package(package, data)
            fatal_msgs += p_fatal_msgs
            local_commit = Repo(data.clone_directory).head.object.hexsha
            packages[package] = PackageData(commit=local_commit, messages=messages)
        path = self.primer_directory / (
            f"output_{'.'.join(str(i) for i in sys.version_info[:3])}_{self.config.type}"
            + (f"_batch{self.config.batchIdx}.txt" if self.config.batches else "")
        )
        print(f"Writing result in {path}")
        with open(path, "w", encoding="utf-8") as f:
            json.dump(packages, f)
        # Assert that a PR run does not introduce new fatal errors
        if self.config.type == "pr":
            plural = "s" if len(fatal_msgs) > 1 else ""
            assert (
                not fatal_msgs
            ), f"We encountered {len(fatal_msgs)} fatal error message{plural} (see log)."

    @staticmethod
    def _filter_fatal_errors(
        messages: list[OldJsonExport],
    ) -> list[Message]:
        """Separate fatal errors so we can report them independently."""
        fatal_msgs: list[Message] = []
        for raw_message in messages:
            message = JSONReporter.deserialize(raw_message)
            if message.category == "fatal":
                if GITHUB_CRASH_TEMPLATE_LOCATION in message.msg:
                    # Remove the crash template location if we're running on GitHub.
                    # We were falsely getting "new" errors when the timestamp changed.
                    message.msg = message.msg.rsplit(CRASH_TEMPLATE_INTRO)[0]
                fatal_msgs.append(message)
        return fatal_msgs

    @staticmethod
    def _print_msgs(msgs: list[Message]) -> str:
        return "\n".join(f"- {JSONReporter.serialize(m)}" for m in msgs)

    def _lint_package(
        self, package_name: str, data: PackageToLint
    ) -> tuple[list[OldJsonExport], list[Message]]:
        # We want to test all the code we can
        enables = ["--enable-all-extensions", "--enable=all"]
        # Duplicate code takes too long and is relatively safe
        # TODO: Find a way to allow cyclic-import and compare output correctly
        disables = ["--disable=duplicate-code,cyclic-import"]
        additional = ["--clear-cache-post-run=y"]
        arguments = data.pylint_args + enables + disables + additional
        output = StringIO()
        reporter = JSONReporter(output)
        print(f"Running 'pylint {', '.join(arguments)}'")
        pylint_exit_code = -1
        try:
            Run(arguments, reporter=reporter)
        except SystemExit as e:
            pylint_exit_code = int(e.code)  # type: ignore[arg-type]
        readable_messages: str = output.getvalue()
        messages: list[OldJsonExport] = json.loads(readable_messages)
        fatal_msgs: list[Message] = []
        if pylint_exit_code % 2 == 0:
            print(f"Successfully primed {package_name}.")
        else:
            fatal_msgs = self._filter_fatal_errors(messages)
            if fatal_msgs:
                warnings.warn(
                    f"Encountered fatal errors while priming {package_name} !\n"
                    f"{self._print_msgs(fatal_msgs)}\n\n",
                    stacklevel=2,
                )
        return messages, fatal_msgs

back/venvmsg/Lib/site-packages/pylint/testutils/_primer/primer_run_command.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Classes and functions used to mimic normal pylint runs.

This module is considered private and can change at any time.
"""

from __future__ import annotations

from collections.abc import Sequence

from pylint.lint import Run as LintRun
from pylint.reporters.base_reporter import BaseReporter
from pylint.testutils.lint_module_test import PYLINTRC


def _add_rcfile_default_pylintrc(args: list[str]) -> list[str]:
    """Add a default pylintrc with the rcfile option in a list of pylint args."""
    if not any("--rcfile" in arg for arg in args):
        args.insert(0, f"--rcfile={PYLINTRC}")
    return args


class _Run(LintRun):

    """Like Run, but we're using an explicitly set empty pylintrc.

    We don't want to use the project's pylintrc during tests, because
    it means that a change in our config could break tests.
    But we want to see if the changes to the default break tests.
    """

    def __init__(
        self,
        args: Sequence[str],
        reporter: BaseReporter | None = None,
        exit: bool = True,  # pylint: disable=redefined-builtin
    ) -> None:
        args = _add_rcfile_default_pylintrc(list(args))
        super().__init__(args, reporter, exit)

back/venvmsg/Lib/site-packages/pylint/testutils/_run.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import contextlib
from collections.abc import Generator, Iterator
from typing import Any

from astroid import nodes

from pylint.constants import IS_PYPY, PY39_PLUS
from pylint.testutils.global_test_linter import linter
from pylint.testutils.output_line import MessageTest
from pylint.testutils.unittest_linter import UnittestLinter
from pylint.utils import ASTWalker


class CheckerTestCase:
    """A base testcase class for unit testing individual checker classes."""

    # TODO: Figure out way to type this as type[BaseChecker] while also
    # setting self.checker correctly.
    CHECKER_CLASS: Any
    CONFIG: dict[str, Any] = {}

    def setup_method(self) -> None:
        self.linter = UnittestLinter()
        self.checker = self.CHECKER_CLASS(self.linter)
        for key, value in self.CONFIG.items():
            setattr(self.checker.linter.config, key, value)
        self.checker.open()

    @contextlib.contextmanager
    def assertNoMessages(self) -> Iterator[None]:
        """Assert that no messages are added by the given method."""
        with self.assertAddsMessages():
            yield

    @contextlib.contextmanager
    def assertAddsMessages(
        self, *messages: MessageTest, ignore_position: bool = False
    ) -> Generator[None, None, None]:
        """Assert that exactly the given method adds the given messages.

        The list of messages must exactly match *all* the messages added by the
        method. Additionally, we check to see whether the args in each message can
        actually be substituted into the message string.

        Using the keyword argument `ignore_position`, all checks for position
        arguments (line, col_offset, ...) will be skipped. This can be used to
        just test messages for the correct node.
        """
        yield
        got = self.linter.release_messages()
        no_msg = "No message."
        expected = "\n".join(repr(m) for m in messages) or no_msg
        got_str = "\n".join(repr(m) for m in got) or no_msg
        msg = (
            "Expected messages did not match actual.\n"
            f"\nExpected:\n{expected}\n\nGot:\n{got_str}\n"
        )

        assert len(messages) == len(got), msg

        for expected_msg, gotten_msg in zip(messages, got):
            assert expected_msg.msg_id == gotten_msg.msg_id, msg
            assert expected_msg.node == gotten_msg.node, msg
            assert expected_msg.args == gotten_msg.args, msg
            assert expected_msg.confidence == gotten_msg.confidence, msg

            if ignore_position:
                # Do not check for line, col_offset etc...
                continue

            assert expected_msg.line == gotten_msg.line, msg
            assert expected_msg.col_offset == gotten_msg.col_offset, msg
            if not IS_PYPY or PY39_PLUS:
                assert expected_msg.end_line == gotten_msg.end_line, msg
                assert expected_msg.end_col_offset == gotten_msg.end_col_offset, msg

    def walk(self, node: nodes.NodeNG) -> None:
        """Recursive walk on the given node."""
        walker = ASTWalker(linter)
        walker.add_checker(self.checker)
        walker.walk(node)

back/venvmsg/Lib/site-packages/pylint/testutils/checker_test_case.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Utility functions for configuration testing."""

from __future__ import annotations

import copy
import json
import logging
import unittest
from pathlib import Path
from typing import Any, Dict
from unittest.mock import Mock

from pylint.lint import Run

# We use Any in this typing because the configuration contains real objects and constants
# that could be a lot of things.
ConfigurationValue = Any
PylintConfiguration = Dict[str, ConfigurationValue]


def get_expected_or_default(
    tested_configuration_file: str | Path,
    suffix: str,
    default: str,
) -> str:
    """Return the expected value from the file if it exists, or the given default."""
    expected = default
    path = Path(tested_configuration_file)
    expected_result_path = path.parent / f"{path.stem}.{suffix}"
    if expected_result_path.exists():
        with open(expected_result_path, encoding="utf8") as f:
            expected = f.read()
        # logging is helpful to realize your file is not taken into
        # account after a misspelling of the file name. The output of the
        # program is checked during the test so printing messes with the result.
        logging.info("%s exists.", expected_result_path)
    else:
        logging.info("%s not found, using '%s'.", expected_result_path, default)
    return expected


EXPECTED_CONF_APPEND_KEY = "functional_append"
EXPECTED_CONF_REMOVE_KEY = "functional_remove"


def get_expected_configuration(
    configuration_path: str, default_configuration: PylintConfiguration
) -> PylintConfiguration:
    """Get the expected parsed configuration of a configuration functional test."""
    result = copy.deepcopy(default_configuration)
    config_as_json = get_expected_or_default(
        configuration_path, suffix="result.json", default="{}"
    )
    to_override = json.loads(config_as_json)
    for key, value in to_override.items():
        if key == EXPECTED_CONF_APPEND_KEY:
            for fkey, fvalue in value.items():
                result[fkey] += fvalue
        elif key == EXPECTED_CONF_REMOVE_KEY:
            for fkey, fvalue in value.items():
                new_value = []
                for old_value in result[fkey]:
                    if old_value not in fvalue:
                        new_value.append(old_value)
                result[fkey] = new_value
        else:
            result[key] = value
    return result


def get_related_files(
    tested_configuration_file: str | Path, suffix_filter: str
) -> list[Path]:
    """Return all the file related to a test conf file ending with a suffix."""
    conf_path = Path(tested_configuration_file)
    return [
        p
        for p in conf_path.parent.iterdir()
        if str(p.stem).startswith(conf_path.stem) and str(p).endswith(suffix_filter)
    ]


def get_expected_output(
    configuration_path: str | Path, user_specific_path: Path
) -> tuple[int, str]:
    """Get the expected output of a functional test."""
    exit_code = 0
    msg = (
        "we expect a single file of the form 'filename.32.out' where 'filename' represents "
        "the name of the configuration file, and '32' the expected error code."
    )
    possible_out_files = get_related_files(configuration_path, suffix_filter="out")
    if len(possible_out_files) > 1:
        logging.error(
            "Too much .out files for %s %s.",
            configuration_path,
            msg,
        )
        return -1, "out file is broken"
    if not possible_out_files:
        # logging is helpful to see what the expected exit code is and why.
        # The output of the program is checked during the test so printing
        # messes with the result.
        logging.info(".out file does not exists, so the expected exit code is 0")
        return 0, ""
    path = possible_out_files[0]
    try:
        exit_code = int(str(path.stem).rsplit(".", maxsplit=1)[-1])
    except Exception as e:  # pylint: disable=broad-except
        logging.error(
            "Wrong format for .out file name for %s %s: %s",
            configuration_path,
            msg,
            e,
        )
        return -1, "out file is broken"

    output = get_expected_or_default(
        configuration_path, suffix=f"{exit_code}.out", default=""
    )
    logging.info(
        "Output exists for %s so the expected exit code is %s",
        configuration_path,
        exit_code,
    )
    return exit_code, output.format(
        abspath=configuration_path,
        relpath=Path(configuration_path).relative_to(user_specific_path),
    )


def run_using_a_configuration_file(
    configuration_path: Path | str, file_to_lint: str = __file__
) -> tuple[Mock, Mock, Run]:
    """Simulate a run with a configuration without really launching the checks."""
    configuration_path = str(configuration_path)
    args = ["--rcfile", configuration_path, file_to_lint]
    # We do not capture the `SystemExit` as then the `runner` variable
    # would not be accessible outside the `with` block.
    with unittest.mock.patch("sys.exit") as mocked_exit:
        # Do not actually run checks, that could be slow. We don't mock
        # `PyLinter.check`: it calls `PyLinter.initialize` which is
        # needed to properly set up messages inclusion/exclusion
        # in `_msg_states`, used by `is_message_enabled`.
        check = "pylint.lint.pylinter.check_parallel"
        with unittest.mock.patch(check) as mocked_check_parallel:
            runner = Run(args)
    return mocked_exit, mocked_check_parallel, runner

back/venvmsg/Lib/site-packages/pylint/testutils/configuration_test.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

import operator
import re
import sys
from pathlib import Path

SYS_VERS_STR = (
    "%d%d%d" % sys.version_info[:3]  # pylint: disable=consider-using-f-string
)
TITLE_UNDERLINES = ["", "=", "-", "."]
UPDATE_OPTION = "--update-functional-output"
UPDATE_FILE = Path("pylint-functional-test-update")
# Common sub-expressions.
_MESSAGE = {"msg": r"[a-z][a-z\-]+"}
# Matches a #,
#  - followed by a comparison operator and a Python version (optional),
#  - followed by a line number with a +/- (optional),
#  - followed by a list of bracketed message symbols.
# Used to extract expected messages from testdata files.
_EXPECTED_RE = re.compile(
    r"\s*#\s*(?:(?P<line>[+-]?[0-9]+):)?"  # pylint: disable=consider-using-f-string
    r"(?:(?P<op>[><=]+) *(?P<version>[0-9.]+):)?"
    r"\s*\[(?P<msgs>{msg}(?:,\s*{msg})*)]".format(**_MESSAGE)
)

_OPERATORS = {">": operator.gt, "<": operator.lt, ">=": operator.ge, "<=": operator.le}

back/venvmsg/Lib/site-packages/pylint/testutils/constants.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import functools
from collections.abc import Callable
from typing import Any

from pylint.testutils.checker_test_case import CheckerTestCase


def set_config(**kwargs: Any) -> Callable[[Callable[..., None]], Callable[..., None]]:
    """Decorator for setting an option on the linter.

    Passing the args and kwargs back to the test function itself
    allows this decorator to be used on parameterized test cases.
    """

    def _wrapper(fun: Callable[..., None]) -> Callable[..., None]:
        @functools.wraps(fun)
        def _forward(
            self: CheckerTestCase, *args: Any, **test_function_kwargs: Any
        ) -> None:
            """Set option via argparse."""
            for key, value in kwargs.items():
                self.linter.set_option(key, value)

            # Reopen checker in case, it may be interested in configuration change
            self.checker.open()

            fun(self, *args, **test_function_kwargs)

        return _forward

    return _wrapper

back/venvmsg/Lib/site-packages/pylint/testutils/decorator.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

__all__ = [
    "FunctionalTestFile",
    "REASONABLY_DISPLAYABLE_VERTICALLY",
    "get_functional_test_files_from_directory",
    "NoFileError",
    "parse_python_version",
    "LintModuleOutputUpdate",
]

from pylint.testutils.functional.find_functional_tests import (
    REASONABLY_DISPLAYABLE_VERTICALLY,
    get_functional_test_files_from_directory,
)
from pylint.testutils.functional.lint_module_output_update import LintModuleOutputUpdate
from pylint.testutils.functional.test_file import (
    FunctionalTestFile,
    NoFileError,
    parse_python_version,
)

back/venvmsg/Lib/site-packages/pylint/testutils/functional/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import os
from collections.abc import Iterator
from pathlib import Path

from pylint.testutils.functional.test_file import FunctionalTestFile

REASONABLY_DISPLAYABLE_VERTICALLY = 49
"""'Wet finger' number of files that are reasonable to display by an IDE.

'Wet finger' as in 'in my settings there are precisely this many'.
"""

IGNORED_PARENT_DIRS = {
    "deprecated_relative_import",
    "ext",
    "regression",
    "regression_02",
}
"""Direct parent directories that should be ignored."""

IGNORED_PARENT_PARENT_DIRS = {
    "docparams",
    "deprecated_relative_import",
    "ext",
}
"""Parents of direct parent directories that should be ignored."""


def get_functional_test_files_from_directory(
    input_dir: Path | str,
    max_file_per_directory: int = REASONABLY_DISPLAYABLE_VERTICALLY,
) -> list[FunctionalTestFile]:
    """Get all functional tests in the input_dir."""
    suite = []

    _check_functional_tests_structure(Path(input_dir), max_file_per_directory)

    for dirpath, dirnames, filenames in os.walk(input_dir):
        if dirpath.endswith("__pycache__"):
            continue
        dirnames.sort()
        filenames.sort()
        for filename in filenames:
            if filename != "__init__.py" and filename.endswith(".py"):
                suite.append(FunctionalTestFile(dirpath, filename))
    return suite


def _check_functional_tests_structure(
    directory: Path, max_file_per_directory: int
) -> None:
    """Check if test directories follow correct file/folder structure.

    Ignore underscored directories or files.
    """
    if Path(directory).stem.startswith("_"):
        return

    files: set[Path] = set()
    dirs: set[Path] = set()

    def _get_files_from_dir(
        path: Path, violations: list[tuple[Path, int]]
    ) -> list[Path]:
        """Return directories and files from a directory and handles violations."""
        files_without_leading_underscore = list(
            p for p in path.iterdir() if not p.stem.startswith("_")
        )
        if len(files_without_leading_underscore) > max_file_per_directory:
            violations.append((path, len(files_without_leading_underscore)))
        return files_without_leading_underscore

    def walk(path: Path) -> Iterator[Path]:
        violations: list[tuple[Path, int]] = []
        violations_msgs: set[str] = set()
        parent_dir_files = _get_files_from_dir(path, violations)
        error_msg = (
            "The following directory contains too many functional tests files:\n"
        )
        for _file_or_dir in parent_dir_files:
            if _file_or_dir.is_dir():
                _files = _get_files_from_dir(_file_or_dir, violations)
                yield _file_or_dir.resolve()
                try:
                    yield from walk(_file_or_dir)
                except AssertionError as e:
                    violations_msgs.add(str(e).replace(error_msg, ""))
            else:
                yield _file_or_dir.resolve()
        if violations or violations_msgs:
            _msg = error_msg
            for offending_file, number in violations:
                _msg += f"- {offending_file}: {number} when the max is {max_file_per_directory}\n"
            for error_msg in violations_msgs:
                _msg += error_msg
            raise AssertionError(_msg)

    # Collect all sub-directories and files in directory
    for file_or_dir in walk(directory):
        if file_or_dir.is_dir():
            dirs.add(file_or_dir)
        elif file_or_dir.suffix == ".py":
            files.add(file_or_dir)

    directory_does_not_exists: list[tuple[Path, Path]] = []
    misplaced_file: list[Path] = []
    for file in files:
        possible_dir = file.parent / file.stem.split("_")[0]
        if possible_dir.exists():
            directory_does_not_exists.append((file, possible_dir))
        # Exclude some directories as they follow a different structure
        if (
            not len(file.parent.stem) == 1  # First letter sub-directories
            and file.parent.stem not in IGNORED_PARENT_DIRS
            and file.parent.parent.stem not in IGNORED_PARENT_PARENT_DIRS
        ):
            if not file.stem.startswith(file.parent.stem):
                misplaced_file.append(file)

    if directory_does_not_exists or misplaced_file:
        msg = "The following functional tests are disorganized:\n"
        for file, possible_dir in directory_does_not_exists:
            msg += (
                f"- In '{directory}', '{file.relative_to(directory)}' "
                f"should go in '{possible_dir.relative_to(directory)}'\n"
            )
        for file in misplaced_file:
            msg += (
                f"- In '{directory}', {file.relative_to(directory)} should go in a directory"
                f" that starts with the first letters"
                f" of '{file.stem}' (not '{file.parent.stem}')\n"
            )
        raise AssertionError(msg)

back/venvmsg/Lib/site-packages/pylint/testutils/functional/find_functional_tests.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import csv
import os

from pylint.testutils.lint_module_test import LintModuleTest, MessageCounter
from pylint.testutils.output_line import OutputLine


class LintModuleOutputUpdate(LintModuleTest):
    """Class to be used if expected output files should be updated instead of
    checked.
    """

    class TestDialect(csv.excel):
        """Dialect used by the csv writer."""

        delimiter = ":"
        lineterminator = "\n"

    csv.register_dialect("test", TestDialect)

    def _check_output_text(
        self,
        _: MessageCounter,
        expected_output: list[OutputLine],
        actual_output: list[OutputLine],
    ) -> None:
        """Overwrite or remove the expected output file based on actual output."""
        # Remove the expected file if no output is actually emitted and a file exists
        if not actual_output:
            if os.path.exists(self._test_file.expected_output):
                os.remove(self._test_file.expected_output)
            return
        # Write file with expected output
        with open(self._test_file.expected_output, "w", encoding="utf-8") as f:
            writer = csv.writer(f, dialect="test")
            for line in actual_output:
                writer.writerow(line.to_csv())

back/venvmsg/Lib/site-packages/pylint/testutils/functional/lint_module_output_update.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import configparser
from collections.abc import Callable
from os.path import basename, exists, join
from typing import TypedDict


def parse_python_version(ver_str: str) -> tuple[int, ...]:
    """Convert python version to a tuple of integers for easy comparison."""
    return tuple(int(digit) for digit in ver_str.split("."))


class NoFileError(Exception):
    pass


class TestFileOptions(TypedDict):
    min_pyver: tuple[int, ...]
    max_pyver: tuple[int, ...]
    min_pyver_end_position: tuple[int, ...]
    requires: list[str]
    except_implementations: list[str]
    exclude_platforms: list[str]
    exclude_from_minimal_messages_config: bool


# mypy need something literal, we can't create this dynamically from TestFileOptions
POSSIBLE_TEST_OPTIONS = {
    "min_pyver",
    "max_pyver",
    "min_pyver_end_position",
    "requires",
    "except_implementations",
    "exclude_platforms",
    "exclude_from_minimal_messages_config",
}


class FunctionalTestFile:
    """A single functional test case file with options."""

    _CONVERTERS: dict[str, Callable[[str], tuple[int, ...] | list[str]]] = {
        "min_pyver": parse_python_version,
        "max_pyver": parse_python_version,
        "min_pyver_end_position": parse_python_version,
        "requires": lambda s: [i.strip() for i in s.split(",")],
        "except_implementations": lambda s: [i.strip() for i in s.split(",")],
        "exclude_platforms": lambda s: [i.strip() for i in s.split(",")],
    }

    def __init__(self, directory: str, filename: str) -> None:
        self._directory = directory
        self.base = filename.replace(".py", "")
        # TODO: 3.0: Deprecate FunctionalTestFile.options and related code
        # We should just parse these options like a normal configuration file.
        self.options: TestFileOptions = {
            "min_pyver": (2, 5),
            "max_pyver": (4, 0),
            "min_pyver_end_position": (3, 8),
            "requires": [],
            "except_implementations": [],
            "exclude_platforms": [],
            "exclude_from_minimal_messages_config": False,
        }
        self._parse_options()

    def __repr__(self) -> str:
        return f"FunctionalTest:{self.base}"

    def _parse_options(self) -> None:
        cp = configparser.ConfigParser()
        cp.add_section("testoptions")
        try:
            cp.read(self.option_file)
        except NoFileError:
            pass

        for name, value in cp.items("testoptions"):
            conv = self._CONVERTERS.get(name, lambda v: v)

            assert (
                name in POSSIBLE_TEST_OPTIONS
            ), f"[testoptions]' can only contains one of {POSSIBLE_TEST_OPTIONS} and had '{name}'"
            self.options[name] = conv(value)  # type: ignore[literal-required]

    @property
    def option_file(self) -> str:
        return self._file_type(".rc")

    @property
    def module(self) -> str:
        package = basename(self._directory)
        return ".".join([package, self.base])

    @property
    def expected_output(self) -> str:
        return self._file_type(".txt", check_exists=False)

    @property
    def source(self) -> str:
        return self._file_type(".py")

    def _file_type(self, ext: str, check_exists: bool = True) -> str:
        name = join(self._directory, self.base + ext)
        if not check_exists or exists(name):
            return name
        raise NoFileError(f"Cannot find '{name}'.")

back/venvmsg/Lib/site-packages/pylint/testutils/functional/test_file.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from glob import glob
from os.path import basename, join, splitext

from pylint.testutils.constants import SYS_VERS_STR


def _get_tests_info(
    input_dir: str, msg_dir: str, prefix: str, suffix: str
) -> list[tuple[str, str]]:
    """Get python input examples and output messages.

    We use following conventions for input files and messages:
    for different inputs:
        test for python  >= x.y    ->  input   =  <name>_pyxy.py
        test for python  <  x.y    ->  input   =  <name>_py_xy.py
    for one input and different messages:
        message for python >=  x.y ->  message =  <name>_pyxy.txt
        lower versions             ->  message with highest num
    """
    result = []
    for fname in glob(join(input_dir, prefix + "*" + suffix)):
        infile = basename(fname)
        fbase = splitext(infile)[0]
        # filter input files :
        pyrestr = fbase.rsplit("_py", 1)[-1]  # like _26 or 26
        if pyrestr.isdigit():  # '24', '25'...
            if pyrestr.isdigit() and int(SYS_VERS_STR) < int(pyrestr):
                continue
        if pyrestr.startswith("_") and pyrestr[1:].isdigit():
            # skip test for higher python versions
            if pyrestr[1:].isdigit() and int(SYS_VERS_STR) >= int(pyrestr[1:]):
                continue
        messages = glob(join(msg_dir, fbase + "*.txt"))
        # the last one will be without ext, i.e. for all or upper versions:
        if messages:
            for outfile in sorted(messages, reverse=True):
                py_rest = outfile.rsplit("_py", 1)[-1][:-4]
                if py_rest.isdigit() and int(SYS_VERS_STR) >= int(py_rest):
                    break
        else:
            # This will provide an error message indicating the missing filename.
            outfile = join(msg_dir, fbase + ".txt")
        result.append((infile, outfile))
    return result

back/venvmsg/Lib/site-packages/pylint/testutils/get_test_info.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from pylint import checkers
from pylint.lint import PyLinter
from pylint.testutils.reporter_for_tests import GenericTestReporter


def create_test_linter() -> PyLinter:
    test_reporter = GenericTestReporter()
    linter_ = PyLinter()
    linter_.set_reporter(test_reporter)
    linter_.config.persistent = 0
    checkers.initialize(linter_)
    return linter_


# Can't be renamed to a constant (easily), it breaks countless tests
linter = create_test_linter()

back/venvmsg/Lib/site-packages/pylint/testutils/global_test_linter.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import csv
import operator
import platform
import sys
from collections import Counter
from io import StringIO
from pathlib import Path
from typing import Counter as CounterType
from typing import TextIO, Tuple

import pytest
from _pytest.config import Config

from pylint import checkers
from pylint.config.config_initialization import _config_initialization
from pylint.constants import IS_PYPY
from pylint.lint import PyLinter
from pylint.message.message import Message
from pylint.testutils.constants import _EXPECTED_RE, _OPERATORS, UPDATE_OPTION

# need to import from functional.test_file to avoid cyclic import
from pylint.testutils.functional.test_file import (
    FunctionalTestFile,
    NoFileError,
    parse_python_version,
)
from pylint.testutils.output_line import OutputLine
from pylint.testutils.reporter_for_tests import FunctionalTestReporter

MessageCounter = CounterType[Tuple[int, str]]

PYLINTRC = Path(__file__).parent / "testing_pylintrc"


class LintModuleTest:
    maxDiff = None

    def __init__(
        self, test_file: FunctionalTestFile, config: Config | None = None
    ) -> None:
        _test_reporter = FunctionalTestReporter()
        self._linter = PyLinter()
        self._linter.config.persistent = 0
        checkers.initialize(self._linter)

        # See if test has its own .rc file, if so we use that one
        rc_file: Path | str = PYLINTRC
        try:
            rc_file = test_file.option_file
            self._linter.disable("suppressed-message")
            self._linter.disable("locally-disabled")
            self._linter.disable("useless-suppression")
        except NoFileError:
            pass

        self._test_file = test_file
        try:
            args = [test_file.source]
        except NoFileError:
            # If we're still raising NoFileError the actual source file doesn't exist
            args = [""]
        if config and config.getoption("minimal_messages_config"):
            with self._open_source_file() as f:
                messages_to_enable = {msg[1] for msg in self.get_expected_messages(f)}
                # Always enable fatal errors
                messages_to_enable.add("astroid-error")
                messages_to_enable.add("fatal")
                messages_to_enable.add("syntax-error")
            args.extend(["--disable=all", f"--enable={','.join(messages_to_enable)}"])

        # Add testoptions
        self._linter._arg_parser.add_argument(
            "--min_pyver", type=parse_python_version, default=(2, 5)
        )
        self._linter._arg_parser.add_argument(
            "--max_pyver", type=parse_python_version, default=(4, 0)
        )
        self._linter._arg_parser.add_argument(
            "--min_pyver_end_position", type=parse_python_version, default=(3, 8)
        )
        self._linter._arg_parser.add_argument(
            "--requires", type=lambda s: [i.strip() for i in s.split(",")], default=[]
        )
        self._linter._arg_parser.add_argument(
            "--except_implementations",
            type=lambda s: [i.strip() for i in s.split(",")],
            default=[],
        )
        self._linter._arg_parser.add_argument(
            "--exclude_platforms",
            type=lambda s: [i.strip() for i in s.split(",")],
            default=[],
        )
        self._linter._arg_parser.add_argument(
            "--exclude_from_minimal_messages_config", default=False
        )

        _config_initialization(
            self._linter, args_list=args, config_file=rc_file, reporter=_test_reporter
        )

        self._check_end_position = (
            sys.version_info >= self._linter.config.min_pyver_end_position
        )
        # TODO: PY3.9: PyPy supports end_lineno from 3.9 and above
        if self._check_end_position and IS_PYPY:
            self._check_end_position = sys.version_info >= (3, 9)  # pragma: no cover

        self._config = config

    def setUp(self) -> None:
        if self._should_be_skipped_due_to_version():
            pytest.skip(
                f"Test cannot run with Python {sys.version.split(' ', maxsplit=1)[0]}."
            )
        missing = []
        for requirement in self._linter.config.requires:
            try:
                __import__(requirement)
            except ImportError:
                missing.append(requirement)
        if missing:
            pytest.skip(f"Requires {','.join(missing)} to be present.")
        except_implementations = self._linter.config.except_implementations
        if except_implementations:
            if platform.python_implementation() in except_implementations:
                msg = "Test cannot run with Python implementation %r"
                pytest.skip(msg % platform.python_implementation())
        excluded_platforms = self._linter.config.exclude_platforms
        if excluded_platforms:
            if sys.platform.lower() in excluded_platforms:
                pytest.skip(f"Test cannot run on platform {sys.platform!r}")
        if (
            self._config
            and self._config.getoption("minimal_messages_config")
            and self._linter.config.exclude_from_minimal_messages_config
        ):
            pytest.skip("Test excluded from --minimal-messages-config")

    def runTest(self) -> None:
        self._runTest()

    def _should_be_skipped_due_to_version(self) -> bool:
        return (  # type: ignore[no-any-return]
            sys.version_info < self._linter.config.min_pyver
            or sys.version_info > self._linter.config.max_pyver
        )

    def __str__(self) -> str:
        return f"{self._test_file.base} ({self.__class__.__module__}.{self.__class__.__name__})"

    @staticmethod
    def get_expected_messages(stream: TextIO) -> MessageCounter:
        """Parses a file and get expected messages.

        :param stream: File-like input stream.
        :type stream: enumerable
        :returns: A dict mapping line,msg-symbol tuples to the count on this line.
        :rtype: dict
        """
        messages: MessageCounter = Counter()
        for i, line in enumerate(stream):
            match = _EXPECTED_RE.search(line)
            if match is None:
                continue
            line = match.group("line")
            if line is None:
                lineno = i + 1
            elif line.startswith("+") or line.startswith("-"):
                lineno = i + 1 + int(line)
            else:
                lineno = int(line)

            version = match.group("version")
            op = match.group("op")
            if version:
                required = parse_python_version(version)
                if not _OPERATORS[op](sys.version_info, required):
                    continue

            for msg_id in match.group("msgs").split(","):
                messages[lineno, msg_id.strip()] += 1
        return messages

    @staticmethod
    def multiset_difference(
        expected_entries: MessageCounter,
        actual_entries: MessageCounter,
    ) -> tuple[MessageCounter, dict[tuple[int, str], int]]:
        """Takes two multisets and compares them.

        A multiset is a dict with the cardinality of the key as the value.
        """
        missing = expected_entries.copy()
        missing.subtract(actual_entries)
        unexpected = {}
        for key, value in list(missing.items()):
            if value <= 0:
                missing.pop(key)
                if value < 0:
                    unexpected[key] = -value
        return missing, unexpected

    def _open_expected_file(self) -> TextIO:
        try:
            return open(self._test_file.expected_output, encoding="utf-8")
        except FileNotFoundError:
            return StringIO("")

    def _open_source_file(self) -> TextIO:
        if self._test_file.base == "invalid_encoded_data":
            return open(self._test_file.source, encoding="utf-8")
        if "latin1" in self._test_file.base:
            return open(self._test_file.source, encoding="latin1")
        return open(self._test_file.source, encoding="utf8")

    def _get_expected(self) -> tuple[MessageCounter, list[OutputLine]]:
        with self._open_source_file() as f:
            expected_msgs = self.get_expected_messages(f)
        if not expected_msgs:
            expected_msgs = Counter()
        with self._open_expected_file() as f:
            expected_output_lines = [
                OutputLine.from_csv(row, self._check_end_position)
                for row in csv.reader(f, "test")
            ]
        return expected_msgs, expected_output_lines

    def _get_actual(self) -> tuple[MessageCounter, list[OutputLine]]:
        messages: list[Message] = self._linter.reporter.messages
        messages.sort(key=lambda m: (m.line, m.symbol, m.msg))
        received_msgs: MessageCounter = Counter()
        received_output_lines = []
        for msg in messages:
            assert (
                msg.symbol != "fatal"
            ), f"Pylint analysis failed because of '{msg.msg}'"
            received_msgs[msg.line, msg.symbol] += 1
            received_output_lines.append(
                OutputLine.from_msg(msg, self._check_end_position)
            )
        return received_msgs, received_output_lines

    def _runTest(self) -> None:
        __tracebackhide__ = True  # pylint: disable=unused-variable
        modules_to_check = [self._test_file.source]
        self._linter.check(modules_to_check)
        expected_messages, expected_output = self._get_expected()
        actual_messages, actual_output = self._get_actual()
        assert (
            expected_messages == actual_messages
        ), self.error_msg_for_unequal_messages(
            actual_messages, expected_messages, actual_output
        )
        self._check_output_text(expected_messages, expected_output, actual_output)

    def error_msg_for_unequal_messages(
        self,
        actual_messages: MessageCounter,
        expected_messages: MessageCounter,
        actual_output: list[OutputLine],
    ) -> str:
        msg = [f'Wrong message(s) raised for "{Path(self._test_file.source).name}":']
        missing, unexpected = self.multiset_difference(
            expected_messages, actual_messages
        )
        if missing:
            msg.append("\nExpected in testdata:")
            msg.extend(f" {msg[0]:3}: {msg[1]}" for msg in sorted(missing))
        if unexpected:
            msg.append("\nUnexpected in testdata:")
            msg.extend(f" {msg[0]:3}: {msg[1]}" for msg in sorted(unexpected))
        error_msg = "\n".join(msg)
        if self._config and self._config.getoption("verbose") > 0:
            error_msg += "\n\nActual pylint output for this file:\n"
            error_msg += "\n".join(str(o) for o in actual_output)
        return error_msg

    def error_msg_for_unequal_output(
        self,
        expected_lines: list[OutputLine],
        received_lines: list[OutputLine],
    ) -> str:
        missing = set(expected_lines) - set(received_lines)
        unexpected = set(received_lines) - set(expected_lines)
        error_msg = f'Wrong output for "{Path(self._test_file.expected_output).name}":'
        sort_by_line_number = operator.attrgetter("lineno")
        if missing:
            error_msg += "\n- Missing lines:\n"
            for line in sorted(missing, key=sort_by_line_number):
                error_msg += f"{line}\n"
        if unexpected:
            error_msg += "\n- Unexpected lines:\n"
            for line in sorted(unexpected, key=sort_by_line_number):
                error_msg += f"{line}\n"
            error_msg += (
                "\nYou can update the expected output automatically with:\n'"
                f"python tests/test_functional.py {UPDATE_OPTION} -k "
                f'"test_functional[{self._test_file.base}]"\'\n\n'
                "Here's the update text in case you can't:\n"
            )
            expected_csv = StringIO()
            writer = csv.writer(expected_csv, dialect="test")
            for line in sorted(received_lines, key=sort_by_line_number):
                writer.writerow(line.to_csv())
            error_msg += expected_csv.getvalue()
        return error_msg

    def _check_output_text(
        self,
        _: MessageCounter,
        expected_output: list[OutputLine],
        actual_output: list[OutputLine],
    ) -> None:
        """This is a function because we want to be able to update the text in
        LintModuleOutputUpdate.
        """
        assert expected_output == actual_output, self.error_msg_for_unequal_output(
            expected_output, actual_output
        )

back/venvmsg/Lib/site-packages/pylint/testutils/lint_module_test.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from collections.abc import Sequence
from typing import Any, NamedTuple, TypeVar

from astroid import nodes

from pylint.interfaces import UNDEFINED, Confidence
from pylint.message.message import Message

_T = TypeVar("_T")


class MessageTest(NamedTuple):
    msg_id: str
    line: int | None = None
    node: nodes.NodeNG | None = None
    args: Any | None = None
    confidence: Confidence | None = UNDEFINED
    col_offset: int | None = None
    end_line: int | None = None
    end_col_offset: int | None = None
    """Used to test messages produced by pylint.

    Class name cannot start with Test as pytest doesn't allow constructors in test classes.
    """


class OutputLine(NamedTuple):
    symbol: str
    lineno: int
    column: int
    end_lineno: int | None
    end_column: int | None
    object: str
    msg: str
    confidence: str

    @classmethod
    def from_msg(cls, msg: Message, check_endline: bool = True) -> OutputLine:
        """Create an OutputLine from a Pylint Message."""
        column = cls._get_column(msg.column)
        end_line = cls._get_py38_none_value(msg.end_line, check_endline)
        end_column = cls._get_py38_none_value(msg.end_column, check_endline)
        return cls(
            msg.symbol,
            msg.line,
            column,
            end_line,
            end_column,
            msg.obj or "",
            msg.msg.replace("\r\n", "\n"),
            msg.confidence.name,
        )

    @staticmethod
    def _get_column(column: str | int) -> int:
        """Handle column numbers."""
        return int(column)

    @staticmethod
    def _get_py38_none_value(value: _T, check_endline: bool) -> _T | None:
        """Used to make end_line and end_column None as indicated by our version
        compared to `min_pyver_end_position`.
        """
        if not check_endline:
            return None  # pragma: no cover
        return value

    @classmethod
    def from_csv(
        cls, row: Sequence[str] | str, check_endline: bool = True
    ) -> OutputLine:
        """Create an OutputLine from a comma separated list (the functional tests
        expected output .txt files).
        """
        if isinstance(row, str):
            row = row.split(",")
        try:
            line = int(row[1])
            column = cls._get_column(row[2])
            end_line = cls._value_to_optional_int(
                cls._get_py38_none_value(row[3], check_endline)
            )
            end_column = cls._value_to_optional_int(
                cls._get_py38_none_value(row[4], check_endline)
            )
            # symbol, line, column, end_line, end_column, node, msg, confidences
            assert len(row) == 8
            return cls(
                row[0], line, column, end_line, end_column, row[5], row[6], row[7]
            )
        except Exception:  # pylint: disable=broad-except
            # We need this to not fail for the update script to work.
            return cls("", 0, 0, None, None, "", "", "")

    def to_csv(self) -> tuple[str, str, str, str, str, str, str, str]:
        """Convert an OutputLine to a tuple of string to be written by a
        csv-writer.
        """
        return (
            str(self.symbol),
            str(self.lineno),
            str(self.column),
            str(self.end_lineno),
            str(self.end_column),
            str(self.object),
            str(self.msg),
            str(self.confidence),
        )

    @staticmethod
    def _value_to_optional_int(value: str | None) -> int | None:
        """Checks if a (stringified) value should be None or a Python integer."""
        if value == "None" or not value:
            return None
        return int(value)

back/venvmsg/Lib/site-packages/pylint/testutils/output_line.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import argparse
import configparser
import shlex
from pathlib import Path
from typing import NamedTuple, TypedDict

from pylint.pyreverse.main import DEFAULT_COLOR_PALETTE


# This class could and should be replaced with a simple dataclass when support for Python < 3.7 is dropped.
# A NamedTuple is not possible as some tests need to modify attributes during the test.
class PyreverseConfig(
    argparse.Namespace
):  # pylint: disable=too-many-instance-attributes, too-many-arguments
    """Holds the configuration options for Pyreverse.

    The default values correspond to the defaults of the options' parser.
    """

    def __init__(
        self,
        mode: str = "PUB_ONLY",
        classes: list[str] | None = None,
        show_ancestors: int | None = None,
        all_ancestors: bool | None = None,
        show_associated: int | None = None,
        all_associated: bool | None = None,
        no_standalone: bool = False,
        show_builtin: bool = False,
        show_stdlib: bool = False,
        module_names: bool | None = None,
        only_classnames: bool = False,
        output_format: str = "dot",
        colorized: bool = False,
        max_color_depth: int = 2,
        color_palette: tuple[str, ...] = DEFAULT_COLOR_PALETTE,
        ignore_list: tuple[str, ...] = tuple(),
        project: str = "",
        output_directory: str = "",
    ) -> None:
        super().__init__()
        self.mode = mode
        if classes:
            self.classes = classes
        else:
            self.classes = []
        self.show_ancestors = show_ancestors
        self.all_ancestors = all_ancestors
        self.show_associated = show_associated
        self.all_associated = all_associated
        self.no_standalone = no_standalone
        self.show_builtin = show_builtin
        self.show_stdlib = show_stdlib
        self.module_names = module_names
        self.only_classnames = only_classnames
        self.output_format = output_format
        self.colorized = colorized
        self.max_color_depth = max_color_depth
        self.color_palette = color_palette
        self.ignore_list = ignore_list
        self.project = project
        self.output_directory = output_directory


class TestFileOptions(TypedDict):
    source_roots: list[str]
    output_formats: list[str]
    command_line_args: list[str]


class FunctionalPyreverseTestfile(NamedTuple):
    """Named tuple containing the test file and the expected output."""

    source: Path
    options: TestFileOptions


def get_functional_test_files(
    root_directory: Path,
) -> list[FunctionalPyreverseTestfile]:
    """Get all functional test files from the given directory."""
    test_files = []
    for path in root_directory.rglob("*.py"):
        if path.stem.startswith("_"):
            continue
        config_file = path.with_suffix(".rc")
        if config_file.exists():
            test_files.append(
                FunctionalPyreverseTestfile(
                    source=path, options=_read_config(config_file)
                )
            )
        else:
            test_files.append(
                FunctionalPyreverseTestfile(
                    source=path,
                    options={
                        "source_roots": [],
                        "output_formats": ["mmd"],
                        "command_line_args": [],
                    },
                )
            )
    return test_files


def _read_config(config_file: Path) -> TestFileOptions:
    config = configparser.ConfigParser()
    config.read(str(config_file))
    source_roots = config.get("testoptions", "source_roots", fallback=None)
    return {
        "source_roots": source_roots.split(",") if source_roots else [],
        "output_formats": config.get(
            "testoptions", "output_formats", fallback="mmd"
        ).split(","),
        "command_line_args": shlex.split(
            config.get("testoptions", "command_line_args", fallback="")
        ),
    }

back/venvmsg/Lib/site-packages/pylint/testutils/pyreverse.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from io import StringIO
from os import getcwd, sep
from typing import TYPE_CHECKING

from pylint.message import Message
from pylint.reporters import BaseReporter

if TYPE_CHECKING:
    from pylint.reporters.ureports.nodes import Section


class GenericTestReporter(BaseReporter):
    """Reporter storing plain text messages."""

    out: StringIO

    def __init__(  # pylint: disable=super-init-not-called # See https://github.com/pylint-dev/pylint/issues/4941
        self,
    ) -> None:
        self.path_strip_prefix: str = getcwd() + sep
        self.reset()

    def reset(self) -> None:
        self.out = StringIO()
        self.messages: list[Message] = []

    def handle_message(self, msg: Message) -> None:
        """Append messages to the list of messages of the reporter."""
        self.messages.append(msg)

    def finalize(self) -> str:
        """Format and print messages in the context of the path."""
        messages: list[str] = []
        for msg in self.messages:
            obj = ""
            if msg.obj:
                obj = f":{msg.obj}"
            messages.append(f"{msg.msg_id[0]}:{msg.line:>3}{obj}: {msg.msg}")

        messages.sort()
        for message in messages:
            print(message, file=self.out)

        result = self.out.getvalue()
        self.reset()
        return result

    def on_set_current_module(self, module: str, filepath: str | None) -> None:
        pass

    # pylint: enable=unused-argument

    def display_reports(self, layout: Section) -> None:
        """Ignore layouts."""

    def _display(self, layout: Section) -> None:
        pass


class MinimalTestReporter(BaseReporter):
    def on_set_current_module(self, module: str, filepath: str | None) -> None:
        self.messages = []

    def _display(self, layout: Section) -> None:
        pass


class FunctionalTestReporter(BaseReporter):
    def display_reports(self, layout: Section) -> None:
        """Ignore layouts and don't call self._display()."""

    def _display(self, layout: Section) -> None:
        pass

back/venvmsg/Lib/site-packages/pylint/testutils/reporter_for_tests.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import tokenize
from io import StringIO
from tokenize import TokenInfo


def _tokenize_str(code: str) -> list[TokenInfo]:
    return list(tokenize.generate_tokens(StringIO(code).readline))

back/venvmsg/Lib/site-packages/pylint/testutils/tokenize_str.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

# pylint: disable=duplicate-code

from __future__ import annotations

from typing import Any, Literal

from astroid import nodes

from pylint.interfaces import UNDEFINED, Confidence
from pylint.lint import PyLinter
from pylint.testutils.output_line import MessageTest


class UnittestLinter(PyLinter):
    """A fake linter class to capture checker messages."""

    def __init__(self) -> None:
        self._messages: list[MessageTest] = []
        super().__init__()

    def release_messages(self) -> list[MessageTest]:
        try:
            return self._messages
        finally:
            self._messages = []

    def add_message(
        self,
        msgid: str,
        line: int | None = None,
        # TODO: Make node non optional
        node: nodes.NodeNG | None = None,
        args: Any = None,
        confidence: Confidence | None = None,
        col_offset: int | None = None,
        end_lineno: int | None = None,
        end_col_offset: int | None = None,
    ) -> None:
        """Add a MessageTest to the _messages attribute of the linter class."""
        # If confidence is None we set it to UNDEFINED as well in PyLinter
        if confidence is None:
            confidence = UNDEFINED

        # Look up "location" data of node if not yet supplied
        if node:
            if node.position:
                if not line:
                    line = node.position.lineno
                if not col_offset:
                    col_offset = node.position.col_offset
                if not end_lineno:
                    end_lineno = node.position.end_lineno
                if not end_col_offset:
                    end_col_offset = node.position.end_col_offset
            else:
                if not line:
                    line = node.fromlineno
                if not col_offset:
                    col_offset = node.col_offset
                if not end_lineno:
                    end_lineno = node.end_lineno
                if not end_col_offset:
                    end_col_offset = node.end_col_offset

        self._messages.append(
            MessageTest(
                msgid,
                line,
                node,
                args,
                confidence,
                col_offset,
                end_lineno,
                end_col_offset,
            )
        )

    @staticmethod
    def is_message_enabled(*unused_args: Any, **unused_kwargs: Any) -> Literal[True]:
        return True

back/venvmsg/Lib/site-packages/pylint/testutils/unittest_linter.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import contextlib
import os
import sys
from collections.abc import Generator, Iterator
from copy import copy
from pathlib import Path
from typing import TextIO


@contextlib.contextmanager
def _patch_streams(out: TextIO) -> Iterator[None]:
    """Patch and subsequently reset a text stream."""
    sys.stderr = sys.stdout = out
    try:
        yield
    finally:
        sys.stderr = sys.__stderr__
        sys.stdout = sys.__stdout__


@contextlib.contextmanager
def _test_sys_path(
    replacement_sys_path: list[str] | None = None,
) -> Generator[None, None, None]:
    original_path = sys.path
    try:
        if replacement_sys_path is not None:
            sys.path = copy(replacement_sys_path)
        yield
    finally:
        sys.path = original_path


@contextlib.contextmanager
def _test_cwd(
    current_working_directory: str | Path | None = None,
) -> Generator[None, None, None]:
    original_dir = os.getcwd()
    try:
        if current_working_directory is not None:
            os.chdir(current_working_directory)
        yield
    finally:
        os.chdir(original_dir)


@contextlib.contextmanager
def _test_environ_pythonpath(
    new_pythonpath: str | None = None,
) -> Generator[None, None, None]:
    original_pythonpath = os.environ.get("PYTHONPATH")
    if new_pythonpath:
        os.environ["PYTHONPATH"] = new_pythonpath
    elif new_pythonpath is None and original_pythonpath is not None:
        # If new_pythonpath is None, make sure to delete PYTHONPATH if present
        del os.environ["PYTHONPATH"]
    try:
        yield
    finally:
        if original_pythonpath is not None:
            os.environ["PYTHONPATH"] = original_pythonpath
        elif "PYTHONPATH" in os.environ:
            del os.environ["PYTHONPATH"]


def create_files(paths: list[str], chroot: str = ".") -> None:
    """Creates directories and files found in <path>.

    :param list paths: list of relative paths to files or directories
    :param str chroot: the root directory in which paths will be created

    >>> from os.path import isdir, isfile
    >>> isdir('/tmp/a')
    False
    >>> create_files(['a/b/foo.py', 'a/b/c/', 'a/b/c/d/e.py'], '/tmp')
    >>> isdir('/tmp/a')
    True
    >>> isdir('/tmp/a/b/c')
    True
    >>> isfile('/tmp/a/b/c/d/e.py')
    True
    >>> isfile('/tmp/a/b/foo.py')
    True
    """
    dirs, files = set(), set()
    for path in paths:
        path = os.path.join(chroot, path)
        filename = os.path.basename(path)
        # path is a directory path
        if not filename:
            dirs.add(path)
        # path is a filename path
        else:
            dirs.add(os.path.dirname(path))
            files.add(path)
    for dirpath in dirs:
        if not os.path.isdir(dirpath):
            os.makedirs(dirpath)
    for filepath in files:
        with open(filepath, "w", encoding="utf-8"):
            pass

back/venvmsg/Lib/site-packages/pylint/testutils/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""A collection of typing utilities."""

from __future__ import annotations

import argparse
from pathlib import Path
from typing import (
    TYPE_CHECKING,
    Any,
    Callable,
    Dict,
    Iterable,
    Literal,
    NamedTuple,
    Optional,
    Pattern,
    Protocol,
    Tuple,
    Type,
    TypedDict,
    Union,
)

if TYPE_CHECKING:
    from pylint.config.callback_actions import _CallbackAction
    from pylint.pyreverse.inspector import Project
    from pylint.reporters.ureports.nodes import Section
    from pylint.utils import LinterStats


class FileItem(NamedTuple):
    """Represents data about a file handled by pylint.

    Each file item has:
    - name: full name of the module
    - filepath: path of the file
    - modname: module name
    """

    name: str
    filepath: str
    modpath: str


class ModuleDescriptionDict(TypedDict):
    """Represents data about a checked module."""

    path: str
    name: str
    isarg: bool
    basepath: str
    basename: str


class ErrorDescriptionDict(TypedDict):
    """Represents data about errors collected during checking of a module."""

    key: Literal["fatal"]
    mod: str
    ex: ImportError | SyntaxError


class MessageLocationTuple(NamedTuple):
    """Tuple with information about the location of a to-be-displayed message."""

    abspath: str
    path: str
    module: str
    obj: str
    line: int
    column: int
    end_line: int | None = None
    end_column: int | None = None


class ManagedMessage(NamedTuple):
    """Tuple with information about a managed message of the linter."""

    name: str | None
    msgid: str
    symbol: str
    line: int | None
    is_disabled: bool


MessageTypesFullName = Literal[
    "convention", "error", "fatal", "info", "refactor", "statement", "warning"
]
"""All possible message categories."""


OptionDict = Dict[
    str,
    Union[
        None,
        str,
        bool,
        int,
        Pattern[str],
        Iterable[Union[str, int, Pattern[str]]],
        Type["_CallbackAction"],
        Callable[[Any], Any],
        Callable[[Any, Any, Any, Any], Any],
    ],
]
Options = Tuple[Tuple[str, OptionDict], ...]


ReportsCallable = Callable[["Section", "LinterStats", Optional["LinterStats"]], None]
"""Callable to create a report."""


class ExtraMessageOptions(TypedDict, total=False):
    """All allowed keys in the extra options for message definitions."""

    scope: str
    old_names: list[tuple[str, str]]
    maxversion: tuple[int, int]
    minversion: tuple[int, int]
    shared: bool
    default_enabled: bool


MessageDefinitionTuple = Union[
    Tuple[str, str, str],
    Tuple[str, str, str, ExtraMessageOptions],
]
DirectoryNamespaceDict = Dict[Path, Tuple[argparse.Namespace, "DirectoryNamespaceDict"]]


class GetProjectCallable(Protocol):
    def __call__(self, module: str, name: str | None = "No Name") -> Project:
        ...  # pragma: no cover

back/venvmsg/Lib/site-packages/pylint/typing.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Some various utilities and helper classes, most of them used in the
main pylint class.
"""

from pylint.utils.ast_walker import ASTWalker
from pylint.utils.docs import print_full_documentation
from pylint.utils.file_state import FileState
from pylint.utils.linterstats import LinterStats, ModuleStats, merge_stats
from pylint.utils.utils import (
    HAS_ISORT_5,
    IsortDriver,
    _check_csv,
    _check_regexp_csv,
    _splitstrip,
    _unquote,
    decoding_stream,
    diff_string,
    format_section,
    get_module_and_frameid,
    get_rst_section,
    get_rst_title,
    normalize_text,
    register_plugins,
    tokenize_module,
)

__all__ = [
    "ASTWalker",
    "HAS_ISORT_5",
    "IsortDriver",
    "_check_csv",
    "_check_regexp_csv",
    "_splitstrip",
    "_unquote",
    "decoding_stream",
    "diff_string",
    "FileState",
    "format_section",
    "get_module_and_frameid",
    "get_rst_section",
    "get_rst_title",
    "normalize_text",
    "register_plugins",
    "tokenize_module",
    "merge_stats",
    "LinterStats",
    "ModuleStats",
    "print_full_documentation",
]

back/venvmsg/Lib/site-packages/pylint/utils/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import sys
import traceback
from collections import defaultdict
from collections.abc import Sequence
from typing import TYPE_CHECKING, Callable

from astroid import nodes

if TYPE_CHECKING:
    from pylint.checkers.base_checker import BaseChecker
    from pylint.lint import PyLinter

# Callable parameter type NodeNG not completely correct.
# Due to contravariance of Callable parameter types,
# it should be a Union of all NodeNG subclasses.
# However, since the methods are only retrieved with
# getattr(checker, member) and thus are inferred as Any,
# NodeNG will work too.
AstCallback = Callable[[nodes.NodeNG], None]


class ASTWalker:
    def __init__(self, linter: PyLinter) -> None:
        # callbacks per node types
        self.nbstatements = 0
        self.visit_events: defaultdict[str, list[AstCallback]] = defaultdict(list)
        self.leave_events: defaultdict[str, list[AstCallback]] = defaultdict(list)
        self.linter = linter
        self.exception_msg = False

    def _is_method_enabled(self, method: AstCallback) -> bool:
        if not hasattr(method, "checks_msgs"):
            return True
        return any(self.linter.is_message_enabled(m) for m in method.checks_msgs)

    def add_checker(self, checker: BaseChecker) -> None:
        """Walk to the checker's dir and collect visit and leave methods."""
        vcids: set[str] = set()
        lcids: set[str] = set()
        visits = self.visit_events
        leaves = self.leave_events
        for member in dir(checker):
            cid = member[6:]
            if cid == "default":
                continue
            if member.startswith("visit_"):
                v_meth = getattr(checker, member)
                # don't use visit_methods with no activated message:
                if self._is_method_enabled(v_meth):
                    visits[cid].append(v_meth)
                    vcids.add(cid)
            elif member.startswith("leave_"):
                l_meth = getattr(checker, member)
                # don't use leave_methods with no activated message:
                if self._is_method_enabled(l_meth):
                    leaves[cid].append(l_meth)
                    lcids.add(cid)
        visit_default = getattr(checker, "visit_default", None)
        if visit_default:
            for cls in nodes.ALL_NODE_CLASSES:
                cid = cls.__name__.lower()
                if cid not in vcids:
                    visits[cid].append(visit_default)
        # For now, we have no "leave_default" method in Pylint

    def walk(self, astroid: nodes.NodeNG) -> None:
        """Call visit events of astroid checkers for the given node, recurse on
        its children, then leave events.
        """
        cid = astroid.__class__.__name__.lower()

        # Detect if the node is a new name for a deprecated alias.
        # In this case, favour the methods for the deprecated
        # alias if any,  in order to maintain backwards
        # compatibility.
        visit_events: Sequence[AstCallback] = self.visit_events.get(cid, ())
        leave_events: Sequence[AstCallback] = self.leave_events.get(cid, ())

        # pylint: disable = too-many-try-statements
        try:
            if astroid.is_statement:
                self.nbstatements += 1
            # generate events for this node on each checker
            for callback in visit_events:
                callback(astroid)
            # recurse on children
            for child in astroid.get_children():
                self.walk(child)
            for callback in leave_events:
                callback(astroid)
        except Exception:
            if self.exception_msg is False:
                file = getattr(astroid.root(), "file", None)
                print(
                    f"Exception on node {astroid!r} in file '{file}'",
                    file=sys.stderr,
                )
                traceback.print_exc()
                self.exception_msg = True
            raise

back/venvmsg/Lib/site-packages/pylint/utils/ast_walker.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

"""Various helper functions to create the docs of a linter object."""

from __future__ import annotations

import sys
from typing import TYPE_CHECKING, Any, TextIO

from pylint.constants import MAIN_CHECKER_NAME
from pylint.utils.utils import get_rst_section, get_rst_title

if TYPE_CHECKING:
    from pylint.lint.pylinter import PyLinter


def _get_checkers_infos(linter: PyLinter) -> dict[str, dict[str, Any]]:
    """Get info from a checker and handle KeyError."""
    by_checker: dict[str, dict[str, Any]] = {}
    for checker in linter.get_checkers():
        name = checker.name
        if name != MAIN_CHECKER_NAME:
            try:
                by_checker[name]["checker"] = checker
                by_checker[name]["options"] += checker._options_and_values()
                by_checker[name]["msgs"].update(checker.msgs)
                by_checker[name]["reports"] += checker.reports
            except KeyError:
                by_checker[name] = {
                    "checker": checker,
                    "options": list(checker._options_and_values()),
                    "msgs": dict(checker.msgs),
                    "reports": list(checker.reports),
                }
    return by_checker


def _get_global_options_documentation(linter: PyLinter) -> str:
    """Get documentation for the main checker."""
    result = get_rst_title("Pylint global options and switches", "-")
    result += """
Pylint provides global options and switches.

"""
    for checker in linter.get_checkers():
        if checker.name == MAIN_CHECKER_NAME and checker.options:
            for section, options in checker._options_by_section():
                if section is None:
                    title = "General options"
                else:
                    title = f"{section.capitalize()} options"
                result += get_rst_title(title, "~")
                assert isinstance(options, list)
                result += f"{get_rst_section(None, options)}\n"
    return result


def _get_checkers_documentation(linter: PyLinter, show_options: bool = True) -> str:
    """Get documentation for individual checkers."""
    if show_options:
        result = _get_global_options_documentation(linter)
    else:
        result = ""

    result += get_rst_title("Pylint checkers' options and switches", "-")
    result += """\

Pylint checkers can provide three set of features:

* options that control their execution,
* messages that they can raise,
* reports that they can generate.

Below is a list of all checkers and their features.

"""
    by_checker = _get_checkers_infos(linter)
    for checker_name in sorted(by_checker):
        information = by_checker[checker_name]
        checker = information["checker"]
        del information["checker"]
        result += checker.get_full_documentation(
            **information, show_options=show_options
        )
    return result


def print_full_documentation(
    linter: PyLinter, stream: TextIO = sys.stdout, show_options: bool = True
) -> None:
    """Output a full documentation in ReST format."""
    print(
        _get_checkers_documentation(linter, show_options=show_options)[:-3], file=stream
    )

back/venvmsg/Lib/site-packages/pylint/utils/docs.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import collections
from collections import defaultdict
from collections.abc import Iterator
from typing import TYPE_CHECKING, Dict, Literal

from astroid import nodes

from pylint.constants import (
    INCOMPATIBLE_WITH_USELESS_SUPPRESSION,
    MSG_STATE_SCOPE_MODULE,
    WarningScope,
)

if TYPE_CHECKING:
    from pylint.message import MessageDefinition, MessageDefinitionStore


MessageStateDict = Dict[str, Dict[int, bool]]


class FileState:
    """Hold internal state specific to the currently analyzed file."""

    def __init__(
        self,
        modname: str,
        msg_store: MessageDefinitionStore,
        node: nodes.Module | None = None,
        *,
        is_base_filestate: bool = False,
    ) -> None:
        self.base_name = modname
        self._module_msgs_state: MessageStateDict = {}
        self._raw_module_msgs_state: MessageStateDict = {}
        self._ignored_msgs: defaultdict[
            tuple[str, int], set[int]
        ] = collections.defaultdict(set)
        self._suppression_mapping: dict[tuple[str, int], int] = {}
        self._module = node
        if node:
            self._effective_max_line_number = node.tolineno
        else:
            self._effective_max_line_number = None
        self._msgs_store = msg_store
        self._is_base_filestate = is_base_filestate
        """If this FileState is the base state made during initialization of
        PyLinter.
        """

    def _set_state_on_block_lines(
        self,
        msgs_store: MessageDefinitionStore,
        node: nodes.NodeNG,
        msg: MessageDefinition,
        msg_state: dict[int, bool],
    ) -> None:
        """Recursively walk (depth first) AST to collect block level options
        line numbers and set the state correctly.
        """
        for child in node.get_children():
            self._set_state_on_block_lines(msgs_store, child, msg, msg_state)
        # first child line number used to distinguish between disable
        # which are the first child of scoped node with those defined later.
        # For instance in the code below:
        #
        # 1.   def meth8(self):
        # 2.        """test late disabling"""
        # 3.        pylint: disable=not-callable, useless-suppression
        # 4.        print(self.blip)
        # 5.        pylint: disable=no-member, useless-suppression
        # 6.        print(self.bla)
        #
        # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6
        #
        # this is necessary to disable locally messages applying to class /
        # function using their fromlineno
        if (
            isinstance(node, (nodes.Module, nodes.ClassDef, nodes.FunctionDef))
            and node.body
        ):
            firstchildlineno = node.body[0].fromlineno
        else:
            firstchildlineno = node.tolineno
        self._set_message_state_in_block(msg, msg_state, node, firstchildlineno)

    def _set_message_state_in_block(
        self,
        msg: MessageDefinition,
        lines: dict[int, bool],
        node: nodes.NodeNG,
        firstchildlineno: int,
    ) -> None:
        """Set the state of a message in a block of lines."""
        first = node.fromlineno
        last = node.tolineno
        for lineno, state in list(lines.items()):
            original_lineno = lineno
            if first > lineno or last < lineno:
                continue
            # Set state for all lines for this block, if the
            # warning is applied to nodes.
            if msg.scope == WarningScope.NODE:
                if lineno > firstchildlineno:
                    state = True
                first_, last_ = node.block_range(lineno)
                # pylint: disable=useless-suppression
                # For block nodes first_ is their definition line. For example, we
                # set the state of line zero for a module to allow disabling
                # invalid-name for the module. For example:
                # 1. # pylint: disable=invalid-name
                # 2. ...
                # OR
                # 1. """Module docstring"""
                # 2. # pylint: disable=invalid-name
                # 3. ...
                #
                # But if we already visited line 0 we don't need to set its state again
                # 1. # pylint: disable=invalid-name
                # 2. # pylint: enable=invalid-name
                # 3. ...
                # The state should come from line 1, not from line 2
                # Therefore, if the 'fromlineno' is already in the states we just start
                # with the lineno we were originally visiting.
                # pylint: enable=useless-suppression
                if (
                    first_ == node.fromlineno
                    and first_ >= firstchildlineno
                    and node.fromlineno in self._module_msgs_state.get(msg.msgid, ())
                ):
                    first_ = lineno

            else:
                first_ = lineno
                last_ = last
            for line in range(first_, last_ + 1):
                # Do not override existing entries. This is especially important
                # when parsing the states for a scoped node where some line-disables
                # have already been parsed.
                if (
                    (
                        isinstance(node, nodes.Module)
                        and node.fromlineno <= line < lineno
                    )
                    or (
                        not isinstance(node, nodes.Module)
                        and node.fromlineno < line < lineno
                    )
                ) and line in self._module_msgs_state.get(msg.msgid, ()):
                    continue
                if line in lines:  # state change in the same block
                    state = lines[line]
                    original_lineno = line

                self._set_message_state_on_line(msg, line, state, original_lineno)

            del lines[lineno]

    def _set_message_state_on_line(
        self,
        msg: MessageDefinition,
        line: int,
        state: bool,
        original_lineno: int,
    ) -> None:
        """Set the state of a message on a line."""
        # Update suppression mapping
        if not state:
            self._suppression_mapping[(msg.msgid, line)] = original_lineno
        else:
            self._suppression_mapping.pop((msg.msgid, line), None)

        # Update message state for respective line
        try:
            self._module_msgs_state[msg.msgid][line] = state
        except KeyError:
            self._module_msgs_state[msg.msgid] = {line: state}

    def set_msg_status(
        self,
        msg: MessageDefinition,
        line: int,
        status: bool,
        scope: str = "package",
    ) -> None:
        """Set status (enabled/disable) for a given message at a given line."""
        assert line > 0
        if scope != "line":
            # Expand the status to cover all relevant block lines
            self._set_state_on_block_lines(
                self._msgs_store, self._module, msg, {line: status}
            )
        else:
            self._set_message_state_on_line(msg, line, status, line)

        # Store the raw value
        try:
            self._raw_module_msgs_state[msg.msgid][line] = status
        except KeyError:
            self._raw_module_msgs_state[msg.msgid] = {line: status}

    def handle_ignored_message(
        self, state_scope: Literal[0, 1, 2] | None, msgid: str, line: int | None
    ) -> None:
        """Report an ignored message.

        state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG,
        depending on whether the message was disabled locally in the module,
        or globally.
        """
        if state_scope == MSG_STATE_SCOPE_MODULE:
            assert isinstance(line, int)  # should always be int inside module scope

            try:
                orig_line = self._suppression_mapping[(msgid, line)]
                self._ignored_msgs[(msgid, orig_line)].add(line)
            except KeyError:
                pass

    def iter_spurious_suppression_messages(
        self,
        msgs_store: MessageDefinitionStore,
    ) -> Iterator[
        tuple[
            Literal["useless-suppression", "suppressed-message"],
            int,
            tuple[str] | tuple[str, int],
        ]
    ]:
        for warning, lines in self._raw_module_msgs_state.items():
            for line, enable in lines.items():
                if (
                    not enable
                    and (warning, line) not in self._ignored_msgs
                    and warning not in INCOMPATIBLE_WITH_USELESS_SUPPRESSION
                ):
                    yield "useless-suppression", line, (
                        msgs_store.get_msg_display_string(warning),
                    )
        # don't use iteritems here, _ignored_msgs may be modified by add_message
        for (warning, from_), ignored_lines in list(self._ignored_msgs.items()):
            for line in ignored_lines:
                yield "suppressed-message", line, (
                    msgs_store.get_msg_display_string(warning),
                    from_,
                )

    def get_effective_max_line_number(self) -> int | None:
        return self._effective_max_line_number  # type: ignore[no-any-return]

back/venvmsg/Lib/site-packages/pylint/utils/file_state.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

from typing import Literal, TypedDict, cast

from pylint.typing import MessageTypesFullName


class BadNames(TypedDict):
    """TypedDict to store counts of node types with bad names."""

    argument: int
    attr: int
    klass: int
    class_attribute: int
    class_const: int
    const: int
    inlinevar: int
    function: int
    method: int
    module: int
    variable: int
    typevar: int
    typealias: int


class CodeTypeCount(TypedDict):
    """TypedDict to store counts of lines of code types."""

    code: int
    comment: int
    docstring: int
    empty: int
    total: int


class DuplicatedLines(TypedDict):
    """TypedDict to store counts of lines of duplicated code."""

    nb_duplicated_lines: int
    percent_duplicated_lines: float


class NodeCount(TypedDict):
    """TypedDict to store counts of different types of nodes."""

    function: int
    klass: int
    method: int
    module: int


class UndocumentedNodes(TypedDict):
    """TypedDict to store counts of undocumented node types."""

    function: int
    klass: int
    method: int
    module: int


class ModuleStats(TypedDict):
    """TypedDict to store counts of types of messages and statements."""

    convention: int
    error: int
    fatal: int
    info: int
    refactor: int
    statement: int
    warning: int


# pylint: disable-next=too-many-instance-attributes
class LinterStats:
    """Class used to linter stats."""

    def __init__(
        self,
        bad_names: BadNames | None = None,
        by_module: dict[str, ModuleStats] | None = None,
        by_msg: dict[str, int] | None = None,
        code_type_count: CodeTypeCount | None = None,
        dependencies: dict[str, set[str]] | None = None,
        duplicated_lines: DuplicatedLines | None = None,
        node_count: NodeCount | None = None,
        undocumented: UndocumentedNodes | None = None,
    ) -> None:
        self.bad_names = bad_names or BadNames(
            argument=0,
            attr=0,
            klass=0,
            class_attribute=0,
            class_const=0,
            const=0,
            inlinevar=0,
            function=0,
            method=0,
            module=0,
            variable=0,
            typevar=0,
            typealias=0,
        )
        self.by_module: dict[str, ModuleStats] = by_module or {}
        self.by_msg: dict[str, int] = by_msg or {}
        self.code_type_count = code_type_count or CodeTypeCount(
            code=0, comment=0, docstring=0, empty=0, total=0
        )

        self.dependencies: dict[str, set[str]] = dependencies or {}
        self.duplicated_lines = duplicated_lines or DuplicatedLines(
            nb_duplicated_lines=0, percent_duplicated_lines=0.0
        )
        self.node_count = node_count or NodeCount(
            function=0, klass=0, method=0, module=0
        )
        self.undocumented = undocumented or UndocumentedNodes(
            function=0, klass=0, method=0, module=0
        )

        self.convention = 0
        self.error = 0
        self.fatal = 0
        self.info = 0
        self.refactor = 0
        self.statement = 0
        self.warning = 0

        self.global_note = 0
        self.nb_duplicated_lines = 0
        self.percent_duplicated_lines = 0.0

    def __repr__(self) -> str:
        return str(self)

    def __str__(self) -> str:
        return f"""{self.bad_names}
        {sorted(self.by_module.items())}
        {sorted(self.by_msg.items())}
        {self.code_type_count}
        {sorted(self.dependencies.items())}
        {self.duplicated_lines}
        {self.undocumented}
        {self.convention}
        {self.error}
        {self.fatal}
        {self.info}
        {self.refactor}
        {self.statement}
        {self.warning}
        {self.global_note}
        {self.nb_duplicated_lines}
        {self.percent_duplicated_lines}"""

    def init_single_module(self, module_name: str) -> None:
        """Use through PyLinter.set_current_module so PyLinter.current_name is
        consistent.
        """
        self.by_module[module_name] = ModuleStats(
            convention=0, error=0, fatal=0, info=0, refactor=0, statement=0, warning=0
        )

    def get_bad_names(
        self,
        node_name: Literal[
            "argument",
            "attr",
            "class",
            "class_attribute",
            "class_const",
            "const",
            "inlinevar",
            "function",
            "method",
            "module",
            "variable",
            "typevar",
            "typealias",
        ],
    ) -> int:
        """Get a bad names node count."""
        if node_name == "class":
            return self.bad_names.get("klass", 0)
        return self.bad_names.get(node_name, 0)

    def increase_bad_name(self, node_name: str, increase: int) -> None:
        """Increase a bad names node count."""
        if node_name not in {
            "argument",
            "attr",
            "class",
            "class_attribute",
            "class_const",
            "const",
            "inlinevar",
            "function",
            "method",
            "module",
            "variable",
            "typevar",
            "typealias",
        }:
            raise ValueError("Node type not part of the bad_names stat")

        node_name = cast(
            Literal[
                "argument",
                "attr",
                "class",
                "class_attribute",
                "class_const",
                "const",
                "inlinevar",
                "function",
                "method",
                "module",
                "variable",
                "typevar",
                "typealias",
            ],
            node_name,
        )
        if node_name == "class":
            self.bad_names["klass"] += increase
        else:
            self.bad_names[node_name] += increase

    def reset_bad_names(self) -> None:
        """Resets the bad_names attribute."""
        self.bad_names = BadNames(
            argument=0,
            attr=0,
            klass=0,
            class_attribute=0,
            class_const=0,
            const=0,
            inlinevar=0,
            function=0,
            method=0,
            module=0,
            variable=0,
            typevar=0,
            typealias=0,
        )

    def get_code_count(
        self, type_name: Literal["code", "comment", "docstring", "empty", "total"]
    ) -> int:
        """Get a code type count."""
        return self.code_type_count.get(type_name, 0)

    def reset_code_count(self) -> None:
        """Resets the code_type_count attribute."""
        self.code_type_count = CodeTypeCount(
            code=0, comment=0, docstring=0, empty=0, total=0
        )

    def reset_duplicated_lines(self) -> None:
        """Resets the duplicated_lines attribute."""
        self.duplicated_lines = DuplicatedLines(
            nb_duplicated_lines=0, percent_duplicated_lines=0.0
        )

    def get_node_count(
        self, node_name: Literal["function", "class", "method", "module"]
    ) -> int:
        """Get a node count while handling some extra conditions."""
        if node_name == "class":
            return self.node_count.get("klass", 0)
        return self.node_count.get(node_name, 0)

    def reset_node_count(self) -> None:
        """Resets the node count attribute."""
        self.node_count = NodeCount(function=0, klass=0, method=0, module=0)

    def get_undocumented(
        self, node_name: Literal["function", "class", "method", "module"]
    ) -> float:
        """Get a undocumented node count."""
        if node_name == "class":
            return self.undocumented["klass"]
        return self.undocumented[node_name]

    def reset_undocumented(self) -> None:
        """Resets the undocumented attribute."""
        self.undocumented = UndocumentedNodes(function=0, klass=0, method=0, module=0)

    def get_global_message_count(self, type_name: str) -> int:
        """Get a global message count."""
        return getattr(self, type_name, 0)

    def get_module_message_count(self, modname: str, type_name: str) -> int:
        """Get a module message count."""
        return getattr(self.by_module[modname], type_name, 0)

    def increase_single_message_count(self, type_name: str, increase: int) -> None:
        """Increase the message type count of an individual message type."""
        setattr(self, type_name, getattr(self, type_name) + increase)

    def increase_single_module_message_count(
        self, modname: str, type_name: MessageTypesFullName, increase: int
    ) -> None:
        """Increase the message type count of an individual message type of a
        module.
        """
        self.by_module[modname][type_name] += increase

    def reset_message_count(self) -> None:
        """Resets the message type count of the stats object."""
        self.convention = 0
        self.error = 0
        self.fatal = 0
        self.info = 0
        self.refactor = 0
        self.warning = 0


def merge_stats(stats: list[LinterStats]) -> LinterStats:
    """Used to merge multiple stats objects into a new one when pylint is run in
    parallel mode.
    """
    merged = LinterStats()
    for stat in stats:
        merged.bad_names["argument"] += stat.bad_names["argument"]
        merged.bad_names["attr"] += stat.bad_names["attr"]
        merged.bad_names["klass"] += stat.bad_names["klass"]
        merged.bad_names["class_attribute"] += stat.bad_names["class_attribute"]
        merged.bad_names["class_const"] += stat.bad_names["class_const"]
        merged.bad_names["const"] += stat.bad_names["const"]
        merged.bad_names["inlinevar"] += stat.bad_names["inlinevar"]
        merged.bad_names["function"] += stat.bad_names["function"]
        merged.bad_names["method"] += stat.bad_names["method"]
        merged.bad_names["module"] += stat.bad_names["module"]
        merged.bad_names["variable"] += stat.bad_names["variable"]
        merged.bad_names["typevar"] += stat.bad_names["typevar"]
        merged.bad_names["typealias"] += stat.bad_names["typealias"]

        for mod_key, mod_value in stat.by_module.items():
            merged.by_module[mod_key] = mod_value

        for msg_key, msg_value in stat.by_msg.items():
            try:
                merged.by_msg[msg_key] += msg_value
            except KeyError:
                merged.by_msg[msg_key] = msg_value

        merged.code_type_count["code"] += stat.code_type_count["code"]
        merged.code_type_count["comment"] += stat.code_type_count["comment"]
        merged.code_type_count["docstring"] += stat.code_type_count["docstring"]
        merged.code_type_count["empty"] += stat.code_type_count["empty"]
        merged.code_type_count["total"] += stat.code_type_count["total"]

        for dep_key, dep_value in stat.dependencies.items():
            try:
                merged.dependencies[dep_key].update(dep_value)
            except KeyError:
                merged.dependencies[dep_key] = dep_value

        merged.duplicated_lines["nb_duplicated_lines"] += stat.duplicated_lines[
            "nb_duplicated_lines"
        ]
        merged.duplicated_lines["percent_duplicated_lines"] += stat.duplicated_lines[
            "percent_duplicated_lines"
        ]

        merged.node_count["function"] += stat.node_count["function"]
        merged.node_count["klass"] += stat.node_count["klass"]
        merged.node_count["method"] += stat.node_count["method"]
        merged.node_count["module"] += stat.node_count["module"]

        merged.undocumented["function"] += stat.undocumented["function"]
        merged.undocumented["klass"] += stat.undocumented["klass"]
        merged.undocumented["method"] += stat.undocumented["method"]
        merged.undocumented["module"] += stat.undocumented["module"]

        merged.convention += stat.convention
        merged.error += stat.error
        merged.fatal += stat.fatal
        merged.info += stat.info
        merged.refactor += stat.refactor
        merged.statement += stat.statement
        merged.warning += stat.warning

        merged.global_note += stat.global_note
    return merged

back/venvmsg/Lib/site-packages/pylint/utils/linterstats.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

import re
from collections.abc import Generator
from typing import NamedTuple

# Allow stopping after the first semicolon/hash encountered,
# so that an option can be continued with the reasons
# why it is active or disabled.
OPTION_RGX = r"""
    (?:^\s*\#.*|\s*|               # Comment line, or whitespaces,
       \s*\#.*(?=\#.*?\bpylint:))  # or a beginning of an inline comment
                                   # followed by "pylint:" pragma
    (\#                            # Beginning of comment
    .*?                            # Anything (as little as possible)
    \bpylint:                      # pylint word and column
    \s*                            # Any number of whitespaces
    ([^;#\n]+))                    # Anything except semicolon or hash or
                                   # newline (it is the second matched group)
                                   # and end of the first matched group
    [;#]{0,1}                      # From 0 to 1 repetition of semicolon or hash
"""
OPTION_PO = re.compile(OPTION_RGX, re.VERBOSE)


class PragmaRepresenter(NamedTuple):
    action: str
    messages: list[str]


ATOMIC_KEYWORDS = frozenset(("disable-all", "skip-file"))
MESSAGE_KEYWORDS = frozenset(
    ("disable-next", "disable-msg", "enable-msg", "disable", "enable")
)
# sorted is necessary because sets are unordered collections and ALL_KEYWORDS
# string should not vary between executions
# reverse is necessary in order to have the longest keywords first, so that, for example,
# 'disable' string should not be matched instead of 'disable-all'
ALL_KEYWORDS = "|".join(
    sorted(ATOMIC_KEYWORDS | MESSAGE_KEYWORDS, key=len, reverse=True)
)


TOKEN_SPECIFICATION = [
    ("KEYWORD", rf"\b({ALL_KEYWORDS:s})\b"),
    ("MESSAGE_STRING", r"[0-9A-Za-z\-\_]{2,}"),  # Identifiers
    ("ASSIGN", r"="),  # Assignment operator
    ("MESSAGE_NUMBER", r"[CREIWF]{1}\d*"),
]

TOK_REGEX = "|".join(
    f"(?P<{token_name:s}>{token_rgx:s})"
    for token_name, token_rgx in TOKEN_SPECIFICATION
)


def emit_pragma_representer(action: str, messages: list[str]) -> PragmaRepresenter:
    if not messages and action in MESSAGE_KEYWORDS:
        raise InvalidPragmaError(
            "The keyword is not followed by message identifier", action
        )
    return PragmaRepresenter(action, messages)


class PragmaParserError(Exception):
    """A class for exceptions thrown by pragma_parser module."""

    def __init__(self, message: str, token: str) -> None:
        """:args message: explain the reason why the exception has been thrown
        :args token: token concerned by the exception.
        """
        self.message = message
        self.token = token
        super().__init__(self.message)


class UnRecognizedOptionError(PragmaParserError):
    """Thrown in case the of a valid but unrecognized option."""


class InvalidPragmaError(PragmaParserError):
    """Thrown in case the pragma is invalid."""


def parse_pragma(pylint_pragma: str) -> Generator[PragmaRepresenter, None, None]:
    action: str | None = None
    messages: list[str] = []
    assignment_required = False
    previous_token = ""

    for mo in re.finditer(TOK_REGEX, pylint_pragma):
        kind = mo.lastgroup
        value = mo.group()

        if kind == "ASSIGN":
            if not assignment_required:
                if action:
                    # A keyword has been found previously but doesn't support assignment
                    raise UnRecognizedOptionError(
                        "The keyword doesn't support assignment", action
                    )
                if previous_token:
                    # Something found previously but not a known keyword
                    raise UnRecognizedOptionError(
                        "The keyword is unknown", previous_token
                    )
                # Nothing at all detected before this assignment
                raise InvalidPragmaError("Missing keyword before assignment", "")
            assignment_required = False
        elif assignment_required:
            raise InvalidPragmaError(
                "The = sign is missing after the keyword", action or ""
            )
        elif kind == "KEYWORD":
            if action:
                yield emit_pragma_representer(action, messages)
            action = value
            messages = []
            assignment_required = action in MESSAGE_KEYWORDS
        elif kind in {"MESSAGE_STRING", "MESSAGE_NUMBER"}:
            messages.append(value)
            assignment_required = False
        else:
            raise RuntimeError("Token not recognized")

        previous_token = value

    if action:
        yield emit_pragma_representer(action, messages)
    else:
        raise UnRecognizedOptionError("The keyword is unknown", previous_token)

back/venvmsg/Lib/site-packages/pylint/utils/pragma_parser.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt

from __future__ import annotations

try:
    import isort.api
    import isort.settings

    HAS_ISORT_5 = True
except ImportError:  # isort < 5
    import isort

    HAS_ISORT_5 = False

import argparse
import codecs
import os
import re
import sys
import textwrap
import tokenize
import warnings
from collections import deque
from collections.abc import Iterable, Sequence
from io import BufferedReader, BytesIO
from typing import (
    TYPE_CHECKING,
    Any,
    List,
    Literal,
    Pattern,
    TextIO,
    Tuple,
    TypeVar,
    Union,
)

from astroid import Module, modutils, nodes

from pylint.constants import PY_EXTS
from pylint.typing import OptionDict

if TYPE_CHECKING:
    from pylint.lint import PyLinter

DEFAULT_LINE_LENGTH = 79

# These are types used to overload get_global_option() and refer to the options type
GLOBAL_OPTION_BOOL = Literal[
    "suggestion-mode",
    "analyse-fallback-blocks",
    "allow-global-unused-variables",
]
GLOBAL_OPTION_INT = Literal["max-line-length", "docstring-min-length"]
GLOBAL_OPTION_LIST = Literal["ignored-modules"]
GLOBAL_OPTION_PATTERN = Literal[
    "no-docstring-rgx",
    "dummy-variables-rgx",
    "ignored-argument-names",
    "mixin-class-rgx",
]
GLOBAL_OPTION_PATTERN_LIST = Literal["exclude-too-few-public-methods", "ignore-paths"]
GLOBAL_OPTION_TUPLE_INT = Literal["py-version"]
GLOBAL_OPTION_NAMES = Union[
    GLOBAL_OPTION_BOOL,
    GLOBAL_OPTION_INT,
    GLOBAL_OPTION_LIST,
    GLOBAL_OPTION_PATTERN,
    GLOBAL_OPTION_PATTERN_LIST,
    GLOBAL_OPTION_TUPLE_INT,
]
T_GlobalOptionReturnTypes = TypeVar(
    "T_GlobalOptionReturnTypes",
    bool,
    int,
    List[str],
    Pattern[str],
    List[Pattern[str]],
    Tuple[int, ...],
)


def normalize_text(
    text: str, line_len: int = DEFAULT_LINE_LENGTH, indent: str = ""
) -> str:
    """Wrap the text on the given line length."""
    return "\n".join(
        textwrap.wrap(
            text, width=line_len, initial_indent=indent, subsequent_indent=indent
        )
    )


CMPS = ["=", "-", "+"]


# py3k has no more cmp builtin
def cmp(a: int | float, b: int | float) -> int:
    return (a > b) - (a < b)


def diff_string(old: int | float, new: int | float) -> str:
    """Given an old and new int value, return a string representing the
    difference.
    """
    diff = abs(old - new)
    diff_str = f"{CMPS[cmp(old, new)]}{diff and f'{diff:.2f}' or ''}"
    return diff_str


def get_module_and_frameid(node: nodes.NodeNG) -> tuple[str, str]:
    """Return the module name and the frame id in the module."""
    frame = node.frame()
    module, obj = "", []
    while frame:
        if isinstance(frame, Module):
            module = frame.name
        else:
            obj.append(getattr(frame, "name", "<lambda>"))
        try:
            frame = frame.parent.frame()
        except AttributeError:
            break
    obj.reverse()
    return module, ".".join(obj)


def get_rst_title(title: str, character: str) -> str:
    """Permit to get a title formatted as ReStructuredText test (underlined with a
    chosen character).
    """
    return f"{title}\n{character * len(title)}\n"


def get_rst_section(
    section: str | None,
    options: list[tuple[str, OptionDict, Any]],
    doc: str | None = None,
) -> str:
    """Format an option's section using as a ReStructuredText formatted output."""
    result = ""
    if section:
        result += get_rst_title(section, "'")
    if doc:
        formatted_doc = normalize_text(doc)
        result += f"{formatted_doc}\n\n"
    for optname, optdict, value in options:
        help_opt = optdict.get("help")
        result += f":{optname}:\n"
        if help_opt:
            assert isinstance(help_opt, str)
            formatted_help = normalize_text(help_opt, indent="  ")
            result += f"{formatted_help}\n"
        if value and optname != "py-version":
            value = str(_format_option_value(optdict, value))
            result += f"\n  Default: ``{value.replace('`` ', '```` ``')}``\n"
    return result


def decoding_stream(
    stream: BufferedReader | BytesIO,
    encoding: str,
    errors: Literal["strict"] = "strict",
) -> codecs.StreamReader:
    try:
        reader_cls = codecs.getreader(encoding or sys.getdefaultencoding())
    except LookupError:
        reader_cls = codecs.getreader(sys.getdefaultencoding())
    return reader_cls(stream, errors)


def tokenize_module(node: nodes.Module) -> list[tokenize.TokenInfo]:
    with node.stream() as stream:
        readline = stream.readline
        return list(tokenize.tokenize(readline))


def register_plugins(linter: PyLinter, directory: str) -> None:
    """Load all module and package in the given directory, looking for a
    'register' function in each one, used to register pylint checkers.
    """
    imported = {}
    for filename in os.listdir(directory):
        base, extension = os.path.splitext(filename)
        if base in imported or base == "__pycache__":
            continue
        if (
            extension in PY_EXTS
            and base != "__init__"
            or (
                not extension
                and os.path.isdir(os.path.join(directory, base))
                and not filename.startswith(".")
            )
        ):
            try:
                module = modutils.load_module_from_file(
                    os.path.join(directory, filename)
                )
            except ValueError:
                # empty module name (usually Emacs auto-save files)
                continue
            except ImportError as exc:
                print(f"Problem importing module {filename}: {exc}", file=sys.stderr)
            else:
                if hasattr(module, "register"):
                    module.register(linter)
                    imported[base] = 1


def _splitstrip(string: str, sep: str = ",") -> list[str]:
    """Return a list of stripped string by splitting the string given as
    argument on `sep` (',' by default), empty strings are discarded.

    >>> _splitstrip('a, b, c   ,  4,,')
    ['a', 'b', 'c', '4']
    >>> _splitstrip('a')
    ['a']
    >>> _splitstrip('a,\nb,\nc,')
    ['a', 'b', 'c']

    :type string: str or unicode
    :param string: a csv line

    :type sep: str or unicode
    :param sep: field separator, default to the comma (',')

    :rtype: str or unicode
    :return: the unquoted string (or the input string if it wasn't quoted)
    """
    return [word.strip() for word in string.split(sep) if word.strip()]


def _unquote(string: str) -> str:
    """Remove optional quotes (simple or double) from the string.

    :param string: an optionally quoted string
    :return: the unquoted string (or the input string if it wasn't quoted)
    """
    if not string:
        return string
    if string[0] in "\"'":
        string = string[1:]
    if string[-1] in "\"'":
        string = string[:-1]
    return string


def _check_csv(value: list[str] | tuple[str] | str) -> Sequence[str]:
    if isinstance(value, (list, tuple)):
        return value
    return _splitstrip(value)


def _check_regexp_csv(value: list[str] | tuple[str] | str) -> Iterable[str]:
    r"""Split a comma-separated list of regexps, taking care to avoid splitting
    a regex employing a comma as quantifier, as in `\d{1,2}`."""
    if isinstance(value, (list, tuple)):
        yield from value
    else:
        # None is a sentinel value here
        regexps: deque[deque[str] | None] = deque([None])
        open_braces = False
        for char in value:
            if char == "{":
                open_braces = True
            elif char == "}" and open_braces:
                open_braces = False

            if char == "," and not open_braces:
                regexps.append(None)
            elif regexps[-1] is None:
                regexps.pop()
                regexps.append(deque([char]))
            else:
                regexps[-1].append(char)
        yield from ("".join(regexp).strip() for regexp in regexps if regexp is not None)


def _comment(string: str) -> str:
    """Return string as a comment."""
    lines = [line.strip() for line in string.splitlines()]
    sep = "\n"
    return "# " + f"{sep}# ".join(lines)


def _format_option_value(optdict: OptionDict, value: Any) -> str:
    """Return the user input's value from a 'compiled' value.

    TODO: Refactor the code to not use this deprecated function
    """
    if optdict.get("type", None) == "py_version":
        value = ".".join(str(item) for item in value)
    elif isinstance(value, (list, tuple)):
        value = ",".join(_format_option_value(optdict, item) for item in value)
    elif isinstance(value, dict):
        value = ",".join(f"{k}:{v}" for k, v in value.items())
    elif hasattr(value, "match"):  # optdict.get('type') == 'regexp'
        # compiled regexp
        value = value.pattern
    elif optdict.get("type") == "yn":
        value = "yes" if value else "no"
    elif isinstance(value, str) and value.isspace():
        value = f"'{value}'"
    return str(value)


def format_section(
    stream: TextIO,
    section: str,
    options: list[tuple[str, OptionDict, Any]],
    doc: str | None = None,
) -> None:
    """Format an option's section using the INI format."""
    warnings.warn(
        "format_section has been deprecated. It will be removed in pylint 3.0.",
        DeprecationWarning,
        stacklevel=2,
    )
    if doc:
        print(_comment(doc), file=stream)
    print(f"[{section}]", file=stream)
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", category=DeprecationWarning)
        _ini_format(stream, options)


def _ini_format(stream: TextIO, options: list[tuple[str, OptionDict, Any]]) -> None:
    """Format options using the INI format."""
    warnings.warn(
        "_ini_format has been deprecated. It will be removed in pylint 3.0.",
        DeprecationWarning,
        stacklevel=2,
    )
    for optname, optdict, value in options:
        # Skip deprecated option
        if "kwargs" in optdict:
            assert isinstance(optdict["kwargs"], dict)
            if "new_names" in optdict["kwargs"]:
                continue
        value = _format_option_value(optdict, value)
        help_opt = optdict.get("help")
        if help_opt:
            assert isinstance(help_opt, str)
            help_opt = normalize_text(help_opt, indent="# ")
            print(file=stream)
            print(help_opt, file=stream)
        else:
            print(file=stream)
        if value in {"None", "False"}:
            print(f"#{optname}=", file=stream)
        else:
            value = str(value).strip()
            if re.match(r"^([\w-]+,)+[\w-]+$", str(value)):
                separator = "\n " + " " * len(optname)
                value = separator.join(x + "," for x in str(value).split(","))
                # remove trailing ',' from last element of the list
                value = value[:-1]
            print(f"{optname}={value}", file=stream)


class IsortDriver:
    """A wrapper around isort API that changed between versions 4 and 5."""

    def __init__(self, config: argparse.Namespace) -> None:
        if HAS_ISORT_5:
            self.isort5_config = isort.settings.Config(
                # There is no typo here. EXTRA_standard_library is
                # what most users want. The option has been named
                # KNOWN_standard_library for ages in pylint, and we
                # don't want to break compatibility.
                extra_standard_library=config.known_standard_library,
                known_third_party=config.known_third_party,
            )
        else:
            # pylint: disable-next=no-member
            self.isort4_obj = isort.SortImports(  # type: ignore[attr-defined]
                file_contents="",
                known_standard_library=config.known_standard_library,
                known_third_party=config.known_third_party,
            )

    def place_module(self, package: str) -> str:
        if HAS_ISORT_5:
            return isort.api.place_module(package, self.isort5_config)
        return self.isort4_obj.place_module(package)  # type: ignore[no-any-return]

back/venvmsg/Lib/site-packages/pylint/utils/utils.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
'''
datetime.tzinfo timezone definitions generated from the
Olson timezone database:

    ftp://elsie.nci.nih.gov/pub/tz*.tar.gz

See the datetime section of the Python Library Reference for information
on how to use these modules.
'''

import sys
import datetime
import os.path

from pytz.exceptions import AmbiguousTimeError
from pytz.exceptions import InvalidTimeError
from pytz.exceptions import NonExistentTimeError
from pytz.exceptions import UnknownTimeZoneError
from pytz.lazy import LazyDict, LazyList, LazySet  # noqa
from pytz.tzinfo import unpickler, BaseTzInfo
from pytz.tzfile import build_tzinfo


# The IANA (nee Olson) database is updated several times a year.
OLSON_VERSION = '2023c'
VERSION = '2023.3.post1'  # pip compatible version number.
__version__ = VERSION

OLSEN_VERSION = OLSON_VERSION  # Old releases had this misspelling

__all__ = [
    'timezone', 'utc', 'country_timezones', 'country_names',
    'AmbiguousTimeError', 'InvalidTimeError',
    'NonExistentTimeError', 'UnknownTimeZoneError',
    'all_timezones', 'all_timezones_set',
    'common_timezones', 'common_timezones_set',
    'BaseTzInfo', 'FixedOffset',
]


if sys.version_info[0] > 2:  # Python 3.x

    # Python 3.x doesn't have unicode(), making writing code
    # for Python 2.3 and Python 3.x a pain.
    unicode = str

    def ascii(s):
        r"""
        >>> ascii('Hello')
        'Hello'
        >>> ascii('\N{TRADE MARK SIGN}') #doctest: +IGNORE_EXCEPTION_DETAIL
        Traceback (most recent call last):
            ...
        UnicodeEncodeError: ...
        """
        if type(s) == bytes:
            s = s.decode('ASCII')
        else:
            s.encode('ASCII')  # Raise an exception if not ASCII
        return s  # But the string - not a byte string.

else:  # Python 2.x

    def ascii(s):
        r"""
        >>> ascii('Hello')
        'Hello'
        >>> ascii(u'Hello')
        'Hello'
        >>> ascii(u'\N{TRADE MARK SIGN}') #doctest: +IGNORE_EXCEPTION_DETAIL
        Traceback (most recent call last):
            ...
        UnicodeEncodeError: ...
        """
        return s.encode('ASCII')


def open_resource(name):
    """Open a resource from the zoneinfo subdir for reading.

    Uses the pkg_resources module if available and no standard file
    found at the calculated location.

    It is possible to specify different location for zoneinfo
    subdir by using the PYTZ_TZDATADIR environment variable.
    """
    name_parts = name.lstrip('/').split('/')
    for part in name_parts:
        if part == os.path.pardir or os.sep in part:
            raise ValueError('Bad path segment: %r' % part)
    zoneinfo_dir = os.environ.get('PYTZ_TZDATADIR', None)
    if zoneinfo_dir is not None:
        filename = os.path.join(zoneinfo_dir, *name_parts)
    else:
        filename = os.path.join(os.path.dirname(__file__),
                                'zoneinfo', *name_parts)
        if not os.path.exists(filename):
            # http://bugs.launchpad.net/bugs/383171 - we avoid using this
            # unless absolutely necessary to help when a broken version of
            # pkg_resources is installed.
            try:
                from pkg_resources import resource_stream
            except ImportError:
                resource_stream = None

            if resource_stream is not None:
                return resource_stream(__name__, 'zoneinfo/' + name)
    return open(filename, 'rb')


def resource_exists(name):
    """Return true if the given resource exists"""
    try:
        if os.environ.get('PYTZ_SKIPEXISTSCHECK', ''):
            # In "standard" distributions, we can assume that
            # all the listed timezones are present. As an
            # import-speed optimization, you can set the
            # PYTZ_SKIPEXISTSCHECK flag to skip checking
            # for the presence of the resource file on disk.
            return True
        open_resource(name).close()
        return True
    except IOError:
        return False


_tzinfo_cache = {}


def timezone(zone):
    r''' Return a datetime.tzinfo implementation for the given timezone

    >>> from datetime import datetime, timedelta
    >>> utc = timezone('UTC')
    >>> eastern = timezone('US/Eastern')
    >>> eastern.zone
    'US/Eastern'
    >>> timezone(unicode('US/Eastern')) is eastern
    True
    >>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc)
    >>> loc_dt = utc_dt.astimezone(eastern)
    >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'
    >>> loc_dt.strftime(fmt)
    '2002-10-27 01:00:00 EST (-0500)'
    >>> (loc_dt - timedelta(minutes=10)).strftime(fmt)
    '2002-10-27 00:50:00 EST (-0500)'
    >>> eastern.normalize(loc_dt - timedelta(minutes=10)).strftime(fmt)
    '2002-10-27 01:50:00 EDT (-0400)'
    >>> (loc_dt + timedelta(minutes=10)).strftime(fmt)
    '2002-10-27 01:10:00 EST (-0500)'

    Raises UnknownTimeZoneError if passed an unknown zone.

    >>> try:
    ...     timezone('Asia/Shangri-La')
    ... except UnknownTimeZoneError:
    ...     print('Unknown')
    Unknown

    >>> try:
    ...     timezone(unicode('\N{TRADE MARK SIGN}'))
    ... except UnknownTimeZoneError:
    ...     print('Unknown')
    Unknown

    '''
    if zone is None:
        raise UnknownTimeZoneError(None)

    if zone.upper() == 'UTC':
        return utc

    try:
        zone = ascii(zone)
    except UnicodeEncodeError:
        # All valid timezones are ASCII
        raise UnknownTimeZoneError(zone)

    zone = _case_insensitive_zone_lookup(_unmunge_zone(zone))
    if zone not in _tzinfo_cache:
        if zone in all_timezones_set:  # noqa
            fp = open_resource(zone)
            try:
                _tzinfo_cache[zone] = build_tzinfo(zone, fp)
            finally:
                fp.close()
        else:
            raise UnknownTimeZoneError(zone)

    return _tzinfo_cache[zone]


def _unmunge_zone(zone):
    """Undo the time zone name munging done by older versions of pytz."""
    return zone.replace('_plus_', '+').replace('_minus_', '-')


_all_timezones_lower_to_standard = None


def _case_insensitive_zone_lookup(zone):
    """case-insensitively matching timezone, else return zone unchanged"""
    global _all_timezones_lower_to_standard
    if _all_timezones_lower_to_standard is None:
        _all_timezones_lower_to_standard = dict((tz.lower(), tz) for tz in _all_timezones_unchecked)  # noqa
    return _all_timezones_lower_to_standard.get(zone.lower()) or zone  # noqa


ZERO = datetime.timedelta(0)
HOUR = datetime.timedelta(hours=1)


class UTC(BaseTzInfo):
    """UTC

    Optimized UTC implementation. It unpickles using the single module global
    instance defined beneath this class declaration.
    """
    zone = "UTC"

    _utcoffset = ZERO
    _dst = ZERO
    _tzname = zone

    def fromutc(self, dt):
        if dt.tzinfo is None:
            return self.localize(dt)
        return super(utc.__class__, self).fromutc(dt)

    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

    def __reduce__(self):
        return _UTC, ()

    def localize(self, dt, is_dst=False):
        '''Convert naive time to local time'''
        if dt.tzinfo is not None:
            raise ValueError('Not naive datetime (tzinfo is already set)')
        return dt.replace(tzinfo=self)

    def normalize(self, dt, is_dst=False):
        '''Correct the timezone information on the given datetime'''
        if dt.tzinfo is self:
            return dt
        if dt.tzinfo is None:
            raise ValueError('Naive time - no tzinfo set')
        return dt.astimezone(self)

    def __repr__(self):
        return "<UTC>"

    def __str__(self):
        return "UTC"


UTC = utc = UTC()  # UTC is a singleton


def _UTC():
    """Factory function for utc unpickling.

    Makes sure that unpickling a utc instance always returns the same
    module global.

    These examples belong in the UTC class above, but it is obscured; or in
    the README.rst, but we are not depending on Python 2.4 so integrating
    the README.rst examples with the unit tests is not trivial.

    >>> import datetime, pickle
    >>> dt = datetime.datetime(2005, 3, 1, 14, 13, 21, tzinfo=utc)
    >>> naive = dt.replace(tzinfo=None)
    >>> p = pickle.dumps(dt, 1)
    >>> naive_p = pickle.dumps(naive, 1)
    >>> len(p) - len(naive_p)
    17
    >>> new = pickle.loads(p)
    >>> new == dt
    True
    >>> new is dt
    False
    >>> new.tzinfo is dt.tzinfo
    True
    >>> utc is UTC is timezone('UTC')
    True
    >>> utc is timezone('GMT')
    False
    """
    return utc


_UTC.__safe_for_unpickling__ = True


def _p(*args):
    """Factory function for unpickling pytz tzinfo instances.

    Just a wrapper around tzinfo.unpickler to save a few bytes in each pickle
    by shortening the path.
    """
    return unpickler(*args)


_p.__safe_for_unpickling__ = True


class _CountryTimezoneDict(LazyDict):
    """Map ISO 3166 country code to a list of timezone names commonly used
    in that country.

    iso3166_code is the two letter code used to identify the country.

    >>> def print_list(list_of_strings):
    ...     'We use a helper so doctests work under Python 2.3 -> 3.x'
    ...     for s in list_of_strings:
    ...         print(s)

    >>> print_list(country_timezones['nz'])
    Pacific/Auckland
    Pacific/Chatham
    >>> print_list(country_timezones['ch'])
    Europe/Zurich
    >>> print_list(country_timezones['CH'])
    Europe/Zurich
    >>> print_list(country_timezones[unicode('ch')])
    Europe/Zurich
    >>> print_list(country_timezones['XXX'])
    Traceback (most recent call last):
    ...
    KeyError: 'XXX'

    Previously, this information was exposed as a function rather than a
    dictionary. This is still supported::

    >>> print_list(country_timezones('nz'))
    Pacific/Auckland
    Pacific/Chatham
    """
    def __call__(self, iso3166_code):
        """Backwards compatibility."""
        return self[iso3166_code]

    def _fill(self):
        data = {}
        zone_tab = open_resource('zone.tab')
        try:
            for line in zone_tab:
                line = line.decode('UTF-8')
                if line.startswith('#'):
                    continue
                code, coordinates, zone = line.split(None, 4)[:3]
                if zone not in all_timezones_set:  # noqa
                    continue
                try:
                    data[code].append(zone)
                except KeyError:
                    data[code] = [zone]
            self.data = data
        finally:
            zone_tab.close()


country_timezones = _CountryTimezoneDict()


class _CountryNameDict(LazyDict):
    '''Dictionary proving ISO3166 code -> English name.

    >>> print(country_names['au'])
    Australia
    '''
    def _fill(self):
        data = {}
        zone_tab = open_resource('iso3166.tab')
        try:
            for line in zone_tab.readlines():
                line = line.decode('UTF-8')
                if line.startswith('#'):
                    continue
                code, name = line.split(None, 1)
                data[code] = name.strip()
            self.data = data
        finally:
            zone_tab.close()


country_names = _CountryNameDict()


# Time-zone info based solely on fixed offsets

class _FixedOffset(datetime.tzinfo):

    zone = None  # to match the standard pytz API

    def __init__(self, minutes):
        if abs(minutes) >= 1440:
            raise ValueError("absolute offset is too large", minutes)
        self._minutes = minutes
        self._offset = datetime.timedelta(minutes=minutes)

    def utcoffset(self, dt):
        return self._offset

    def __reduce__(self):
        return FixedOffset, (self._minutes, )

    def dst(self, dt):
        return ZERO

    def tzname(self, dt):
        return None

    def __repr__(self):
        return 'pytz.FixedOffset(%d)' % self._minutes

    def localize(self, dt, is_dst=False):
        '''Convert naive time to local time'''
        if dt.tzinfo is not None:
            raise ValueError('Not naive datetime (tzinfo is already set)')
        return dt.replace(tzinfo=self)

    def normalize(self, dt, is_dst=False):
        '''Correct the timezone information on the given datetime'''
        if dt.tzinfo is self:
            return dt
        if dt.tzinfo is None:
            raise ValueError('Naive time - no tzinfo set')
        return dt.astimezone(self)


def FixedOffset(offset, _tzinfos={}):
    """return a fixed-offset timezone based off a number of minutes.

        >>> one = FixedOffset(-330)
        >>> one
        pytz.FixedOffset(-330)
        >>> str(one.utcoffset(datetime.datetime.now()))
        '-1 day, 18:30:00'
        >>> str(one.dst(datetime.datetime.now()))
        '0:00:00'

        >>> two = FixedOffset(1380)
        >>> two
        pytz.FixedOffset(1380)
        >>> str(two.utcoffset(datetime.datetime.now()))
        '23:00:00'
        >>> str(two.dst(datetime.datetime.now()))
        '0:00:00'

    The datetime.timedelta must be between the range of -1 and 1 day,
    non-inclusive.

        >>> FixedOffset(1440)
        Traceback (most recent call last):
        ...
        ValueError: ('absolute offset is too large', 1440)

        >>> FixedOffset(-1440)
        Traceback (most recent call last):
        ...
        ValueError: ('absolute offset is too large', -1440)

    An offset of 0 is special-cased to return UTC.

        >>> FixedOffset(0) is UTC
        True

    There should always be only one instance of a FixedOffset per timedelta.
    This should be true for multiple creation calls.

        >>> FixedOffset(-330) is one
        True
        >>> FixedOffset(1380) is two
        True

    It should also be true for pickling.

        >>> import pickle
        >>> pickle.loads(pickle.dumps(one)) is one
        True
        >>> pickle.loads(pickle.dumps(two)) is two
        True
    """
    if offset == 0:
        return UTC

    info = _tzinfos.get(offset)
    if info is None:
        # We haven't seen this one before. we need to save it.

        # Use setdefault to avoid a race condition and make sure we have
        # only one
        info = _tzinfos.setdefault(offset, _FixedOffset(offset))

    return info


FixedOffset.__safe_for_unpickling__ = True


def _test():
    import doctest
    sys.path.insert(0, os.pardir)
    import pytz
    return doctest.testmod(pytz)


if __name__ == '__main__':
    _test()
_all_timezones_unchecked = \
['Africa/Abidjan',
 'Africa/Accra',
 'Africa/Addis_Ababa',
 'Africa/Algiers',
 'Africa/Asmara',
 'Africa/Asmera',
 'Africa/Bamako',
 'Africa/Bangui',
 'Africa/Banjul',
 'Africa/Bissau',
 'Africa/Blantyre',
 'Africa/Brazzaville',
 'Africa/Bujumbura',
 'Africa/Cairo',
 'Africa/Casablanca',
 'Africa/Ceuta',
 'Africa/Conakry',
 'Africa/Dakar',
 'Africa/Dar_es_Salaam',
 'Africa/Djibouti',
 'Africa/Douala',
 'Africa/El_Aaiun',
 'Africa/Freetown',
 'Africa/Gaborone',
 'Africa/Harare',
 'Africa/Johannesburg',
 'Africa/Juba',
 'Africa/Kampala',
 'Africa/Khartoum',
 'Africa/Kigali',
 'Africa/Kinshasa',
 'Africa/Lagos',
 'Africa/Libreville',
 'Africa/Lome',
 'Africa/Luanda',
 'Africa/Lubumbashi',
 'Africa/Lusaka',
 'Africa/Malabo',
 'Africa/Maputo',
 'Africa/Maseru',
 'Africa/Mbabane',
 'Africa/Mogadishu',
 'Africa/Monrovia',
 'Africa/Nairobi',
 'Africa/Ndjamena',
 'Africa/Niamey',
 'Africa/Nouakchott',
 'Africa/Ouagadougou',
 'Africa/Porto-Novo',
 'Africa/Sao_Tome',
 'Africa/Timbuktu',
 'Africa/Tripoli',
 'Africa/Tunis',
 'Africa/Windhoek',
 'America/Adak',
 'America/Anchorage',
 'America/Anguilla',
 'America/Antigua',
 'America/Araguaina',
 'America/Argentina/Buenos_Aires',
 'America/Argentina/Catamarca',
 'America/Argentina/ComodRivadavia',
 'America/Argentina/Cordoba',
 'America/Argentina/Jujuy',
 'America/Argentina/La_Rioja',
 'America/Argentina/Mendoza',
 'America/Argentina/Rio_Gallegos',
 'America/Argentina/Salta',
 'America/Argentina/San_Juan',
 'America/Argentina/San_Luis',
 'America/Argentina/Tucuman',
 'America/Argentina/Ushuaia',
 'America/Aruba',
 'America/Asuncion',
 'America/Atikokan',
 'America/Atka',
 'America/Bahia',
 'America/Bahia_Banderas',
 'America/Barbados',
 'America/Belem',
 'America/Belize',
 'America/Blanc-Sablon',
 'America/Boa_Vista',
 'America/Bogota',
 'America/Boise',
 'America/Buenos_Aires',
 'America/Cambridge_Bay',
 'America/Campo_Grande',
 'America/Cancun',
 'America/Caracas',
 'America/Catamarca',
 'America/Cayenne',
 'America/Cayman',
 'America/Chicago',
 'America/Chihuahua',
 'America/Ciudad_Juarez',
 'America/Coral_Harbour',
 'America/Cordoba',
 'America/Costa_Rica',
 'America/Creston',
 'America/Cuiaba',
 'America/Curacao',
 'America/Danmarkshavn',
 'America/Dawson',
 'America/Dawson_Creek',
 'America/Denver',
 'America/Detroit',
 'America/Dominica',
 'America/Edmonton',
 'America/Eirunepe',
 'America/El_Salvador',
 'America/Ensenada',
 'America/Fort_Nelson',
 'America/Fort_Wayne',
 'America/Fortaleza',
 'America/Glace_Bay',
 'America/Godthab',
 'America/Goose_Bay',
 'America/Grand_Turk',
 'America/Grenada',
 'America/Guadeloupe',
 'America/Guatemala',
 'America/Guayaquil',
 'America/Guyana',
 'America/Halifax',
 'America/Havana',
 'America/Hermosillo',
 'America/Indiana/Indianapolis',
 'America/Indiana/Knox',
 'America/Indiana/Marengo',
 'America/Indiana/Petersburg',
 'America/Indiana/Tell_City',
 'America/Indiana/Vevay',
 'America/Indiana/Vincennes',
 'America/Indiana/Winamac',
 'America/Indianapolis',
 'America/Inuvik',
 'America/Iqaluit',
 'America/Jamaica',
 'America/Jujuy',
 'America/Juneau',
 'America/Kentucky/Louisville',
 'America/Kentucky/Monticello',
 'America/Knox_IN',
 'America/Kralendijk',
 'America/La_Paz',
 'America/Lima',
 'America/Los_Angeles',
 'America/Louisville',
 'America/Lower_Princes',
 'America/Maceio',
 'America/Managua',
 'America/Manaus',
 'America/Marigot',
 'America/Martinique',
 'America/Matamoros',
 'America/Mazatlan',
 'America/Mendoza',
 'America/Menominee',
 'America/Merida',
 'America/Metlakatla',
 'America/Mexico_City',
 'America/Miquelon',
 'America/Moncton',
 'America/Monterrey',
 'America/Montevideo',
 'America/Montreal',
 'America/Montserrat',
 'America/Nassau',
 'America/New_York',
 'America/Nipigon',
 'America/Nome',
 'America/Noronha',
 'America/North_Dakota/Beulah',
 'America/North_Dakota/Center',
 'America/North_Dakota/New_Salem',
 'America/Nuuk',
 'America/Ojinaga',
 'America/Panama',
 'America/Pangnirtung',
 'America/Paramaribo',
 'America/Phoenix',
 'America/Port-au-Prince',
 'America/Port_of_Spain',
 'America/Porto_Acre',
 'America/Porto_Velho',
 'America/Puerto_Rico',
 'America/Punta_Arenas',
 'America/Rainy_River',
 'America/Rankin_Inlet',
 'America/Recife',
 'America/Regina',
 'America/Resolute',
 'America/Rio_Branco',
 'America/Rosario',
 'America/Santa_Isabel',
 'America/Santarem',
 'America/Santiago',
 'America/Santo_Domingo',
 'America/Sao_Paulo',
 'America/Scoresbysund',
 'America/Shiprock',
 'America/Sitka',
 'America/St_Barthelemy',
 'America/St_Johns',
 'America/St_Kitts',
 'America/St_Lucia',
 'America/St_Thomas',
 'America/St_Vincent',
 'America/Swift_Current',
 'America/Tegucigalpa',
 'America/Thule',
 'America/Thunder_Bay',
 'America/Tijuana',
 'America/Toronto',
 'America/Tortola',
 'America/Vancouver',
 'America/Virgin',
 'America/Whitehorse',
 'America/Winnipeg',
 'America/Yakutat',
 'America/Yellowknife',
 'Antarctica/Casey',
 'Antarctica/Davis',
 'Antarctica/DumontDUrville',
 'Antarctica/Macquarie',
 'Antarctica/Mawson',
 'Antarctica/McMurdo',
 'Antarctica/Palmer',
 'Antarctica/Rothera',
 'Antarctica/South_Pole',
 'Antarctica/Syowa',
 'Antarctica/Troll',
 'Antarctica/Vostok',
 'Arctic/Longyearbyen',
 'Asia/Aden',
 'Asia/Almaty',
 'Asia/Amman',
 'Asia/Anadyr',
 'Asia/Aqtau',
 'Asia/Aqtobe',
 'Asia/Ashgabat',
 'Asia/Ashkhabad',
 'Asia/Atyrau',
 'Asia/Baghdad',
 'Asia/Bahrain',
 'Asia/Baku',
 'Asia/Bangkok',
 'Asia/Barnaul',
 'Asia/Beirut',
 'Asia/Bishkek',
 'Asia/Brunei',
 'Asia/Calcutta',
 'Asia/Chita',
 'Asia/Choibalsan',
 'Asia/Chongqing',
 'Asia/Chungking',
 'Asia/Colombo',
 'Asia/Dacca',
 'Asia/Damascus',
 'Asia/Dhaka',
 'Asia/Dili',
 'Asia/Dubai',
 'Asia/Dushanbe',
 'Asia/Famagusta',
 'Asia/Gaza',
 'Asia/Harbin',
 'Asia/Hebron',
 'Asia/Ho_Chi_Minh',
 'Asia/Hong_Kong',
 'Asia/Hovd',
 'Asia/Irkutsk',
 'Asia/Istanbul',
 'Asia/Jakarta',
 'Asia/Jayapura',
 'Asia/Jerusalem',
 'Asia/Kabul',
 'Asia/Kamchatka',
 'Asia/Karachi',
 'Asia/Kashgar',
 'Asia/Kathmandu',
 'Asia/Katmandu',
 'Asia/Khandyga',
 'Asia/Kolkata',
 'Asia/Krasnoyarsk',
 'Asia/Kuala_Lumpur',
 'Asia/Kuching',
 'Asia/Kuwait',
 'Asia/Macao',
 'Asia/Macau',
 'Asia/Magadan',
 'Asia/Makassar',
 'Asia/Manila',
 'Asia/Muscat',
 'Asia/Nicosia',
 'Asia/Novokuznetsk',
 'Asia/Novosibirsk',
 'Asia/Omsk',
 'Asia/Oral',
 'Asia/Phnom_Penh',
 'Asia/Pontianak',
 'Asia/Pyongyang',
 'Asia/Qatar',
 'Asia/Qostanay',
 'Asia/Qyzylorda',
 'Asia/Rangoon',
 'Asia/Riyadh',
 'Asia/Saigon',
 'Asia/Sakhalin',
 'Asia/Samarkand',
 'Asia/Seoul',
 'Asia/Shanghai',
 'Asia/Singapore',
 'Asia/Srednekolymsk',
 'Asia/Taipei',
 'Asia/Tashkent',
 'Asia/Tbilisi',
 'Asia/Tehran',
 'Asia/Tel_Aviv',
 'Asia/Thimbu',
 'Asia/Thimphu',
 'Asia/Tokyo',
 'Asia/Tomsk',
 'Asia/Ujung_Pandang',
 'Asia/Ulaanbaatar',
 'Asia/Ulan_Bator',
 'Asia/Urumqi',
 'Asia/Ust-Nera',
 'Asia/Vientiane',
 'Asia/Vladivostok',
 'Asia/Yakutsk',
 'Asia/Yangon',
 'Asia/Yekaterinburg',
 'Asia/Yerevan',
 'Atlantic/Azores',
 'Atlantic/Bermuda',
 'Atlantic/Canary',
 'Atlantic/Cape_Verde',
 'Atlantic/Faeroe',
 'Atlantic/Faroe',
 'Atlantic/Jan_Mayen',
 'Atlantic/Madeira',
 'Atlantic/Reykjavik',
 'Atlantic/South_Georgia',
 'Atlantic/St_Helena',
 'Atlantic/Stanley',
 'Australia/ACT',
 'Australia/Adelaide',
 'Australia/Brisbane',
 'Australia/Broken_Hill',
 'Australia/Canberra',
 'Australia/Currie',
 'Australia/Darwin',
 'Australia/Eucla',
 'Australia/Hobart',
 'Australia/LHI',
 'Australia/Lindeman',
 'Australia/Lord_Howe',
 'Australia/Melbourne',
 'Australia/NSW',
 'Australia/North',
 'Australia/Perth',
 'Australia/Queensland',
 'Australia/South',
 'Australia/Sydney',
 'Australia/Tasmania',
 'Australia/Victoria',
 'Australia/West',
 'Australia/Yancowinna',
 'Brazil/Acre',
 'Brazil/DeNoronha',
 'Brazil/East',
 'Brazil/West',
 'CET',
 'CST6CDT',
 'Canada/Atlantic',
 'Canada/Central',
 'Canada/Eastern',
 'Canada/Mountain',
 'Canada/Newfoundland',
 'Canada/Pacific',
 'Canada/Saskatchewan',
 'Canada/Yukon',
 'Chile/Continental',
 'Chile/EasterIsland',
 'Cuba',
 'EET',
 'EST',
 'EST5EDT',
 'Egypt',
 'Eire',
 'Etc/GMT',
 'Etc/GMT+0',
 'Etc/GMT+1',
 'Etc/GMT+10',
 'Etc/GMT+11',
 'Etc/GMT+12',
 'Etc/GMT+2',
 'Etc/GMT+3',
 'Etc/GMT+4',
 'Etc/GMT+5',
 'Etc/GMT+6',
 'Etc/GMT+7',
 'Etc/GMT+8',
 'Etc/GMT+9',
 'Etc/GMT-0',
 'Etc/GMT-1',
 'Etc/GMT-10',
 'Etc/GMT-11',
 'Etc/GMT-12',
 'Etc/GMT-13',
 'Etc/GMT-14',
 'Etc/GMT-2',
 'Etc/GMT-3',
 'Etc/GMT-4',
 'Etc/GMT-5',
 'Etc/GMT-6',
 'Etc/GMT-7',
 'Etc/GMT-8',
 'Etc/GMT-9',
 'Etc/GMT0',
 'Etc/Greenwich',
 'Etc/UCT',
 'Etc/UTC',
 'Etc/Universal',
 'Etc/Zulu',
 'Europe/Amsterdam',
 'Europe/Andorra',
 'Europe/Astrakhan',
 'Europe/Athens',
 'Europe/Belfast',
 'Europe/Belgrade',
 'Europe/Berlin',
 'Europe/Bratislava',
 'Europe/Brussels',
 'Europe/Bucharest',
 'Europe/Budapest',
 'Europe/Busingen',
 'Europe/Chisinau',
 'Europe/Copenhagen',
 'Europe/Dublin',
 'Europe/Gibraltar',
 'Europe/Guernsey',
 'Europe/Helsinki',
 'Europe/Isle_of_Man',
 'Europe/Istanbul',
 'Europe/Jersey',
 'Europe/Kaliningrad',
 'Europe/Kiev',
 'Europe/Kirov',
 'Europe/Kyiv',
 'Europe/Lisbon',
 'Europe/Ljubljana',
 'Europe/London',
 'Europe/Luxembourg',
 'Europe/Madrid',
 'Europe/Malta',
 'Europe/Mariehamn',
 'Europe/Minsk',
 'Europe/Monaco',
 'Europe/Moscow',
 'Europe/Nicosia',
 'Europe/Oslo',
 'Europe/Paris',
 'Europe/Podgorica',
 'Europe/Prague',
 'Europe/Riga',
 'Europe/Rome',
 'Europe/Samara',
 'Europe/San_Marino',
 'Europe/Sarajevo',
 'Europe/Saratov',
 'Europe/Simferopol',
 'Europe/Skopje',
 'Europe/Sofia',
 'Europe/Stockholm',
 'Europe/Tallinn',
 'Europe/Tirane',
 'Europe/Tiraspol',
 'Europe/Ulyanovsk',
 'Europe/Uzhgorod',
 'Europe/Vaduz',
 'Europe/Vatican',
 'Europe/Vienna',
 'Europe/Vilnius',
 'Europe/Volgograd',
 'Europe/Warsaw',
 'Europe/Zagreb',
 'Europe/Zaporozhye',
 'Europe/Zurich',
 'GB',
 'GB-Eire',
 'GMT',
 'GMT+0',
 'GMT-0',
 'GMT0',
 'Greenwich',
 'HST',
 'Hongkong',
 'Iceland',
 'Indian/Antananarivo',
 'Indian/Chagos',
 'Indian/Christmas',
 'Indian/Cocos',
 'Indian/Comoro',
 'Indian/Kerguelen',
 'Indian/Mahe',
 'Indian/Maldives',
 'Indian/Mauritius',
 'Indian/Mayotte',
 'Indian/Reunion',
 'Iran',
 'Israel',
 'Jamaica',
 'Japan',
 'Kwajalein',
 'Libya',
 'MET',
 'MST',
 'MST7MDT',
 'Mexico/BajaNorte',
 'Mexico/BajaSur',
 'Mexico/General',
 'NZ',
 'NZ-CHAT',
 'Navajo',
 'PRC',
 'PST8PDT',
 'Pacific/Apia',
 'Pacific/Auckland',
 'Pacific/Bougainville',
 'Pacific/Chatham',
 'Pacific/Chuuk',
 'Pacific/Easter',
 'Pacific/Efate',
 'Pacific/Enderbury',
 'Pacific/Fakaofo',
 'Pacific/Fiji',
 'Pacific/Funafuti',
 'Pacific/Galapagos',
 'Pacific/Gambier',
 'Pacific/Guadalcanal',
 'Pacific/Guam',
 'Pacific/Honolulu',
 'Pacific/Johnston',
 'Pacific/Kanton',
 'Pacific/Kiritimati',
 'Pacific/Kosrae',
 'Pacific/Kwajalein',
 'Pacific/Majuro',
 'Pacific/Marquesas',
 'Pacific/Midway',
 'Pacific/Nauru',
 'Pacific/Niue',
 'Pacific/Norfolk',
 'Pacific/Noumea',
 'Pacific/Pago_Pago',
 'Pacific/Palau',
 'Pacific/Pitcairn',
 'Pacific/Pohnpei',
 'Pacific/Ponape',
 'Pacific/Port_Moresby',
 'Pacific/Rarotonga',
 'Pacific/Saipan',
 'Pacific/Samoa',
 'Pacific/Tahiti',
 'Pacific/Tarawa',
 'Pacific/Tongatapu',
 'Pacific/Truk',
 'Pacific/Wake',
 'Pacific/Wallis',
 'Pacific/Yap',
 'Poland',
 'Portugal',
 'ROC',
 'ROK',
 'Singapore',
 'Turkey',
 'UCT',
 'US/Alaska',
 'US/Aleutian',
 'US/Arizona',
 'US/Central',
 'US/East-Indiana',
 'US/Eastern',
 'US/Hawaii',
 'US/Indiana-Starke',
 'US/Michigan',
 'US/Mountain',
 'US/Pacific',
 'US/Samoa',
 'UTC',
 'Universal',
 'W-SU',
 'WET',
 'Zulu']
all_timezones = LazyList(
        tz for tz in _all_timezones_unchecked if resource_exists(tz))
        
all_timezones_set = LazySet(all_timezones)
common_timezones = \
['Africa/Abidjan',
 'Africa/Accra',
 'Africa/Addis_Ababa',
 'Africa/Algiers',
 'Africa/Asmara',
 'Africa/Bamako',
 'Africa/Bangui',
 'Africa/Banjul',
 'Africa/Bissau',
 'Africa/Blantyre',
 'Africa/Brazzaville',
 'Africa/Bujumbura',
 'Africa/Cairo',
 'Africa/Casablanca',
 'Africa/Ceuta',
 'Africa/Conakry',
 'Africa/Dakar',
 'Africa/Dar_es_Salaam',
 'Africa/Djibouti',
 'Africa/Douala',
 'Africa/El_Aaiun',
 'Africa/Freetown',
 'Africa/Gaborone',
 'Africa/Harare',
 'Africa/Johannesburg',
 'Africa/Juba',
 'Africa/Kampala',
 'Africa/Khartoum',
 'Africa/Kigali',
 'Africa/Kinshasa',
 'Africa/Lagos',
 'Africa/Libreville',
 'Africa/Lome',
 'Africa/Luanda',
 'Africa/Lubumbashi',
 'Africa/Lusaka',
 'Africa/Malabo',
 'Africa/Maputo',
 'Africa/Maseru',
 'Africa/Mbabane',
 'Africa/Mogadishu',
 'Africa/Monrovia',
 'Africa/Nairobi',
 'Africa/Ndjamena',
 'Africa/Niamey',
 'Africa/Nouakchott',
 'Africa/Ouagadougou',
 'Africa/Porto-Novo',
 'Africa/Sao_Tome',
 'Africa/Tripoli',
 'Africa/Tunis',
 'Africa/Windhoek',
 'America/Adak',
 'America/Anchorage',
 'America/Anguilla',
 'America/Antigua',
 'America/Araguaina',
 'America/Argentina/Buenos_Aires',
 'America/Argentina/Catamarca',
 'America/Argentina/Cordoba',
 'America/Argentina/Jujuy',
 'America/Argentina/La_Rioja',
 'America/Argentina/Mendoza',
 'America/Argentina/Rio_Gallegos',
 'America/Argentina/Salta',
 'America/Argentina/San_Juan',
 'America/Argentina/San_Luis',
 'America/Argentina/Tucuman',
 'America/Argentina/Ushuaia',
 'America/Aruba',
 'America/Asuncion',
 'America/Atikokan',
 'America/Bahia',
 'America/Bahia_Banderas',
 'America/Barbados',
 'America/Belem',
 'America/Belize',
 'America/Blanc-Sablon',
 'America/Boa_Vista',
 'America/Bogota',
 'America/Boise',
 'America/Cambridge_Bay',
 'America/Campo_Grande',
 'America/Cancun',
 'America/Caracas',
 'America/Cayenne',
 'America/Cayman',
 'America/Chicago',
 'America/Chihuahua',
 'America/Ciudad_Juarez',
 'America/Costa_Rica',
 'America/Creston',
 'America/Cuiaba',
 'America/Curacao',
 'America/Danmarkshavn',
 'America/Dawson',
 'America/Dawson_Creek',
 'America/Denver',
 'America/Detroit',
 'America/Dominica',
 'America/Edmonton',
 'America/Eirunepe',
 'America/El_Salvador',
 'America/Fort_Nelson',
 'America/Fortaleza',
 'America/Glace_Bay',
 'America/Goose_Bay',
 'America/Grand_Turk',
 'America/Grenada',
 'America/Guadeloupe',
 'America/Guatemala',
 'America/Guayaquil',
 'America/Guyana',
 'America/Halifax',
 'America/Havana',
 'America/Hermosillo',
 'America/Indiana/Indianapolis',
 'America/Indiana/Knox',
 'America/Indiana/Marengo',
 'America/Indiana/Petersburg',
 'America/Indiana/Tell_City',
 'America/Indiana/Vevay',
 'America/Indiana/Vincennes',
 'America/Indiana/Winamac',
 'America/Inuvik',
 'America/Iqaluit',
 'America/Jamaica',
 'America/Juneau',
 'America/Kentucky/Louisville',
 'America/Kentucky/Monticello',
 'America/Kralendijk',
 'America/La_Paz',
 'America/Lima',
 'America/Los_Angeles',
 'America/Lower_Princes',
 'America/Maceio',
 'America/Managua',
 'America/Manaus',
 'America/Marigot',
 'America/Martinique',
 'America/Matamoros',
 'America/Mazatlan',
 'America/Menominee',
 'America/Merida',
 'America/Metlakatla',
 'America/Mexico_City',
 'America/Miquelon',
 'America/Moncton',
 'America/Monterrey',
 'America/Montevideo',
 'America/Montserrat',
 'America/Nassau',
 'America/New_York',
 'America/Nome',
 'America/Noronha',
 'America/North_Dakota/Beulah',
 'America/North_Dakota/Center',
 'America/North_Dakota/New_Salem',
 'America/Nuuk',
 'America/Ojinaga',
 'America/Panama',
 'America/Paramaribo',
 'America/Phoenix',
 'America/Port-au-Prince',
 'America/Port_of_Spain',
 'America/Porto_Velho',
 'America/Puerto_Rico',
 'America/Punta_Arenas',
 'America/Rankin_Inlet',
 'America/Recife',
 'America/Regina',
 'America/Resolute',
 'America/Rio_Branco',
 'America/Santarem',
 'America/Santiago',
 'America/Santo_Domingo',
 'America/Sao_Paulo',
 'America/Scoresbysund',
 'America/Sitka',
 'America/St_Barthelemy',
 'America/St_Johns',
 'America/St_Kitts',
 'America/St_Lucia',
 'America/St_Thomas',
 'America/St_Vincent',
 'America/Swift_Current',
 'America/Tegucigalpa',
 'America/Thule',
 'America/Tijuana',
 'America/Toronto',
 'America/Tortola',
 'America/Vancouver',
 'America/Whitehorse',
 'America/Winnipeg',
 'America/Yakutat',
 'Antarctica/Casey',
 'Antarctica/Davis',
 'Antarctica/DumontDUrville',
 'Antarctica/Macquarie',
 'Antarctica/Mawson',
 'Antarctica/McMurdo',
 'Antarctica/Palmer',
 'Antarctica/Rothera',
 'Antarctica/Syowa',
 'Antarctica/Troll',
 'Antarctica/Vostok',
 'Arctic/Longyearbyen',
 'Asia/Aden',
 'Asia/Almaty',
 'Asia/Amman',
 'Asia/Anadyr',
 'Asia/Aqtau',
 'Asia/Aqtobe',
 'Asia/Ashgabat',
 'Asia/Atyrau',
 'Asia/Baghdad',
 'Asia/Bahrain',
 'Asia/Baku',
 'Asia/Bangkok',
 'Asia/Barnaul',
 'Asia/Beirut',
 'Asia/Bishkek',
 'Asia/Brunei',
 'Asia/Chita',
 'Asia/Choibalsan',
 'Asia/Colombo',
 'Asia/Damascus',
 'Asia/Dhaka',
 'Asia/Dili',
 'Asia/Dubai',
 'Asia/Dushanbe',
 'Asia/Famagusta',
 'Asia/Gaza',
 'Asia/Hebron',
 'Asia/Ho_Chi_Minh',
 'Asia/Hong_Kong',
 'Asia/Hovd',
 'Asia/Irkutsk',
 'Asia/Jakarta',
 'Asia/Jayapura',
 'Asia/Jerusalem',
 'Asia/Kabul',
 'Asia/Kamchatka',
 'Asia/Karachi',
 'Asia/Kathmandu',
 'Asia/Khandyga',
 'Asia/Kolkata',
 'Asia/Krasnoyarsk',
 'Asia/Kuala_Lumpur',
 'Asia/Kuching',
 'Asia/Kuwait',
 'Asia/Macau',
 'Asia/Magadan',
 'Asia/Makassar',
 'Asia/Manila',
 'Asia/Muscat',
 'Asia/Nicosia',
 'Asia/Novokuznetsk',
 'Asia/Novosibirsk',
 'Asia/Omsk',
 'Asia/Oral',
 'Asia/Phnom_Penh',
 'Asia/Pontianak',
 'Asia/Pyongyang',
 'Asia/Qatar',
 'Asia/Qostanay',
 'Asia/Qyzylorda',
 'Asia/Riyadh',
 'Asia/Sakhalin',
 'Asia/Samarkand',
 'Asia/Seoul',
 'Asia/Shanghai',
 'Asia/Singapore',
 'Asia/Srednekolymsk',
 'Asia/Taipei',
 'Asia/Tashkent',
 'Asia/Tbilisi',
 'Asia/Tehran',
 'Asia/Thimphu',
 'Asia/Tokyo',
 'Asia/Tomsk',
 'Asia/Ulaanbaatar',
 'Asia/Urumqi',
 'Asia/Ust-Nera',
 'Asia/Vientiane',
 'Asia/Vladivostok',
 'Asia/Yakutsk',
 'Asia/Yangon',
 'Asia/Yekaterinburg',
 'Asia/Yerevan',
 'Atlantic/Azores',
 'Atlantic/Bermuda',
 'Atlantic/Canary',
 'Atlantic/Cape_Verde',
 'Atlantic/Faroe',
 'Atlantic/Madeira',
 'Atlantic/Reykjavik',
 'Atlantic/South_Georgia',
 'Atlantic/St_Helena',
 'Atlantic/Stanley',
 'Australia/Adelaide',
 'Australia/Brisbane',
 'Australia/Broken_Hill',
 'Australia/Darwin',
 'Australia/Eucla',
 'Australia/Hobart',
 'Australia/Lindeman',
 'Australia/Lord_Howe',
 'Australia/Melbourne',
 'Australia/Perth',
 'Australia/Sydney',
 'Canada/Atlantic',
 'Canada/Central',
 'Canada/Eastern',
 'Canada/Mountain',
 'Canada/Newfoundland',
 'Canada/Pacific',
 'Europe/Amsterdam',
 'Europe/Andorra',
 'Europe/Astrakhan',
 'Europe/Athens',
 'Europe/Belgrade',
 'Europe/Berlin',
 'Europe/Bratislava',
 'Europe/Brussels',
 'Europe/Bucharest',
 'Europe/Budapest',
 'Europe/Busingen',
 'Europe/Chisinau',
 'Europe/Copenhagen',
 'Europe/Dublin',
 'Europe/Gibraltar',
 'Europe/Guernsey',
 'Europe/Helsinki',
 'Europe/Isle_of_Man',
 'Europe/Istanbul',
 'Europe/Jersey',
 'Europe/Kaliningrad',
 'Europe/Kirov',
 'Europe/Kyiv',
 'Europe/Lisbon',
 'Europe/Ljubljana',
 'Europe/London',
 'Europe/Luxembourg',
 'Europe/Madrid',
 'Europe/Malta',
 'Europe/Mariehamn',
 'Europe/Minsk',
 'Europe/Monaco',
 'Europe/Moscow',
 'Europe/Oslo',
 'Europe/Paris',
 'Europe/Podgorica',
 'Europe/Prague',
 'Europe/Riga',
 'Europe/Rome',
 'Europe/Samara',
 'Europe/San_Marino',
 'Europe/Sarajevo',
 'Europe/Saratov',
 'Europe/Simferopol',
 'Europe/Skopje',
 'Europe/Sofia',
 'Europe/Stockholm',
 'Europe/Tallinn',
 'Europe/Tirane',
 'Europe/Ulyanovsk',
 'Europe/Vaduz',
 'Europe/Vatican',
 'Europe/Vienna',
 'Europe/Vilnius',
 'Europe/Volgograd',
 'Europe/Warsaw',
 'Europe/Zagreb',
 'Europe/Zurich',
 'GMT',
 'Indian/Antananarivo',
 'Indian/Chagos',
 'Indian/Christmas',
 'Indian/Cocos',
 'Indian/Comoro',
 'Indian/Kerguelen',
 'Indian/Mahe',
 'Indian/Maldives',
 'Indian/Mauritius',
 'Indian/Mayotte',
 'Indian/Reunion',
 'Pacific/Apia',
 'Pacific/Auckland',
 'Pacific/Bougainville',
 'Pacific/Chatham',
 'Pacific/Chuuk',
 'Pacific/Easter',
 'Pacific/Efate',
 'Pacific/Fakaofo',
 'Pacific/Fiji',
 'Pacific/Funafuti',
 'Pacific/Galapagos',
 'Pacific/Gambier',
 'Pacific/Guadalcanal',
 'Pacific/Guam',
 'Pacific/Honolulu',
 'Pacific/Kanton',
 'Pacific/Kiritimati',
 'Pacific/Kosrae',
 'Pacific/Kwajalein',
 'Pacific/Majuro',
 'Pacific/Marquesas',
 'Pacific/Midway',
 'Pacific/Nauru',
 'Pacific/Niue',
 'Pacific/Norfolk',
 'Pacific/Noumea',
 'Pacific/Pago_Pago',
 'Pacific/Palau',
 'Pacific/Pitcairn',
 'Pacific/Pohnpei',
 'Pacific/Port_Moresby',
 'Pacific/Rarotonga',
 'Pacific/Saipan',
 'Pacific/Tahiti',
 'Pacific/Tarawa',
 'Pacific/Tongatapu',
 'Pacific/Wake',
 'Pacific/Wallis',
 'US/Alaska',
 'US/Arizona',
 'US/Central',
 'US/Eastern',
 'US/Hawaii',
 'US/Mountain',
 'US/Pacific',
 'UTC']
common_timezones = LazyList(
            tz for tz in common_timezones if tz in all_timezones)
        
common_timezones_set = LazySet(common_timezones)

back/venvmsg/Lib/site-packages/pytz/__init__.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
import abc
from typing import BinaryIO, Iterable, Text

from ._compat import runtime_checkable, Protocol


class ResourceReader(metaclass=abc.ABCMeta):
    """Abstract base class for loaders to provide resource reading support."""

    @abc.abstractmethod
    def open_resource(self, resource: Text) -> BinaryIO:
        """Return an opened, file-like object for binary reading.

        The 'resource' argument is expected to represent only a file name.
        If the resource cannot be found, FileNotFoundError is raised.
        """
        # This deliberately raises FileNotFoundError instead of
        # NotImplementedError so that if this method is accidentally called,
        # it'll still do the right thing.
        raise FileNotFoundError

    @abc.abstractmethod
    def resource_path(self, resource: Text) -> Text:
        """Return the file system path to the specified resource.

        The 'resource' argument is expected to represent only a file name.
        If the resource does not exist on the file system, raise
        FileNotFoundError.
        """
        # This deliberately raises FileNotFoundError instead of
        # NotImplementedError so that if this method is accidentally called,
        # it'll still do the right thing.
        raise FileNotFoundError

    @abc.abstractmethod
    def is_resource(self, path: Text) -> bool:
        """Return True if the named 'path' is a resource.

        Files are resources, directories are not.
        """
        raise FileNotFoundError

    @abc.abstractmethod
    def contents(self) -> Iterable[str]:
        """Return an iterable of entries in `package`."""
        raise FileNotFoundError


@runtime_checkable
class Traversable(Protocol):
    """
    An object with a subset of pathlib.Path methods suitable for
    traversing directories and opening files.
    """

    @abc.abstractmethod
    def iterdir(self):
        """
        Yield Traversable objects in self
        """

    def read_bytes(self):
        """
        Read contents of self as bytes
        """
        with self.open('rb') as strm:
            return strm.read()

    def read_text(self, encoding=None):
        """
        Read contents of self as text
        """
        with self.open(encoding=encoding) as strm:
            return strm.read()

    @abc.abstractmethod
    def is_dir(self) -> bool:
        """
        Return True if self is a directory
        """

    @abc.abstractmethod
    def is_file(self) -> bool:
        """
        Return True if self is a file
        """

    @abc.abstractmethod
    def joinpath(self, child):
        """
        Return Traversable child in self
        """

    def __truediv__(self, child):
        """
        Return Traversable child in self
        """
        return self.joinpath(child)

    @abc.abstractmethod
    def open(self, mode='r', *args, **kwargs):
        """
        mode may be 'r' or 'rb' to open as text or binary. Return a handle
        suitable for reading (same as pathlib.Path.open).

        When opening as text, accepts encoding parameters such as those
        accepted by io.TextIOWrapper.
        """

    @abc.abstractproperty
    def name(self) -> str:
        """
        The base name of this object without any parent references.
        """


class TraversableResources(ResourceReader):
    """
    The required interface for providing traversable
    resources.
    """

    @abc.abstractmethod
    def files(self):
        """Return a Traversable object for the loaded package."""

    def open_resource(self, resource):
        return self.files().joinpath(resource).open('rb')

    def resource_path(self, resource):
        raise FileNotFoundError(resource)

    def is_resource(self, path):
        return self.files().joinpath(path).is_file()

    def contents(self):
        return (item.name for item in self.files().iterdir())

back/venvmsg/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
# ISC License
#
# Copyright (c) 2018-2021, Andrea Giammarchi, @WebReflection
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.

import json as _json

class _Known:
    def __init__(self):
        self.key = []
        self.value = []

class _String:
    def __init__(self, value):
        self.value = value


def _array_keys(value):
    keys = []
    i = 0
    for _ in value:
        keys.append(i)
        i += 1
    return keys

def _object_keys(value):
    keys = []
    for key in value:
        keys.append(key)
    return keys

def _is_array(value):
    return isinstance(value, list) or isinstance(value, tuple)

def _is_object(value):
    return isinstance(value, dict)

def _is_string(value):
    return isinstance(value, str)

def _index(known, input, value):
    input.append(value)
    index = str(len(input) - 1)
    known.key.append(value)
    known.value.append(index)
    return index

def _loop(keys, input, known, output):
    for key in keys:
        value = output[key]
        if isinstance(value, _String):
            _ref(key, input[int(value.value)], input, known, output)

    return output

def _ref(key, value, input, known, output):
    if _is_array(value) and not value in known:
        known.append(value)
        value = _loop(_array_keys(value), input, known, value)
    elif _is_object(value) and not value in known:
        known.append(value)
        value = _loop(_object_keys(value), input, known, value)

    output[key] = value

def _relate(known, input, value):
    if _is_string(value) or _is_array(value) or _is_object(value):
        try:
            return known.value[known.key.index(value)]
        except:
            return _index(known, input, value)

    return value

def _transform(known, input, value):
    if _is_array(value):
        output = []
        for val in value:
            output.append(_relate(known, input, val))
        return output

    if _is_object(value):
        obj = {}
        for key in value:
            obj[key] = _relate(known, input, value[key])
        return obj

    return value

def _wrap(value):
    if _is_string(value):
        return _String(value)

    if _is_array(value):
        i = 0
        for val in value:
            value[i] = _wrap(val)
            i += 1

    elif _is_object(value):
        for key in value:
            value[key] = _wrap(value[key])

    return value

def parse(value, *args, **kwargs):
    json = _json.loads(value, *args, **kwargs)
    wrapped = []
    for value in json:
        wrapped.append(_wrap(value))

    input = []
    for value in wrapped:
        if isinstance(value, _String):
            input.append(value.value)
        else:
            input.append(value)

    value = input[0]

    if _is_array(value):
        return _loop(_array_keys(value), input, [value], value)

    if _is_object(value):
        return _loop(_object_keys(value), input, [value], value)

    return value


def stringify(value, *args, **kwargs):
    known = _Known()
    input = []
    output = []
    i = int(_index(known, input, value))
    while i < len(input):
        output.append(_transform(known, input, input[i]))
        i += 1
    return _json.dumps(output, *args, **kwargs)

node_modules/flatted/python/flatted.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8
from flatted import stringify as _stringify, parse

def stringify(value):
    return _stringify(value, separators=(',', ':'))

assert stringify([None, None]) == '[[null,null]]'

a = []
o = {}

assert stringify(a) == '[[]]'
assert stringify(o) == '[{}]'

a.append(a)
o['o'] = o

assert stringify(a) == '[["0"]]'
assert stringify(o) == '[{"o":"0"}]'

b = parse(stringify(a))
assert isinstance(b, list) and b[0] == b

a.append(1)
a.append('two')
a.append(True)
o['one'] = 1
o['two'] = 'two'
o['three'] = True

assert stringify(a) == '[["0",1,"1",true],"two"]'
assert stringify(o) == '[{"o":"0","one":1,"two":"1","three":true},"two"]'

a.append(o)
o['a'] = a

assert stringify(a) == '[["0",1,"1",true,"2"],"two",{"o":"2","one":1,"two":"1","three":true,"a":"0"}]'
assert stringify(o) == '[{"o":"0","one":1,"two":"1","three":true,"a":"2"},"two",["2",1,"1",true,"0"]]'

a.append({'test': 'OK'})
a.append([1, 2, 3])

o['test'] = {'test': 'OK'}
o['array'] = [1, 2, 3]

assert stringify(a) == '[["0",1,"1",true,"2","3","4"],"two",{"o":"2","one":1,"two":"1","three":true,"a":"0","test":"3","array":"4"},{"test":"5"},[1,2,3],"OK"]'
assert stringify(o) == '[{"o":"0","one":1,"two":"1","three":true,"a":"2","test":"3","array":"4"},"two",["2",1,"1",true,"0","3","4"],{"test":"5"},[1,2,3],"OK"]'

a2 = parse(stringify(a));
o2 = parse(stringify(o));

assert a2[0] == a2
assert o2['o'] == o2

assert a2[1] == 1 and a2[2] == 'two' and a2[3] == True and isinstance(a2[4], dict)
assert a2[4] == a2[4]['o'] and a2 == a2[4]['o']['a']

str = parse('[{"prop":"1","a":"2","b":"3"},{"value":123},["4","5"],{"e":"6","t":"7","p":4},{},{"b":"8"},"f",{"a":"9"},["10"],"sup",{"a":1,"d":2,"c":"7","z":"11","h":1},{"g":2,"a":"7","b":"12","f":6},{"r":4,"u":"7","c":5}]')
assert str['b']['t']['a'] == 'sup' and str['a'][1]['b'][0]['c'] == str['b']['t']

oo = parse('[{"a":"1","b":"0","c":"2"},{"aa":"3"},{"ca":"4","cb":"5","cc":"6","cd":"7","ce":"8","cf":"9"},{"aaa":"10"},{"caa":"4"},{"cba":"5"},{"cca":"2"},{"cda":"4"},"value2","value3","value1"]');
assert oo['a']['aa']['aaa'] == 'value1' and oo == oo['b'] and oo['c']['ca']['caa'] == oo['c']['ca']

print('OK')

node_modules/flatted/python/test.py
CSID-DGU/2023-2-OSSProj-MSG_UP-8